这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 05树莓派5解析OBD_II数据

共3条 1/1 1 跳转至

05树莓派5解析OBD_II数据

菜鸟
2025-06-21 00:45:05     打赏

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)

image-20250621003905903.png

三,环境搭建加载驱动

安装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 发送序列帧

三、高级监听与过滤

命令功能示例说明





带过滤的 candumpID 过滤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, &current_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(&current_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(&current_data, 0, sizeof(OBDData));
    gettimeofday(&current_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(&current_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, &current_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

五,成果演示

image-20250621003548618.pngimage-20250621003600996.png可以看到设置模拟器转载7035,树莓派解析出来的数据也是7035


专家
2025-06-21 17:39:36     打赏
2楼

感谢分享


专家
2025-06-21 18:05:41     打赏
3楼

感谢分享


共3条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]