Commit ffa4cc2c authored by 957dd's avatar 957dd

加入了大窝船,网络连接加入了其他域名

parent 2267989c
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
project(car project(car
VERSION 1.2.19 VERSION 1.3.11
LANGUAGES C LANGUAGES C
) )
......
...@@ -99,9 +99,11 @@ int hash_insert_init(HashTable_t *HashTable_t) { ...@@ -99,9 +99,11 @@ int hash_insert_init(HashTable_t *HashTable_t) {
insert(HashTable_t, "0206", TANK_0206); insert(HashTable_t, "0206", TANK_0206);
insert(HashTable_t, "0207", TANK_0207); insert(HashTable_t, "0207", TANK_0207);
insert(HashTable_t, "0301", SHIP_0301); insert(HashTable_t, "0301", SHIP_0301);
insert(HashTable_t, "0302", SHIP_0302);
insert(HashTable_t, "0401", PAO_0401); insert(HashTable_t, "0401", PAO_0401);
insert(HashTable_t, "0404", PAO_0404); insert(HashTable_t, "0404", PAO_0404);
insert(HashTable_t, "0405", PAO_0405); insert(HashTable_t, "0405", PAO_0405);
insert(HashTable_t, "0406", PAO_0406);
insert(HashTable_t, "0403", PGGPS_0403); insert(HashTable_t, "0403", PGGPS_0403);
insert(HashTable_t, "0501", ROBOT_DOG0501); insert(HashTable_t, "0501", ROBOT_DOG0501);
} }
...@@ -140,6 +142,9 @@ int device_judg(CodeEnum_t code,char *sub_str) { ...@@ -140,6 +142,9 @@ int device_judg(CodeEnum_t code,char *sub_str) {
}else if(code == SHIP_0301) { }else if(code == SHIP_0301) {
device_init(DEVICE_SHIP0301); device_init(DEVICE_SHIP0301);
my_zlog_info("使用34号船,型号%s",sub_str); my_zlog_info("使用34号船,型号%s",sub_str);
}else if(code == SHIP_0302) {
device_init(DEVICE_SHIP0302);
my_zlog_info("使用ship0302船,型号%s",sub_str);
}else if(code ==PAO_0401) { }else if(code ==PAO_0401) {
device_init(DEVICE_PAO_PTZ0401); device_init(DEVICE_PAO_PTZ0401);
my_zlog_info("使用型号%s",sub_str); my_zlog_info("使用型号%s",sub_str);
...@@ -149,6 +154,9 @@ int device_judg(CodeEnum_t code,char *sub_str) { ...@@ -149,6 +154,9 @@ int device_judg(CodeEnum_t code,char *sub_str) {
}else if(code ==PAO_0405) { }else if(code ==PAO_0405) {
device_init(DEVICE_PAO_PTZ0405); device_init(DEVICE_PAO_PTZ0405);
my_zlog_info("使用云台0405,型号%s",sub_str); my_zlog_info("使用云台0405,型号%s",sub_str);
}else if(code ==PAO_0406) {
device_init(DEVICE_PAO_PTZ0406);
my_zlog_info("使用水枪炮台0406,型号%s",sub_str);
}else if(code ==PGGPS_0403) { }else if(code ==PGGPS_0403) {
device_init(DEVICE_PG_GPS0403); device_init(DEVICE_PG_GPS0403);
my_zlog_info("使用定位设备%s",sub_str); my_zlog_info("使用定位设备%s",sub_str);
......
...@@ -24,7 +24,9 @@ typedef enum { ...@@ -24,7 +24,9 @@ typedef enum {
PAO_0404, PAO_0404,
PAO_0405, PAO_0405,
PGGPS_0403, PGGPS_0403,
ROBOT_DOG0501 ROBOT_DOG0501,
SHIP_0302,
PAO_0406
} CodeEnum_t; } CodeEnum_t;
//哈希健 //哈希健
......
...@@ -174,12 +174,25 @@ static void check_and_install_dependencies() { ...@@ -174,12 +174,25 @@ static void check_and_install_dependencies() {
} }
} }
// 检测是否有互联网连接 (只检查百度) // 检测是否有互联网连接(多目标探测,任意一个成功即视为有网)
static int has_internet_connection() { static int has_internet_connection() {
// -c 1: 发送1个包, -W 2: 超时2秒 static const char *targets[] = {
int ret = system("ping -c 1 -W 2 baidu.com > /dev/null 2>&1"); "baidu.com", // 国内常见目标
// ret == 0 表示 ping 成功 "google.com", // 海外常见目标
return (ret == 0); "1.1.1.1", // Cloudflare DNS(不依赖域名解析)
"8.8.8.8" // Google DNS(不依赖域名解析)
};
// -c 1: 发送 1 个包, -W 2: 超时 2 秒
for (size_t i = 0; i < sizeof(targets) / sizeof(targets[0]); i++) {
char cmd[256];
snprintf(cmd, sizeof(cmd), "ping -c 1 -W 2 %s > /dev/null 2>&1", targets[i]);
if (system(cmd) == 0) {
my_zlog_debug("网络探测成功: %s", targets[i]);
return 1; // 任意一个成功,直接返回
}
}
return 0;
} }
// 尝试连接 WiFi // 尝试连接 WiFi
...@@ -206,7 +219,7 @@ void check_and_autoconfig_wifi() { ...@@ -206,7 +219,7 @@ void check_and_autoconfig_wifi() {
} }
my_zlog_warn("无法连接互联网,进入扫码配网模式 (拍照方案)。"); my_zlog_warn("无法连接互联网,进入扫码配网模式 (拍照方案)。");
my_zlog_info("进入扫码循环,将每隔几秒提示一次..."); my_zlog_warn("进入扫码循环,将每隔几秒提示一次...");
const char *img_path = "/tmp/wifi_scan.jpg"; const char *img_path = "/tmp/wifi_scan.jpg";
time_t last_prompt_time = 0; time_t last_prompt_time = 0;
......
...@@ -785,6 +785,30 @@ drivers/devicecontrol/ship0301_control.c.s: ...@@ -785,6 +785,30 @@ drivers/devicecontrol/ship0301_control.c.s:
$(MAKE) $(MAKESILENT) -f CMakeFiles/main.dir/build.make CMakeFiles/main.dir/drivers/devicecontrol/ship0301_control.c.s $(MAKE) $(MAKESILENT) -f CMakeFiles/main.dir/build.make CMakeFiles/main.dir/drivers/devicecontrol/ship0301_control.c.s
.PHONY : drivers/devicecontrol/ship0301_control.c.s .PHONY : drivers/devicecontrol/ship0301_control.c.s
drivers/devicecontrol/ship0302_control.o: drivers/devicecontrol/ship0302_control.c.o
.PHONY : drivers/devicecontrol/ship0302_control.o
# target to build an object file
drivers/devicecontrol/ship0302_control.c.o:
$(MAKE) $(MAKESILENT) -f CMakeFiles/main.dir/build.make CMakeFiles/main.dir/drivers/devicecontrol/ship0302_control.c.o
.PHONY : drivers/devicecontrol/ship0302_control.c.o
drivers/devicecontrol/ship0302_control.i: drivers/devicecontrol/ship0302_control.c.i
.PHONY : drivers/devicecontrol/ship0302_control.i
# target to preprocess a source file
drivers/devicecontrol/ship0302_control.c.i:
$(MAKE) $(MAKESILENT) -f CMakeFiles/main.dir/build.make CMakeFiles/main.dir/drivers/devicecontrol/ship0302_control.c.i
.PHONY : drivers/devicecontrol/ship0302_control.c.i
drivers/devicecontrol/ship0302_control.s: drivers/devicecontrol/ship0302_control.c.s
.PHONY : drivers/devicecontrol/ship0302_control.s
# target to generate assembly for a file
drivers/devicecontrol/ship0302_control.c.s:
$(MAKE) $(MAKESILENT) -f CMakeFiles/main.dir/build.make CMakeFiles/main.dir/drivers/devicecontrol/ship0302_control.c.s
.PHONY : drivers/devicecontrol/ship0302_control.c.s
drivers/devicecontrol/steering_control.o: drivers/devicecontrol/steering_control.c.o drivers/devicecontrol/steering_control.o: drivers/devicecontrol/steering_control.c.o
.PHONY : drivers/devicecontrol/steering_control.o .PHONY : drivers/devicecontrol/steering_control.o
...@@ -2186,6 +2210,9 @@ help: ...@@ -2186,6 +2210,9 @@ help:
@echo "... drivers/devicecontrol/ship0301_control.o" @echo "... drivers/devicecontrol/ship0301_control.o"
@echo "... drivers/devicecontrol/ship0301_control.i" @echo "... drivers/devicecontrol/ship0301_control.i"
@echo "... drivers/devicecontrol/ship0301_control.s" @echo "... drivers/devicecontrol/ship0301_control.s"
@echo "... drivers/devicecontrol/ship0302_control.o"
@echo "... drivers/devicecontrol/ship0302_control.i"
@echo "... drivers/devicecontrol/ship0302_control.s"
@echo "... drivers/devicecontrol/steering_control.o" @echo "... drivers/devicecontrol/steering_control.o"
@echo "... drivers/devicecontrol/steering_control.i" @echo "... drivers/devicecontrol/steering_control.i"
@echo "... drivers/devicecontrol/steering_control.s" @echo "... drivers/devicecontrol/steering_control.s"
......
#define PROJECT_VERSION_MAJOR 1 #define PROJECT_VERSION_MAJOR 1
#define PROJECT_VERSION_MINOR 2 #define PROJECT_VERSION_MINOR 3
#define PROJECT_VERSION_PATCH 19 #define PROJECT_VERSION_PATCH 11
#define GIT_HASH "" #define GIT_HASH ""
#define BUILD_TIMESTAMP "" #define BUILD_TIMESTAMP ""
#define BUILD_USER "" #define BUILD_USER ""
No preview for this file type
...@@ -6,9 +6,9 @@ CMAKE_PROGRESS_5 = ...@@ -6,9 +6,9 @@ CMAKE_PROGRESS_5 =
CMAKE_PROGRESS_6 = CMAKE_PROGRESS_6 =
CMAKE_PROGRESS_7 = 83 CMAKE_PROGRESS_7 = 83
CMAKE_PROGRESS_8 = CMAKE_PROGRESS_8 =
CMAKE_PROGRESS_9 = CMAKE_PROGRESS_9 = 84
CMAKE_PROGRESS_10 = 84 CMAKE_PROGRESS_10 =
CMAKE_PROGRESS_11 = CMAKE_PROGRESS_11 =
CMAKE_PROGRESS_12 = CMAKE_PROGRESS_12 = 85
CMAKE_PROGRESS_13 = 85 CMAKE_PROGRESS_13 =
...@@ -41,5 +41,5 @@ CMAKE_PROGRESS_40 = ...@@ -41,5 +41,5 @@ CMAKE_PROGRESS_40 =
CMAKE_PROGRESS_41 = CMAKE_PROGRESS_41 =
CMAKE_PROGRESS_42 = 14 CMAKE_PROGRESS_42 = 14
CMAKE_PROGRESS_43 = CMAKE_PROGRESS_43 =
CMAKE_PROGRESS_44 = 15 CMAKE_PROGRESS_44 =
CMAKE_PROGRESS_1 = CMAKE_PROGRESS_1 = 15
CMAKE_PROGRESS_2 = CMAKE_PROGRESS_2 =
CMAKE_PROGRESS_3 = 16 CMAKE_PROGRESS_3 =
CMAKE_PROGRESS_4 = CMAKE_PROGRESS_4 = 16
CMAKE_PROGRESS_5 = CMAKE_PROGRESS_5 =
CMAKE_PROGRESS_6 = 17 CMAKE_PROGRESS_6 = 17
CMAKE_PROGRESS_7 = CMAKE_PROGRESS_7 =
...@@ -38,8 +38,8 @@ CMAKE_PROGRESS_37 = ...@@ -38,8 +38,8 @@ CMAKE_PROGRESS_37 =
CMAKE_PROGRESS_38 = CMAKE_PROGRESS_38 =
CMAKE_PROGRESS_39 = 28 CMAKE_PROGRESS_39 = 28
CMAKE_PROGRESS_40 = CMAKE_PROGRESS_40 =
CMAKE_PROGRESS_41 = 29 CMAKE_PROGRESS_41 =
CMAKE_PROGRESS_42 = CMAKE_PROGRESS_42 = 29
CMAKE_PROGRESS_43 = CMAKE_PROGRESS_43 =
CMAKE_PROGRESS_44 = 30 CMAKE_PROGRESS_44 =
CMAKE_PROGRESS_1 = CMAKE_PROGRESS_1 =
CMAKE_PROGRESS_2 = CMAKE_PROGRESS_2 = 56
CMAKE_PROGRESS_3 = 56 CMAKE_PROGRESS_3 =
CMAKE_PROGRESS_4 = CMAKE_PROGRESS_4 =
CMAKE_PROGRESS_5 = CMAKE_PROGRESS_5 = 57
CMAKE_PROGRESS_6 = 57 CMAKE_PROGRESS_6 =
CMAKE_PROGRESS_7 = CMAKE_PROGRESS_7 =
CMAKE_PROGRESS_8 = 58 CMAKE_PROGRESS_8 = 58
CMAKE_PROGRESS_9 = CMAKE_PROGRESS_9 =
...@@ -31,20 +31,20 @@ CMAKE_PROGRESS_30 = ...@@ -31,20 +31,20 @@ CMAKE_PROGRESS_30 =
CMAKE_PROGRESS_31 = CMAKE_PROGRESS_31 =
CMAKE_PROGRESS_32 = 66 CMAKE_PROGRESS_32 = 66
CMAKE_PROGRESS_33 = CMAKE_PROGRESS_33 =
CMAKE_PROGRESS_34 = CMAKE_PROGRESS_34 = 67
CMAKE_PROGRESS_35 = 67 CMAKE_PROGRESS_35 =
CMAKE_PROGRESS_36 = CMAKE_PROGRESS_36 =
CMAKE_PROGRESS_37 = CMAKE_PROGRESS_37 = 68
CMAKE_PROGRESS_38 = 68 CMAKE_PROGRESS_38 =
CMAKE_PROGRESS_39 = CMAKE_PROGRESS_39 =
CMAKE_PROGRESS_40 = CMAKE_PROGRESS_40 = 69
CMAKE_PROGRESS_41 = 69 CMAKE_PROGRESS_41 =
CMAKE_PROGRESS_42 = CMAKE_PROGRESS_42 =
CMAKE_PROGRESS_43 = CMAKE_PROGRESS_43 = 70
CMAKE_PROGRESS_44 = 70 CMAKE_PROGRESS_44 =
CMAKE_PROGRESS_45 = CMAKE_PROGRESS_45 =
CMAKE_PROGRESS_46 = CMAKE_PROGRESS_46 = 71
CMAKE_PROGRESS_47 = 71 CMAKE_PROGRESS_47 =
CMAKE_PROGRESS_48 = CMAKE_PROGRESS_48 =
CMAKE_PROGRESS_49 = 72 CMAKE_PROGRESS_49 = 72
CMAKE_PROGRESS_50 = CMAKE_PROGRESS_50 =
......
...@@ -49,6 +49,10 @@ static const device_didrive s_didrive_control_config[]={ ...@@ -49,6 +49,10 @@ static const device_didrive s_didrive_control_config[]={
.device_didrive_control = ship0301_change .device_didrive_control = ship0301_change
}, },
{ {
.device_id = DEVICE_SHIP0302,
.device_didrive_control = ship0302_change
},
{
.device_id = DEVICE_PAO_PTZ0401, .device_id = DEVICE_PAO_PTZ0401,
.device_didrive_control = ptz_driver_change .device_didrive_control = ptz_driver_change
}, },
...@@ -60,6 +64,10 @@ static const device_didrive s_didrive_control_config[]={ ...@@ -60,6 +64,10 @@ static const device_didrive s_didrive_control_config[]={
.device_id = DEVICE_PAO_PTZ0405, .device_id = DEVICE_PAO_PTZ0405,
.device_didrive_control = ptz_driver_change .device_didrive_control = ptz_driver_change
}, },
{
.device_id = DEVICE_PAO_PTZ0406,
.device_didrive_control = ptz_driver_change
},
// 结束标记 // 结束标记
{ .device_id = -1 } { .device_id = -1 }
}; };
...@@ -154,6 +162,14 @@ static const device_abnormal_close_t s_devcontrol_config[]= { ...@@ -154,6 +162,14 @@ static const device_abnormal_close_t s_devcontrol_config[]= {
}, },
{ {
.device_id = DEVICE_SHIP0302,
.device_abnormal_stop = ship0302_stop,
.device_close = NULL,
.gpio_pin_pulled=pin_all_default,
.gpio_pwm_pulled=pwm_all_default
},
{
.device_id = DEVICE_PAO_PTZ0401, .device_id = DEVICE_PAO_PTZ0401,
.device_abnormal_stop = ptz_driver_stop, .device_abnormal_stop = ptz_driver_stop,
.device_close = device_poilthread_close, .device_close = device_poilthread_close,
...@@ -178,6 +194,14 @@ static const device_abnormal_close_t s_devcontrol_config[]= { ...@@ -178,6 +194,14 @@ static const device_abnormal_close_t s_devcontrol_config[]= {
}, },
{ {
.device_id = DEVICE_PAO_PTZ0406,
.device_abnormal_stop = ptz_driver_stop,
.device_close = device_poilthread_close,
.gpio_pin_pulled=pin_all_default,
.gpio_pwm_pulled=pwm_all_default
},
{
.device_id = DEVICE_ROBOT_DOG0501, .device_id = DEVICE_ROBOT_DOG0501,
.device_abnormal_stop = car0101_middle_pwm, .device_abnormal_stop = car0101_middle_pwm,
.device_close = NULL, // TANK0206没有单独的关闭函数 .device_close = NULL, // TANK0206没有单独的关闭函数
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "tank0206_control.h" #include "tank0206_control.h"
#include "tank0207_control.h" #include "tank0207_control.h"
#include "ship0301_control.h" #include "ship0301_control.h"
#include "ship0302_control.h"
#include "pg0403_serial.h" #include "pg0403_serial.h"
#include "tank_common.h" #include "tank_common.h"
#include "common.h" #include "common.h"
...@@ -31,9 +32,11 @@ ...@@ -31,9 +32,11 @@
#define DEVICE_TANK0206 206 //可以喷水坦克 #define DEVICE_TANK0206 206 //可以喷水坦克
#define DEVICE_TANK0207 207 //可以发射水弹坦克 #define DEVICE_TANK0207 207 //可以发射水弹坦克
#define DEVICE_SHIP0301 301 // 32号船 #define DEVICE_SHIP0301 301 // 32号船
#define DEVICE_SHIP0302 302 // 与0301引脚/逻辑暂同,独立设备号
#define DEVICE_PAO_PTZ0401 401 //云台 #define DEVICE_PAO_PTZ0401 401 //云台
#define DEVICE_PAO_PTZ0404 404 //第二个烟花大云台 #define DEVICE_PAO_PTZ0404 404 //第二个烟花大云台
#define DEVICE_PAO_PTZ0405 405 //云台,仅射击pin27 #define DEVICE_PAO_PTZ0405 405 //云台,仅射击pin27
#define DEVICE_PAO_PTZ0406 406 // 水枪炮台0406,与0405逻辑分离,参数暂同
#define DEVICE_PG_GPS0403 403 //定位设备 #define DEVICE_PG_GPS0403 403 //定位设备
#define DEVICE_ROBOT_DOG0501 501 //机械狗 #define DEVICE_ROBOT_DOG0501 501 //机械狗
......
...@@ -7,11 +7,20 @@ ...@@ -7,11 +7,20 @@
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#define CONTROL_PERIOD 0.02f /* 须与 ptz_driver_task_function 里 delay_ms 一致,否则角速度积分/斜坡会偏快或偏慢 */
#define CONTROL_PERIOD 0.01f
#define STOP_DEADZONE 0.2f #define STOP_DEADZONE 0.2f
#define STEP_ANGLE 10.0f #define STEP_ANGLE 10.0f
/* 轮盘输入死区:避免 0 附近噪声;每轴独立,全 0 时保持当前角度不回中位 */ /* 轮盘输入死区:略小更易「拖一点就有反应」,过大则像没动 */
#define JOY_INPUT_DEADZONE 0.05f #define JOY_INPUT_DEADZONE 0.014f
/* 仅轮盘生效:整体最大角速度比例(调大=最快更猛) */
#define JOY_SPEED_SCALE 0.64f
/* 目标角速度低通:加速跟手 / 减速快停(WebRTC+MQTT 延迟下,松手后慢衰减会「滑很多」) */
#define JOY_RATE_LP_ATTACK 0.34f
#define JOY_RATE_LP_DECAY 0.72f
/* 轮盘角速度斜坡:加速略提高,小目标角速度更快跟上 */
#define JOY_SLEW_ACCEL_SCALE 0.62f
#define JOY_SLEW_DECEL_SCALE 2.6f
static const ptz_config_t s_ptz_configs[] = { static const ptz_config_t s_ptz_configs[] = {
{ {
...@@ -36,8 +45,17 @@ static const ptz_config_t s_ptz_configs[] = { ...@@ -36,8 +45,17 @@ static const ptz_config_t s_ptz_configs[] = {
.device_id = DEVICE_PAO_PTZ0405, .device_id = DEVICE_PAO_PTZ0405,
.pan_max_speed = 150.0f, .pan_accel = 180.0f, .pan_max_speed = 150.0f, .pan_accel = 180.0f,
.tilt_max_speed = 100.0f, .tilt_accel = 80.0f, .tilt_max_speed = 100.0f, .tilt_accel = 80.0f,
.joy_pan_max_speed = 160.0f, .joy_pan_accel = 180.0f, .joy_pan_max_speed = 75.0f, .joy_pan_accel = 110.0f,
.joy_tilt_max_speed = 120.0f, .joy_tilt_accel = 80.0f, .joy_tilt_max_speed = 52.0f, .joy_tilt_accel = 90.0f,
.pan_initial = 135.0f, .pan_min = 90.0f, .pan_max = 180.0f,
.tilt_initial = 70.0f, .tilt_min = 10.0f, .tilt_max = 110.0f,
},
{
.device_id = DEVICE_PAO_PTZ0406,
.pan_max_speed = 150.0f, .pan_accel = 180.0f,
.tilt_max_speed = 100.0f, .tilt_accel = 80.0f,
.joy_pan_max_speed = 75.0f, .joy_pan_accel = 110.0f,
.joy_tilt_max_speed = 52.0f, .joy_tilt_accel = 90.0f,
.pan_initial = 135.0f, .pan_min = 90.0f, .pan_max = 180.0f, .pan_initial = 135.0f, .pan_min = 90.0f, .pan_max = 180.0f,
.tilt_initial = 70.0f, .tilt_min = 10.0f, .tilt_max = 110.0f, .tilt_initial = 70.0f, .tilt_min = 10.0f, .tilt_max = 110.0f,
}, },
...@@ -51,6 +69,12 @@ static float s_joy_x = 0.0f; ...@@ -51,6 +69,12 @@ static float s_joy_x = 0.0f;
static float s_joy_y = 0.0f; static float s_joy_y = 0.0f;
static float s_joy_r = 0.0f; static float s_joy_r = 0.0f;
static bool s_joy_active = false; static bool s_joy_active = false;
/* 轮盘模式当前角速度(度/秒),仅 joy 分支使用;经 joy_*_accel 斜坡逼近目标 */
static float s_joy_pan_rate = 0.0f;
static float s_joy_tilt_rate = 0.0f;
/* 轮盘目标角速度低通状态,减轻手机采样抖动、速度阶跃感 */
static float s_joy_pan_tgt_lp = 0.0f;
static float s_joy_tilt_tgt_lp = 0.0f;
static float ptz_clampf(float value, float min_v, float max_v) static float ptz_clampf(float value, float min_v, float max_v)
{ {
...@@ -59,6 +83,31 @@ static float ptz_clampf(float value, float min_v, float max_v) ...@@ -59,6 +83,31 @@ static float ptz_clampf(float value, float min_v, float max_v)
return value; return value;
} }
/* 过死区后 [|dz|,1] 线性映射到 [-1,1]:拖动量与目标角速度成正比,便于「拖少一点就慢慢动」 */
static float ptz_joy_axis_eff(float raw)
{
float a = fabsf(raw);
if (a < JOY_INPUT_DEADZONE) {
return 0.0f;
}
float span = 1.0f - JOY_INPUT_DEADZONE;
if (span <= 0.0f) {
return 0.0f;
}
float t = (a - JOY_INPUT_DEADZONE) / span;
if (t > 1.0f) {
t = 1.0f;
}
/* 小行程略抬高有效量,避免「刚过死区×低速×低通」在画面上像完全没动 */
if (t > 0.0f && t < 0.22f) {
t = t * 1.28f + 0.012f;
if (t > 1.0f) {
t = 1.0f;
}
}
return (raw >= 0.0f) ? t : -t;
}
static bool ptz_parse_float_item(cJSON *obj, const char *key, float *out) static bool ptz_parse_float_item(cJSON *obj, const char *key, float *out)
{ {
cJSON *item = cJSON_GetObjectItem(obj, key); cJSON *item = cJSON_GetObjectItem(obj, key);
...@@ -94,6 +143,26 @@ static const ptz_config_t *ptz_find_config(int device_id) ...@@ -94,6 +143,26 @@ static const ptz_config_t *ptz_find_config(int device_id)
return NULL; return NULL;
} }
/* 角速度向 target_rate 逼近;|目标|比当前小时用更大步长,松手/回拉更快停 */
static float ptz_joy_rate_slew(float current, float target_rate, float max_accel, float dt,
float decel_scale)
{
bool fast_brake = fabsf(target_rate) < fabsf(current) - 0.3f;
float step = max_accel * dt * (fast_brake ? decel_scale : 1.0f);
if (current < target_rate) {
current += step;
if (current > target_rate) {
current = target_rate;
}
} else if (current > target_rate) {
current -= step;
if (current < target_rate) {
current = target_rate;
}
}
return current;
}
static float ptz_calculate_pulse_width(float angle) static float ptz_calculate_pulse_width(float angle)
{ {
if (angle < 0) angle = 0; if (angle < 0) angle = 0;
...@@ -182,6 +251,11 @@ void ptz_driver_init(void) ...@@ -182,6 +251,11 @@ void ptz_driver_init(void)
digitalWrite(6, HIGH); digitalWrite(6, HIGH);
my_zlog_info("PTZ0405 init: gpio6 set HIGH"); my_zlog_info("PTZ0405 init: gpio6 set HIGH");
} }
if (g_device_type == DEVICE_PAO_PTZ0406) {
pinMode(6, OUTPUT);
digitalWrite(6, HIGH);
my_zlog_info("PTZ0406 init: gpio6 set HIGH");
}
s_pan = (ServoAxis){ s_pan = (ServoAxis){
.angle = s_cfg->pan_initial, .target = s_cfg->pan_initial, .speed = 0.0f, .angle = s_cfg->pan_initial, .target = s_cfg->pan_initial, .speed = 0.0f,
...@@ -207,6 +281,10 @@ void ptz_driver_stop(void) ...@@ -207,6 +281,10 @@ void ptz_driver_stop(void)
s_pan.speed = 0.0f; s_pan.speed = 0.0f;
s_tilt.target = s_tilt.angle; s_tilt.target = s_tilt.angle;
s_tilt.speed = 0.0f; s_tilt.speed = 0.0f;
s_joy_pan_rate = 0.0f;
s_joy_tilt_rate = 0.0f;
s_joy_pan_tgt_lp = 0.0f;
s_joy_tilt_tgt_lp = 0.0f;
} }
void ptz_driver_change(int *buf) void ptz_driver_change(int *buf)
...@@ -263,8 +341,9 @@ void ptz_driver_set_joystick(float x, float y, float r) ...@@ -263,8 +341,9 @@ void ptz_driver_set_joystick(float x, float y, float r)
s_joy_r = ptz_clampf(r, 0.0f, 1.0f); s_joy_r = ptz_clampf(r, 0.0f, 1.0f);
s_joy_active = true; s_joy_active = true;
// 约定:r=0 表示立刻停住,但仍保持轮盘模式 // 约定:r=0 或 x/y 回中时立刻停住,但仍保持轮盘模式
if (s_joy_r <= 0.0f) { if (s_joy_r <= 0.0f ||
(fabsf(s_joy_x) < JOY_INPUT_DEADZONE && fabsf(s_joy_y) < JOY_INPUT_DEADZONE)) {
ptz_driver_stop(); ptz_driver_stop();
} }
} }
...@@ -309,29 +388,51 @@ static void ptz_driver_task(void) ...@@ -309,29 +388,51 @@ static void ptz_driver_task(void)
if (s_joy_r <= 0.0f) { if (s_joy_r <= 0.0f) {
ptz_driver_stop(); ptz_driver_stop();
} else { } else {
/* 线性角速度:rate = -stick * max_speed * r,与旧版 target 偏移符号一致 */ /* 目标角速度 = -stick * max_speed * r;经 joy_*_accel 线性斜坡到目标,再积分到角度 */
float x_eff = (fabsf(s_joy_x) < JOY_INPUT_DEADZONE) ? 0.0f : s_joy_x;
float y_eff = (fabsf(s_joy_y) < JOY_INPUT_DEADZONE) ? 0.0f : s_joy_y;
s_pan.speed = 0.0f; s_pan.speed = 0.0f;
s_tilt.speed = 0.0f; s_tilt.speed = 0.0f;
/* 角速度与 joy_pan_max_speed / joy_tilt_max_speed 线性相关;joy_*_accel 供表内单独配置,当前轮盘为直接跟手未做加减速 */ float x_curve = ptz_joy_axis_eff(s_joy_x);
if (x_eff != 0.0f || y_eff != 0.0f) { float y_curve = ptz_joy_axis_eff(s_joy_y);
float pan_rate = -x_eff * s_cfg->joy_pan_max_speed * s_joy_r;
float tilt_rate = -y_eff * s_cfg->joy_tilt_max_speed * s_joy_r; if (x_curve == 0.0f && y_curve == 0.0f) {
s_pan.angle += pan_rate * CONTROL_PERIOD; ptz_driver_stop();
s_tilt.angle += tilt_rate * CONTROL_PERIOD; } else {
float pan_target_rate = -x_curve * s_cfg->joy_pan_max_speed * s_joy_r * JOY_SPEED_SCALE;
float tilt_target_rate = -y_curve * s_cfg->joy_tilt_max_speed * s_joy_r * JOY_SPEED_SCALE;
float ap = (fabsf(pan_target_rate) < fabsf(s_joy_pan_tgt_lp) - 0.05f)
? JOY_RATE_LP_DECAY
: JOY_RATE_LP_ATTACK;
float at = (fabsf(tilt_target_rate) < fabsf(s_joy_tilt_tgt_lp) - 0.05f)
? JOY_RATE_LP_DECAY
: JOY_RATE_LP_ATTACK;
s_joy_pan_tgt_lp += ap * (pan_target_rate - s_joy_pan_tgt_lp);
s_joy_tilt_tgt_lp += at * (tilt_target_rate - s_joy_tilt_tgt_lp);
float a_pan = s_cfg->joy_pan_accel * JOY_SLEW_ACCEL_SCALE;
float a_tilt = s_cfg->joy_tilt_accel * JOY_SLEW_ACCEL_SCALE;
s_joy_pan_rate = ptz_joy_rate_slew(s_joy_pan_rate, s_joy_pan_tgt_lp, a_pan, CONTROL_PERIOD,
JOY_SLEW_DECEL_SCALE);
s_joy_tilt_rate = ptz_joy_rate_slew(s_joy_tilt_rate, s_joy_tilt_tgt_lp, a_tilt, CONTROL_PERIOD,
JOY_SLEW_DECEL_SCALE);
s_pan.angle += s_joy_pan_rate * CONTROL_PERIOD;
s_tilt.angle += s_joy_tilt_rate * CONTROL_PERIOD;
s_pan.angle = ptz_clampf(s_pan.angle, s_cfg->pan_min, s_cfg->pan_max); s_pan.angle = ptz_clampf(s_pan.angle, s_cfg->pan_min, s_cfg->pan_max);
s_tilt.angle = ptz_clampf(s_tilt.angle, s_cfg->tilt_min, s_cfg->tilt_max); s_tilt.angle = ptz_clampf(s_tilt.angle, s_cfg->tilt_min, s_cfg->tilt_max);
}
s_pan.target = s_pan.angle; s_pan.target = s_pan.angle;
s_tilt.target = s_tilt.angle; s_tilt.target = s_tilt.angle;
} }
} }
}
if (!joy_handled) { if (!joy_handled) {
s_joy_pan_rate = 0.0f;
s_joy_tilt_rate = 0.0f;
s_joy_pan_tgt_lp = 0.0f;
s_joy_tilt_tgt_lp = 0.0f;
s_pan.max_speed_cfg = s_cfg->pan_max_speed; s_pan.max_speed_cfg = s_cfg->pan_max_speed;
s_tilt.max_speed_cfg = s_cfg->tilt_max_speed; s_tilt.max_speed_cfg = s_cfg->tilt_max_speed;
ptz_servo_update(&s_pan); ptz_servo_update(&s_pan);
...@@ -347,6 +448,6 @@ void ptz_driver_task_function(void *arg) ...@@ -347,6 +448,6 @@ void ptz_driver_task_function(void *arg)
my_zlog_info("PTZ driver task start (device %d)", g_device_type); my_zlog_info("PTZ driver task start (device %d)", g_device_type);
while (1) { while (1) {
ptz_driver_task(); ptz_driver_task();
delay_ms(20); delay_ms(10);
} }
} }
...@@ -9,7 +9,7 @@ void ship0301_stop() { ...@@ -9,7 +9,7 @@ void ship0301_stop() {
} }
void ship0301_mode_lift_flont(int gval) { void ship0301_mode_lift_flont(int gval) {
int b=1; int b=6;
if (gval < 50) { if (gval < 50) {
pwmWrite(PWM_PIN_SPEED, 75); pwmWrite(PWM_PIN_SPEED, 75);
} }
......
#include "common.h"
#include "modules_common.h"
#include "ship0302_control.h"
#include "gpio_common.h"
/*
* 软 PWM 频率(wiringPi / wiringOP 的 softPwm.c):
* 每档时间固定 delayMicroseconds(n*100),整周期 = pwmRange×100µs
* → f = 1/(pwmRange×100µs) = 10000/pwmRange Hz
* 要约 350Hz:pwmRange ≈ 10000/350 ≈ 28.6,取 29 → 约 344.8Hz(取 28 → 约 357Hz)。
* 与 gpio_init 里 init_gpiopwm 的 range=100(约 100Hz)不同,此处单独为 ship0302 定档。
*/
#define SHIP0302_SOFT_PWM_MIN 0
/* ship0302 专用软 PWM 引脚(WiringPi),仅在本文件初始化 */
static const int s_ship0302_soft_pwm_pins[] = { 11, 12, 14, 17, 20, -1 };
/** 百分比 0~100 → softPwm 档位 0~SHIP0302_SOFT_PWM_RANGE;100% 即满档,效果上等同输出恒为高 */
static int ship0302_duty_from_percent(int pct_0_100) {
if (pct_0_100 < 0) {
pct_0_100 = 0;
} else if (pct_0_100 > 100) {
pct_0_100 = 100;
}
return (pct_0_100 * SHIP0302_SOFT_PWM_RANGE + 50) / 100;
}
void device_ship0302_init(void) {
physics_pwm_init();
for (int i = 0; s_ship0302_soft_pwm_pins[i] != -1; i++) {
int pin = s_ship0302_soft_pwm_pins[i];
softPwmCreate(pin, SHIP0302_SOFT_PWM_MIN, SHIP0302_SOFT_PWM_RANGE);
}
pinMode(18, OUTPUT);
digitalWrite(18, LOW);
}
void ship0302_stop(void) {
pwmWrite(PWM_PIN_SPEED, 0);
pwmWrite(PWM_PIN_CHANGE, 0);
for (int i = 0; s_ship0302_soft_pwm_pins[i] != -1; i++) {
softPwmWrite(s_ship0302_soft_pwm_pins[i], 0);
}
}
void ship0302_mode_lift_flont(int gval) {
softPwmWrite(12, ship0302_duty_from_percent(100));
softPwmWrite(14, ship0302_duty_from_percent(0));
}
void ship0302_mode_right_flont(int gval) {
softPwmWrite(20, ship0302_duty_from_percent(60));
softPwmWrite(11, ship0302_duty_from_percent(0));
}
void ship0302_mode_lift_back(int gval) {
softPwmWrite(12, ship0302_duty_from_percent(0));
softPwmWrite(14, ship0302_duty_from_percent(100));
}
void ship0302_mode_right_back(int gval) {
softPwmWrite(20, ship0302_duty_from_percent(0));
softPwmWrite(11, ship0302_duty_from_percent(80));
}
void ship0302_mode_lift_stop(int gval) {
softPwmWrite(14, ship0302_duty_from_percent(0));
softPwmWrite(12, ship0302_duty_from_percent(0));
}
void ship0302_mode_right_stop(int gval) {
softPwmWrite(20, ship0302_duty_from_percent(0));
softPwmWrite(11, ship0302_duty_from_percent(0));
}
void ship0302_mode_stop(void) {
digitalWrite(18, LOW);
softPwmWrite(14, ship0302_duty_from_percent(0));
softPwmWrite(12, ship0302_duty_from_percent(0));
softPwmWrite(20, ship0302_duty_from_percent(0));
softPwmWrite(11, ship0302_duty_from_percent(0));
}
/*
* MQTT:mode 仅 1~4(前/后/左/右),val 为业务数值;某轴 val==0 表示该轴回中。
* 底层 ship0302_mode_* 为固定 PWM,不按 val 调速,表中只传「普通」front_val/steer_val/0。
*
* 前后轴 front_t:0 无 1 前 2 后
* 左右轴 steer_t:0 无 1 左 2 右(MQTT mode 3/4 写入,与下面表 idx 不是同一套数)
* 九格表索引 idx 不能用 front_t*3+steer_t:否则 (1,0) 纯前会得到 3,与「左」idx 冲突。
* 应用 ship0302_dir_idx(front_t, steer_t) 映射到 0~8。
*/
typedef void (*ship0302_dir_apply_fn)(int front_val, int steer_val);
typedef struct {
ship0302_dir_apply_fn apply;
} ship0302_dir_entry_t;
static void ship0302_dir_row_stop(int front_val, int steer_val) {
(void)front_val;
(void)steer_val;
ship0302_mode_stop();
}
static void ship0302_dir_row_fwd_straight(int front_val, int steer_val) {
(void)steer_val;
ship0302_mode_lift_flont(front_val);
ship0302_mode_right_flont(front_val);
}
static void ship0302_dir_row_back_straight(int front_val, int steer_val) {
(void)steer_val;
ship0302_mode_lift_back(front_val);
ship0302_mode_right_back(front_val);
}
static void ship0302_dir_row_left_only(int front_val, int steer_val) {
(void)front_val;
ship0302_mode_right_flont(steer_val);
ship0302_mode_lift_stop(steer_val);
}
static void ship0302_dir_row_right_only(int front_val, int steer_val) {
(void)front_val;
ship0302_mode_lift_flont(steer_val);
ship0302_mode_right_stop(steer_val);
}
static void ship0302_dir_row_fwd_left(int front_val, int steer_val) {
ship0302_mode_right_flont(steer_val);
ship0302_mode_lift_stop(steer_val);
}
static void ship0302_dir_row_fwd_right(int front_val, int steer_val) {
ship0302_mode_lift_flont(steer_val);
ship0302_mode_right_stop(steer_val);
}
static void ship0302_dir_row_back_left(int front_val, int steer_val) {
//ship0302_mode_lift_back(front_val);
ship0302_mode_right_back(steer_val);
ship0302_mode_lift_stop(steer_val);
}
static void ship0302_dir_row_back_right(int front_val, int steer_val) {
ship0302_mode_lift_back(steer_val);
ship0302_mode_right_stop(steer_val);
//ship0302_mode_right_back(front_val);
}
/*
* idx = ship0302_dir_idx(front_t, steer_t)
* idx | 方位 | front_t | steer_t
* -----+------------+---------+--------
* 0 | 停 | 0 | 0
* 1 | 前 | 1 | 0
* 2 | 后 | 2 | 0
* 3 | 左 | 0 | 1
* 4 | 右 | 0 | 2
* 5 | 前左 | 1 | 1
* 6 | 前右 | 1 | 2
* 7 | 后左 | 2 | 1
* 8 | 后右 | 2 | 2
*/
static int ship0302_dir_idx(int front_t, int steer_t) {
if (steer_t == 0) {
return front_t;
}
if (front_t == 0) {
return 2 + steer_t;
}
return 4 + (front_t - 1) * 2 + steer_t;
}
static const ship0302_dir_entry_t s_ship0302_dir_table[] = {
{ ship0302_dir_row_stop },
{ ship0302_dir_row_fwd_straight },
{ ship0302_dir_row_back_straight },
{ ship0302_dir_row_left_only },
{ ship0302_dir_row_right_only },
{ ship0302_dir_row_fwd_left },
{ ship0302_dir_row_fwd_right },
{ ship0302_dir_row_back_left },
{ ship0302_dir_row_back_right },
};
#define SHIP0302_DIR_TABLE_LEN (sizeof(s_ship0302_dir_table) / sizeof(s_ship0302_dir_table[0]))
static void ship0302_apply_direction_table(int idx, int front_val, int steer_val) {
if (idx < 0 || idx >= (int)SHIP0302_DIR_TABLE_LEN) {
ship0302_mode_stop();
return;
}
s_ship0302_dir_table[idx].apply(front_val, steer_val);
}
void ship0302_change(int *buf) {
(void)buf[0];
int mode = buf[1];
int val = buf[2];
static int ship0302_front_t = 0;
static int ship0302_steering_t = 0;
static int ship0302_front_val = 0;
static int ship0302_steering_val = 0;
/* 收到某轴 val==0:清该轴状态(回中) */
if ((mode == 1 || mode == 2) && val == 0) {
ship0302_front_t = 0;
}
if ((mode == 3 || mode == 4) && val == 0) {
ship0302_steering_t = 0;
}
/* 前后轴 */
if (mode == 1 && val != 0) {
digitalWrite(18, HIGH);
ship0302_front_val = val;
ship0302_front_t = 1;
} else if (mode == 2 && val != 0) {
digitalWrite(18, HIGH);
ship0302_front_t = 2;
ship0302_front_val = val;
}
/* 左右轴 */
if (mode == 3 && val != 0) {
digitalWrite(18, HIGH);
ship0302_steering_t = 1;
ship0302_steering_val = val;
} else if (mode == 4 && val != 0) {
digitalWrite(18, HIGH);
ship0302_steering_t = 2;
ship0302_steering_val = val;
}
int idx = ship0302_dir_idx(ship0302_front_t, ship0302_steering_t);
ship0302_apply_direction_table(idx, ship0302_front_val, ship0302_steering_val);
}
#ifndef SHIP0302_CONTROL_H__
#define SHIP0302_CONTROL_H__
/**
* wiringPi softPwm:周期 = range×100µs,频率 f = 10000/range Hz(库内写死 100µs/档)。
* 本设备软 PWM 用 range=29 → 约 345Hz;softPwmWrite 取值 0~29,勿按 0~100 写。
*/
#define SHIP0302_SOFT_PWM_RANGE 29
/** ship0302:50Hz 硬件 PWM(physics_pwm_init)+ 本文件内指定脚软 PWM(约 350Hz) */
void device_ship0302_init(void);
void ship0302_stop();
void ship0302_change(int *buf);
#endif
...@@ -125,6 +125,16 @@ static const deviceconfig_t s_device_configs[] = { ...@@ -125,6 +125,16 @@ static const deviceconfig_t s_device_configs[] = {
.emergency_code = 301 .emergency_code = 301
}, },
{ {
.device_id = DEVICE_SHIP0302,
.device_name = "ship0302",
.gpio_pins = {6, 16, 20, 22, 23,-1},
.gpio_pwms = {5 , 7 ,24,26, 27,-1},
.gpio_inputs={-1},
.device_pwm_init = device_ship0302_init,
.device_control_stop = ship0302_stop,
.emergency_code = 302
},
{
.device_id = DEVICE_PAO_PTZ0401, .device_id = DEVICE_PAO_PTZ0401,
.device_name = "ptz0401", .device_name = "ptz0401",
.gpio_pins = {5, 6, 7, 10, 16, 20, 22, 23, 24, 25, 26,27,-1},/* 补充GPIO引脚 */ .gpio_pins = {5, 6, 7, 10, 16, 20, 22, 23, 24, 25, 26,27,-1},/* 补充GPIO引脚 */
...@@ -156,6 +166,16 @@ static const deviceconfig_t s_device_configs[] = { ...@@ -156,6 +166,16 @@ static const deviceconfig_t s_device_configs[] = {
.device_control_stop = ptz_driver_init, .device_control_stop = ptz_driver_init,
.emergency_code = 405 .emergency_code = 405
}, },
{
.device_id = DEVICE_PAO_PTZ0406,
.device_name = "ptz0406",
.gpio_pins = {5, 7, 10, 16, 20, 22, 23, 24, 25, 26,27,-1},
.gpio_pwms = {-1},
.gpio_inputs={-1},
.device_pwm_init = ptz_driver_pwm_hz,
.device_control_stop = ptz_driver_init,
.emergency_code = 406
},
{ {
.device_id = DEVICE_PG_GPS0403, .device_id = DEVICE_PG_GPS0403,
......
...@@ -29,6 +29,7 @@ void tank0204_pwm_value(int pin,int value); ...@@ -29,6 +29,7 @@ void tank0204_pwm_value(int pin,int value);
void tank0206_pwm_value(int pin,int value); void tank0206_pwm_value(int pin,int value);
void tank0207_pwm_value(int pin,int value); void tank0207_pwm_value(int pin,int value);
void ship0301_pwm_value(int pin,int value); void ship0301_pwm_value(int pin,int value);
void ship0302_pwm_value(int pin,int value);
void dog0501_pwm_value(int pin,int value); void dog0501_pwm_value(int pin,int value);
static TankFireControl s_device_shot_t; static TankFireControl s_device_shot_t;
...@@ -284,6 +285,12 @@ static const gpiocontrol_t s_gpio_configs[] = { ...@@ -284,6 +285,12 @@ static const gpiocontrol_t s_gpio_configs[] = {
.device_pwm_value =ship0301_pwm_value .device_pwm_value =ship0301_pwm_value
}, },
{ {
.device_id = DEVICE_SHIP0302,
.category_id=WATER_SHIP,
.device_pin_value =public_pin_value,
.device_pwm_value =ship0302_pwm_value
},
{
.device_id = DEVICE_PAO_PTZ0401, .device_id = DEVICE_PAO_PTZ0401,
.category_id=0, .category_id=0,
.device_pin_value =ptz0401_pin_value, .device_pin_value =ptz0401_pin_value,
...@@ -302,6 +309,12 @@ static const gpiocontrol_t s_gpio_configs[] = { ...@@ -302,6 +309,12 @@ static const gpiocontrol_t s_gpio_configs[] = {
.device_pwm_value =public_pwm_value .device_pwm_value =public_pwm_value
}, },
{ {
.device_id = DEVICE_PAO_PTZ0406,
.category_id=0,
.device_pin_value =public_pin_value,
.device_pwm_value =public_pwm_value
},
{
.device_id = DEVICE_ROBOT_DOG0501, .device_id = DEVICE_ROBOT_DOG0501,
.category_id=0, .category_id=0,
.device_pin_value =public_pin_value, .device_pin_value =public_pin_value,
...@@ -770,6 +783,32 @@ void ship0301_pwm_value(int pin,int value) { //软件陪我们控制调速 ...@@ -770,6 +783,32 @@ void ship0301_pwm_value(int pin,int value) { //软件陪我们控制调速
my_zlog_info("tank0301 pwm"); my_zlog_info("tank0301 pwm");
} }
void ship0302_pwm_value(int pin,int value) { // 与 ship0301 引脚/软 PWM 行为一致
for(int i = 0 ; i <= g_gpio_softpwmcount ; i++) {
if(pin == g_gpioPwm[i]) {
break;
}
if(i == g_gpio_softpwmcount) {
return ;
}
}
if(value==1) {
if(pin == 27){
softPwmWrite(26, 35);
} else {
softPwmWrite(pin, 35);
my_zlog_info("pwm:%d",pin);
}
}else if(value==0) {
if(pin == 27) softPwmWrite(26, 0);
softPwmWrite(pin, 0);
my_zlog_info("pwm:%d,0",pin);
}
my_zlog_info("ship0302 pwm");
}
void dog0501_pwm_value(int pin,int value) { //软件陪我们控制调速 void dog0501_pwm_value(int pin,int value) { //软件陪我们控制调速
for(int i = 0 ; i <= g_gpio_softpwmcount ; i++) { for(int i = 0 ; i <= g_gpio_softpwmcount ; i++) {
......
...@@ -100,4 +100,3 @@ void physics_pwm_init() { ...@@ -100,4 +100,3 @@ void physics_pwm_init() {
pwmSetRange(PWM_PIN_CHANGE,1000);//占空比范围 pwmSetRange(PWM_PIN_CHANGE,1000);//占空比范围
} }
#!/usr/bin/env python3
import argparse
import json
import math
import subprocess
import time
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
MQTT_BROKER = "119.45.167.177"
MQTT_PORT = 1883
MQTT_TOPIC = "app2dev/CN040500000000"
MQTT_USERNAME = "admin"
MQTT_PASSWORD = "admin"
HTML_PAGE = """<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PTZ 轮盘测试</title>
<style>
:root { color-scheme: dark; }
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Microsoft YaHei", sans-serif;
background: #0f172a;
color: #e2e8f0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.wrap {
width: min(900px, 95vw);
background: #111827;
border: 1px solid #334155;
border-radius: 14px;
padding: 18px;
box-shadow: 0 20px 50px rgba(0, 0, 0, .35);
}
.title {
font-size: 20px;
font-weight: 700;
margin-bottom: 8px;
}
.tip {
color: #94a3b8;
margin-bottom: 14px;
}
.row {
display: grid;
grid-template-columns: 360px 1fr;
gap: 16px;
}
@media (max-width: 800px) {
.row { grid-template-columns: 1fr; }
}
.panel {
background: #0b1220;
border: 1px solid #253146;
border-radius: 12px;
padding: 12px;
}
#pad {
width: 100%;
max-width: 360px;
aspect-ratio: 1 / 1;
touch-action: none;
display: block;
margin: 0 auto;
background: radial-gradient(circle at 50% 50%, #1e293b, #0b1220);
border-radius: 50%;
border: 1px solid #334155;
cursor: grab;
}
#pad.dragging { cursor: grabbing; }
.kv {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 14px;
line-height: 1.8;
white-space: pre-wrap;
background: #020617;
border: 1px solid #1e293b;
border-radius: 10px;
padding: 10px;
overflow-wrap: anywhere;
}
.btns {
margin-top: 10px;
display: flex;
gap: 8px;
flex-wrap: wrap;
}
button {
border: 1px solid #334155;
background: #1d4ed8;
color: #fff;
border-radius: 8px;
padding: 8px 12px;
cursor: pointer;
}
button.secondary { background: #334155; }
.small {
color: #94a3b8;
font-size: 13px;
margin-top: 8px;
}
</style>
</head>
<body>
<div class="wrap">
<div class="title">PTZ 轮盘测试页(message_type=7)</div>
<div class="tip">拖动左侧轮盘测试。x/y/r 全部按字符串输出,松开自动回到 "0"。</div>
<div class="row">
<div class="panel">
<canvas id="pad" width="360" height="360"></canvas>
<div class="small">规则:x/y ∈ [-1,1],r ∈ [0,1],r=0 表示停住。</div>
</div>
<div class="panel">
<div id="values" class="kv"></div>
<div class="btns">
<button id="copyBtn">复制 JSON</button>
<button id="sendBtn" class="secondary">发到本地 /api/joystick</button>
<button id="zeroBtn" class="secondary">发送全0</button>
</div>
<div class="small" id="sendStatus">状态:未发送</div>
</div>
</div>
</div>
<script>
const canvas = document.getElementById("pad");
const ctx = canvas.getContext("2d");
const valuesEl = document.getElementById("values");
const sendStatusEl = document.getElementById("sendStatus");
const copyBtn = document.getElementById("copyBtn");
const sendBtn = document.getElementById("sendBtn");
const zeroBtn = document.getElementById("zeroBtn");
const cx = canvas.width / 2;
const cy = canvas.height / 2;
const maxR = 140;
const knobR = 26;
let dragging = false;
let px = 0; // pixel offset
let py = 0;
let lastSend = 0;
const sendInterval = 80; // ms
function clamp(v, min, max) { return Math.max(min, Math.min(max, v)); }
function round3(v) { return (Math.round(v * 1000) / 1000).toFixed(3); }
function getNorm() {
const nx = clamp(px / maxR, -1, 1);
const nyCanvas = clamp(py / maxR, -1, 1);
const ny = -nyCanvas; // 上为正,下为负
const nr = clamp(Math.sqrt(nx * nx + ny * ny), 0, 1);
return { x: nx, y: ny, r: nr };
}
function buildPayload(n) {
return {
head: { message_type: 7 },
body: {
joystick_ctrl: {
x: String(round3(n.x)),
y: String(round3(n.y)),
r: String(round3(n.r))
}
}
};
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.strokeStyle = "#334155";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(cx, cy, maxR, 0, Math.PI * 2);
ctx.stroke();
ctx.strokeStyle = "#1e293b";
for (let i = 1; i <= 3; i++) {
ctx.beginPath();
ctx.arc(cx, cy, (maxR / 3) * i, 0, Math.PI * 2);
ctx.stroke();
}
ctx.beginPath(); ctx.moveTo(cx - maxR, cy); ctx.lineTo(cx + maxR, cy); ctx.stroke();
ctx.beginPath(); ctx.moveTo(cx, cy - maxR); ctx.lineTo(cx, cy + maxR); ctx.stroke();
ctx.restore();
const kx = cx + px;
const ky = cy + py;
ctx.save();
const grad = ctx.createRadialGradient(kx - 7, ky - 9, 6, kx, ky, knobR + 6);
grad.addColorStop(0, "#60a5fa");
grad.addColorStop(1, "#1d4ed8");
ctx.fillStyle = grad;
ctx.beginPath();
ctx.arc(kx, ky, knobR, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
function updateText() {
const n = getNorm();
const payload = buildPayload(n);
valuesEl.textContent = JSON.stringify(payload, null, 2);
}
function setByPointer(clientX, clientY) {
const rect = canvas.getBoundingClientRect();
const x = clientX - rect.left;
const y = clientY - rect.top;
let dx = x - cx;
let dy = y - cy;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist > maxR) {
const ratio = maxR / dist;
dx *= ratio;
dy *= ratio;
}
px = dx;
py = dy;
draw();
updateText();
}
function resetZero() {
px = 0;
py = 0;
draw();
updateText();
}
async function sendLocal() {
const n = getNorm();
const payload = buildPayload(n);
try {
const res = await fetch("/api/joystick", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
sendStatusEl.textContent = "状态:发送成功 " + new Date().toLocaleTimeString() + " (HTTP " + res.status + ")";
} catch (e) {
sendStatusEl.textContent = "状态:发送失败 " + e;
}
}
function maybeAutoSend() {
const now = Date.now();
if (now - lastSend >= sendInterval) {
lastSend = now;
sendLocal();
}
}
canvas.addEventListener("pointerdown", (e) => {
dragging = true;
canvas.classList.add("dragging");
canvas.setPointerCapture(e.pointerId);
setByPointer(e.clientX, e.clientY);
maybeAutoSend();
});
canvas.addEventListener("pointermove", (e) => {
if (!dragging) return;
setByPointer(e.clientX, e.clientY);
maybeAutoSend();
});
canvas.addEventListener("pointerup", () => {
dragging = false;
canvas.classList.remove("dragging");
resetZero();
sendLocal();
});
canvas.addEventListener("pointercancel", () => {
dragging = false;
canvas.classList.remove("dragging");
resetZero();
sendLocal();
});
copyBtn.addEventListener("click", async () => {
const n = getNorm();
const payload = buildPayload(n);
await navigator.clipboard.writeText(JSON.stringify(payload, null, 2));
sendStatusEl.textContent = "状态:已复制 JSON";
});
sendBtn.addEventListener("click", sendLocal);
zeroBtn.addEventListener("click", () => {
resetZero();
sendLocal();
});
draw();
updateText();
</script>
</body>
</html>
"""
class Handler(BaseHTTPRequestHandler):
mqtt_broker = MQTT_BROKER
mqtt_port = MQTT_PORT
mqtt_topic = MQTT_TOPIC
mqtt_username = MQTT_USERNAME
mqtt_password = MQTT_PASSWORD
def _write_json(self, code, obj):
data = json.dumps(obj, ensure_ascii=False).encode("utf-8")
self.send_response(code)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def do_GET(self):
if self.path in ("/", "/index.html"):
data = HTML_PAGE.encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
return
self._write_json(404, {"ok": False, "msg": "not found"})
def do_POST(self):
if self.path != "/api/joystick":
self._write_json(404, {"ok": False, "msg": "not found"})
return
try:
length = int(self.headers.get("Content-Length", "0"))
except ValueError:
length = 0
raw = self.rfile.read(length)
try:
payload = json.loads(raw.decode("utf-8"))
ts = time.strftime("%H:%M:%S")
payload_text = json.dumps(payload, ensure_ascii=False)
print(f"[{ts}] recv: {payload_text}", flush=True)
except Exception as e:
self._write_json(400, {"ok": False, "msg": f"bad json: {e}"})
return
cmd = [
"mosquitto_pub",
"-h", self.mqtt_broker,
"-p", str(self.mqtt_port),
"-u", self.mqtt_username,
"-P", self.mqtt_password,
"-t", self.mqtt_topic,
"-m", payload_text,
]
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
if result.returncode != 0:
self._write_json(500, {
"ok": False,
"msg": "mqtt publish failed",
"stderr": (result.stderr or "").strip()
})
return
except Exception as e:
self._write_json(500, {"ok": False, "msg": f"mqtt publish exception: {e}"})
return
self._write_json(200, {"ok": True, "topic": self.mqtt_topic})
def log_message(self, fmt, *args):
return
def main():
parser = argparse.ArgumentParser(description="PTZ joystick web tester")
parser.add_argument("--host", default="0.0.0.0", help="listen host")
parser.add_argument("--port", type=int, default=85, help="listen port, default 85")
parser.add_argument("--mqtt-host", default=MQTT_BROKER, help="mqtt broker host")
parser.add_argument("--mqtt-port", type=int, default=MQTT_PORT, help="mqtt broker port")
parser.add_argument("--mqtt-topic", default=MQTT_TOPIC, help="mqtt publish topic")
parser.add_argument("--mqtt-user", default=MQTT_USERNAME, help="mqtt username")
parser.add_argument("--mqtt-pass", default=MQTT_PASSWORD, help="mqtt password")
args = parser.parse_args()
Handler.mqtt_broker = args.mqtt_host
Handler.mqtt_port = args.mqtt_port
Handler.mqtt_topic = args.mqtt_topic
Handler.mqtt_username = args.mqtt_user
Handler.mqtt_password = args.mqtt_pass
server = None
bind_port = args.port
try:
server = ThreadingHTTPServer((args.host, bind_port), Handler)
except PermissionError:
if bind_port < 1024:
fallback_port = 8085
print(
f"Port {bind_port} permission denied, fallback to {fallback_port}.",
flush=True
)
bind_port = fallback_port
server = ThreadingHTTPServer((args.host, bind_port), Handler)
else:
raise
print(f"PTZ test web running on http://{args.host}:{bind_port}", flush=True)
print(
f"MQTT target: {Handler.mqtt_broker}:{Handler.mqtt_port} topic={Handler.mqtt_topic} user={Handler.mqtt_username}",
flush=True
)
print("Tip: use sudo if you must bind port <1024.", flush=True)
try:
server.serve_forever()
except KeyboardInterrupt:
pass
finally:
server.server_close()
if __name__ == "__main__":
main()
...@@ -9,4 +9,4 @@ file perms = 600 ...@@ -9,4 +9,4 @@ file perms = 600
millisecond = "%d(%Y-%m-%d %H:%M:%S).%ms [%V] %m%n" millisecond = "%d(%Y-%m-%d %H:%M:%S).%ms [%V] %m%n"
[rules] [rules]
my_log.* "/home/orangepi/car/master/log/log_2026-03-25.log"; millisecond my_log.* "/home/orangepi/car/master/log/log_2026-04-03.log"; millisecond
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment