05树莓派5 解析OBD_II数据
一,方案确定
由于树莓派没有can外设,只能通过转换芯片来实现,可以是串口转can,也可以现在spi转can。我这里使用的是spi转can的方案,采用MCP2515模块对OBD_II数据信息进行解析。
MCP2515模块连接:
VCC -> 3.3V
GND -> GND
CS -> SPI片选引脚 (默认SPI0_CE0/GPIO8)
SO -> SPI_MISO (GPIO9)
SI -> SPI_MOSI (GPIO10)
SCK -> SPI_SCLK (GPIO11)
INT -> GPIO25 (物理引脚22)

三,环境搭建加载驱动
安装socketcan工具以及cantools工具
sudo apt install can-utils
pip3 install cantools
修改config.txt文件用于加载mcp2515的驱动,这个文件以前是在/boot/中,打开之后他会指引你到/boot/firmware目录中。
#打开文件
sudo nano /boot/firmware/config.txt
#末尾添加
dtparam=spi=on
dtoverlay=mcp2515-can0,oscillator=8000000,interrupt=25,cs=0
需要注意的是自己的模块晶振的大小,我的这里是8Mhz,修改这里oscillator=8000000
然后重启
sudo reboot
配置验证方法:
检查SPI设备:
ls /dev/spi*
# 应显示: /dev/spidev0.0 /dev/spidev1.0
检查CAN接口:
ip link show
# 应显示: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN
查看加载的驱动:
dtparam=spi=on
dtoverlay=mcp2515-can0,oscillator=12000000,interrupt=25,spimaxfrequency=2000000
检测MCP2515是否被正确挂载
ifconfig -a
常见问题解决:
CAN接口不工作:
检查晶振频率是否匹配(常见有8M/16M/20M),我的这里是8M的晶振
验证接线是否正确
常用命令一、CAN 接口管理命令
命令功 能示例说明
| ip link set | 启用 CAN 接口 | sudo ip link set can0 type can bitrate 500000 | 设置波特率 500kbps |
| ip link set up | 启动接口 | sudo ip link set can0 up | 激活 CAN 接口 |
| ip link set down | 关闭接口 | sudo ip link set can0 down | 禁用 CAN 接口 |
| ip -d link show | 查看接口状态 | ip -d link show can0 | 显示详细信息 |
| ifconfig | 基础状态查看 | ifconfig can0 | 检查接口状态 |
二、数据收发操作
命令功能示例说明
| cansend | 发送 CAN 帧 | cansend can0 123#AABBCCDD | 发送标准帧 ID=0x123 |
| cangen | 自动生成 CAN 帧 | cangen can0 -g 100 -I 123 -D i | 每 100ms 发送随机数据 |
| candump | 监听 CAN 数据 | candump can0 | 实时显示所有帧 |
| canplayer | 回放 CAN 日志 | canplayer -I candump.log | 从文件回放数据 |
| cansequence | 序列测试 | cansequence -p can0 -g 50 | 每 50ms 发送序列帧 |
三、高级监听与过滤
命令功能示例说明
| 带过滤的 candump | ID 过滤 | candump can0,100:7FF | 只显示 ID 0x100-0x7FF |
| 时间戳监听 | 精确时间 | candump -ta can0 | 显示微秒级时间戳 |
| 颜色高亮 | 视觉区分 | candump -c can0 | 不同 ID 用不同颜色 |
| 数据格式 | 多种输出 | candump -l can0 | 记录到文件 (candump.log) |
| 回环测试 | 自发自收 | sudo ip link set can0 type can loopback on | 启用回环模式 |
四、错误诊断与统计
命令功能示例说明
| ip -s link | 错误统计 | ip -s -s link show can0 | 显示错误计数器 |
| canbusload | 总线负载 | canbusload can0@500000 | 计算总线利用率 |
| canerror | 错误帧检测 | canerror can0 | 监听错误帧 |
| canecho | 回显接收 | canecho can0 | 显示接收的原始数据 |
| cansniffer | 高级嗅探 | cansniffer -c can0 | 按 ID 分组显示 |
五、系统配置管理
文件/命令功能配置示例说明
| /etc/network/interfaces | 开机自启 | <pre>auto can0 iface can0 can bitrate 500000</pre> | 系统启动时自动启用 |
| ip details | 查看参数 | ip -details link show can0 | 显示当前配置 |
| 波特率计算 | 复杂速率 | ip link set can0 type can tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1 | 手动配置时序参数 |
| CAN FD 配置 | 高速模式 | sudo ip link set can0 type can bitrate 500000 dbitrate 2000000 fd on |
六、CAN 四种模式设置命令
模式命令功能说明典型应用场景
| 正常模式 (Normal) | sudo ip link set can0 type can bitrate 500000 sudo ip link set can0 up | 标准通信模式,可正常收发数据 | 实际总线通信 |
| 回环模式 (Loopback) | sudo ip link set can0 down sudo ip link set can0 type can bitrate 500000 loopback on sudo ip link set can0 up | 自发自收,不连接物理总线 | 驱动自测、应用开发 |
| 只听模式 (Listen-Only) | sudo ip link set can0 down sudo ip link set can0 type can bitrate 500000 listen-only on sudo ip link set can0 up | 只接收数据,不发送任何帧 | 总线监听、逆向分析 |
| 三态模式 (Triple-Sampling) | sudo ip link set can0 down sudo ip link set can0 type can bitrate 500000 triple-sampling on sudo ip link set can0 up | 每个位采样3次提高 |
永久配置(开机自启)
编辑 /etc/network/interfaces:
auto can0 iface can0 can bitrate 500000 loopback off # 正常模式 # listen-only on # 听只听模式 # triple-sampling on # 三态模式 up /sbin/ip link set $IFACE up down /sbin/ip link set $IFACE down
每次修改都要先关闭can设备再开启。
四,代码编写
主程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <time.h>
#include <pthread.h>
#include <sys/time.h>
#include <jansson.h> // JSON 库
// OBD-II PID定义
#define PID_ENGINE_RPM 0x0C
#define PID_VEHICLE_SPEED 0x0D
#define PID_COOLANT_TEMP 0x05
#define PID_FUEL_PRESSURE 0x0A
#define PID_FUEL_LEVEL 0x2F
#define PID_ENGINE_LOAD 0x04
// CAN总线配置
#define CAN_INTERFACE "can0"
#define CAN_BITRATE 500000
// UDP配置
#define UDP_IP "127.0.0.1" // 目标IP地址
#define UDP_PORT 5000 // 目标端口
#define MAX_UDP_PACKET 1024 // 最大UDP包大小
// 数据结构存储OBD数据
typedef struct {
int rpm;
int speed;
int coolant_temp;
float fuel_pressure;
float fuel_level;
int engine_load;
struct timeval timestamp;
} OBDData;
// 全局变量
volatile int running = 1;
OBDData current_data;
pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER;
int udp_sock = -1;
struct sockaddr_in udp_addr;
// CAN总线初始化
int init_can_socket(const char *ifname) {
int s;
struct sockaddr_can addr;
struct ifreq ifr;
// 创建socket
if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("Socket creation failed");
return -1;
}
// 设置接口名
strcpy(ifr.ifr_name, ifname);
if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
perror("I/O control failed");
close(s);
return -1;
}
// 绑定socket到CAN接口
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Socket binding failed");
close(s);
return -1;
}
return s;
}
// UDP初始化
int init_udp_socket(const char *ip, int port) {
int sock;
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("UDP socket creation failed");
return -1;
}
memset(&udp_addr, 0, sizeof(udp_addr));
udp_addr.sin_family = AF_INET;
udp_addr.sin_port = htons(port);
// 转换IP地址
if (inet_pton(AF_INET, ip, &udp_addr.sin_addr) <= 0) {
perror("Invalid UDP address");
close(sock);
return -1;
}
// 设置超时选项
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100000; // 100ms超时
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
return sock;
}
// 发送OBD请求
void send_obd_request(int can_sock, uint8_t pid) {
struct can_frame frame;
// 标准OBD请求格式
frame.can_id = 0x7DF; // 广播地址
frame.can_dlc = 8; // 数据长度
// OBD-II请求格式: [模式, PID, 填充0]
frame.data[0] = 0x02; // 数据字节数
frame.data[1] = 0x01; // 模式: 显示当前数据
frame.data[2] = pid; // 请求的PID
frame.data[3] = 0x00; // 填充
frame.data[4] = 0x00;
frame.data[5] = 0x00;
frame.data[6] = 0x00;
frame.data[7] = 0x00;
// 发送请求
if (write(can_sock, &frame, sizeof(frame)) != sizeof(frame)) {
perror("CAN frame send failed");
}
}
// 解析OBD响应
void parse_obd_response(struct can_frame *frame, OBDData *data) {
// 检查是否为ECU响应 (ID 0x7E8 - 0x7EF)
if (frame->can_id < 0x7E8 || frame->can_id > 0x7EF)
return;
// 检查响应格式: [长度, 模式+0x40, PID, 数据...]
if (frame->data[0] < 2 || frame->data[1] != 0x41)
return;
uint8_t pid = frame->data[2];
gettimeofday(&data->timestamp, NULL); // 获取精确时间戳
pthread_mutex_lock(&data_mutex);
// 根据PID解析数据
switch (pid) {
case PID_ENGINE_RPM:
// 公式: (256 * A + B) / 4
data->rpm = (frame->data[3] * 256 + frame->data[4]) / 4;
break;
case PID_VEHICLE_SPEED:
// 公式: A
data->speed = frame->data[3];
break;
case PID_COOLANT_TEMP:
// 公式: A - 40
data->coolant_temp = frame->data[3] - 40;
break;
case PID_FUEL_PRESSURE:
// 公式: 3 * A
data->fuel_pressure = frame->data[3] * 3.0;
break;
case PID_FUEL_LEVEL:
// 公式: 100 * A / 255
data->fuel_level = frame->data[3] * 100.0 / 255.0;
break;
case PID_ENGINE_LOAD:
// 公式: 100 * A / 255
data->engine_load = frame->data[3] * 100 / 255;
break;
}
pthread_mutex_unlock(&data_mutex);
}
// 将OBD数据转换为JSON格式
char* create_obd_json(OBDData *data) {
json_t *root = json_object();
// 添加时间戳(毫秒精度)
char timestamp_str[64];
struct tm *tm_info;
time_t seconds = data->timestamp.tv_sec;
tm_info = localtime(&seconds);
strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%dT%H:%M:%S", tm_info);
// 添加毫秒部分
char full_timestamp[70];
snprintf(full_timestamp, sizeof(full_timestamp), "%s.%03ldZ",
timestamp_str, data->timestamp.tv_usec / 1000);
// 添加数据字段
json_object_set_new(root, "timestamp", json_string(full_timestamp));
json_object_set_new(root, "rpm", json_integer(data->rpm));
json_object_set_new(root, "speed", json_integer(data->speed));
json_object_set_new(root, "coolant_temp", json_integer(data->coolant_temp));
json_object_set_new(root, "fuel_pressure", json_real(data->fuel_pressure));
json_object_set_new(root, "fuel_level", json_real(data->fuel_level));
json_object_set_new(root, "engine_load", json_integer(data->engine_load));
// 生成JSON字符串
char *json_str = json_dumps(root, JSON_COMPACT | JSON_PRESERVE_ORDER);
json_decref(root);
return json_str;
}
// 通过UDP发送JSON数据
int send_json_via_udp(const char *json_str) {
if (udp_sock < 0) return -1;
size_t len = strlen(json_str);
if (len > MAX_UDP_PACKET - 1) {
fprintf(stderr, "JSON too large for UDP packet\n");
return -1;
}
ssize_t sent = sendto(udp_sock, json_str, len, 0,
(struct sockaddr *)&udp_addr, sizeof(udp_addr));
if (sent < 0) {
perror("UDP send failed");
return -1;
}
return sent;
}
// 数据采集线程
void *data_acquisition_thread(void *arg) {
int can_sock = *(int *)arg;
struct can_frame frame;
int nbytes;
// 初始化PID轮询顺序
uint8_t pids[] = {
PID_ENGINE_RPM,
PID_VEHICLE_SPEED,
PID_COOLANT_TEMP,
PID_FUEL_PRESSURE,
PID_FUEL_LEVEL,
PID_ENGINE_LOAD
};
int pid_count = sizeof(pids) / sizeof(pids[0]);
int current_pid = 0;
struct timeval last_request;
gettimeofday(&last_request, NULL);
while (running) {
struct timeval now;
gettimeofday(&now, NULL);
// 计算时间差(毫秒)
long elapsed = (now.tv_sec - last_request.tv_sec) * 1000 +
(now.tv_usec - last_request.tv_usec) / 1000;
// 每200ms发送一个新的PID请求
if (elapsed >= 200) {
send_obd_request(can_sock, pids[current_pid]);
current_pid = (current_pid + 1) % pid_count;
last_request = now;
}
// 读取CAN帧(非阻塞)
struct timeval tv = {0, 10000}; // 10ms超时
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(can_sock, &readfds);
if (select(can_sock + 1, &readfds, NULL, NULL, &tv) > 0) {
nbytes = read(can_sock, &frame, sizeof(struct can_frame));
if (nbytes < 0) {
perror("CAN read error");
continue;
}
if (nbytes == sizeof(struct can_frame)) {
parse_obd_response(&frame, ¤t_data);
}
}
usleep(5000); // 5ms延迟减少CPU使用
}
return NULL;
}
// 数据发送线程
void *data_sending_thread(void *arg) {
(void)arg; // 未使用参数
while (running) {
// 创建JSON数据
pthread_mutex_lock(&data_mutex);
char *json_str = create_obd_json(¤t_data);
pthread_mutex_unlock(&data_mutex);
// 发送UDP数据
if (json_str) {
send_json_via_udp(json_str);
free(json_str);
}
// 每秒发送10次 (100ms间隔)
usleep(100000);
}
return NULL;
}
int main() {
printf("OBD-II 数据采集与转发程序\n");
printf("目标UDP: %s:%d\n", UDP_IP, UDP_PORT);
// 初始化CAN总线
int can_sock = init_can_socket(CAN_INTERFACE);
if (can_sock < 0) {
fprintf(stderr, "CAN初始化失败\n");
return EXIT_FAILURE;
}
// 初始化UDP
udp_sock = init_udp_socket(UDP_IP, UDP_PORT);
if (udp_sock < 0) {
fprintf(stderr, "UDP初始化失败\n");
close(can_sock);
return EXIT_FAILURE;
}
// 初始化数据结构
memset(¤t_data, 0, sizeof(OBDData));
gettimeofday(¤t_data.timestamp, NULL);
// 创建数据采集线程
pthread_t acq_thread;
if (pthread_create(&acq_thread, NULL, data_acquisition_thread, &can_sock) != 0) {
fprintf(stderr, "无法创建采集线程\n");
close(can_sock);
close(udp_sock);
return EXIT_FAILURE;
}
// 创建数据发送线程
pthread_t send_thread;
if (pthread_create(&send_thread, NULL, data_sending_thread, NULL) != 0) {
fprintf(stderr, "无法创建发送线程\n");
running = 0;
pthread_join(acq_thread, NULL);
close(can_sock);
close(udp_sock);
return EXIT_FAILURE;
}
// 主线程等待退出
printf("程序运行中,按Enter键退出...\n");
getchar(); // 等待用户输入
running = 0;
// 清理资源
pthread_join(acq_thread, NULL);
pthread_join(send_thread, NULL);
close(can_sock);
close(udp_sock);
printf("程序已退出\n");
return EXIT_SUCCESS;
}# 安装json库 sudo apt install libjansson-dev #编译 gcc odb.c -o odb -ljansson -lpthread ./odb
测试程序
include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <jansson.h> // JSON解析库
#include <time.h>
#define UDP_PORT 5000 // 监听端口
#define BUFFER_SIZE 1024 // 缓冲区大小
#define STATS_INTERVAL 5 // 统计信息输出间隔(秒)
typedef struct {
long total_packets; // 总接收包数
long valid_json; // 有效JSON包数
long invalid_json; // 无效JSON包数
long parse_errors; // JSON解析错误数
long missing_fields; // 缺失字段数
double min_latency; // 最小延迟(ms)
double max_latency; // 最大延迟(ms)
double total_latency; // 总延迟
struct timeval last_print; // 上次统计时间
} ReceiverStats;
// 解析时间戳字符串为timeval结构
int parse_timestamp(const char *timestamp_str, struct timeval *tv) {
struct tm tm;
long milliseconds;
char *dot = strchr(timestamp_str, '.');
char *tz = strchr(timestamp_str, 'Z');
if (!dot || !tz) return -1;
// 解析日期和时间部分
strptime(timestamp_str, "%Y-%m-%dT%H:%M:%S", &tm);
// 解析毫秒部分
milliseconds = atol(dot + 1);
// 转换为time_t
tv->tv_sec = mktime(&tm);
tv->tv_usec = milliseconds * 1000; // 毫秒转微秒
return 0;
}
// 计算当前时间与时间戳之间的延迟(毫秒)
double calculate_latency(const struct timeval *timestamp, const struct timeval *current) {
long sec_diff = current->tv_sec - timestamp->tv_sec;
long usec_diff = current->tv_usec - timestamp->tv_usec;
if (usec_diff < 0) {
sec_diff--;
usec_diff += 1000000;
}
return (sec_diff * 1000.0) + (usec_diff / 1000.0);
}
// 更新统计信息
void update_stats(ReceiverStats *stats, int valid, double latency) {
stats->total_packets++;
if (valid) {
stats->valid_json++;
// 更新延迟统计
if (stats->min_latency < 0 || latency < stats->min_latency) {
stats->min_latency = latency;
}
if (latency > stats->max_latency) {
stats->max_latency = latency;
}
stats->total_latency += latency;
} else {
stats->invalid_json++;
}
}
// 打印统计信息
void print_stats(const ReceiverStats *stats) {
double avg_latency = (stats->valid_json > 0) ?
stats->total_latency / stats->valid_json : 0.0;
printf("\n=== 接收统计 ===\n");
printf("总包数: %ld\n", stats->total_packets);
printf("有效JSON: %ld (%.2f%%)\n", stats->valid_json,
(stats->total_packets > 0) ?
(100.0 * stats->valid_json / stats->total_packets) : 0.0);
printf("无效JSON: %ld (%.2f%%)\n", stats->invalid_json,
(stats->total_packets > 0) ?
(100.0 * stats->invalid_json / stats->total_packets) : 0.0);
printf("解析错误: %ld\n", stats->parse_errors);
printf("缺失字段: %ld\n", stats->missing_fields);
printf("延迟(ms): 最小=%.2f, 最大=%.2f, 平均=%.2f\n",
stats->min_latency, stats->max_latency, avg_latency);
printf("================\n");
}
// 处理接收到的JSON数据
void process_json_data(const char *json_str, ReceiverStats *stats) {
json_t *root;
json_error_t error;
// 解析JSON
root = json_loads(json_str, 0, &error);
if (!root) {
stats->parse_errors++;
fprintf(stderr, "JSON解析错误 (行 %d, 列 %d): %s\n",
error.line, error.column, error.text);
return;
}
// 获取当前时间
struct timeval current_time;
gettimeofday(¤t_time, NULL);
// 提取时间戳
json_t *timestamp_json = json_object_get(root, "timestamp");
if (!json_is_string(timestamp_json)) {
stats->missing_fields++;
json_decref(root);
return;
}
const char *timestamp_str = json_string_value(timestamp_json);
struct timeval data_time;
if (parse_timestamp(timestamp_str, &data_time) ){
fprintf(stderr, "时间戳格式错误: %s\n", timestamp_str);
stats->missing_fields++;
json_decref(root);
return;
}
// 计算延迟
double latency = calculate_latency(&data_time, ¤t_time);
// 提取数据字段
int rpm = 0, speed = 0, coolant_temp = 0, engine_load = 0;
float fuel_pressure = 0.0, fuel_level = 0.0;
json_t *rpm_json = json_object_get(root, "rpm");
if (json_is_integer(rpm_json)) rpm = json_integer_value(rpm_json);
json_t *speed_json = json_object_get(root, "speed");
if (json_is_integer(speed_json)) speed = json_integer_value(speed_json);
json_t *coolant_temp_json = json_object_get(root, "coolant_temp");
if (json_is_integer(coolant_temp_json)) coolant_temp = json_integer_value(coolant_temp_json);
json_t *engine_load_json = json_object_get(root, "engine_load");
if (json_is_integer(engine_load_json)) engine_load = json_integer_value(engine_load_json);
json_t *fuel_pressure_json = json_object_get(root, "fuel_pressure");
if (json_is_real(fuel_pressure_json)) fuel_pressure = json_real_value(fuel_pressure_json);
else if (json_is_integer(fuel_pressure_json)) fuel_pressure = json_integer_value(fuel_pressure_json);
json_t *fuel_level_json = json_object_get(root, "fuel_level");
if (json_is_real(fuel_level_json)) fuel_level = json_real_value(fuel_level_json);
else if (json_is_integer(fuel_level_json)) fuel_level = json_integer_value(fuel_level_json);
// 打印接收到的数据
printf("\n===== 车辆数据 =====\n");
printf("时间戳: %s (延迟: %.2f ms)\n", timestamp_str, latency);
printf("发动机转速: %d RPM\n", rpm);
printf("车速: %d km/h\n", speed);
printf("冷却液温度: %d ℃\n", coolant_temp);
printf("燃油压力: %.1f kPa\n", fuel_pressure);
printf("燃油液位: %.1f%%\n", fuel_level);
printf("发动机负载: %d%%\n", engine_load);
printf("====================\n");
// 更新统计信息
update_stats(stats, 1, latency);
// 清理JSON对象
json_decref(root);
}
int main() {
int sockfd;
struct sockaddr_in servaddr, cliaddr;
socklen_t len;
char buffer[BUFFER_SIZE];
ReceiverStats stats = {0};
// 初始化统计
stats.min_latency = -1;
stats.max_latency = -1;
gettimeofday(&stats.last_print, NULL);
printf("===== OBD数据UDP接收测试程序 =====\n");
printf("监听端口: %d\n", UDP_PORT);
printf("按Ctrl+C退出...\n\n");
// 创建UDP套接字
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&cliaddr, 0, sizeof(cliaddr));
// 配置服务器地址
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(UDP_PORT);
// 绑定套接字
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("bind failed");
close(sockfd);
exit(EXIT_FAILURE);
}
// 设置接收超时
struct timeval tv;
tv.tv_sec = 1; // 1秒超时
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
while (1) {
len = sizeof(cliaddr);
ssize_t n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, 0,
(struct sockaddr *)&cliaddr, &len);
if (n < 0) {
// 超时或其他错误,检查是否应该打印统计信息
struct timeval now;
gettimeofday(&now, NULL);
double elapsed = (now.tv_sec - stats.last_print.tv_sec) +
(now.tv_usec - stats.last_print.tv_usec) / 1000000.0;
if (elapsed >= STATS_INTERVAL) {
print_stats(&stats);
stats.last_print = now;
}
continue;
}
// 确保字符串以空字符结束
buffer[n] = '\0';
// 打印原始数据
printf("收到 %ld 字节数据:\n%s\n", n, buffer);
// 处理JSON数据
process_json_data(buffer, &stats);
// 检查是否应该打印统计信息
struct timeval now;
gettimeofday(&now, NULL);
double elapsed = (now.tv_sec - stats.last_print.tv_sec) +
(now.tv_usec - stats.last_print.tv_usec) / 1000000.0;
if (elapsed >= STATS_INTERVAL) {
print_stats(&stats);
stats.last_print = now;
}
}
close(sockfd);
return 0;
}#编译 gcc obd_rive.c -o obd_rive -ljansson -lpthread ./obd_rive
五,成果演示

可以看到设置模拟器转载7035,树莓派解析出来的数据也是7035
我要赚赏金
