AP05/main.c
2026-01-06 14:22:05 +08:00

3610 lines
145 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "main.h"
#include "json-c/json.h"
#include "uart_utils/uart_utils.h"
#include "uart_can/uart_can.h"
#include "mqtt_utils/mqtt_utils.h"
#include "queue/queue.h"
#define PRINT_TIME_TAG
#define DBG_TAG "main"
#define DBG_LVL DBG_INFO
#include "debug_print/debug_print.h"
#include <errno.h>
// UUID generation functions (defined in mqtt/WebSocket.c)
typedef unsigned char uuid_t[16];
extern void uuid_generate(uuid_t out);
extern void uuid_unparse(uuid_t uu, char *out);
// 全局变量声明
jt_led_or_group_package_t tags;
jt_led_or_group_package_t led_ctrl;
// 灯光命令队列结构体 - 解决竞态条件问题
#define MAX_LIGHT_COMMANDS 32
typedef struct {
char deviceName[16]; // 设备名称12位十六进制
char fullTagId[16]; // 完整的灯条ID
char taskId[64]; // 任务ID
uint16_t tagCodeHead; // 灯条ID头部
uint32_t tagCode; // 灯条ID
uint8_t color; // 颜色设置
uint8_t sound; // 声音设置
uint8_t flash; // 闪烁设置
int duration; // 持续时间
int beep; // 蜂鸣器状态
int flash_value; // 闪烁值(用于上报)
int light_r; // 红色
int light_g; // 绿色
int light_b; // 蓝色
bool is_valid; // 命令是否有效
uint64_t sequence; // 序列号,用于跟踪
} light_command_t;
// 命令队列和相关变量
light_command_t light_commands[MAX_LIGHT_COMMANDS];
int light_command_head = 0;
int light_command_tail = 0;
pthread_mutex_t lightCommandMutex = PTHREAD_MUTEX_INITIALIZER;
uint64_t command_sequence = 0;
// 函数声明
void light_status_report(void);
void update_app(void);
// MQTT协议相关常量定义
#define MQTT_ALGORITHM "AUK1_MQTT_HMAC_SHA1"
#define MQTT_SECURE_MODE "TCP"
#define MQTT_AUTH_TYPE_REGISTER "REGISTER"
#define MQTT_AUTH_TYPE_AUTH "AUTH"
pthread_t pt_uart_send;
pthread_t pt_uart_recv_ack;
pthread_t pt_uart_recv_data;
pthread_t pt_uart_recv_back;
pthread_t pt_mqtt_recv;
pthread_t pt_readqr;
pthread_t pt_reporttag;
pthread_t pt_mqtt;
pthread_t pt_removelog;
pthread_t pt_watchdog;
pthread_t pt_removeduplicatetag;
pthread_t pt_keycheck;
pthread_t pt_mqtt_status;
pthread_t pt_station_heartbeat;
pthread_t pt_simulate_light;
pthread_t pt_all_light;
pthread_t pt_simulate_mqtt_topic;
uart_utils_t uartSend = {0};
uart_utils_t uartRecvData = {0};
uart_utils_t uartRecvBack = {0};
mqtt_parm_t mqtt_parm={0};
mqtt_utils_t mqtt_config;
struct input_event buff;
bool newappDownloaded=false;
bool isLightOn=false;
bool isLightOnById=false;
bool isLightOnByGroup=false;
bool isBindTag=false;
bool isSendComEnd=true;
bool isStopBroadcast=false;
bool isStopBroadcastBegin=false;
bool isAllLightOn=false; // 全场亮灯标志
uint8_t allLightColor=4; // 全场亮灯颜色,默认红色
uint8_t allLightFlash=3; // 全场亮灯闪烁模式,默认闪烁
uint8_t allLightSound=0; // 全场亮灯蜂鸣,默认关闭
int allLightDuration=30; // 全场亮灯持续时间(秒)
bool isOtaEnable=false;
bool isMqttConnected=false; // MQTT连接状态标志
jt_only_tag_t onlyTags[200]={0};
int tagCount=0;
// 心跳检测相关
#define HEARTBEAT_TIMEOUT_SEC (30 * 60) // 30分钟超时
#define MAX_LIGHTBAR_NUM 5000
typedef struct {
uint16_t tagCodeHead; // 灯条ID头部
uint32_t tagCode; // 灯条ID主体
char fullTagId[16]; // 完整的灯条ID (12位十六进制)
time_t lastHeartbeat; // 最后心跳时间
bool isOnline; // 是否在线
} lightbar_heartbeat_t;
lightbar_heartbeat_t lightbarHeartbeat[MAX_LIGHTBAR_NUM] = {0};
int lightbarHeartbeatCount = 0;
pthread_mutex_t heartbeatMutex = PTHREAD_MUTEX_INITIALIZER;
// 亮灯任务确认相关
#define MAX_PENDING_LIGHT_TASKS 1024
typedef struct {
uint16_t tagCodeHead; // 灯条ID头部
uint32_t tagCode; // 灯条ID主体tag的后6位
char fullTagId[16]; // 完整的灯条ID (12位十六进制)
char taskId[64]; // 任务ID
int beep; // 蜂鸣器状态0或1
int flash; // 闪烁状态0或1
int light_r; // 红色0或1
int light_g; // 绿色0或1
int light_b; // 蓝色0或1
int duration; // 持续时间(秒)
time_t reportTime; // 上报时间
bool reported; // 是否已上报
bool lightOffReported; // 是否已上报灭灯
} pending_light_task_t;
pending_light_task_t pendingLightTasks[MAX_PENDING_LIGHT_TASKS] = {0};
int pendingLightTaskCount = 0;
pthread_mutex_t lightTaskMutex = PTHREAD_MUTEX_INITIALIZER;
// 按键防抖相关
#define MAX_KEY_DEBOUNCE 50
typedef struct {
uint16_t tagCodeHead; // 灯条ID头部
uint32_t tagCode;
time_t lastKeyTime;
} key_debounce_t;
key_debounce_t keyDebounceList[MAX_KEY_DEBOUNCE] = {0};
int keyDebounceCount = 0;
pthread_mutex_t keyDebounceMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_t pt_heartbeat_check;
pthread_t pt_light_off_check;
int lightbars_size=0;
int lightbars_count=0;
uint8_t changecolor=0;
uint8_t changesound=0;
uint8_t changeflash=3; // 1=常亮, 3=闪烁
uint8_t groupno=0;
int fd;
int UPCASE=0;
int count_value=0;
int getPayloadTime=100*1000;//usecond
char softwareVersion[16]="2.1.27";
//char stationsn[16]="d126ei4lj4cc00";//TJ250995217957
//char productid[8]="10045";
//char appSecret[64]="s3izIliw0CF48Pcsi16rjOmoFRf5WEt8";
char stationsn[32]="90A9F73002CD";
char productid[16]="WcLightStrip";
char appKey[32]="fdhQmhqhvbL1cf1K9mUqt";
char appSecret[64]="RxU8NZjfZaxsKg2B3Dr6sx";
char hostDomain[64]="auk-iot.test1.zservey.com";
int mqtt_port=1883;
//char hostDomain[64]="127.0.0.1";
//int mqtt_port=1884;
char mqttRawPassword[64]="";
char input_value[1024]={0};//94b4e39caf1d45ee9a071d701cc037b4
char input_value_copy[1024]={0};
char *getDevRegisterStatusUrl="https://gaea-zt-express.com/device/modifyRegisterInfo";
char *updateUrl="https://idata-creat.oss-cn-shenzhen.aliyuncs.com/jd_ota.zip";
char *getDevRawPasswordUrl="https://gaea.zt-express.com/device/modifyRegisterInfo";
//char *hostDomain="emqx.thingtalk.jdl.com";
char lightsn1[7]={0};
char lightsn2[7]={0};
char lightsn3[7]={0};
char lightsn4[7]={0};
char lightsn5[7]={0};
char lightsn6[7]={0};
char lightsn7[7]={0};
char lightsn8[7]={0};
char lightsn9[7]={0};
char lightsn10[7]={0};
char lightsn11[7]={0};
char lightsn12[7]={0};
char lightsn13[7]={0};
char lightsn14[7]={0};
char lightsn15[7]={0};
char lightsn16[7]={0};
char lightsn17[7]={0};
char lightsn18[7]={0};
char lightsn19[7]={0};
char lightsn20[7]={0};
char lightsn21[7]={0};
char lightsn22[7]={0};
// 【修复】添加全局变量保存当前设备的tagCode避免竞态条件
uint32_t g_current_tagCode = 0;
uint16_t g_current_tagCodeHead = 0;
// 【增强】添加设备特定的tagCode映射解决多设备并发问题
typedef struct {
char deviceName[16];
uint32_t tagCode;
uint16_t tagCodeHead;
uint64_t timestamp; // 时间戳,用于清理过期映射
} device_tag_mapping_t;
#define MAX_DEVICE_MAPPINGS 16
device_tag_mapping_t g_device_mappings[MAX_DEVICE_MAPPINGS];
pthread_mutex_t deviceMappingMutex = PTHREAD_MUTEX_INITIALIZER;
char lightsn23[7]={0};
char lightsn24[7]={0};
char lightsn25[7]={0};
char lightsn26[7]={0};
char lightsn27[7]={0};
char lightsn28[7]={0};
char lightsn29[7]={0};
char lightsn30[7]={0};
int lightsnNum=0;
void hmacsha1_hex(char *key, char* data, char *signhex, int signhex_len);
void *thread_mqtt_status_check(void *arg);
void *thread_station_heartbeat(void *arg);
void *thread_heartbeat_check(void *arg);
void *thread_light_off_check(void *arg);
void *thread_simulate_light(void *arg);
void *thread_all_light(void *arg);
void *thread_simulate_mqtt_topic(void *arg);
void update_lightbar_heartbeat(uint16_t tagCodeHead, uint32_t tagCode);
void report_lightbar_login(uint16_t tagCodeHead, uint32_t tagCode);
void report_lightbar_logout(uint16_t tagCodeHead, uint32_t tagCode);
void add_pending_light_task(uint16_t tagCodeHead, uint32_t tagCode, const char *taskId, int beep, int flash, int r, int g, int b, int duration);
void report_light_success(uint16_t tagCodeHead, uint32_t tagCode);
void report_light_off(uint16_t tagCodeHead, uint32_t tagCode);
// 命令队列操作函数
int add_light_command(const char *deviceName, const char *taskId, uint16_t tagCodeHead, uint32_t tagCode,
uint8_t color, uint8_t sound, uint8_t flash, int duration,
int beep, int flash_value, int r, int g, int b);
int get_light_command(light_command_t *cmd);
void clear_light_command_queue(void);
// 【新增】设备映射管理函数
void add_device_mapping(const char *deviceName, uint16_t tagCodeHead, uint32_t tagCode);
int get_device_tag_mapping(const char *deviceName, uint32_t *tagCode, uint16_t *tagCodeHead);
void cleanup_expired_mappings(void);
/*================================================================================*/
// 上报灯条子设备登录
void report_lightbar_login(uint16_t tagCodeHead, uint32_t tagCode) {
char subDeviceKey[32] = {0};
char topic[256] = {0};
char payload[512] = {0};
char password[64] = {0};
char stringToSign[256] = {0};
char timestamp_str[32] = {0};
struct timeval tv;
// 构建subDeviceKey: 使用完整的tagID (tagCodeHead + tagCode)
snprintf(subDeviceKey, sizeof(subDeviceKey), "%04X%08X", tagCodeHead, tagCode);
// 获取当前时间戳(毫秒)
gettimeofday(&tv, NULL);
long timestamp = tv.tv_sec * 1000 + tv.tv_usec / 1000;
snprintf(timestamp_str, sizeof(timestamp_str), "%ld", timestamp);
// 构建待签名串: subDeviceKey + subDeviceKey + productKey + algorithm + timestamp
snprintf(stringToSign, sizeof(stringToSign), "%s%s%s%s%s",
subDeviceKey, subDeviceKey, "WcSubLightStrip", MQTT_ALGORITHM, timestamp_str);
// 使用HMAC-SHA1计算password
hmacsha1_hex("EKmmjqyLRgghANEiQAA5LZ", stringToSign, password, sizeof(password));
// 构建topic
snprintf(topic, sizeof(topic), "/auk/iot/things/v1/sub/device/up/login/WcSubLightStrip/%s/%ld",
subDeviceKey, timestamp);
// 构建JSON payload
snprintf(payload, sizeof(payload),
"{\"password\":\"%s\",\"timestamp\":%ld,\"signingAlgorithm\":\"%s\"}",
password, timestamp, MQTT_ALGORITHM);
//LOG_I("Lightbar login report - topic: %s\n", topic);
//LOG_I("Lightbar login report - payload: %s\n", payload);
// 发送MQTT消息
mqtt_utils_publish(&mqtt_config, topic, 0, payload, strlen(payload));
}
/*================================================================================*/
// 添加待确认的亮灯任务
void add_pending_light_task(uint16_t tagCodeHead, uint32_t tagCode, const char *taskId, int beep, int flash, int r, int g, int b, int duration) {
pthread_mutex_lock(&lightTaskMutex);
// 检查是否已存在
for (int i = 0; i < pendingLightTaskCount; i++) {
if (pendingLightTasks[i].tagCode == tagCode && pendingLightTasks[i].tagCodeHead == tagCodeHead && !pendingLightTasks[i].reported) {
// 更新任务信息
strncpy(pendingLightTasks[i].taskId, taskId, sizeof(pendingLightTasks[i].taskId) - 1);
pendingLightTasks[i].beep = beep;
pendingLightTasks[i].flash = flash;
pendingLightTasks[i].light_r = r;
pendingLightTasks[i].light_g = g;
pendingLightTasks[i].light_b = b;
pendingLightTasks[i].duration = duration;
pendingLightTasks[i].reportTime = time(NULL);
LOG_I("Updated pending light task for tag %08X, taskId: %s, beep: %d, flash: %d, RGB: %d,%d,%d, duration: %d\n",
tagCode, taskId, beep, flash, r, g, b, duration);
pthread_mutex_unlock(&lightTaskMutex);
return;
}
}
// 添加新任务
if (pendingLightTaskCount < MAX_PENDING_LIGHT_TASKS) {
pendingLightTasks[pendingLightTaskCount].tagCodeHead = tagCodeHead;
pendingLightTasks[pendingLightTaskCount].tagCode = tagCode;
snprintf(pendingLightTasks[pendingLightTaskCount].fullTagId, sizeof(pendingLightTasks[pendingLightTaskCount].fullTagId), "%04X%08X", tagCodeHead, tagCode);
strncpy(pendingLightTasks[pendingLightTaskCount].taskId, taskId,
sizeof(pendingLightTasks[pendingLightTaskCount].taskId) - 1);
pendingLightTasks[pendingLightTaskCount].beep = beep;
pendingLightTasks[pendingLightTaskCount].flash = flash;
pendingLightTasks[pendingLightTaskCount].light_r = r;
pendingLightTasks[pendingLightTaskCount].light_g = g;
pendingLightTasks[pendingLightTaskCount].light_b = b;
pendingLightTasks[pendingLightTaskCount].reported = false;
pendingLightTasks[pendingLightTaskCount].lightOffReported = false;
pendingLightTasks[pendingLightTaskCount].reportTime = time(NULL);
pendingLightTasks[pendingLightTaskCount].duration = duration;
LOG_I("Added pending light task for tag %08X, taskId: %s, beep: %d, flash: %d, RGB: %d,%d,%d, duration: %d\n",
tagCode, taskId, beep, flash, r, g, b, duration);
pendingLightTaskCount++;
} else {
LOG_I("Pending light task queue full!\n");
}
pthread_mutex_unlock(&lightTaskMutex);
}
/*================================================================================*/
// 上报亮灯成功
void report_light_success(uint16_t tagCodeHead, uint32_t tagCode) {
pthread_mutex_lock(&lightTaskMutex);
// 查找对应的待确认任务
for (int i = 0; i < pendingLightTaskCount; i++) {
if (pendingLightTasks[i].tagCode == tagCode && pendingLightTasks[i].tagCodeHead == tagCodeHead && !pendingLightTasks[i].reported) {
// 构建上报topic和payload
char topic[256] = {0};
char payload[1024] = {0};
// 使用完整的tagID
snprintf(topic, sizeof(topic), "/sys/WcSubLightStrip/%s/thing/service/lightOperate/invoke_reply", pendingLightTasks[i].fullTagId);
// 获取当前时间戳(毫秒)
struct timeval tv;
gettimeofday(&tv, NULL);
long long timestamp = (long long)tv.tv_sec * 1000 + tv.tv_usec / 1000;
// 使用任务中的真实数据构建payload
// light是一个struct包含R、G、B三个bool字段
snprintf(payload, sizeof(payload),
"{\"id\":\"%s\",\"code\":0,\"msg\":\"success\",\"data\":{\"beep\":{\"value\":%d,\"time\":%lld},\"flash\":{\"value\":%d,\"time\":%lld},\"light\":{\"value\":{\"R\":%d,\"G\":%d,\"B\":%d},\"time\":%lld}},\"method\":\"thing.service.lightOperate.invoke\",\"time\":%lld}",
pendingLightTasks[i].taskId,
pendingLightTasks[i].beep,
timestamp,
pendingLightTasks[i].flash,
timestamp,
pendingLightTasks[i].light_r,
pendingLightTasks[i].light_g,
pendingLightTasks[i].light_b,
timestamp,
timestamp);
LOG_I("Reporting light success - topic: %s\n", topic);
LOG_I("Reporting light success - payload: %s\n", payload);
LOG_I("Task ID: %s, beep: %d, flash: %d, RGB: %d,%d,%d\n",
pendingLightTasks[i].taskId, pendingLightTasks[i].beep,
pendingLightTasks[i].flash, pendingLightTasks[i].light_r,
pendingLightTasks[i].light_g, pendingLightTasks[i].light_b);
// 发送MQTT消息
mqtt_utils_publish(&mqtt_config, topic, 0, payload, strlen(payload));
// 标记为已上报
pendingLightTasks[i].reported = true;
pthread_mutex_unlock(&lightTaskMutex);
return;
}
}
LOG_I("No pending light task found for tag %08X\n", tagCode);
pthread_mutex_unlock(&lightTaskMutex);
}
/*================================================================================*/
// 上报灭灯调用时必须已持有lightTaskMutex锁
void report_light_off(uint16_t tagCodeHead, uint32_t tagCode) {
// 查找对应的已上报亮灯成功的任务
for (int i = 0; i < pendingLightTaskCount; i++) {
if (pendingLightTasks[i].tagCode == tagCode && pendingLightTasks[i].tagCodeHead == tagCodeHead &&
pendingLightTasks[i].reported &&
!pendingLightTasks[i].lightOffReported) {
// 构建上报topic和payload
char topic[256] = {0};
char payload[1024] = {0};
char uuid_str[37] = {0};
uuid_t uuid;
// 生成随机UUID
uuid_generate(uuid);
uuid_unparse(uuid, uuid_str);
// 使用完整的tagID
snprintf(topic, sizeof(topic), "/sys/WcSubLightStrip/%s/thing/property/post", pendingLightTasks[i].fullTagId);
// 获取当前时间戳(毫秒)
struct timeval tv;
gettimeofday(&tv, NULL);
long long timestamp = (long long)tv.tv_sec * 1000 + tv.tv_usec / 1000;
// 使用任务中的真实数据构建payload
snprintf(payload, sizeof(payload),
"{\"id\":\"%s\",\"version\":\"1.0\",\"arg\":{\"beep\":{\"value\":%d,\"time\":%lld},\"flash\":{\"value\":%d,\"time\":%lld},\"light\":{\"value\":{\"R\":%d,\"G\":%d,\"B\":%d},\"time\":%lld}},\"method\":\"thing.property.post\",\"time\":%lld}",
uuid_str,
0,
timestamp,
0,
timestamp,
0,
0,
0,
timestamp,
timestamp);
LOG_I("Reporting light off - topic: %s\n", topic);
LOG_I("Reporting light off - payload: %s\n", payload);
LOG_I("Tag: %08X, UUID: %s\n", tagCode, uuid_str);
// 发送MQTT消息
mqtt_utils_publish(&mqtt_config, topic, 0, payload, strlen(payload));
// 标记为已上报灭灯
pendingLightTasks[i].lightOffReported = true;
// 清理已完成的任务 - 将最后一个任务移到当前位置
if (i < pendingLightTaskCount - 1) {
pendingLightTasks[i] = pendingLightTasks[pendingLightTaskCount - 1];
}
pendingLightTaskCount--;
return;
}
}
LOG_I("No pending light task found for light-off report for tag %08X\n", tagCode);
}
/*================================================================================*/
// 命令队列操作函数实现
// 添加灯光命令到队列
int add_light_command(const char *deviceName, const char *taskId, uint16_t tagCodeHead, uint32_t tagCode,
uint8_t color, uint8_t sound, uint8_t flash, int duration,
int beep, int flash_value, int r, int g, int b) {
pthread_mutex_lock(&lightCommandMutex);
// 检查队列是否已满
int next_tail = (light_command_tail + 1) % MAX_LIGHT_COMMANDS;
if (next_tail == light_command_head) {
LOG_I("Light command queue is full!\n");
pthread_mutex_unlock(&lightCommandMutex);
return -1;
}
// 添加新命令
light_command_t *cmd = &light_commands[light_command_tail];
memset(cmd, 0, sizeof(light_command_t));
strncpy(cmd->deviceName, deviceName, sizeof(cmd->deviceName) - 1);
strncpy(cmd->fullTagId, deviceName, sizeof(cmd->fullTagId) - 1);
strncpy(cmd->taskId, taskId, sizeof(cmd->taskId) - 1);
cmd->tagCodeHead = tagCodeHead;
cmd->tagCode = tagCode;
cmd->color = color;
cmd->sound = sound;
cmd->flash = flash;
cmd->duration = duration;
cmd->beep = beep;
cmd->flash_value = flash_value;
cmd->light_r = r;
cmd->light_g = g;
cmd->light_b = b;
cmd->is_valid = true;
cmd->sequence = ++command_sequence;
LOG_I("Added light command to queue - device: %s, seq: %llu, RGB: %d,%d,%d\n",
deviceName, cmd->sequence, r, g, b);
light_command_tail = next_tail;
pthread_mutex_unlock(&lightCommandMutex);
return 0;
}
// 从队列获取灯光命令
int get_light_command(light_command_t *cmd) {
pthread_mutex_lock(&lightCommandMutex);
if (light_command_head == light_command_tail) {
// 队列为空
pthread_mutex_unlock(&lightCommandMutex);
return -1;
}
// 获取命令
*cmd = light_commands[light_command_head];
light_command_head = (light_command_head + 1) % MAX_LIGHT_COMMANDS;
LOG_I("Got light command from queue - device: %s, seq: %llu\n",
cmd->deviceName, cmd->sequence);
pthread_mutex_unlock(&lightCommandMutex);
return 0;
}
// 清空命令队列
void clear_light_command_queue(void) {
pthread_mutex_lock(&lightCommandMutex);
light_command_head = 0;
light_command_tail = 0;
memset(light_commands, 0, sizeof(light_commands));
LOG_I("Light command queue cleared\n");
pthread_mutex_unlock(&lightCommandMutex);
}
// 【新增】设备映射管理函数实现
void add_device_mapping(const char *deviceName, uint16_t tagCodeHead, uint32_t tagCode) {
pthread_mutex_lock(&deviceMappingMutex);
// 查找是否已存在该设备的映射
int existing_index = -1;
for (int i = 0; i < MAX_DEVICE_MAPPINGS; i++) {
if (strcmp(g_device_mappings[i].deviceName, deviceName) == 0) {
existing_index = i;
break;
}
}
// 如果不存在,找一个空位
if (existing_index == -1) {
for (int i = 0; i < MAX_DEVICE_MAPPINGS; i++) {
if (strlen(g_device_mappings[i].deviceName) == 0) {
existing_index = i;
break;
}
}
}
// 如果找到位置,更新映射
if (existing_index != -1) {
strncpy(g_device_mappings[existing_index].deviceName, deviceName, sizeof(g_device_mappings[existing_index].deviceName) - 1);
g_device_mappings[existing_index].tagCodeHead = tagCodeHead;
g_device_mappings[existing_index].tagCode = tagCode;
g_device_mappings[existing_index].timestamp = time(NULL) * 1000; // 毫秒时间戳
LOG_I("Added/Updated device mapping: %s -> %08X\n", deviceName, tagCode);
} else {
LOG_I("Device mapping table full!\n");
}
pthread_mutex_unlock(&deviceMappingMutex);
}
int get_device_tag_mapping(const char *deviceName, uint32_t *tagCode, uint16_t *tagCodeHead) {
pthread_mutex_lock(&deviceMappingMutex);
for (int i = 0; i < MAX_DEVICE_MAPPINGS; i++) {
if (strcmp(g_device_mappings[i].deviceName, deviceName) == 0) {
*tagCode = g_device_mappings[i].tagCode;
*tagCodeHead = g_device_mappings[i].tagCodeHead;
pthread_mutex_unlock(&deviceMappingMutex);
return 0; // 找到
}
}
pthread_mutex_unlock(&deviceMappingMutex);
return -1; // 未找到
}
void cleanup_expired_mappings(void) {
pthread_mutex_lock(&deviceMappingMutex);
uint64_t current_time = time(NULL) * 1000;
uint64_t expire_time = 30 * 1000; // 30秒过期
for (int i = 0; i < MAX_DEVICE_MAPPINGS; i++) {
if (strlen(g_device_mappings[i].deviceName) > 0) {
if (current_time - g_device_mappings[i].timestamp > expire_time) {
LOG_I("Cleaning up expired mapping: %s\n", g_device_mappings[i].deviceName);
memset(&g_device_mappings[i], 0, sizeof(device_tag_mapping_t));
}
}
}
pthread_mutex_unlock(&deviceMappingMutex);
}
/*================================================================================*/
// 上报灯条子设备登出
void report_lightbar_logout(uint16_t tagCodeHead, uint32_t tagCode) {
char subDeviceKey[32] = {0};
char topic[256] = {0};
char payload[512] = {0};
char password[64] = {0};
char stringToSign[256] = {0};
char timestamp_str[32] = {0};
struct timeval tv;
// 构建subDeviceKey: 使用完整的tagID (tagCodeHead + tagCode)
snprintf(subDeviceKey, sizeof(subDeviceKey), "%04X%08X", tagCodeHead, tagCode);
// 获取当前时间戳(毫秒)
gettimeofday(&tv, NULL);
long timestamp = tv.tv_sec * 1000 + tv.tv_usec / 1000;
snprintf(timestamp_str, sizeof(timestamp_str), "%ld", timestamp);
// 构建待签名串: subDeviceKey + subDeviceKey + productKey + algorithm + timestamp
snprintf(stringToSign, sizeof(stringToSign), "%s%s%s%s%s",
subDeviceKey, subDeviceKey, "WcSubLightStrip", MQTT_ALGORITHM, timestamp_str);
// 使用HMAC-SHA1计算password
hmacsha1_hex("EKmmjqyLRgghANEiQAA5LZ", stringToSign, password, sizeof(password));
// 构建topic (logout)
snprintf(topic, sizeof(topic), "/auk/iot/things/v1/sub/device/up/logout/WcSubLightStrip/%s/%ld",
subDeviceKey, timestamp);
// 构建JSON payload
snprintf(payload, sizeof(payload),
"{\"password\":\"%s\",\"timestamp\":%ld,\"signingAlgorithm\":\"%s\"}",
password, timestamp, MQTT_ALGORITHM);
LOG_I("Lightbar logout report - topic: %s\n", topic);
LOG_I("Lightbar logout report - payload: %s\n", payload);
// 发送MQTT消息
mqtt_utils_publish(&mqtt_config, topic, 0, payload, strlen(payload));
}
/*================================================================================*/
// 更新灯条心跳时间
void update_lightbar_heartbeat(uint16_t tagCodeHead, uint32_t tagCode) {
pthread_mutex_lock(&heartbeatMutex);
// 查找是否已存在该灯条
int found = -1;
for (int i = 0; i < lightbarHeartbeatCount; i++) {
if (lightbarHeartbeat[i].tagCode == tagCode && lightbarHeartbeat[i].tagCodeHead == tagCodeHead) {
found = i;
break;
}
}
time_t now = time(NULL);
if (found >= 0) {
// 心跳丢失后心跳返回,更新已存在灯条的心跳时间
lightbarHeartbeat[found].lastHeartbeat = now;
if (!lightbarHeartbeat[found].isOnline) {
lightbarHeartbeat[found].isOnline = true;
LOG_I("Lightbar %04X%08X back online\n", tagCodeHead, tagCode);
// 只有MQTT连接后才上报登录
if (isMqttConnected) {
report_lightbar_login(tagCodeHead, tagCode);
} else {
LOG_I("MQTT not connected yet, skip lightbar login report\n");
}
}
} else {
// 添加新的灯条记录
if (lightbarHeartbeatCount < MAX_LIGHTBAR_NUM) {
lightbarHeartbeat[lightbarHeartbeatCount].tagCodeHead = tagCodeHead;
lightbarHeartbeat[lightbarHeartbeatCount].tagCode = tagCode;
snprintf(lightbarHeartbeat[lightbarHeartbeatCount].fullTagId, sizeof(lightbarHeartbeat[lightbarHeartbeatCount].fullTagId), "%04X%08X", tagCodeHead, tagCode);
lightbarHeartbeat[lightbarHeartbeatCount].lastHeartbeat = now;
lightbarHeartbeat[lightbarHeartbeatCount].isOnline = true;
lightbarHeartbeatCount++;
//LOG_I("New lightbar %04X%08X registered, total: %d\n", tagCodeHead, tagCode, lightbarHeartbeatCount);
// 只有MQTT连接后才上报登录
if (isMqttConnected) {
report_lightbar_login(tagCodeHead, tagCode);
} else {
LOG_I("MQTT not connected yet, skip lightbar login report\n");
}
}
}
pthread_mutex_unlock(&heartbeatMutex);
}
// 心跳检测线程 - 每分钟检查一次
void *thread_heartbeat_check(void *arg) {
LOG_I("Heartbeat check thread started\n");
while (1) {
sleep(60); // 每60秒检查一次
time_t now = time(NULL);
pthread_mutex_lock(&heartbeatMutex);
for (int i = 0; i < lightbarHeartbeatCount; i++) {
// 只检查已经有过心跳记录的灯条(避免启动时误报)
if (lightbarHeartbeat[i].isOnline && lightbarHeartbeat[i].lastHeartbeat > 0) {
time_t elapsed = now - lightbarHeartbeat[i].lastHeartbeat;
if (elapsed > HEARTBEAT_TIMEOUT_SEC) {
lightbarHeartbeat[i].isOnline = false;
LOG_I("!!! Lightbar %06X OFFLINE - no heartbeat for %ld seconds (>%d sec)\n",
lightbarHeartbeat[i].tagCode, (long)elapsed, HEARTBEAT_TIMEOUT_SEC);
report_lightbar_logout(lightbarHeartbeat[i].tagCodeHead, lightbarHeartbeat[i].tagCode);
}
}
}
pthread_mutex_unlock(&heartbeatMutex);
}
return NULL;
}
// 灭灯检测线程 - 每5秒检查一次
void *thread_light_off_check(void *arg) {
LOG_I("Light off check thread started\n");
while (1) {
sleep(5); // 每5秒检查一次
time_t now = time(NULL);
pthread_mutex_lock(&lightTaskMutex);
// 先清理已完成的任务
for (int i = 0; i < pendingLightTaskCount; ) {
if (pendingLightTasks[i].reported && pendingLightTasks[i].lightOffReported) {
// 清理已完成的任务 - 将最后一个任务移到当前位置
if (i < pendingLightTaskCount - 1) {
pendingLightTasks[i] = pendingLightTasks[pendingLightTaskCount - 1];
}
pendingLightTaskCount--;
// 不增加i因为当前位置现在是新任务
} else {
i++;
}
}
for (int i = 0; i < pendingLightTaskCount; i++) {
// 只检查已上报亮灯成功但未上报灭灯的任务
if (pendingLightTasks[i].reported && !pendingLightTasks[i].lightOffReported) {
time_t elapsed = now - pendingLightTasks[i].reportTime;
// 检查是否超过持续时间
if (elapsed >= pendingLightTasks[i].duration) {
LOG_I("Light-off triggered for tag %08X - elapsed %ld seconds (duration: %d)\n",
pendingLightTasks[i].tagCode, (long)elapsed, pendingLightTasks[i].duration);
// 上报灭灯
report_light_off(pendingLightTasks[i].tagCodeHead, pendingLightTasks[i].tagCode);
}
}
}
pthread_mutex_unlock(&lightTaskMutex);
}
return NULL;
}
/*================================================================================*/
// 模拟亮灯线程函数 - 每30秒发送一次亮灯任务
void *thread_simulate_light(void *arg){
LOG_I("thread_simulate_light started\n");
char sim_payload[1024] = {0};
char device_specific_payload[1024] = {0};
char last_deviceName[256] = {0}; // 记录上次的deviceName
int first_run = 1; // 首次运行标志
while(1){
sleep(30); // 每30秒执行一次
// 检查是否有从topic解析出的deviceName
if(strlen(g_mqtt_deviceName) > 0) {
// 检查deviceName是否有变化
if(strcmp(g_mqtt_deviceName, last_deviceName) != 0 || first_run) {
LOG_I("simulate_light: detected new deviceName from topic: %s\n", g_mqtt_deviceName);
// 保存当前deviceName
strcpy(last_deviceName, g_mqtt_deviceName);
first_run = 0;
// 立即响应使用deviceName构建设备特定的payload
snprintf(device_specific_payload, sizeof(device_specific_payload),
"{\"deviceId\":\"%s\",\"params\":{\"LightSwitch\":1}}",
g_mqtt_deviceName);
LOG_I("simulate_light: immediate response with device_specific payload: %s\n", device_specific_payload);
// 直接把设备特定的payload放入消息队列
PutDataIntoMQueue(device_specific_payload);
} else {
LOG_I("simulate_light: deviceName unchanged: %s, skipping duplicate simulation\n", g_mqtt_deviceName);
}
} else {
// 从/root/payload文件读取内容原有逻辑
FILE *fp = fopen("/root/payload", "r");
if(fp == NULL){
LOG_I("simulate_light: cannot open /root/payload and no deviceName from topic\n");
continue;
}
memset(sim_payload, 0, sizeof(sim_payload));
if(fgets(sim_payload, sizeof(sim_payload), fp) != NULL){
// 去掉换行符
int len = strlen(sim_payload);
if(len > 0 && sim_payload[len-1] == '\n'){
sim_payload[len-1] = '\0';
}
LOG_I("simulate_light: using payload from file\n");
LOG_I("payload: %s\n", sim_payload);
// 直接把payload放入消息队列由thread_mqtt_recv处理
PutDataIntoMQueue(sim_payload);
}
fclose(fp);
}
}
pthread_exit(NULL);
}
/*================================================================================*/
// 模拟MQTT topic线程 - 监控文件并模拟接收MQTT消息
void *thread_simulate_mqtt_topic(void *arg){
LOG_I("thread_simulate_mqtt_topic started\n");
char sim_topic[512] = {0};
char sim_payload[1024] = {0};
while(1){
sleep(2); // 每2秒检查一次
// 检查模拟topic文件
FILE *fp_topic = fopen("/root/sim_topic", "r");
if(fp_topic == NULL){
continue;
}
// 读取topic内容
memset(sim_topic, 0, sizeof(sim_topic));
if(fgets(sim_topic, sizeof(sim_topic), fp_topic) != NULL){
// 去掉换行符
int len = strlen(sim_topic);
if(len > 0 && sim_topic[len-1] == '\n'){
sim_topic[len-1] = '\0';
}
// 检查是否是lightOperate/invoke topic
if(strstr(sim_topic, "/sys/") && strstr(sim_topic, "/thing/service/lightOperate/invoke")) {
// 解析topic获取deviceName
char productKey[256] = {0};
char deviceName[256] = {0};
// 使用类似mqtt_utils_parse_sys_lightOperate_invoke_topic的逻辑
if(sscanf(sim_topic, "/sys/%255[^/]/%255[^/]/thing/service/lightOperate/invoke", productKey, deviceName) == 2) {
LOG_I("simulate_mqtt_topic: parsed productKey=%s, deviceName=%s\n", productKey, deviceName);
// 设置全局变量模拟mqtt_utils_message_arrived的行为
memset(g_mqtt_deviceName, 0, sizeof(g_mqtt_deviceName));
strncpy(g_mqtt_deviceName, deviceName, sizeof(g_mqtt_deviceName) - 1);
LOG_I("simulate_mqtt_topic: set g_mqtt_deviceName=%s\n", g_mqtt_deviceName);
// 读取payload文件
FILE *fp_payload = fopen("/root/sim_payload", "r");
if(fp_payload != NULL) {
memset(sim_payload, 0, sizeof(sim_payload));
if(fgets(sim_payload, sizeof(sim_payload), fp_payload) != NULL) {
// 去掉换行符
len = strlen(sim_payload);
if(len > 0 && sim_payload[len-1] == '\n'){
sim_payload[len-1] = '\0';
}
LOG_I("simulate_mqtt_topic: sending payload to queue: %s\n", sim_payload);
// 将payload放入消息队列模拟接收到的MQTT消息
PutDataIntoMQueue(sim_payload);
}
fclose(fp_payload);
// 删除payload文件
unlink("/root/sim_payload");
} else {
// 使用默认payload
char default_payload[] = "{\"method\":\"thing.service.lightOperate\",\"params\":{\"LightSwitch\":1}}";
LOG_I("simulate_mqtt_topic: using default payload: %s\n", default_payload);
PutDataIntoMQueue(default_payload);
}
}
}
}
fclose(fp_topic);
// 删除topic文件
unlink("/root/sim_topic");
}
pthread_exit(NULL);
}
/*================================================================================*/
// 全场亮灯线程 - 从/root/all_light文件读取配置并触发全场亮灯
void *thread_all_light(void *arg){
LOG_I("thread_all_light started\n");
char all_light_payload[256] = {0};
while(1){
sleep(5); // 每5秒检查一次
// 从/root/all_light文件读取内容
FILE *fp = fopen("/root/all_light", "r");
if(fp == NULL){
continue;
}
memset(all_light_payload, 0, sizeof(all_light_payload));
if(fgets(all_light_payload, sizeof(all_light_payload), fp) != NULL){
// 去掉换行符
int len = strlen(all_light_payload);
if(len > 0 && all_light_payload[len-1] == '\n'){
all_light_payload[len-1] = '\0';
}
// 解析配置: 格式为 "color,flash,sound,duration" 例如 "4,3,0,30"
// color: 0=不亮,1=蓝,2=绿,3=青,4=红,5=紫,6=黄,7=白
// flash: 1=常亮,3=闪烁
// sound: 0=关闭,1=开启
// duration: 持续时间(秒)
int color=4, flash=3, sound=0, duration=30;
if(sscanf(all_light_payload, "%d,%d,%d,%d", &color, &flash, &sound, &duration) >= 1){
LOG_I("all_light: color=%d, flash=%d, sound=%d, duration=%d\n", color, flash, sound, duration);
// 设置全场亮灯参数
allLightColor = color;
allLightFlash = flash;
allLightSound = sound;
allLightDuration = duration;
// 等待上一个命令完成
while(!isSendComEnd){
usleep(100*1000);
}
// 清空串口缓冲区
tcflush(uartSend.uart_fd, TCIOFLUSH);
// 设置全场亮灯标志
isSendComEnd = false;
isLightOnByGroup = false;
isLightOnById = false;
isLightOn = false;
isBindTag = false;
isAllLightOn = true;
// 发送按ID范围点亮命令 '*' 表示按ID点亮
// timeout单位是5秒所以 duration(秒) / 5 = timeout
uart_data_send_head(&uartSend, '*', 5, duration/5, 0);
LOG_I("all_light: command sent\n");
}
}
fclose(fp);
// 删除文件,避免重复触发
remove("/root/all_light");
}
pthread_exit(NULL);
}
/*================================================================================*/
void doCommand_help(int argc, char *argv[])
{
printf("************************************************************\r\n"\
"all commands are case insensitive\r\n"\
"help print this message\r\n"\
"SND [01-60] set sound S01-S60\r\n"\
"************************************************************\r\n");
}
void doCommand(int argc, char *argv[])
{
//sendData(argv[1],argv[0]);
}
__command_initialize("RON", doCommand);
__command_initialize("help",doCommand_help);
/*================================================================================*/
void do_removelog(void)
{
LOG_I("%s\n",__func__);
FILE *fp;
char buffer[5];
char syscmd[32];
int logcount=0;
fp=popen("ls Log.*|wc -l", "r");
fgets(buffer,sizeof(buffer),fp);
logcount=atoi(buffer);
LOG_I("logcount:%d\n",logcount);
if(logcount>=3){
sprintf(syscmd,"ls Log.*|head -%d|xargs rm -fr",logcount-2);
LOG_I("%s\n",syscmd);
system(syscmd);
}else{
LOG_I("logcount less than 3,not remove\n");
}
}
void removeLog(){
int len=0;
char *savedtime=NULL;
char nowtime[16]={0};
long now=0;
long saved=0;
while(1){
now=getCurrentTime();
sprintf(nowtime,"%ld",now);
savedtime=file_to_buffer("logTime",&len);
if(savedtime==NULL){
buffer_to_file("logTime",nowtime,strlen(nowtime),"wb");
savedtime=file_to_buffer("logTime",&len);
}
saved=atol(savedtime);
LOG_I("now=%ld,saved=%ld,nowtime=%s,savedtime=%s\n",now,saved,nowtime,savedtime);
if(now-saved>=(60*60*24*5)){
//if(now-saved>=10){
buffer_to_file("logTime",nowtime,strlen(nowtime),"wb");
do_removelog();
}
sleep(60*60*1);
}
}
void enableWatchDog(){
system("echo 1 > /sys/class/gpio/export");//watchdog enable pin SGM820
system("echo 112 > /sys/class/gpio/export");//feed watchdog pin red
system("echo out > /sys/class/gpio/gpio1/direction");
system("echo out > /sys/class/gpio/gpio112/direction");
system("echo 1 > /sys/class/gpio/gpio1/value");
LOG_I("enable watchdog\n");
system("echo 1 > /sys/class/gpio/gpio112/value");
}
void feedWatchDog(){
while(1){
//LOG_I("feed watchdog\n");
system("echo 0 > /sys/class/gpio/gpio112/value");
usleep(100*1000);
system("echo 1 > /sys/class/gpio/gpio112/value");
sleep(1);
}
}
void report_tag(void){
while(1){
sleep(60*10);
LOG_I("report_tag\n");
//light_status_report();
}
}
// GPIO113控制函数
void setGpio113High() {
int fd = open("/sys/class/gpio/gpio113/value", O_WRONLY);
if (fd < 0) {
LOG_I("Failed to open gpio113 value: %s\n", strerror(errno));
return;
}
write(fd, "0", 1);
usleep(100*1000);
write(fd, "1", 1);
close(fd);
LOG_I("GPIO113 set to HIGH\n");
}
// GPIO114控制函数
void setGpio114High() {
int fd = open("/sys/class/gpio/gpio114/value", O_WRONLY);
if (fd < 0) {
LOG_I("Failed to open gpio114 value: %s\n", strerror(errno));
return;
}
write(fd, "0", 1);
usleep(100*1000);
write(fd, "1", 1);
close(fd);
LOG_I("GPIO114 set to HIGH\n");
}
void setGpio113Low() {
int fd = open("/sys/class/gpio/gpio113/value", O_WRONLY);
if (fd < 0) {
LOG_I("Failed to open gpio113 value: %s\n", strerror(errno));
return;
}
write(fd, "0", 1);
close(fd);
LOG_I("GPIO113 set to LOW\n");
}
// GPIO115控制函数
void setGpio115High() {
int fd = open("/sys/class/gpio/gpio115/value", O_WRONLY);
if (fd < 0) {
LOG_I("Failed to open gpio115 value: %s\n", strerror(errno));
return;
}
write(fd, "0", 1);
usleep(100*1000);
write(fd, "1", 1);
close(fd);
LOG_I("GPIO115 set to HIGH\n");
}
void setGpio115Low() {
int fd = open("/sys/class/gpio/gpio115/value", O_WRONLY);
if (fd < 0) {
LOG_I("Failed to open gpio115 value: %s\n", strerror(errno));
return;
}
write(fd, "0", 1);
close(fd);
LOG_I("GPIO115 set to LOW\n");
}
// GPIO117控制函数
void setGpio117High() {
int fd = open("/sys/class/gpio/gpio117/value", O_WRONLY);
if (fd < 0) {
LOG_I("Failed to open gpio117 value: %s\n", strerror(errno));
return;
}
write(fd, "0", 1);
usleep(100*1000);
write(fd, "1", 1);
close(fd);
LOG_I("GPIO117 set to HIGH\n");
}
void setGpio117Low() {
int fd = open("/sys/class/gpio/gpio117/value", O_WRONLY);
if (fd < 0) {
LOG_I("Failed to open gpio117 value: %s\n", strerror(errno));
return;
}
write(fd, "0", 1);
close(fd);
LOG_I("GPIO117 set to LOW\n");
}
int readQrcode()
{
fd = open("/dev/input/event2", O_RDONLY);
if (fd < 0)
{
LOG_I("can not open scanner input event2!\n");
goto error;
}
while (1)
{
while (read(fd, &buff, sizeof(struct input_event)) == 0)
{
;
}
if (buff.type == EV_KEY){
if(buff.value==0){
#if 0
LOG_I("type:%d code:%d value:%d\n", buff.type, buff.code, buff.value);
#else
switch(buff.code){
case 2:
if(UPCASE==1){
input_value[count_value]='!';
}else{
input_value[count_value]='1';
}
UPCASE=0;
count_value++;
break;
case 3:
if(UPCASE==1){
input_value[count_value]='@';
}else{
input_value[count_value]='2';
}
UPCASE=0;
count_value++;
break;
case 4:
if(UPCASE==1){
input_value[count_value]='#';
}else{
input_value[count_value]='3';
}
UPCASE=0;
count_value++;
break;
case 5:
if(UPCASE==1){
input_value[count_value]='$';
}else{
input_value[count_value]='4';
}
UPCASE=0;
count_value++;
break;
case 6:
if(UPCASE==1){
input_value[count_value]='%';
}else{
input_value[count_value]='5';
}
UPCASE=0;
count_value++;
break;
case 7:
if(UPCASE==1){
input_value[count_value]='^';
}else{
input_value[count_value]='6';
}
UPCASE=0;
count_value++;
break;
case 8:
if(UPCASE==1){
input_value[count_value]='&';
}else{
input_value[count_value]='7';
}
UPCASE=0;
count_value++;
break;
case 9:
if(UPCASE==1){
input_value[count_value]='*';
}else{
input_value[count_value]='8';
}
UPCASE=0;
count_value++;
break;
case 10:
if(UPCASE==1){
input_value[count_value]='(';
}else{
input_value[count_value]='9';
}
UPCASE=0;
count_value++;
break;
case 11:
if(UPCASE==1){
input_value[count_value]=')';
}else{
input_value[count_value]='0';
}
UPCASE=0;
count_value++;
break;
case 12:
if(UPCASE==1){
input_value[count_value]='_';
}else{
input_value[count_value]='-';
}
UPCASE=0;
count_value++;
break;
case 13:
if(UPCASE==1){
input_value[count_value]='+';
}else{
input_value[count_value]='=';
}
UPCASE=0;
count_value++;
break;
case 16:
if(UPCASE==1){
input_value[count_value]='Q';
}else{
input_value[count_value]='q';
}
UPCASE=0;
count_value++;
break;
case 17:
if(UPCASE==1){
input_value[count_value]='W';
}else{
input_value[count_value]='w';
}
UPCASE=0;
count_value++;
break;
case 18:
if(UPCASE==1){
input_value[count_value]='E';
}else{
input_value[count_value]='e';
}
UPCASE=0;
count_value++;
break;
case 19:
if(UPCASE==1){
input_value[count_value]='R';
}else{
input_value[count_value]='r';
}
UPCASE=0;
count_value++;
break;
case 20:
if(UPCASE==1){
input_value[count_value]='T';
}else{
input_value[count_value]='T';
}
UPCASE=0;
count_value++;
break;
case 21:
if(UPCASE==1){
input_value[count_value]='Y';
}else{
input_value[count_value]='y';
}
UPCASE=0;
count_value++;
break;
case 22:
if(UPCASE==1){
input_value[count_value]='U';
}else{
input_value[count_value]='u';
}
UPCASE=0;
count_value++;
break;
case 23:
if(UPCASE==1){
input_value[count_value]='I';
}else{
input_value[count_value]='i';
}
UPCASE=0;
count_value++;
break;
case 24:
if(UPCASE==1){
input_value[count_value]='O';
}else{
input_value[count_value]='o';
}
UPCASE=0;
count_value++;
break;
case 25:
if(UPCASE==1){
input_value[count_value]='P';
}else{
input_value[count_value]='p';
}
UPCASE=0;
count_value++;
break;
case 26:
if(UPCASE==1){
input_value[count_value]='{';
}else{
input_value[count_value]='[';
}
UPCASE=0;
count_value++;
break;
case 27:
if(UPCASE==1){
input_value[count_value]='}';
}else{
input_value[count_value]=']';
}
UPCASE=0;
count_value++;
break;
case 28:
input_value[count_value]='\0';
//LOG_I("%s\n",input_value);
memset(input_value_copy,0,1024);
memcpy(input_value_copy,input_value,1024);
memset(input_value,0,1024);
count_value=0;
int ret=-1;
pthread_t pt_handleqrcode;
ret = pthread_create(&pt_handleqrcode,NULL,actHandleQrcode,input_value_copy);
if(ret!=0){
LOG_I("pthread_create handleqrcode fail\n");
system("reboot");
}else{
pthread_detach(pt_handleqrcode);
}
break;
case 30:
if(UPCASE==1){
input_value[count_value]='A';
}else{
input_value[count_value]='a';
}
UPCASE=0;
count_value++;
break;
case 31:
if(UPCASE==1){
input_value[count_value]='S';
}else{
input_value[count_value]='s';
}
UPCASE=0;
count_value++;
break;
case 32:
if(UPCASE==1){
input_value[count_value]='D';
}else{
input_value[count_value]='d';
}
UPCASE=0;
count_value++;
break;
case 33:
if(UPCASE==1){
input_value[count_value]='F';
}else{
input_value[count_value]='f';
}
UPCASE=0;
count_value++;
break;
case 34:
if(UPCASE==1){
input_value[count_value]='G';
}else{
input_value[count_value]='g';
}
UPCASE=0;
count_value++;
break;
case 35:
if(UPCASE==1){
input_value[count_value]='H';
}else{
input_value[count_value]='h';
}
UPCASE=0;
count_value++;
break;
case 36:
if(UPCASE==1){
input_value[count_value]='J';
}else{
input_value[count_value]='j';
}
UPCASE=0;
count_value++;
break;
case 37:
if(UPCASE==1){
input_value[count_value]='K';
}else{
input_value[count_value]='k';
}
UPCASE=0;
count_value++;
break;
case 38:
if(UPCASE==1){
input_value[count_value]='L';
}else{
input_value[count_value]='l';
}
UPCASE=0;
count_value++;
break;
case 39:
if(UPCASE==1){
input_value[count_value]=':';
}else{
input_value[count_value]=';';
}
UPCASE=0;
count_value++;
break;
case 40:
if(UPCASE==1){
input_value[count_value]='"';
}else{
input_value[count_value]='\'';
}
UPCASE=0;
count_value++;
break;
case 42:
UPCASE=1;
break;
case 43:
if(UPCASE==1){
input_value[count_value]='|';
}else{
input_value[count_value]='\\';
}
UPCASE=0;
count_value++;
break;
case 44:
if(UPCASE==1){
input_value[count_value]='Z';
}else{
input_value[count_value]='z';
}
UPCASE=0;
count_value++;
break;
case 45:
if(UPCASE==1){
input_value[count_value]='X';
}else{
input_value[count_value]='x';
}
UPCASE=0;
count_value++;
break;
case 46:
if(UPCASE==1){
input_value[count_value]='C';
}else{
input_value[count_value]='c';
}
UPCASE=0;
count_value++;
break;
case 47:
if(UPCASE==1){
input_value[count_value]='V';
}else{
input_value[count_value]='v';
}
UPCASE=0;
count_value++;
break;
case 48:
if(UPCASE==1){
input_value[count_value]='B';
}else{
input_value[count_value]='b';
}
UPCASE=0;
count_value++;
break;
case 49:
if(UPCASE==1){
input_value[count_value]='N';
}else{
input_value[count_value]='n';
}
UPCASE=0;
count_value++;
break;
case 50:
if(UPCASE==1){
input_value[count_value]='M';
}else{
input_value[count_value]='m';
}
UPCASE=0;
count_value++;
break;
case 51:
if(UPCASE==1){
input_value[count_value]='<';
}else{
input_value[count_value]=',';
}
UPCASE=0;
count_value++;
break;
case 52:
if(UPCASE==1){
input_value[count_value]='>';
}else{
input_value[count_value]='.';
}
UPCASE=0;
count_value++;
break;
case 53:
if(UPCASE==1){
input_value[count_value]='?';
}else{
input_value[count_value]='/';
}
UPCASE=0;
count_value++;
break;
}
#endif
}
}
}
close(fd);
return 0;
error:
return 1;
}
#if 0
void writeWpa(char *filename,char *ssid,char *psw){
LOG_I("%s\n",__func__);
buffer_to_file(filename,"# WPA-PSK/TKIP\n",strlen("# WPA-PSK/TKIP\n"),"wb");
buffer_to_file(filename,"ap_scan=1\n",strlen("ap_scan=1\n"),"a");
buffer_to_file(filename,"ctrl_interface=/var/run/wpa_supplicant\n",strlen("ctrl_interface=/var/run/wpa_supplicant\n"),"a");
buffer_to_file(filename,"network={\n",strlen("network={\n"),"a");
buffer_to_file(filename,"ssid=\"",strlen("ssid=\""),"a");
buffer_to_file(filename,ssid,strlen(ssid),"a");
buffer_to_file(filename,"\"",strlen("\""),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
buffer_to_file(filename,"key_mgmt=WPA-PSK\n",strlen("key_mgmt=WPA-PSK\n"),"a");
buffer_to_file(filename,"proto=WPA RSN\n",strlen("proto=WPA RSN\n"),"a");
buffer_to_file(filename,"pairwise=CCMP TKIP\n",strlen("pairwise=CCMP TKIP\n"),"a");
buffer_to_file(filename,"group=CCMP TKIP\n",strlen("group=CCMP TKIP\n"),"a");
buffer_to_file(filename,"psk=\"",strlen("psk=\""),"a");
buffer_to_file(filename,psw,strlen(psw),"a");
buffer_to_file(filename,"\"",strlen("\""),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
buffer_to_file(filename,"}\n",strlen("}\n"),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
}
void writeWlan(char *filename,char *netway,char *gate,char *ip){
LOG_I("%s\n",__func__);
buffer_to_file(filename,"METHOD=",strlen("METHOD="),"wb");
buffer_to_file(filename,netway,strlen(netway),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
if(strcmp(netway,"STATIC")==0){
buffer_to_file(filename,"IPADDR=",strlen("IPADDR="),"a");
buffer_to_file(filename,ip,strlen(ip),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
buffer_to_file(filename,"NETMASK=255.255.255.0\n",strlen("NETMASK=255.255.255.0\n"),"a");
buffer_to_file(filename,"GATEWAY=",strlen("GATEWAY="),"a");
buffer_to_file(filename,gate,strlen(gate),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
}
}
void removeFile(char *filename){
char cmd[32]={0};
sprintf(cmd,"rm %s",filename);
//LOG_I("%s\n",cmd);
system(cmd);
}
#endif
void hmacsha1_hex(char *key, char* data, char *signhex, int signhex_len) {
unsigned char result[256] = {0};
size_t len = sizeof(result);
hmac_sha1((const uint8_t*)key, strlen(key), (const uint8_t*)data, strlen(data), result, &len);
// 转换为十六进制格式
for (int i = 0; i < len && i * 2 + 1 < signhex_len; i++) {
snprintf(signhex + i * 2, 3, "%02x", result[i]);
}
}
void writeNetworkInterface(char *filename,char *dest){
buffer_to_file(filename,"# interfaces(5) file used by ifup(8) and ifdown(8)",
strlen("# interfaces(5) file used by ifup(8) and ifdown(8)"),"wb");
buffer_to_file(filename,"\n",strlen("\n"),"a");
buffer_to_file(filename,"# Include files from /etc/network/interfaces.d:",strlen("# Include files from /etc/network/interfaces.d:"),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
buffer_to_file(filename,"source-directory /etc/network/interfaces.d",strlen("source-directory /etc/network/interfaces.d"),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
buffer_to_file(filename,"auto lo",strlen("auto lo"),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
buffer_to_file(filename,"iface lo inet loopback",strlen("iface lo inet loopback"),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
buffer_to_file(filename,"auto eth0",strlen("auto eth0"),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
buffer_to_file(filename,"iface eth0 inet dhcp",strlen("iface eth0 inet dhcp"),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
buffer_to_file(filename,"# address 10.10.12.12",strlen("# address 10.10.12.12"),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
buffer_to_file(filename,"# netmask 255.255.255.0",strlen("# netmask 255.255.255.0"),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
buffer_to_file(filename,"# gateway 10.10.12.1",strlen("# gateway 10.10.12.1"),"a");
buffer_to_file(filename,"\n",strlen("\n"),"a");
buffer_to_file(filename,"hwaddress ether ",strlen("hwaddress ether "),"a");
buffer_to_file(filename,dest,strlen(dest),"a");
system("sync");
}
char* insert_colon(const char* src, int interval) {
if (!src || interval <= 0) return NULL;
size_t len = strlen(src);
if (len == 0) { // 空字符串返回空
char* res = malloc(1);
if (res) res[0] = '\0';
return res;
}
// 计算新字符串长度
size_t sections = (len + interval - 1) / interval; // 总段数
size_t new_len = len + (sections - 1); // 新长度 = 原长 + 冒号数
char* new_str = malloc(new_len + 1); // 分配内存:ml-citation{ref="2" data="citationList"}
if (!new_str) return NULL;
size_t src_idx = 0, dst_idx = 0;
while (src_idx < len) {
// 计算本次复制的字符数
size_t copy_size = (len - src_idx < (size_t)interval) ? (len - src_idx) : (size_t)interval;
memcpy(new_str + dst_idx, src + src_idx, copy_size); // 块复制:ml-citation{ref="3" data="citationList"}
src_idx += copy_size;
dst_idx += copy_size;
// 非末尾段时插入冒号
if (src_idx < len) new_str[dst_idx++] = ':';
}
new_str[dst_idx] = '\0'; // 终止符:ml-citation{ref="7" data="citationList"}
return new_str;
}
void *actHandleQrcode(void *parm){
char *str=(char *)parm;
LOG_I("scan:%s\n",str);
if(strstr(str,"{")!=NULL){
#if 0
char deviceSecret[64]={0};
char deviceCode[64]={0};
char netWorkType[64]={0};
char netWorkSsid[64]={0};
char netWorkPassword[64]={0};
get_string_from_json_string_by_key_unescape(str, "deviceSecret",deviceSecret,64);
LOG_I("deviceSecret:%s\n",deviceSecret);
get_string_from_json_string_by_key_unescape(str, "deviceCode",deviceCode,64);
LOG_I("deviceCode:%s\n",deviceCode);
if(strcmp(deviceSecret,"")!=0 && strcmp(deviceCode,"")!=0){
buffer_to_file("deviceSecret",deviceSecret,strlen(deviceSecret),"wb");
buffer_to_file("savedDevSn",deviceCode,strlen(deviceCode),"wb");
}
get_string_from_json_string_by_key_unescape(str, "netWorkType",netWorkType,64);
LOG_I("netWorkType:%s\n",netWorkType);
if(strcmp(netWorkType,"net_wifi")==0){
buffer_to_file("nettype","net_wifi",strlen("net_wifi"),"wb");
get_string_from_json_string_by_key_unescape(str, "netWorkSsid",netWorkSsid,64);
LOG_I("netWorkSsid:%s\n",netWorkSsid);
get_string_from_json_string_by_key_unescape(str, "netWorkPassword",netWorkPassword,64);
LOG_I("netWorkPassword:%s\n",netWorkPassword);
if(strcmp(netWorkSsid,"")!=0 && strcmp(netWorkPassword,"")!=0){
writeWpa("/etc/wpa_supplicant.conf",netWorkSsid,netWorkPassword);
sleep(1);
system("reboot");
}else{
}
}else if(strcmp(netWorkType,"net_wired")==0){
buffer_to_file("nettype","net_wired",strlen("net_wired"),"wb");
LOG_I("use wired\n");
sleep(1);
system("reboot");
}else if(strcmp(netWorkType,"net_mobile")==0){
buffer_to_file("nettype","net_mobile",strlen("net_mobile"),"wb");
LOG_I("use mobile\n");
sleep(1);
system("reboot");
}
#endif
}else{
#if 1
if(strstr(str,"d")!=NULL && strlen(str)>=27){
LOG_I("ok\n");
char *result=NULL;
char *p;
result=strtok_r(str,":",&p);
while(result!=NULL){
if(strstr(result,"d")!=NULL){
LOG_I("save sn:%s\n",result);
buffer_to_file("savedDevSn",result,strlen(result),"wb");
}else{
//LOG_I("save mac:%s\n",result);
//buffer_to_file("savedDevMac",result,strlen(result),"wb");
char* destmac = insert_colon(result, 2);
LOG_I("save mac:%s\n",destmac);
writeNetworkInterface("/etc/network/interfaces",destmac);
}
result=strtok_r(NULL,",",&p);
}
}
#else
if(strstr(str,"d1")!=NULL){
buffer_to_file("savedDevSn",str,strlen(str),"wb");
}
#endif
}
pthread_exit(NULL);
}
char *file_to_buffer(const char *pathname, unsigned int *size){
FILE *fp = NULL;
char *temp = NULL;
fp = fopen(pathname, "rb");
if(fp==NULL){
//LOG_I("open %s error\n", pathname);
}else{
fseek(fp,0,2);
*size = ftell(fp);
rewind(fp);
temp = (char *)malloc(*size);
fread(temp, 1, *size, fp);
fclose(fp);
}
return temp;
}
/*================================================================================*/
void showShellInfo(char *command){
FILE *fp;
char buffer[512]={0};
fp=popen(command, "r");
fread(buffer,1,sizeof(buffer),fp);
LOG_I("\n%s",buffer);
}
void getTimeStr(char *buffer,int len){
time_t timep;
struct tm *p;
time (&timep);
p=localtime(&timep);
strftime (buffer,len,"%Y-%m-%d %H:%M:%S",p);
}
long getCurrentTime(){
struct timeval begin;
gettimeofday(&begin,NULL);
long long beginTime = (long long)begin.tv_sec * 1000 + (long long)begin.tv_usec / 1000;
long last=beginTime/1000;
return last;
}
void myrand(char *randnum,int len){
int i;
srand((unsigned)time(0));
for(i=0;i<=len-1;i++)
randnum[i]=rand()%10 +'0';
randnum[len]='\0';
}
void removeSpaces(char *str) {
int i = 0, j = 0;
while (str[i] != '\0') {
if (str[i] != ' ') {
str[j++] = str[i];
}
i++;
}
str[j] = '\0';
}
void getDevRawPassword(char *devsn){
LOG_I("devsn:%s\n",devsn);
json_object *root = NULL;
const char *payload = NULL;
char send_request[256]={0};
char signbase64[256]={0};
char signtmp[256]={0};
char jsondata[256] = {0};
char tmpdata[256] = {0};
//char tmpdata1[64] = {0};
char buffer[512];
FILE *fp;
root = json_object_new_object();
if(root == NULL){
LOG_I("json_object_new_object error\n");
}else{
json_object_object_add(root, "deviceId",json_object_new_string(devsn));
json_object_object_add(root, "oldPassword",json_object_new_string("12345"));
payload = json_object_to_json_string(root);
removeSpaces(payload);
strcpy(signtmp,payload);
strcat(signtmp,appSecret);
//LOG_I("signtmp:%s\n",signtmp);
get_base64_md5_from_string(signbase64, sizeof(signbase64), signtmp, strlen(signtmp));
//LOG_I("signbase64:%s\n",signbase64);
sprintf(send_request,"curl -X POST -H 'x-appKey:%s' -H 'x-datadigest:%s' -H 'Content-Type: application/json' -d '%s' %s",
appKey,signbase64,payload,getDevRawPasswordUrl);
LOG_I("send_request:%s\n",send_request);
fp=popen(send_request,"r");
fgets(buffer,sizeof(buffer),fp);
//get_string_from_json_string_by_key_unescape(buffer, "message", tmpdata1, sizeof(tmpdata1));
//LOG_I("message: %s\n",tmpdata1);
get_string_from_json_string_by_key_unescape(buffer, "result", tmpdata, sizeof(tmpdata));
LOG_I("result: %s\n",tmpdata);
snprintf(jsondata, sizeof(jsondata), "{%s}", tmpdata);
//LOG_I("jsondata:%s\n",jsondata);
get_string_from_json_string_by_key_unescape(jsondata, "password", mqttRawPassword, sizeof(mqttRawPassword));
LOG_I("save mqttRawPassword:%s\n",mqttRawPassword);
if(strcmp(mqttRawPassword,"")!=0){
buffer_to_file("mqttRawPassword",mqttRawPassword,strlen(mqttRawPassword),"wb");
}
}
}
void checkOtaKey(void){
int len=0;
int keycount=0;
char *readresult=NULL;
while(1){
if(isOtaEnable){
//system("echo 0 > /sys/class/gpio/gpio113/value");
//sleep(1);
//system("echo 1 > /sys/class/gpio/gpio113/value");
sleep(1);
}else{
readresult=file_to_buffer("/sys/class/gpio/gpio63/value",&len);
//LOG_I("resetKey:%s\n",readresult);
if(readresult!=NULL){
if(strcmp(readresult,"0\n")==0){
keycount++;
if(keycount==3){
keycount=0;
isOtaEnable=true;
//isLEDOtaSuccess=false;
//isAPOtaSuccess=false;
//isSendComEnd=true;
LOG_I("OTA enable\n");
update_app();
}
}
}
usleep(100*1000);
}
}
}
void update_app(void){
system("rm -fr ota");
system("mkdir ota");
char otaCmd[256]={0};
sprintf(otaCmd,"curl -o /root/ota/jd_ota.zip %s",updateUrl);
system(otaCmd);
sleep(15);
system("unzip ota/jd_ota.zip");
system("mv jd_ota/* ota");
system("rm -fr jd_ota");
system("mv ota/start.sh /root");
sleep(1);
system("reboot");
}
/*================================================================================*/
void mqtt_init(){
int ret=0;
char username[64] = {0};
char password[64] = {0};
char clientid[256] = {0};
char stringToSign[512] = {0}; // 待签名串
char hostip[16]={0};
struct timeval tv;
// 获取当前时间戳(毫秒)
gettimeofday(&tv, NULL);
long timestamp = tv.tv_sec * 1000 + tv.tv_usec / 1000;
// 使用实时时间戳,确保与服务器时间同步
LOG_I("%ld\n", timestamp);
// 使用白名单中的设备ID
char unique_device_id[32];
// 使用白名单中的设备ID
strcpy(unique_device_id, stationsn);
LOG_I("使用白名单设备ID: %s\n", unique_device_id);
// 使用REGISTER模式进行设备注册
// 因为设备在白名单中但还没注册,需要先注册
const char* authType = MQTT_AUTH_TYPE_AUTH;
char* secretKey = appSecret; // 使用ProductSecret进行注册
LOG_I("%s\n", unique_device_id);
// 根据新协议格式构建ClientId: ProductKey&/DeviceClientId/SecureMode/Algorithm/Timestamp/AuthType
snprintf(clientid, sizeof(clientid), "%s&/%s/%s/%s/%ld/%s",
productid, unique_device_id, MQTT_SECURE_MODE, MQTT_ALGORITHM, timestamp, authType);
// 根据新协议格式构建Username: /ProductKey/DeviceKey
snprintf(username, sizeof(username), "/%s/%s", productid, unique_device_id);
// 构建待签名串: deviceKey + deviceKey + productKey + algorithm + timestamp
// 格式与Python代码保持一致: deviceKey + deviceKey + productKey + 'AUK1_MQTT_HMAC_SHA1' + timestamp
char timestamp_str[32];
snprintf(timestamp_str, sizeof(timestamp_str), "%ld", timestamp);
snprintf(stringToSign, sizeof(stringToSign), "%s%s%s%s%s",
unique_device_id, unique_device_id, productid, MQTT_ALGORITHM, timestamp_str);
// 使用HMAC-SHA1算法生成密码签名十六进制格式
// 根据设备注册状态选择签名密钥:
// - REGISTER模式使用ProductSecret (appSecret)
// - AUTH模式使用DeviceSecret (mqttRawPassword)
hmacsha1_hex(secretKey, stringToSign, password, sizeof(password));
LOG_I("=== 白名单设备注册信息 ===\n");
LOG_I("设备Key: %s\n", unique_device_id);
LOG_I("设备密钥: %s\n", secretKey);
LOG_I("认证模式: %s\n", authType);
LOG_I("ClientId: %s\n", clientid);
LOG_I("Username: %s\n", username);
LOG_I("StringToSign: %s\n", stringToSign);
LOG_I("Password: %s\n", password);
LOG_I("时间戳: %ld\n", timestamp);
LOG_I("========================\n");
mqtt_config.tracelevel=MQTTASYNC_TRACE_PROTOCOL;
mqtt_config.retain=0;
mqtt_config.port=mqtt_port;
mqtt_config.keepalive=180;
memset(mqtt_config.clientid,0,sizeof(mqtt_config.clientid));
memcpy(mqtt_config.clientid, clientid, strlen(clientid)+1);
memset(mqtt_config.productcode, 0, sizeof(mqtt_config.productcode));
memcpy(mqtt_config.productcode, productid, strlen(productid)+1);
memset(mqtt_config.username, 0, sizeof(mqtt_config.username));
memcpy(mqtt_config.username, username, strlen(username)+1);
memset(mqtt_config.password, 0, sizeof(mqtt_config.password));
memcpy(mqtt_config.password, password, strlen(password)+1);
get_ip_by_domain(hostDomain,hostip,16);
LOG_I("MQTT服务器域名: %s, 解析IP: %s\n", hostDomain, hostip);
// 如果DNS解析失败使用硬编码的IP地址
if (strlen(hostip) == 0 || strcmp(hostip, "0.0.0.0") == 0) {
LOG_I("DNS解析失败使用硬编码IP地址\n");
strcpy(hostip, "103.37.154.120"); // 使用日志中看到的IP
}
memset(mqtt_config.host, 0, sizeof(mqtt_config.host));
memcpy(mqtt_config.host, hostip, strlen(hostip)+1);
mqtt_config.MQTTVersion = 4;
/* will options */
mqtt_config.will_topic = NULL;
mqtt_config.will_payload = NULL;
mqtt_config.will_qos = 1;
mqtt_config.will_retain = 0;
/* TLS options */
mqtt_config.insecure = 0;
mqtt_config.capath = NULL;
mqtt_config.cert = NULL;
mqtt_config.cafile = NULL;
mqtt_config.key = NULL;
mqtt_config.keypass = NULL;
mqtt_config.ciphers = NULL;
ret = sem_init(&mqtt_config.sem_connect, 0, 0);
if (ret != 0)
{
LOG_I("sem_connect init error\n");
goto error;
}
ret = mqtt_utils_init(&mqtt_config);
if (ret < 0)
{
LOG_I("mqtt_utils_init error\n");
ret = -1;
goto error;
}
sem_wait(&mqtt_config.sem_connect);
ret = 0;
error:
return;
}
/*================================================================================*/
void *thread_keycheck(void *arg){
checkOtaKey();
}
void *thread_reporttag(void *arg){
report_tag();
}
void *thread_mqtt(void *arg){
mqtt_init();
}
void *thread_removelog(void *arg){
removeLog();
}
void *thread_feed_watchdog(void *arg){
feedWatchDog();
return NULL;
}
void *thread_readqr(void *arg){
readQrcode();
}
void *thread_uart_recv_ack(void *arg){
uint16_t parm_ack;
int ret=0;
while(1){
// 只有在发送命令后才等待ACK避免读取残留数据
if(isSendComEnd){
usleep(100*1000);
continue;
}
ret=uart_data_receive_ack(&uartSend,&parm_ack);
if(ret>0){
LOG_I("ack:%x\n",parm_ack);
if(parm_ack==0x2323){
// 【关键修改】从命令队列获取命令,而不是使用全局变量
light_command_t cmd;
if(get_light_command(&cmd) != 0) {
LOG_I("No light command in queue, using fallback global variables\n");
// 队列为空时的回退处理(保持兼容性)
memset(&cmd, 0, sizeof(cmd));
strncpy(cmd.deviceName, g_mqtt_deviceName, sizeof(cmd.deviceName) - 1);
cmd.color = changecolor;
cmd.sound = changesound;
cmd.flash = changeflash;
// 【修复】明确设置RGB值确保熄灭命令检测正确工作
if (changecolor == 0) {
cmd.light_r = 0;
cmd.light_g = 0;
cmd.light_b = 0;
}
// 【增强】优先使用设备映射,解决多设备并发问题
uint32_t mapped_tagCode;
uint16_t mapped_tagCodeHead;
// 【修复】优先使用命令中保存的deviceName而不是可能被覆盖的全局变量
const char *target_deviceName = cmd.deviceName;
if (strlen(target_deviceName) == 0) {
// 如果命令中没有deviceName才使用全局变量
target_deviceName = g_mqtt_deviceName;
}
// 【新增】检查是否是熄灭命令,如果是,避免在多设备场景下处理
if (cmd.light_r == 0 && cmd.light_g == 0 && cmd.light_b == 0) {
LOG_I("Fallback detected OFF command for %s (RGB=0,0,0), skipping to avoid multi-device conflicts\n", target_deviceName);
continue; // 直接跳过所有熄灭回退命令,避免干扰点亮命令
}
if (get_device_tag_mapping(target_deviceName, &mapped_tagCode, &mapped_tagCodeHead) == 0) {
cmd.tagCode = mapped_tagCode;
cmd.tagCodeHead = mapped_tagCodeHead;
LOG_I("Fallback: using device mapping for %s -> %08X\n", target_deviceName, cmd.tagCode);
} else {
// 回退到全局变量
cmd.tagCode = g_current_tagCode;
cmd.tagCodeHead = g_current_tagCodeHead;
LOG_I("Fallback: using global variables for %s -> %08X\n", target_deviceName, cmd.tagCode);
}
}
// 使用队列中的命令参数构建控制包
jt_led_or_group_package_t led_ctrl={
.s.color=cmd.color,
.s.sound=cmd.sound,
.s.single=0,
.s.flash=cmd.flash,
};//0xC1 11000001 flash高位
jt_led_or_group_package_t group={
.group=groupno,
};//0xC1 11000001 flash高位
//jt_led_or_group_package_t stop={
// .group=0x72,
//};//0xC1 11000001 flash高位
jt_group_package_t groups[1]={{groupno,led_ctrl}};
//jt_group_package_t groups_stop[2]={{0x58,stop},{0xE3,0x00}};
#if 0
uint32_t tag1=strtol(lightsn1,NULL,16);
uint32_t tag2=strtol(lightsn2,NULL,16);
uint32_t tag3=strtol(lightsn3,NULL,16);
uint32_t tag4=strtol(lightsn4,NULL,16);
uint32_t tag5=strtol(lightsn5,NULL,16);
uint32_t tag6=strtol(lightsn6,NULL,16);
uint32_t tag7=strtol(lightsn7,NULL,16);
uint32_t tag8=strtol(lightsn8,NULL,16);
uint32_t tag9=strtol(lightsn9,NULL,16);
uint32_t tag10=strtol(lightsn10,NULL,16);
uint32_t tag11=strtol(lightsn11,NULL,16);
uint32_t tag12=strtol(lightsn12,NULL,16);
uint32_t tag13=strtol(lightsn13,NULL,16);
uint32_t tag14=strtol(lightsn14,NULL,16);
uint32_t tag15=strtol(lightsn15,NULL,16);
uint32_t tag16=strtol(lightsn16,NULL,16);
uint32_t tag17=strtol(lightsn17,NULL,16);
uint32_t tag18=strtol(lightsn18,NULL,16);
uint32_t tag19=strtol(lightsn19,NULL,16);
uint32_t tag20=strtol(lightsn20,NULL,16);
uint32_t tag21=strtol(lightsn21,NULL,16);
uint32_t tag22=strtol(lightsn22,NULL,16);
uint32_t tag23=strtol(lightsn23,NULL,16);
uint32_t tag24=strtol(lightsn24,NULL,16);
uint32_t tag25=strtol(lightsn25,NULL,16);
uint32_t tag26=strtol(lightsn26,NULL,16);
uint32_t tag27=strtol(lightsn27,NULL,16);
uint32_t tag28=strtol(lightsn28,NULL,16);
uint32_t tag29=strtol(lightsn29,NULL,16);
uint32_t tag30=strtol(lightsn30,NULL,16);
#else
//low power
#if 0
uint32_t tag1=strtol("000511",NULL,16);
uint32_t tag2=strtol("00051C",NULL,16);
uint32_t tag3=strtol("000E99",NULL,16);
uint32_t tag4=strtol("004137",NULL,16);
uint32_t tag5=strtol("004348",NULL,16);
uint32_t tag6=strtol("004391",NULL,16);
uint32_t tag7=strtol("004DB8",NULL,16);
uint32_t tag8=strtol("005966",NULL,16);
uint32_t tag9=strtol("014BEE",NULL,16);
uint32_t tag10=strtol("014BF0",NULL,16);
#else
// 【关键修改】使用从队列获取的命令参数
uint32_t tag1=cmd.tagCode;
LOG_I("Using command from queue - device: %s, seq: %llu, tag1=%08X, RGB: %d,%d,%d\n",
cmd.deviceName, cmd.sequence, tag1, cmd.light_r, cmd.light_g, cmd.light_b);
#endif
// 【修复】在多设备场景下只发送给目标设备其他设备设为0
uint32_t tag2=0, tag3=0, tag4=0, tag5=0, tag6=0, tag7=0, tag8=0, tag9=0, tag10=0;
uint32_t tag11=0, tag12=0, tag13=0, tag14=0, tag15=0;
uint32_t tag16=0, tag17=0, tag18=0, tag19=0, tag20=0;
uint32_t tag21=0, tag22=0, tag23=0, tag24=0, tag25=0;
uint32_t tag26=0, tag27=0, tag28=0, tag29=0, tag30=0;
#endif
#if 0
if(isStopBroadcast){
uart_data_send_lighton_by_group(&uartSend,groups_stop,2);
isStopBroadcast=false;
}
#endif
if(isLightOn){
jt_tag_package_t tags[30]={{(tag1>>16)&0xFF,(tag1>>8)&0xFF,(tag1)&0xFF,led_ctrl},
{(tag2>>16)&0xFF,(tag2>>8)&0xFF,(tag2)&0xFF,led_ctrl},
{(tag3>>16)&0xFF,(tag3>>8)&0xFF,(tag3)&0xFF,led_ctrl},
{(tag4>>16)&0xFF,(tag4>>8)&0xFF,(tag4)&0xFF,led_ctrl},
{(tag5>>16)&0xFF,(tag5>>8)&0xFF,(tag5)&0xFF,led_ctrl},
{(tag6>>16)&0xFF,(tag6>>8)&0xFF,(tag6)&0xFF,led_ctrl},
{(tag7>>16)&0xFF,(tag7>>8)&0xFF,(tag7)&0xFF,led_ctrl},
{(tag8>>16)&0xFF,(tag8>>8)&0xFF,(tag8)&0xFF,led_ctrl},
{(tag9>>16)&0xFF,(tag9>>8)&0xFF,(tag9)&0xFF,led_ctrl},
{(tag10>>16)&0xFF,(tag10>>8)&0xFF,(tag10)&0xFF,led_ctrl},
{(tag11>>16)&0xFF,(tag11>>8)&0xFF,(tag11)&0xFF,led_ctrl},
{(tag12>>16)&0xFF,(tag12>>8)&0xFF,(tag12)&0xFF,led_ctrl},
{(tag13>>16)&0xFF,(tag13>>8)&0xFF,(tag13)&0xFF,led_ctrl},
{(tag14>>16)&0xFF,(tag14>>8)&0xFF,(tag14)&0xFF,led_ctrl},
{(tag15>>16)&0xFF,(tag15>>8)&0xFF,(tag15)&0xFF,led_ctrl},
{(tag16>>16)&0xFF,(tag16>>8)&0xFF,(tag16)&0xFF,led_ctrl},
{(tag17>>16)&0xFF,(tag17>>8)&0xFF,(tag17)&0xFF,led_ctrl},
{(tag18>>16)&0xFF,(tag18>>8)&0xFF,(tag18)&0xFF,led_ctrl},
{(tag19>>16)&0xFF,(tag19>>8)&0xFF,(tag19)&0xFF,led_ctrl},
{(tag20>>16)&0xFF,(tag20>>8)&0xFF,(tag20)&0xFF,led_ctrl},
{(tag21>>16)&0xFF,(tag21>>8)&0xFF,(tag21)&0xFF,led_ctrl},
{(tag22>>16)&0xFF,(tag22>>8)&0xFF,(tag22)&0xFF,led_ctrl},
{(tag23>>16)&0xFF,(tag23>>8)&0xFF,(tag23)&0xFF,led_ctrl},
{(tag24>>16)&0xFF,(tag24>>8)&0xFF,(tag24)&0xFF,led_ctrl},
{(tag25>>16)&0xFF,(tag25>>8)&0xFF,(tag25)&0xFF,led_ctrl},
{(tag26>>16)&0xFF,(tag26>>8)&0xFF,(tag26)&0xFF,led_ctrl},
{(tag27>>16)&0xFF,(tag27>>8)&0xFF,(tag27)&0xFF,led_ctrl},
{(tag28>>16)&0xFF,(tag28>>8)&0xFF,(tag28)&0xFF,led_ctrl},
{(tag29>>16)&0xFF,(tag29>>8)&0xFF,(tag29)&0xFF,led_ctrl},
{(tag30>>16)&0xFF,(tag30>>8)&0xFF,(tag30)&0xFF,led_ctrl}};
uart_data_send_lighton_or_group(&uartSend,tags,lightbars_size);
isLightOn=false;
#if 0
isStopBroadcastBegin=false;
while(1){
usleep(150*1000);
if(isStopBroadcastBegin){
break;
}
}
uart_data_send_head(&uartSend,'R',5,30/5,1);//stop broadcast by groupno
isStopBroadcast=true;
#endif
}
if(isBindTag){
jt_tag_package_t tags[30]={{(tag1>>16)&0xFF,(tag1>>8)&0xFF,(tag1)&0xFF,group},
{(tag2>>16)&0xFF,(tag2>>8)&0xFF,(tag2)&0xFF,group},
{(tag3>>16)&0xFF,(tag3>>8)&0xFF,(tag3)&0xFF,group},
{(tag4>>16)&0xFF,(tag4>>8)&0xFF,(tag4)&0xFF,group},
{(tag5>>16)&0xFF,(tag5>>8)&0xFF,(tag5)&0xFF,group},
{(tag6>>16)&0xFF,(tag6>>8)&0xFF,(tag6)&0xFF,group},
{(tag7>>16)&0xFF,(tag7>>8)&0xFF,(tag7)&0xFF,group},
{(tag8>>16)&0xFF,(tag8>>8)&0xFF,(tag8)&0xFF,group},
{(tag9>>16)&0xFF,(tag9>>8)&0xFF,(tag9)&0xFF,group},
{(tag10>>16)&0xFF,(tag10>>8)&0xFF,(tag10)&0xFF,group},
{(tag11>>16)&0xFF,(tag11>>8)&0xFF,(tag11)&0xFF,group},
{(tag12>>16)&0xFF,(tag12>>8)&0xFF,(tag12)&0xFF,group},
{(tag13>>16)&0xFF,(tag13>>8)&0xFF,(tag13)&0xFF,group},
{(tag14>>16)&0xFF,(tag14>>8)&0xFF,(tag14)&0xFF,group},
{(tag15>>16)&0xFF,(tag15>>8)&0xFF,(tag15)&0xFF,group},
{(tag16>>16)&0xFF,(tag16>>8)&0xFF,(tag16)&0xFF,group},
{(tag17>>16)&0xFF,(tag17>>8)&0xFF,(tag17)&0xFF,group},
{(tag18>>16)&0xFF,(tag18>>8)&0xFF,(tag18)&0xFF,group},
{(tag19>>16)&0xFF,(tag19>>8)&0xFF,(tag19)&0xFF,group},
{(tag20>>16)&0xFF,(tag20>>8)&0xFF,(tag20)&0xFF,group},
{(tag21>>16)&0xFF,(tag21>>8)&0xFF,(tag21)&0xFF,group},
{(tag22>>16)&0xFF,(tag22>>8)&0xFF,(tag22)&0xFF,group},
{(tag23>>16)&0xFF,(tag23>>8)&0xFF,(tag23)&0xFF,group},
{(tag24>>16)&0xFF,(tag24>>8)&0xFF,(tag24)&0xFF,group},
{(tag25>>16)&0xFF,(tag25>>8)&0xFF,(tag25)&0xFF,group},
{(tag26>>16)&0xFF,(tag26>>8)&0xFF,(tag26)&0xFF,group},
{(tag27>>16)&0xFF,(tag27>>8)&0xFF,(tag27)&0xFF,group},
{(tag28>>16)&0xFF,(tag28>>8)&0xFF,(tag28)&0xFF,group},
{(tag29>>16)&0xFF,(tag29>>8)&0xFF,(tag29)&0xFF,group},
{(tag30>>16)&0xFF,(tag30>>8)&0xFF,(tag30)&0xFF,group}};
uart_data_send_lighton_or_group(&uartSend,tags,lightbars_size);
isBindTag=false;
}
if(isLightOnById){
// 【关键修改】使用从队列获取的命令参数
uart_data_send_lighton_by_id(&uartSend,0x00,0xFF,cmd.tagCode,cmd.tagCode,led_ctrl,0x0000);
LOG_I("LightOnById: sent command for device %s, tagCode=%08X\n", cmd.deviceName, cmd.tagCode);
isLightOnById=false;
}
if(isLightOnByGroup){
uart_data_send_lighton_by_group(&uartSend,groups,1);
isLightOnByGroup=false;
}
if(isAllLightOn){
// 全场亮灯 - 使用ID范围0x00000000到0xFFFFFFFF点亮所有灯条
jt_led_or_group_package_t all_led_ctrl={
.s.color=allLightColor,
.s.sound=allLightSound,
.s.single=0,
.s.flash=allLightFlash,
};
uart_data_send_lighton_by_id(&uartSend, 0x00, 0xFF, 0x00000000, 0xFFFFFFFF, all_led_ctrl, 0x0000);
isAllLightOn=false;
LOG_I("all_light: id range command sent, color=%d, flash=%d, sound=%d\n",
allLightColor, allLightFlash, allLightSound);
}
}else if(parm_ack==0x4646){
isSendComEnd=true;
}else if(parm_ack==0x5252){
uart_data_receive_version(&uartSend);
isSendComEnd=true;
}
}else{
isSendComEnd=true;
}
usleep(100*1000);
}
}
void *thread_uart_recv_data(void *arg){
uint16_t parmAck;
uint16_t tagCodeHead;
uint32_t tagCode;
uint8_t tagSignal;
uint8_t totalLen;
uint8_t tagFeature;
uint8_t count;
uint8_t batteryV;
uint16_t version;
uint8_t ledCtrl;
uint16_t signCode;
uint16_t reserve;
uint32_t lableParm;
while(1){
uart_data_receive_data_back(&uartRecvData,&parmAck,&tagCodeHead,&tagCode,&tagSignal,&totalLen,&tagFeature,
&count,&batteryV,&version,&ledCtrl,&signCode,&reserve,&lableParm);
LOG_I("recv_data:%04x,%04x,tag:%08x,%02x,%02x,%02x,%02x,battery:%02x,%04x,%02x,%04x,reserve:%04x,%08x\n",
parmAck,tagCodeHead,tagCode,tagSignal,totalLen,tagFeature,count,batteryV,version,ledCtrl,signCode,reserve,lableParm);
// 检查是否有待确认的亮灯任务如果tag匹配则上报亮灯成功
report_light_success(tagCodeHead, tagCode);
isStopBroadcastBegin=true;
//sleep(2);
}
}
void *thread_uart_recv_back(void *arg){
uint16_t parmAck;
uint16_t tagCodeHead;
uint32_t tagCode;
uint8_t tagSignal;
uint8_t totalLen;
uint8_t tagFeature;
uint8_t count;
uint8_t batteryV;
uint16_t version;
uint8_t ledCtrl;
uint16_t signCode;
uint16_t reserve;
uint32_t lableParm;
while(1){
uart_data_receive_data_back(&uartRecvBack,&parmAck,&tagCodeHead,&tagCode,&tagSignal,&totalLen,&tagFeature,
&count,&batteryV,&version,&ledCtrl,&signCode,&reserve,&lableParm);
// 打印除心跳外的所有接收数据
if(tagFeature != 0xFF) {
LOG_I("recv_back:%04x,%04x,tag:%08x,%02x,%02x,%02x,%02x,battery:%02x,%04x,%02x,%04x,reserve:%04x,%08x\n",
parmAck,tagCodeHead,tagCode,tagSignal,totalLen,tagFeature,count,batteryV,version,ledCtrl,signCode,reserve,lableParm);
}
PutDataIntoQueue(tagCode,batteryV,reserve);
if(tagFeature==0xFF){
//LOG_I("heart beat from lightbar %06X\n", tagCode);
update_lightbar_heartbeat(tagCodeHead, tagCode);
}else if(tagFeature==0xFD){
LOG_I("key pressed from tag %08X\n", tagCode);
// 按键防抖2秒内只处理一次
time_t now = time(NULL);
pthread_mutex_lock(&keyDebounceMutex);
bool shouldProcess = true;
int debounceIndex = -1;
// 查找是否已有该tag的记录
for (int i = 0; i < keyDebounceCount; i++) {
if (keyDebounceList[i].tagCode == tagCode && keyDebounceList[i].tagCodeHead == tagCodeHead) {
debounceIndex = i;
if (now - keyDebounceList[i].lastKeyTime < 2) {
shouldProcess = false;
LOG_I("Key debounce: ignoring key press from tag %08X (within 2s)\n", tagCode);
}
break;
}
}
// 更新或添加记录
if (shouldProcess) {
if (debounceIndex >= 0) {
keyDebounceList[debounceIndex].lastKeyTime = now;
} else if (keyDebounceCount < MAX_KEY_DEBOUNCE) {
keyDebounceList[keyDebounceCount].tagCodeHead = tagCodeHead;
keyDebounceList[keyDebounceCount].tagCode = tagCode;
keyDebounceList[keyDebounceCount].lastKeyTime = now;
keyDebounceCount++;
}
}
pthread_mutex_unlock(&keyDebounceMutex);
if (!shouldProcess) {
continue; // 跳过此次按键处理
}
// 按键按下时立即上报灭灯
pthread_mutex_lock(&lightTaskMutex);
bool found = false;
for (int i = 0; i < pendingLightTaskCount; i++) {
if (pendingLightTasks[i].tagCode == tagCode &&
pendingLightTasks[i].reported &&
!pendingLightTasks[i].lightOffReported) {
LOG_I("Key pressed - triggering immediate light-off for tag %08X (with task)\n", tagCode);
report_light_off(tagCodeHead, tagCode);
found = true;
break;
}
}
// 即使没有任务也要上报灭灯
if (!found) {
LOG_I("Key pressed - sending light-off for tag %08X (no task)\n", tagCode);
char topic[256] = {0};
char payload[1024] = {0};
char tagStr[16] = {0};
char uuid_str[37] = {0};
uuid_t uuid;
uuid_generate(uuid);
uuid_unparse(uuid, uuid_str);
// 使用完整的tagID
snprintf(tagStr, sizeof(tagStr), "%04X%08X", tagCodeHead, tagCode);
snprintf(topic, sizeof(topic), "/sys/WcSubLightStrip/%s/thing/property/post", tagStr);
struct timeval tv;
gettimeofday(&tv, NULL);
long long timestamp = (long long)tv.tv_sec * 1000 + tv.tv_usec / 1000;
snprintf(payload, sizeof(payload),
"{\"id\":\"%s\",\"version\":\"1.0\",\"arg\":{\"beep\":{\"value\":0,\"time\":%lld},\"flash\":{\"value\":0,\"time\":%lld},\"light\":{\"value\":{\"R\":0,\"G\":0,\"B\":0},\"time\":%lld}},\"method\":\"thing.property.post\",\"time\":%lld}",
uuid_str,
timestamp,
timestamp,
timestamp,
timestamp);
LOG_I("Key light-off - topic: %s\n", topic);
LOG_I("Key light-off - payload: %s\n", payload);
mqtt_utils_publish(&mqtt_config, topic, 0, payload, strlen(payload));
}
pthread_mutex_unlock(&lightTaskMutex);
}else if(tagFeature==0xFC){
LOG_I("broadcast parm\n");
}
}
}
void functions_reply(char *task_id,char *msg_id){
json_object *functions = NULL;
functions=json_object_new_array();
json_object *function = json_object_new_object();
json_object *out = json_object_new_object();
json_object_object_add(function, "key", json_object_new_string("relay-lightbar.control"));
json_object_object_add(out, "dev-name", json_object_new_string(""));
json_object_object_add(out, "task-id", json_object_new_string(task_id));
json_object_object_add(out, "code", json_object_new_int(200));
json_object_object_add(out, "desc", json_object_new_string("success"));
json_object_object_add(out, "ext1", json_object_new_string(""));
json_object_object_add(out, "ext2", json_object_new_string(""));
json_object_object_add(function, "parameters", out);
json_object_array_add(functions,function);
mqtt_server_reply(stationsn,msg_id,functions);
}
void station_status_report(){
char myid[32]={0};
char time_buffer[16] = "";
char app_time_buffer[16] = "";
struct timeval tv;
myrand(myid,19);
gettimeofday(&tv, NULL);
snprintf(time_buffer,sizeof(time_buffer),"%ld",tv.tv_sec*1000+tv.tv_usec);
snprintf(app_time_buffer,sizeof(app_time_buffer),"%ld",tv.tv_sec*1000+tv.tv_usec);
json_object *root = json_object_new_object();
json_object *arg = json_object_new_object();
json_object *app = json_object_new_object();
json_object *value = json_object_new_object();
// 构建value对象
json_object_object_add(value, "version", json_object_new_string(softwareVersion));
// 构建APP对象
json_object_object_add(app, "value", value);
json_object_object_add(app, "time", json_object_new_string(app_time_buffer));
// 构建arg对象
json_object_object_add(arg, "APP", app);
// 构建根对象
json_object_object_add(root, "id", json_object_new_string(myid));
json_object_object_add(root, "version", json_object_new_string("1.0"));
json_object_object_add(root, "method", json_object_new_string("thing.firmware.ota.inform"));
json_object_object_add(root, "arg", arg);
json_object_object_add(root, "time", json_object_new_string(time_buffer));
mqtt_server_events_report(stationsn,myid,root,productid);
}
void light_status_report(){
LOG_I("%s:tagCount:%d\n",__func__,tagCount);
if(tagCount <= 0) {
return;
}
// 创建TaskResult根对象
json_object *task_result = json_object_new_object();
json_object *results_array = json_object_new_array();
char myid[32]={0};
char time_buffer[16] = "";
struct timeval tv;
myrand(myid,19);
gettimeofday(&tv, NULL);
snprintf(time_buffer,sizeof(time_buffer),"%ld",tv.tv_sec*1000+tv.tv_usec/1000);
// 添加TaskResult字段
json_object_object_add(task_result, "ID", json_object_new_string(stationsn)); // 使用基站ID
json_object_object_add(task_result, "TotalCount", json_object_new_int(tagCount));
json_object_object_add(task_result, "SendCount", json_object_new_int(tagCount));
// 构建Results数组每个元素是TaskItemResult
for(int i=0;i<tagCount;i++){
char tagName[9]={0};
char group[3]={0};
int readType = 0; // 数据回收类型默认0
sprintf(tagName,"%06X",onlyTags[i].name);
sprintf(group,"%02X",(onlyTags[i].reserve>>8)&0xFF);
// 根据分组判断readType
if(strcmp(group,"00")==0 || strcmp(group,"FF")==0){
readType = 1; // 0xFD 接触返回
}else{
readType = 2; // 0xFE 通信返回
}
// 创建TaskItemResult对象
json_object *task_item = json_object_new_object();
json_object_object_add(task_item, "TagID", json_object_new_string(tagName));
json_object_object_add(task_item, "Version", json_object_new_string("1.0"));
json_object_object_add(task_item, "ReadType", json_object_new_int(readType));
json_object_object_add(task_item, "RfPowerSend", json_object_new_int(-25)); // AP方发送RF功率dBm
json_object_object_add(task_item, "RfPowerRecv", json_object_new_int(-30)); // 灯条RF功率dBm
// 电池电量处理
int battery_percent = 0;
if(onlyTags[i].battery>=30){
battery_percent = 100;
}else if(onlyTags[i].battery>=29){
battery_percent = 90;
}else if(onlyTags[i].battery>=28){
battery_percent = 70;
}else if(onlyTags[i].battery>=27){
battery_percent = 60;
}else if(onlyTags[i].battery>=26){
battery_percent = 50;
}else if(onlyTags[i].battery>=25){
battery_percent = 40;
}else if(onlyTags[i].battery>=24){
battery_percent = 30;
}else{
battery_percent = 0;
}
json_object_object_add(task_item, "Battery", json_object_new_int(battery_percent));
// Colors RGB数组
json_object *colors = json_object_new_object();
json_object_object_add(colors, "R", json_object_new_boolean(false)); // 红色状态
json_object_object_add(colors, "G", json_object_new_boolean(false)); // 绿色状态
json_object_object_add(colors, "B", json_object_new_boolean(false)); // 蓝色状态
json_object_object_add(task_item, "Colors", colors);
// Group分组号
int group_num = (int)strtol(group, NULL, 16);
json_object_object_add(task_item, "Group", json_object_new_int(group_num));
json_object_array_add(results_array, task_item);
}
json_object_object_add(task_result, "Results", results_array);
// 发送到MQTT主题 /estation/{ID}/result
char topic[128] = {0};
snprintf(topic, sizeof(topic), "/estation/%s/result", stationsn);
const char *json_string = json_object_to_json_string(task_result);
LOG_I("Light status report JSON: %s\n", json_string);
// TODO: 发送到MQTT服务器
// mqtt_publish(topic, json_string);
json_object_put(task_result);
// 清空标签数据
tagCount=0;
memset(onlyTags,0,sizeof(onlyTags));
}
void *thread_station_heartbeat(void *arg){
while(1){
sleep(20); // 20秒间隔
char local_mac[32] = {0};
char local_ip[32] = {0};
getLocalMac(local_mac);
getLocalIp(local_ip);
json_object *root = json_object_new_object();
json_object *estation_info = json_object_new_object();
// 添加基站心跳信息
json_object_object_add(estation_info, "ID", json_object_new_string(stationsn));
json_object_object_add(estation_info, "MAC", json_object_new_string(local_mac));
json_object_object_add(estation_info, "Alias", json_object_new_string(""));
json_object_object_add(estation_info, "ClientType", json_object_new_int(2));
json_object_object_add(estation_info, "ServerAddress", json_object_new_string(""));
json_object_object_add(estation_info, "Parameters", json_object_new_string(""));
json_object_object_add(estation_info, "LocalIP", json_object_new_string(local_ip));
json_object_object_add(estation_info, "SubnetMask", json_object_new_string(""));
json_object_object_add(estation_info, "Gateway", json_object_new_string(""));
json_object_object_add(estation_info, "Heartbeat", json_object_new_int(20));
json_object_object_add(estation_info, "DummyVersion", json_object_new_string(""));
json_object_object_add(estation_info, "AppVersion", json_object_new_string(softwareVersion));
json_object_object_add(estation_info, "TotalCount", json_object_new_int(0));
json_object_object_add(estation_info, "SendCount", json_object_new_int(0));
json_object_object_add(root, "eStationInfor", estation_info);
const char *json_string = json_object_to_json_string(root);
//LOG_I("station_heartbeat:%s\n", json_string);
// 发送心跳到 /estation/{ID}/heartbeat topic
char topic[128] = {0};
snprintf(topic, sizeof(topic), "/estation/%s/heartbeat", stationsn);
// 这里需要使用MQTT发送函数暂时用LOG输出
//LOG_I("Sending heartbeat to topic: %s\n", topic);
json_object_put(root);
}
return NULL;
}
void addOnlyTag(uint32_t tagname,uint16_t battery,uint16_t reserve){
int i=0;
if(tagCount==0){
jt_only_tag_t jt_only_tag = {
.name=tagname,
.battery=battery,
.reserve=reserve,
};
onlyTags[tagCount]=jt_only_tag;
tagCount++;
}else{
for(i=0;i<tagCount;i++){
if(tagname==onlyTags[i].name){
break;
}
}
if(i==tagCount){
jt_only_tag_t jt_only_tag = {
.name=tagname,
.battery=battery,
.reserve=reserve,
};
onlyTags[tagCount]=jt_only_tag;
tagCount++;
}
}
if(tagCount==100){
//light_status_report();
}
}
void *thread_remove_duplicate_tag(void *arg){
uint32_t tagname=0;
uint16_t battery=0;
uint16_t reserve=0;
while(1){
if(GetDataFromQueue(&tagname,&battery,&reserve)==0){
//LOG_I("%08x,%04x\n",tagname,battery);
addOnlyTag(tagname,battery,reserve);
}
}
}
void *thread_mqtt_recv(void *arg){
char payload[1024]={0};
char deviceName[16]={0}; // 【新增】从队列获取的设备名称
char msg_items[1024] ={0};
char msg_item_value[1024] ={0};
char msg_colors[512] ={0};
char msg_color_value[512] ={0};
char msg_lightbars[4] ={0};
int items_size = 0;
int colors_size = 0;
int task_id = 0;
while(1){
if(isSendComEnd){
// 【修复】优先使用带设备名称的版本
if(GetDataFromMQueueWithDevice(payload, deviceName)==0){
// 如果获取到了设备名称,使用它;否则使用全局变量
if (strlen(deviceName) > 0) {
strncpy(g_mqtt_deviceName, deviceName, sizeof(g_mqtt_deviceName) - 1);
LOG_I("Using device from queue: %s\n", g_mqtt_deviceName);
}
} else if(GetDataFromMQueue(payload)==0) {
// 回退到原来的版本(兼容性)
LOG_I("Using legacy queue without device name\n");
} else {
Sleep(100);
continue;
}
// 调试用固定payload
//strcpy(payload, "{ \"Items\": [ { \"Beep\": true, \"Colors\": [ { \"B\": false, \"G\": false, \"R\": true } ], \"Flashing\": true, \"TagID\": \"AD1000014C17\" } ], \"Time\": 120 }");
// 清理payload截断最后一个'}'之后的内容
char clean_payload[4096] = {0};
strcpy(clean_payload, payload);
char *last_brace = strrchr(clean_payload, '}');
if (last_brace != NULL) {
*(last_brace + 1) = '\0'; // 在最后一个'}'之后截断
}
strcpy(payload, clean_payload); // 将清理后的内容复制回原payload
LOG_I("payload:%s\n",payload);
// 重置变量
items_size = 0;
colors_size = 0;
// 清空设备名称,避免串用上一次的值
//memset(g_mqtt_deviceName, 0, sizeof(g_mqtt_deviceName));
// 解析新格式payload
LOG_I("Parsing new payload format with params\n");
// 先打印完整的payload以便调试
LOG_I("Complete payload: %s\n", payload);
// 解析params中的各个字段
char params_str[1024] = {0};
char params_json[1024] = {0}; // 【修复】移到外面,扩大作用域
char color_json[512] = {0}; // 提前声明color_json
int params_ret = get_string_from_json_string_by_key(payload, "params", params_str, sizeof(params_str));
LOG_I("get_string_from_json_string_by_key return: %d\n", params_ret);
LOG_I("params_str: %s\n", params_str);
// 如果params_str为空尝试直接从payload解析
if(strlen(params_str) == 0) {
LOG_I("params_str is empty, trying direct parse from payload\n");
// 直接从payload解析各个字段
// 【新增】解析LightSwitch字段
int light_switch = 1; // 默认为开灯
if(get_int_from_json_string_by_key(payload, "LightSwitch", &light_switch) == 0) {
LOG_I("Direct parse LightSwitch = %d\n", light_switch);
}
int beep_enable = 0;
get_int_from_json_string_by_key(payload, "beep", &beep_enable);
LOG_I("Direct parse Beep = %d\n", beep_enable);
changesound = beep_enable ? 1 : 0;
int flashing_enable = 0;
get_int_from_json_string_by_key(payload, "flashing", &flashing_enable);
changeflash = flashing_enable ? 3 : 1;
LOG_I("Direct parse Flashing = %d, changeflash = %d\n", flashing_enable, changeflash);
int duration = 0;
get_int_from_json_string_by_key(payload, "duration", &duration);
mqtt_parm.msg_duration = duration;
LOG_I("Direct parse Duration = %d\n", mqtt_parm.msg_duration);
if(mqtt_parm.msg_duration <= 5) {
mqtt_parm.msg_duration = 5;
LOG_I("new msg_duration = %d\n", mqtt_parm.msg_duration);
} else {
// 向上取整到5的倍数避免硬件提前灭灯
mqtt_parm.msg_duration = ((mqtt_parm.msg_duration + 4) / 5) * 5;
LOG_I("Rounded up msg_duration = %d\n", mqtt_parm.msg_duration);
}
// 解析颜色
char color_str[512] = {0};
get_string_from_json_string_by_key(payload, "color", color_str, sizeof(color_str));
LOG_I("Direct parse color_str: %s\n", color_str);
// 包装color_str为有效的JSON对象
snprintf(color_json, sizeof(color_json), "{%s}", color_str);
LOG_I("color_json: %s\n", color_json);
} else {
// 包装params_str为有效的JSON对象
snprintf(params_json, sizeof(params_json), "{%s}", params_str);
LOG_I("params_json: %s\n", params_json);
// 【新增】解析LightSwitch字段
int light_switch = 1; // 默认为开灯
if(get_int_from_json_string_by_key(params_json, "LightSwitch", &light_switch) == 0) {
LOG_I("Parse LightSwitch = %d\n", light_switch);
}
int beep_enable = 0;
get_int_from_json_string_by_key(params_json, "beep", &beep_enable);
LOG_I("Beep = %d\n", beep_enable);
changesound = beep_enable ? 1 : 0;
int flash_enable = 0;
get_int_from_json_string_by_key(params_json, "flash", &flash_enable);
changeflash = flash_enable ? 3 : 1; // 1=闪烁, 3=常亮注意flash=1表示闪烁
LOG_I("Flash = %d, changeflash = %d\n", flash_enable, changeflash);
int duration = 0;
get_int_from_json_string_by_key(params_json, "duration", &duration);
mqtt_parm.msg_duration = duration;
LOG_I("Duration = %d\n", mqtt_parm.msg_duration);
if(mqtt_parm.msg_duration <= 5) {
mqtt_parm.msg_duration = 5;
LOG_I("new msg_duration = %d\n", mqtt_parm.msg_duration);
} else {
// 向上取整到5的倍数避免硬件提前灭灯
mqtt_parm.msg_duration = ((mqtt_parm.msg_duration + 4) / 5) * 5;
LOG_I("Rounded up msg_duration = %d\n", mqtt_parm.msg_duration);
}
// 解析颜色
char color_str[512] = {0};
get_string_from_json_string_by_key(params_json, "color", color_str, sizeof(color_str));
LOG_I("color_str: %s\n", color_str);
// 包装color_str为有效的JSON对象
snprintf(color_json, sizeof(color_json), "{%s}", color_str);
LOG_I("color_json: %s\n", color_json);
}
// 解析RED/GREEN/BLUE或R/G/B作为键值
int r_val = 0, g_val = 0, b_val = 0;
// 先尝试完整名称
get_int_from_json_string_by_key(color_json, "RED", &r_val);
get_int_from_json_string_by_key(color_json, "GREEN", &g_val);
get_int_from_json_string_by_key(color_json, "BLUE", &b_val);
// 如果完整名称没有值,尝试缩写
if(r_val == 0 && g_val == 0 && b_val == 0) {
get_int_from_json_string_by_key(color_json, "R", &r_val);
get_int_from_json_string_by_key(color_json, "G", &g_val);
get_int_from_json_string_by_key(color_json, "B", &b_val);
}
LOG_I("RGB values: R=%d, G=%d, B=%d\n", r_val, g_val, b_val);
// 根据RGB值设置颜色0=不亮, 1=亮)
// 【修复】先保存原始RGB值用于判断是否是关灯命令
bool is_off_command = false;
// 如果LightSwitch=0标记为关灯命令
int light_switch = 1;
if(strlen(params_str) > 0) {
get_int_from_json_string_by_key(params_json, "LightSwitch", &light_switch);
} else {
get_int_from_json_string_by_key(payload, "LightSwitch", &light_switch);
}
if(light_switch == 0) {
is_off_command = true;
LOG_I("LightSwitch=0 detected, this is an OFF command\n");
}
if(r_val == 0 && g_val == 0 && b_val == 0 && !is_off_command) {
changecolor = 0; // 不亮(非关灯命令时的默认值)
} else if(r_val == 1 && g_val == 0 && b_val == 0) {
changecolor = 4; // 红色
} else if(r_val == 0 && g_val == 1 && b_val == 0) {
changecolor = 2; // 绿色
} else if(r_val == 0 && g_val == 0 && b_val == 1) {
changecolor = 1; // 蓝色
} else if(r_val == 1 && g_val == 1 && b_val == 0) {
changecolor = 6; // 黄色
} else if(r_val == 1 && g_val == 0 && b_val == 1) {
changecolor = 5; // 紫色
} else if(r_val == 0 && g_val == 1 && b_val == 1) {
changecolor = 3; // 青色
} else if(r_val == 1 && g_val == 1 && b_val == 1) {
changecolor = 7; // 白色
} else if(is_off_command) {
changecolor = 0; // 关灯命令强制设置为不亮
}
LOG_I("changecolor=%d (is_off_command=%d)\n", changecolor, is_off_command);
// 判断任务类型根据method字段判断是灯条任务还是OTA任务
char method[128] = {0};
int has_method = get_string_from_json_string_by_key(payload, "method", method, sizeof(method));
int is_lightbar_task = 0;
if(has_method == 0 && strstr(method, "lightOperate") != NULL) {
// 灯条任务
is_lightbar_task = 1;
items_size = 1;
lightbars_size = 1;
// 解析id字段
char taskId[64] = {0};
get_string_from_json_string_by_key(payload, "id", taskId, sizeof(taskId));
LOG_I("Task ID: %s\n", taskId);
// 从topic解析出的deviceName获取设备ID
LOG_I("Processing lightbar command, g_mqtt_deviceName: %s\n", g_mqtt_deviceName);
// 【关键修复】使用从队列获取的设备名称,而不是可能被覆盖的全局变量
if (strlen(deviceName) > 0) {
// 【关键修复】立即保存deviceName到局部变量避免被其他并发MQTT消息覆盖
char current_deviceName[16] = {0};
strncpy(current_deviceName, deviceName, sizeof(current_deviceName) - 1);
LOG_I("Device ID from queue: %s\n", current_deviceName);
// 将deviceName设置为灯条ID
memset(mqtt_parm.msg_sn, 0, sizeof(mqtt_parm.msg_sn));
strncpy(mqtt_parm.msg_sn, current_deviceName, sizeof(mqtt_parm.msg_sn) - 1);
LOG_I("Set light bar ID: %s\n", mqtt_parm.msg_sn);
// 获取完整的12位设备ID
int len = strlen(current_deviceName);
LOG_I("Device name length: %d\n", len);
if(len == 12) {
char fullTagId[13] = {0};
strncpy(fullTagId, current_deviceName, 12);
// 解析前4位作为tagCodeHead和后8位作为tagCode
uint16_t tagCodeHead = 0;
uint32_t tagCode = 0;
char head[5] = {0};
char tail[9] = {0};
strncpy(head, fullTagId, 4);
strncpy(tail, fullTagId + 4, 8);
sscanf(head, "%hx", &tagCodeHead);
sscanf(tail, "%x", &tagCode);
// 【修复】更新全局变量保存当前设备的tagCode
g_current_tagCodeHead = tagCodeHead;
g_current_tagCode = tagCode;
// 【增强】添加设备映射,解决多设备并发问题
add_device_mapping(current_deviceName, tagCodeHead, tagCode);
// 获取后6位设置到lightsn1保留兼容性
char *last6 = fullTagId + 6;
memset(lightsn1, 0, sizeof(lightsn1));
strncpy(lightsn1, last6, 6);
LOG_I("Full tag ID: %s, head: %04X, tail: %08X, lightsn1: %s\n", fullTagId, tagCodeHead, tagCode, lightsn1);
// 添加待确认的亮灯任务传入真实的beep、flash、RGB数据
// flash值需要转换changeflash=1表示常亮(上报0)changeflash=3表示闪烁(上报1)
int flash_value = (changeflash == 3) ? 1 : 0;
if(strlen(taskId) > 0) {
add_pending_light_task(tagCodeHead, tagCode, taskId, changesound, flash_value, r_val, g_val, b_val, mqtt_parm.msg_duration);
}
// 【关键修改】将命令添加到队列,而不是设置全局变量
// 这样可以避免快速连续命令时的竞态条件
if(add_light_command(current_deviceName, taskId, tagCodeHead, tagCode,
changecolor, changesound, changeflash, mqtt_parm.msg_duration,
changesound, flash_value, r_val, g_val, b_val) == 0) {
LOG_I("Light command added to queue successfully\n");
} else {
LOG_I("Failed to add light command to queue - queue full!\n");
}
} else {
LOG_I("Device name length is not 12 characters: %s (len=%d)\n", current_deviceName, len);
}
// 【新增】立即处理队列中的命令,确保不被其他消息干扰
// 这样可以确保每个MQTT消息都能立即触发UART处理
LOG_I("Immediately processing command queue for %s\n", current_deviceName);
} else {
LOG_I("g_mqtt_deviceName is empty or null\n");
}
} else {
// OTA任务或其他任务
items_size = 0;
lightbars_size = 0;
}
// 根据任务类型进行处理
// 任务1发布灯条指令 (topic: /estation/设备ID/task)
// 任务2发布基站OTA任务 (topic: /estation/设备ID/ota)
// 判断任务类型如果有Items数组且包含TagID则为灯条任务
if(items_size > 0) {
LOG_I("Processing lightbar task (task_id: lightbar)\n");
// 灯条点亮任务
lightbars_count += lightbars_size;
if(lightbars_count >= 30) {
// 处理超过30个灯条的情况
} else {
// 正常处理
}
// 清空串口缓冲区,避免残留数据干扰
tcflush(uartSend.uart_fd, TCIOFLUSH);
isSendComEnd = false;
isLightOnByGroup = false;
isLightOnById = false;
isLightOn = true;
isBindTag = false;
// P点灯 B绑定 &群控(不支持) *根据id点亮
// timeout单位是5秒所以 Time(秒) / 5 = timeout
// 只发送头包数据包在thread_uart_recv_ack中处理
uart_data_send_head(&uartSend, 'P', 5, mqtt_parm.msg_duration/5, lightbars_size);
} else {
LOG_I("Processing OTA task (task_id: ota)\n");
// 解析新的OTA任务JSON格式
LOG_I("OTA payload debug: %s\n", payload);
LOG_I("OTA payload after cleaning: %s\n", payload);
// 首先检查是否存在data数组
int data_array_size = 0;
int size_check_result = get_size_from_json_string_arry_by_key(payload, "data", &data_array_size);
LOG_I("Data array size check result: %d, size: %d\n", size_check_result, data_array_size);
int has_data_array = (size_check_result == 0 && data_array_size > 0);
if (has_data_array) {
LOG_I("Found data array with %d items\n", data_array_size);
// 从data数组中获取第一个对象的完整JSON字符串
char data_item[2048] = {0};
int extract_result = get_string_from_json_string_arry_by_key_unescape(payload, "data", data_item, sizeof(data_item), 0);
LOG_I("Extract data array item result: %d\n", extract_result);
if (extract_result == 0) {
LOG_I("Found data array item: %s\n", data_item);
// 构造完整的JSON对象字符串添加花括号
char complete_json[2048] = {0};
snprintf(complete_json, sizeof(complete_json), "{%s}", data_item);
LOG_I("Complete JSON object: %s\n", complete_json);
// 解析data项中的各个字段
get_string_from_json_string_by_key_unescape(complete_json, "url", mqtt_parm.msg_zipPath, sizeof(mqtt_parm.msg_zipPath));
get_string_from_json_string_by_key_unescape(complete_json, "sign", mqtt_parm.msg_md5, sizeof(mqtt_parm.msg_md5));
get_string_from_json_string_by_key_unescape(complete_json, "firmware", mqtt_parm.msg_type, sizeof(mqtt_parm.msg_type));
get_string_from_json_string_by_key_unescape(complete_json, "tv", mqtt_parm.msg_version, sizeof(mqtt_parm.msg_version));
} else {
LOG_I("Failed to extract data array item\n");
}
} else {
LOG_I("No data array found, trying legacy format\n");
// 如果没有data数组尝试旧格式兼容
get_string_from_json_string_by_key_unescape(payload, "Firmware", mqtt_parm.msg_zipPath, sizeof(mqtt_parm.msg_zipPath));
get_string_from_json_string_by_key_unescape(payload, "MD5", mqtt_parm.msg_md5, sizeof(mqtt_parm.msg_md5));
get_string_from_json_string_by_key_unescape(payload, "Type", mqtt_parm.msg_type, sizeof(mqtt_parm.msg_type));
get_string_from_json_string_by_key_unescape(payload, "Version", mqtt_parm.msg_version, sizeof(mqtt_parm.msg_version));
}
LOG_I("OTA firmware path: %s\n", mqtt_parm.msg_zipPath);
LOG_I("OTA firmware MD5: %s\n", mqtt_parm.msg_md5);
LOG_I("OTA firmware Type: %s\n", mqtt_parm.msg_type);
LOG_I("OTA firmware Version: %s\n", mqtt_parm.msg_version);
// 如果解析到了firmware路径执行下载命令
if (strlen(mqtt_parm.msg_zipPath) > 0) {
char otaCmd[1024];
sprintf(otaCmd, "curl -o /root/mt_ota.zip %s", mqtt_parm.msg_zipPath);
LOG_I("Executing OTA download command: %s\n", otaCmd);
// 执行下载命令
int result = system(otaCmd);
if (result == 0) {
LOG_I("OTA firmware download successful\n");
sleep(15);
// 下载成功后执行解压和安装操作
system("unzip /root/mt_ota.zip");
system("rm -fr /root/ota");
system("mv /root/mt_ota /root/ota");
system("cp /root/ota/mt_server /tmp/mt_server_new");
LOG_I("cp /root/ota/mt_server /tmp/mt_server_new\n");
system("chmod +x /tmp/mt_server_new");
system("mv /tmp/mt_server_new /root/mt_server");
LOG_I("mv /tmp/mt_server_new /root/mt_server\n");
system("systemctl restart mt_server");
// OTA成功后重置状态
isSendComEnd = false;
isLightOnByGroup = false;
isLightOnById = false;
isLightOn = false;
isBindTag = false;
} else {
LOG_I("OTA firmware download failed with code: %d\n", result);
// OTA失败时保持isSendComEnd为true允许后续消息处理
isSendComEnd = true;
isLightOnByGroup = false;
isLightOnById = false;
isLightOn = false;
isBindTag = false;
}
} else {
LOG_I("No firmware path found in OTA task\n");
// OTA解析失败时保持isSendComEnd为true允许后续消息处理
isSendComEnd = true;
isLightOnByGroup = false;
isLightOnById = false;
isLightOn = false;
isBindTag = false;
}
}
// 发送回复消息(如果需要)
// functions_reply(mqtt_parm.msg_taskId, mqtt_parm.msg_messageId);
}
}
usleep(getPayloadTime);
}
/*================================================================================*/
void hmacsha1(char *key,char* data,char *signbase64,int signbase64_len){
char result[256]={0};
size_t len=sizeof(result);
//LOG_I("key_len:%d,data_len:%d\n",strlen(key),strlen(data));
hmac_sha1(key,strlen(key),data,strlen(data),result,&len);
//LOG_I("result_len:%d\n",len);
int t_i;
//for(t_i=0;t_i<len;t_i++)
// printf("%x ",result[t_i]);
Base64_encode( signbase64, signbase64_len, result,strlen(result));
//printf("last:%s\n",signbase64);
}
void getLocalIp(char *local_ip){
FILE *fp;
char buffer[64]={0};
fp=popen("ifconfig eth0 | grep 'inet' | awk '{print $2}' | cut -d':' -f2","r");
fgets(buffer,sizeof(buffer),fp);
//LOG_I("buffer:%s\n",buffer);
memcpy(local_ip,buffer,strlen(buffer)-1);
}
void getLocalMac(char *local_mac){
FILE *fp;
char buffer[64]={0};
fp=popen("ifconfig eth0 | grep 'ether' | awk '{print $2}' | tr -d ':'","r");
fgets(buffer,sizeof(buffer),fp);
//LOG_I("buffer:%s\n",buffer);
memcpy(local_mac,buffer,strlen(buffer)-1);
}
void saveStartUpTime(){
system("rm ./startUpTime");
struct timeval tv;
char startUpTime[32] = {0};
gettimeofday(&tv, NULL);
snprintf(startUpTime,sizeof(startUpTime),"%ld",tv.tv_sec);
buffer_to_file("startUpTime",startUpTime,strlen(startUpTime),"wb");
}
void calculateStartUpTime(char *startTime){
char *result=NULL;
struct timeval tv;
int len=0;
result=file_to_buffer("startUpTime",&len);
if(result!=NULL){
LOG_I("startUpTime:%s\n",result);
gettimeofday(&tv, NULL);
LOG_I("nowUpTime:%ld\n",tv.tv_sec);
snprintf(startTime,11,"%ld",tv.tv_sec-atoi(result));
}
}
void updateStationInfo(char *msg_id){
char local_ip[32] = {0};
char startTime[11]={0};
getLocalIp(local_ip);
LOG_I("local_ip:%s\n",local_ip);
calculateStartUpTime(startTime);
LOG_I("Time:%s\n",startTime);
//mqtt_server_station_status_report(msg_id,productid,stationsn,local_ip,softwareVersion,"1",startTime);
}
bool timeNew(){
struct timeval tv;
gettimeofday(&tv, NULL);
long gettime=tv.tv_sec*1000+tv.tv_usec;
LOG_I("gettime:%ld\n",gettime);
if(gettime<1744000000000){
return false;
}
return true;
}
//==========================================================================================
int main(int argc, char *argv[])
{
int ret = -1;
uint32_t can_id;
char recv_data[8]={0};
char command_buffer[256] = "";
int len=0;
int getTimeCount=0;
char *readresult=NULL;
char networktype[32]={0};
LOG_I("version:%s\n",softwareVersion);
// 【新增】初始化命令队列
memset(light_commands, 0, sizeof(light_commands));
light_command_head = 0;
light_command_tail = 0;
command_sequence = 0;
LOG_I("Light command queue initialized\n");
// 【新增】初始化设备映射
memset(g_device_mappings, 0, sizeof(g_device_mappings));
LOG_I("Device mapping table initialized\n");
system("insmod /system/lib/modules/wk2xxx_spi.ko");
system("timedatectl set-timezone Asia/Shanghai");
uart_open(&uartSend,"/dev/ttyS0");//U12 ttyS0,U14 ttyS4,U21 ttysWK0 U13 ttysWK1 U15 ttysWK2 U22 ttysWK3 U20 ttyS1
uart_init(&uartSend,115200,8,1,'N',0);
uart_open(&uartRecvData,"/dev/ttyS4");
uart_init(&uartRecvData,115200,8,1,'N',0);
uart_open(&uartRecvBack,"/dev/ttysWK0");
uart_init(&uartRecvBack,115200,8,1,'N',0);
//doCommand_help(0, NULL);
system("echo 113 > /sys/class/gpio/export");//pin 113 yellow
system("echo out > /sys/class/gpio/gpio113/direction");
system("echo 63 > /sys/class/gpio/export");//pin 63 key
system("echo in > /sys/class/gpio/gpio63/direction");
#if 1
enableWatchDog();
ret = pthread_create(&pt_watchdog,NULL,thread_feed_watchdog,NULL);
if(ret!=0){
LOG_I("pthread_create watchdog fail\n");
system("reboot");
}else{
LOG_I("pthread_create watchdog success\n");
pthread_detach(pt_watchdog);
}
#endif
ret = pthread_create(&pt_uart_recv_ack,NULL,thread_uart_recv_ack,NULL);
if(ret!=0){
LOG_I("pthread_create uart_recv_ack fail\n");
system("reboot");
}else{
pthread_detach(pt_uart_recv_ack);
LOG_I("pthread_create uart_recv_ack success\n");
}
ret = pthread_create(&pt_uart_recv_data,NULL,thread_uart_recv_data,NULL);
if(ret!=0){
LOG_I("pthread_create uart_recv_data fail\n");
system("reboot");
}else{
pthread_detach(pt_uart_recv_data);
LOG_I("pthread_create uart_recv_data success\n");
}
ret = pthread_create(&pt_uart_recv_back,NULL,thread_uart_recv_back,NULL);
if(ret!=0){
LOG_I("pthread_create uart_recv_back fail\n");
system("reboot");
}else{
pthread_detach(pt_uart_recv_back);
LOG_I("pthread_create uart_recv_back success\n");
}
ret = pthread_create(&pt_mqtt_recv,NULL,thread_mqtt_recv,NULL);
if(ret!=0){
LOG_I("pthread_create mqtt_recv fail\n");
system("reboot");
}else{
pthread_detach(pt_mqtt_recv);
LOG_I("pthread_create mqtt_recv success\n");
}
ret = pthread_create(&pt_removeduplicatetag,NULL,thread_remove_duplicate_tag,NULL);
if(ret!=0){
LOG_I("pthread_create remove duplicate tag send fail\n");
}else{
LOG_I("pthread_create remove duplicate tag success\n");
pthread_detach(pt_removeduplicatetag);
}
ret = pthread_create(&pt_removelog,NULL,thread_removelog,NULL);
if(ret!=0){
LOG_I("pthread_create removelog fail\n");
system("reboot");
}else{
LOG_I("pthread_create removelog success\n");
pthread_detach(pt_removelog);
}
#if 0
ret = pthread_create(&pt_readqr,NULL,thread_readqr,NULL);
if(ret!=0){
LOG_I("pthread_create readqr fail\n");
system("reboot");
}else{
LOG_I("pthread_create readqr success\n");
pthread_detach(pt_readqr);
}
#endif
ret = pthread_create(&pt_reporttag,NULL,thread_reporttag,NULL);
if(ret!=0){
LOG_I("pthread_create reporttag fail\n");
system("reboot");
}else{
LOG_I("pthread_create reporttag success\n");
pthread_detach(pt_reporttag);
}
ret = pthread_create(&pt_keycheck,NULL,thread_keycheck,NULL);
if(ret!=0){
LOG_I("pthread_create keycheck fail\n");
system("reboot");
}else{
LOG_I("pthread_create keycheck success\n");
pthread_detach(pt_keycheck);
}
ret = pthread_create(&pt_station_heartbeat,NULL,thread_station_heartbeat,NULL);
if(ret!=0){
LOG_I("pthread_create station_heartbeat fail\n");
system("reboot");
}else{
LOG_I("pthread_create station_heartbeat success\n");
pthread_detach(pt_station_heartbeat);
}
ret = pthread_create(&pt_heartbeat_check,NULL,thread_heartbeat_check,NULL);
if(ret!=0){
LOG_I("pthread_create heartbeat_check fail\n");
}else{
LOG_I("pthread_create heartbeat_check success\n");
pthread_detach(pt_heartbeat_check);
}
#if 0
ret = pthread_create(&pt_simulate_light,NULL,thread_simulate_light,NULL);
if(ret!=0){
LOG_I("pthread_create simulate_light fail\n");
}else{
LOG_I("pthread_create simulate_light success\n");
pthread_detach(pt_simulate_light);
}
ret = pthread_create(&pt_all_light,NULL,thread_all_light,NULL);
if(ret!=0){
LOG_I("pthread_create all_light fail\n");
}else{
LOG_I("pthread_create all_light success\n");
pthread_detach(pt_all_light);
}
#endif
#if 0
readresult=file_to_buffer("mqttRawPassword",&len);
if(readresult!=NULL){
strncpy(mqttRawPassword,readresult,len);
readresult=NULL;
LOG_I("saved mqttRawPassword:%s\n",mqttRawPassword);
}else{
if((ping("8.8.8.8") == 0)||(ping("8.8.4.4") == 0)){
getDevRawPassword(stationsn);
}else{
LOG_I("getDevRawPassword net not ready\n");
sleep(3);
}
}
readresult=file_to_buffer("LightEnable",&len);
if(readresult!=NULL){
if(strcmp(readresult,"enable")==0){
isLightEnable=true;
}else{
isLightEnable=false;
}
readresult=NULL;
}
#endif
// 字符串清理函数,去除末尾的空白字符
void trim_whitespace(char *str) {
if (str == NULL) return;
int len = strlen(str);
while (len > 0 && (str[len-1] == '\n' || str[len-1] == '\r' ||
str[len-1] == ' ' || str[len-1] == '\t')) {
str[--len] = '\0';
}
}
readresult=file_to_buffer("savedDevSn",&len);
while(readresult==NULL){
readresult=file_to_buffer("savedDevSn",&len);
sleep(5);
LOG_I("please scan sn\n");
}
strncpy(stationsn,readresult,len);
trim_whitespace(stationsn); // 清理空白字符
readresult=NULL;
LOG_I("saved stationsn:%s\n",stationsn);
// 读取productid配置
readresult=file_to_buffer("productid",&len);
if(readresult!=NULL){
memset(productid, 0, sizeof(productid));
strncpy(productid,readresult,len);
trim_whitespace(productid); // 清理空白字符
readresult=NULL;
LOG_I("saved productid:%s\n",productid);
} else {
LOG_I("use default productid:%s\n",productid);
}
// 读取appSecret配置
readresult=file_to_buffer("appSecret",&len);
if(readresult!=NULL){
memset(appSecret, 0, sizeof(appSecret));
strncpy(appSecret,readresult,len);
trim_whitespace(appSecret); // 清理空白字符
readresult=NULL;
LOG_I("saved appSecret:%s\n",appSecret);
} else {
LOG_I("use default appSecret:%s\n",appSecret);
}
// 读取hostDomain配置
readresult=file_to_buffer("hostDomain",&len);
if(readresult!=NULL){
memset(hostDomain, 0, sizeof(hostDomain));
strncpy(hostDomain,readresult,len);
trim_whitespace(hostDomain); // 清理空白字符
readresult=NULL;
LOG_I("saved hostDomain:%s\n",hostDomain);
} else {
LOG_I("use default hostDomain:%s\n",hostDomain);
}
#if 0
isSendComEnd=false;
isLightOnByGroup=false;
isLightOnById=false;
isLightOn=true;
isBindTag=false;
lightbars_size=5;
changecolor=4;
changesound=1;
groupno=1;
//uart_data_send_head(&uartSend,'B',5,0,lightbars_size);//bind groupno
uart_data_send_head(&uartSend,'P',5,10/5,lightbars_size);//lighton by tag
//uart_data_send_head(&uartSend,'*',5,5/5,0);//lighton by id
//uart_data_send_head(&uartSend,'&',5,5/5,1);//lighton by groupno
//uart_data_send_head(&uartSend,'R',5,30/5,1);//stop broadcast by groupno
//isStopBroadcast=true;
#endif
#if 1
while(1){
if((ping("8.8.8.8") == 0)||(ping("8.8.4.4") == 0)){
LOG_I("net ok\n");
while(!timeNew()){
//LOG_I("sleep\n");
getTimeCount++;
if(getTimeCount==100){
break;
}
sleep(1);
}
//saveStartUpTime();
ret = pthread_create(&pt_mqtt,NULL,thread_mqtt,NULL);
if(ret!=0){
LOG_I("pthread_create mqtt fail\n");
system("reboot");
}else{
pthread_detach(pt_mqtt);
LOG_I("pthread_create mqtt success\n");
}
#if 0
// 添加MQTT连接状态检查线程
ret = pthread_create(&pt_mqtt_status,NULL,thread_mqtt_status_check,NULL);
if(ret!=0){
LOG_I("pthread_create mqtt_status_check fail\n");
}else{
pthread_detach(pt_mqtt_status);
LOG_I("pthread_create mqtt_status_check success\n");
}
#endif
break;
}else{
LOG_I("net not ready\n");
sleep(3);
}
}
#endif
while(1)
{
fgets(command_buffer, sizeof(command_buffer), stdin);
if(execute_command(command_buffer, (command_t *)&__start_command, (&__stop_command - &__start_command)/2) < 0)
{
LOG_I("unsupport command!\r\n");
}
}
return 0;
}