求职关键词-程序员宅基地

技术标签: 求职  

嵌入式工程师

常用的实时操作系统

μC/OS-II和μC/OS-III:
μC/OS是专为嵌入式系统设计的RTOS,由Micrium公司开发。
μC/OS-II是一个可移植、可固化、可剪裁的小型RTOS,提供了任务管理、时间管理、内存管理和同步通信等基本功能。
μC/OS-III是μC/OS-II的升级版,增加了对多核处理器的支持,改进了任务调度算法,并增强了内存管理和通信机制。
FreeRTOS:
FreeRTOS是一个开源的RTOS,适用于各种微控制器和小型嵌入式系统。
它提供了一个轻量级的任务调度器、互斥锁、信号量、消息队列、软件定时器和内存管理等功能。
FreeRTOS具有良好的可移植性,支持多种架构和编译器,并且有丰富的文档和社区支持。
eCos (Embeddable Configurable Operating System):
eCos是一个开源的、高度可配置和可移植的RTOS,特别适合于嵌入式设备。
它提供了一种灵活的配置工具,允许开发者根据具体应用需求选择和优化所需的内核服务和硬件驱动。
eCos支持多线程、网络协议栈、文件系统、设备驱动和各种中间件组件。
VxWorks:
VxWorks是由Wind River Systems开发的商业RTOS,广泛应用于航空、国防、工业自动化等领域。
它提供了强大的任务调度、中断处理、网络通信、文件系统和设备驱动等功能。
VxWorks支持多种架构,包括ARM、PowerPC和MIPS等,并且具有高度的安全性和可靠性。
QNX Neutrino RTOS:
QNX Neutrino是由QNX Software Systems开发的高性能RTOS,以其微内核架构和高可靠性而闻名。
它支持多进程、多线程、实时调度、网络通信、图形用户界面和安全特性。
QNX Neutrino被广泛应用于汽车、医疗设备、工业控制和国防等领域。
NuttX:
NuttX是一个开源的RTOS,旨在提供类似Linux的POSIX接口和功能集。
它支持多任务、多线程、定时器、信号量、消息队列、文件系统和网络协议栈等功能。
NuttX具有良好的可移植性,支持多种架构,并且可以作为嵌入式Linux的一个轻量级替代品。
mbed OS:
mbed OS是由ARM公司开发的开源RTOS,主要针对基于ARM Cortex-M系列微控制器的物联网设备。
它提供了一个完整的物联网平台,包括安全性、连接性、设备管理和云服务集成等功能。
mbed OS支持多种无线和有线通信协议,如Wi-Fi、蓝牙、Ethernet和低功耗广域网(LPWAN)。
RT-Thread:
RT-Thread是中国开源社区主导的RTOS,具有良好的可扩展性和模块化设计。
它提供了任务管理、时间管理、同步对象、内存管理、文件系统和网络协议栈等功能。
RT-Thread支持多种架构,包括ARM、RISC-V和MIPS等,并且有丰富的组件库和社区支持。

鸿蒙是rtos吗?

鸿蒙OS确实包含两个内核版本,liteos_A和liteos_M。liteos_A是类似于Linux的内核,主要对标于Linux这种分时操作系统。而liteos_M则是类似FreeRTOS、rt-thread、UCOS这些实时操作系统,具备嵌入式RTOS的特性。因此,可以说鸿蒙OS中的liteos_M是RTOS。
华为LiteOS是华为在2012年提出的,适用于物联网嵌入式设备的操作系统,它最早的目的是要对标全球已有的RTOS(如FreeRTOS,UCOSII等)来开发一款国产的RTOS系统。然而,鸿蒙OS与LiteOS之间并不完全等同,尽管它们都包含了轻量级的RTOS内核。总的来说,我们可以认为鸿蒙OS具有RTOS的特性,但它不仅仅是一个RTOS。

bms软件开发工程师

1.负责BMS软件开发,包括外设驱动、电量计驱动以相关AFE驱动开发等;
2.负责BMS软件功能开发,包括开关机逻辑、充电管理、故障诊断、多电池包并机算法开发等;
3.协助硬件开发人员,完成BMS的调试和相关测试工作;
4.参与BMS需求分析评审,编写软件需求文档和软件设计文档;
任职资格:
1.统招本科及以上学历,1年以上嵌入式软件开发经验,3年以上BMS软件开发工作经验;
2.熟练使用Keil、MDK等软件开发及相应仿真工具, 熟悉GD32、STM32、NXP等芯片平台软件开发;
3.熟练掌握CAN、UART、SPI、IIC等通讯协议开发;
4.熟悉锂电池性能,掌握锂电池SOC和SOH算法设计, 熟悉TI的电量计算法者优先;
5.有储能电池和电动车电池管理系统软件设计经验优先;

BMS软件开发要求

BMS(Battery Management System,电池管理系统)软件开发涉及的知识较为专业和复杂,主要包括以下几个方面:
电池原理与BMS系统知识:
需要理解电池的基本工作原理,包括电池的化学特性、充放电过程、电池的老化与故障机理等。同时,要熟悉BMS系统的功能与架构,BMS主要负责电池的监控、管理、保护与均衡,确保电池安全可靠地运行。
理解电池的工作原理、电池类型、电池特性等是基础。同时,需要熟悉电池的各种参数,如单体电池电压、电池极柱温度、电池回路电流、电池组端电压、电池系统绝缘电阻等。
软件开发基础:
必须掌握至少一种编程语言,如C语言。C语言因其执行效率高、底层操作能力强而被广泛应用于BMS软件开发。同时,需要熟悉软件开发流程,包括需求分析、设计、编码、测试和维护等。
嵌入式系统开发:
BMS通常运行在嵌入式系统中,因此要熟悉嵌入式系统的开发,包括嵌入式操作系统(如RTOS)的使用,以及嵌入式硬件(如微控制器MCU)的编程。
硬件设备交互:
需了解如何与各种硬件设备进行交互,包括传感器、执行器和控制器等。这通常涉及串行通信、并行通信、网络通信等知识。
了解主板、从板(LCU)、BDU等硬件组件的功能和作用。例如,主板作为BMS的核心,负责收集各个从板的采样信息,并与整车通讯;从板则实时监控电池的各项状态,并将信息传输给主板。
BMS中的主板和从板之间、以及与整车之间的通讯通常会采用特定的通讯协议,如CAN通讯或菊花链通讯。还有其他协议UART、SPI、IIC
外设驱动开发:
BMS系统需要与多种外设协作,例如电量计、温度传感器等,因此需要掌握外设驱动程序的编写,确保系统能正确读取外设数据并进行相应的处理。
电量计驱动与AFE驱动开发:电量计(Battery Fuel Gauge)负责监测电池的电量,而AFE(Analog Front End,模拟前端)负责采集电池的电压、电流和温度等数据。开发者需熟悉这两种硬件的工作原理,并能够编写相应的驱动程序。
电池管理算法:
理解并能够实现电池状态估计算法,如SOC(State of Charge)、SOH(State of Health)、SOP(State of Power)等。
掌握电池均衡、热管理、故障诊断等相关算法。
数据管理与处理:
BMS系统需要处理大量实时数据,因此需要掌握数据库知识,包括数据库的设计、管理和SQL语言等,以确保数据的有效存储和处理。
功能安全开发:
由于BMS系统关乎电池安全,因此需要按照功能安全标准进行开发,包括对系统的可靠性、故障容错能力等方面的考量。
模型驱动开发:
对于复杂的BMS系统,可能采用模型驱动的方法进行开发,这需要开发者理解模型驱动的开发流程,并能够使用相关的工具进行模型构建和转换。
测试与验证:
开发者需要掌握BMS系统的测试方法,包括单元测试、集成测试和系统测试等,以确保软件在实际应用中稳定可靠。

常用的串行通信协议

CAN、UART、SPI和IIC(也称为I²C)是四种常见的串行通信协议,它们在各种嵌入式系统中广泛应用。下面将详细介绍这四种通信协议。

CAN(Controller Area Network)通信协议:

CAN是一种高性能、高可靠性的网络通信协议,主要用于汽车电子领域。它采用多主控制方式,通过消息广播的方式实现设备之间的通信。
由德国电气商博世公司在1986年率先提出。CAN协议经过ISO标准化后有两个标准:ISO11898标准和ISO11519-2标准。其中ISO11898是针对通信速率为125Kbps~1Mbps的高速通信标准,而ISO11519-2是针对通信速率为125Kbps以下的低速通信标准。
CAN协议的主要特点包括:
多主控制:在网络中的每个节点都有权利主动发送消息,没有固定的主从关系。
异步通信:CAN协议采用异步通信方式,发送方和接收方的时钟独立,数据传输速率可调。
差分信号:CAN协议使用差分信号传输,抗干扰能力强,传输距离远。
消息优先级:CAN协议支持消息优先级,高优先级消息优先发送。通过报文标识符的仲裁场来决定消息的优先级,标识符越小,优先级越高。
错误检测与处理:CAN协议具有强大的错误检测和处理能力,能够保证数据的可靠性。
数据速率:常见的数据速率范围是5kbps到1Mbps。

UART(Universal Asynchronous Receiver/Transmitter)通信协议:

UART是一种通用的异步收发通信协议,主要用于计算机和外设之间的串行通信。它采用两根线(TX和RX)进行数据的传输,发送端使用TX线向接收端发送数据,接收端使用RX线接收数据。UART协议的主要特点包括:
异步通信:UART采用异步通信方式,发送方和接收方的时钟独立,数据传输速率可调。
简单的物理层:UART协议只需要两根信号线(TX和RX)和一根地线(GND)即可实现通信。
多种数据位宽:UART支持多种数据位宽,如5位、6位、7位和8位。
校验位:UART可选校验位,用于错误校验,提高数据传输的可靠性。
停止位:UART协议使用停止位作为数据传输的结束标志。
数据速率:常见的数据速率范围是300bps到115200bps或更高

SPI(Serial Peripheral Interface)通信协议:

SPI是一种高速的同步串行通信协议,主要用于微控制器和外部设备之间的通信。SPI协议的主要特点包括:
同步通信:SPI采用同步通信方式,发送方和接收方的时钟同步,数据传输速率固定。
四线制:SPI协议使用四根线进行通信,包括两根数据线(MISO和MOSI)、一根时钟线(SCK)和一根片选线(SS)。
主从设备:SPI协议中,主设备控制通信过程,从设备响应主设备的数据请求。
传输模式:SPI协议支持两种传输模式,分别为单线传输和双线传输。
高速传输:由于其同步特性,SPI通常支持较高的数据速率。

IIC(Inter-Integrated Circuit)通信协议:

IIC,也称为I²C,是一种低速度的同步串行通信协议,主要用于集成电路之间的通信。IIC协议的主要特点包括:
同步通信:IIC采用同步通信方式,发送方和接收方的时钟同步,数据传输速率可调。
半双工通信:在同一时间,数据只能在一个方向上传输
两线制:IIC协议使用两根线进行通信,一根是串行数据线(SDA),另一根是串行时钟线(SCL)。
主从设备:IIC协议中,主设备控制通信过程,从设备响应主设备的数据请求。有一个主设备和一个或多个从设备,主设备控制总线并产生时钟信号。
地址识别:IIC协议通过设备地址进行通信,每个设备都有一个唯一的地址。可以7位或10位寻址,即从设备地址可以通过7位或10位进行编码
错误检测:IIC协议具有错误检测功能,能够检测数据传输过程中的错误。
总线仲裁:当多个主设备同时尝试控制总线时,通过线与逻辑自动解决冲突
低速传输:相比于SPI和UART,IIC的数据速率较低,通常在kHz级别。
总结:
CAN协议适用于汽车电子领域,具有高可靠性、抗干扰能力强等特点。
UART协议适用于计算机和外设之间的串行通信,具有简单的物理层、多种数据位宽等特点。
SPI协议适用于微控制器和外部设备之间的高速通信,具有同步通信、四线制等特点。
IIC协议适用于集成电路之间的低速度通信,具有同步通信、两线制等特点。

ANDROID 工程师

LINUX 工程师

Linux开发专家

精通C/C++语言编程;熟悉Linux系统、Shell、Makefile,图像系统(LVGL,QT,MINI GUI)
熟悉常用Linux命令及shell编程,git版本管理、GNU 工具链;
精通TCP/IP、HTTPS、MQTT、485、232、SIP、CAN、USB、Ethernet等常见接口编程;熟悉WIFI/BLE/SPI/EMMC等开发经验
精通Linux系统裁剪,驱动移植,有uboot开发经验;有驱动开发调试经验;有量产产品开发经验;
熟悉ARM、MIPS等32位嵌入式处理器的体系结构;
熟悉Python开发经验优先;
熟悉UVC/V4L2优先;
有RTSP、RTMP开发经验优先;
熟悉AV 编解码优先;
有AI平台开发经验优先;

LVGL、QT和MINI GUI的异同

LVGL、QT和MINI GUI都是用于开发图形用户界面(GUI)的库,它们有各自的特点和应用场景。
LVGL(LittleVGL): LVGL是一个开源的嵌入式GUI库,主要用于资源受限的微控制器和处理器,如STM32、ESP32等。它用C语言编写,具有丰富的控件库和高级图形效果,支持多种输入设备和多种显示设备。LVGL的内存占用小,可移植性高,适合于需要在嵌入式设备上实现精美界面的项目。
QT: QT是诺基亚开发的一个跨平台的开发框架,用于开发具有图形用户界面的应用程序,包括桌面应用、嵌入式应用等。QT使用C++语言编写,提供了丰富的控件库和窗口管理功能。QT具有强大的事件处理机制,支持多线程、网络通信等。它还提供了Qt Designer工具,可以方便地设计界面。QT更适合于复杂、功能丰富的应用程序开发。
MINI GUI: MINI GUI是一个小型的嵌入式GUI库,主要用于嵌入式设备和实时操作系统。它用C语言编写,具有较小的体积和较高的运行效率。MINI GUI提供了基本的控件和窗口管理功能,适合于需要在资源受限的环境中实现简单界面的项目。
总结: LVGL、QT和MINI GUI三者各有特点,适用于不同的开发场景。LVGL适合于资源受限的嵌入式设备,QT适合于功能复杂的桌面和嵌入式应用程序,而MINI GUI则适用于实时操作系统和嵌入式设备。

资源占用与性能:
MiniGUI:起初专为嵌入式平台设计,代码量相对较小,最低可裁剪到400k左右,特别适用于资源受限的嵌入式设备。它考虑到了嵌入式平台对硬件的要求和成本控制。
QT:最初是为PC设计的桌面环境,架构相对复杂,虽然可以裁剪,但在裁剪后其库可能失去使用价值。它在计算性能较强的系统上表现良好,如使用MPU(ARM9或Cortex-A内核)的核心处理器。适用于开发复杂的桌面应用程序和移动应用程序。
LVGL:是目前相对较好的选择,尤其在渲染性能、许可费用和社区活跃度方面。它对硬件要求相对较低,并且在资源受限的嵌入式设备上也有出色的性能。
支持与特性:
MiniGUI:支持多种技术特性,包括在含有MMU的32位处理器架构上运行、支持低端和高端显示设备、具备副屏支持的功能以及为不同操作系统和环境设计的三种运行模式。
QT:是一个跨平台的C++框架,提供了丰富的组件和工具,支持多种平台和硬件,如Windows、Linux、macOS、Android和iOS等;支持多种编程语言,包括C++和Python等;提供了强大的事件处理机制、网络、数据库、多线程等高级功能
LVGL:是一个开源的嵌入式图形库,主要关注嵌入式系统和微控制器,提供丰富的基础功能。支持多种语言和开发环境,如C/C++、Python等。LVGL不包含窗口管理或者多线程支持,更适合单任务或简单的多任务环境。
应用领域:
MiniGUI:广泛应用于数控、工控、机顶盒到高端的PMP、手机、智能家居等领域。
QT:因其强大的功能和跨平台特性,常被用于各种需要高性能图形显示的应用,如工控、机顶盒、游戏终端等。
LVGL:由于其轻量级和高效的渲染性能,特别适合资源受限的嵌入式设备

嵌入式linux中TCP/IP、HTTPS、MQTT、485、232、SIP、CAN、USB、Ethernet等常见接口编程

在进行嵌入式Linux接口编程时,除了理解相关协议和API外,还需要注意资源限制、实时性要求、电源管理等问题。同时,对于特定硬件平台,可能还需要查阅相关的开发文档和示例代码。

TCP/IP:

在嵌入式Linux中,TCP/IP协议栈通常已经集成在内核中。编程时,可以使用套接字(Socket)API进行网络通信。例如,可以使用socket()、bind()、listen()、accept()等函数来实现服务器和客户端的通信。
可以使用BSD sockets API进行TCP/IP编程。
首先需要包含<sys/socket.h>,<netinet/in.h>等头文件。
使用socket(), bind(), listen(), accept(), send(), recv()等系统调用来创建、绑定、监听、接受连接以及发送和接收数据。
示例:使用socket API在C语言中创建一个TCP服务器或客户端

TCP服务器示例:
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 8080
#define MAX_CLIENTS 5

int main() {
    
    int server_socket, client_socket;
    struct sockaddr_in server_addr, client_addr;
    socklen_t addr_len = sizeof(struct sockaddr_in);
    char buffer[1024] = {
    0};

    // 创建socket
    if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
    
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    // 设置socket选项
    int opt = 1;
    if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
    
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }

    // 绑定socket到特定地址和端口
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
    
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // 开始监听连接请求
    if (listen(server_socket, MAX_CLIENTS) == -1) {
    
        perror("listen");
        exit(EXIT_FAILURE);
    }

    printf("Server is listening on port %d...\n", PORT);

    while (1) {
    
        // 接受客户端连接
        if ((client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &addr_len)) < 0) {
    
            perror("accept");
            continue;
        }

        printf("Connected with %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

        // 与客户端通信
        while (recv(client_socket, buffer, 1024, 0) > 0) {
    
            printf("Received: %s\n", buffer);
            send(client_socket, buffer, strlen(buffer), 0);
        }

        close(client_socket);
    }

    close(server_socket);

    return 0;
}
TCP客户端示例:
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SERVER_IP "127.0.0.1"
#define PORT 8080
#define MAX_BUFFER_SIZE 1024

int main() {
    
    int client_socket;
    struct sockaddr_in server_addr;

    // 创建socket
    if ((client_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    // 设置服务器地址和端口
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {
    
        perror("invalid address / address not supported");
        exit(EXIT_FAILURE);
    }

    // 连接到服务器
    if (connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
    
        perror("connect failed");
        exit(EXIT_FAILURE);
    }

    printf("Connected to %s:%d\n", SERVER_IP, PORT);

    char buffer[MAX_BUFFER_SIZE];

    // 发送和接收数据
    printf("Enter message to send: ");
    fgets(buffer, MAX_BUFFER_SIZE, stdin);
    send(client_socket, buffer, strlen(buffer), 0);

    if (recv(client_socket, buffer, MAX_BUFFER_SIZE, 0) < 0) {
    
        perror("recv");
    } else {
    
        printf("Received: %s\n", buffer);
    }

    close(client_socket);

    return 0;
}

HTTPS:

在嵌入式Linux中实现HTTPS通常需要依赖第三方库,如OpenSSL。
需要配置和编译OpenSSL库,并在应用程序中链接该库。
使用OpenSSL提供的API来处理SSL/TLS连接和加密通信。
示例:使用libcurl在C语言中发送HTTPS请求
首先初始化了libcurl全局环境,然后创建了一个curl会话。设置要请求的URL、SSL验证选项以及一个回调函数来处理接收到的数据。
调用curl_easy_perform()执行请求。如果请求成功,我们将接收到的数据打印出来。最后,清理了curl会话和libcurl全局环境。

#include <stdio.h>
#include <curl/curl.h>

// 这是libcurl的回调函数,用于处理接收的数据
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
    
    ((char*)userp)[size * nmemb] = '\0';
    return size * nmemb;
}

int main()
{
    
    CURL* curl;
    CURLcode res;
    char* url = "https://example.com";
    char response_buffer[10240]; // 创建一个缓冲区来存储响应数据

    // 初始化libcurl
    curl_global_init(CURL_GLOBAL_DEFAULT);

    // 创建curl会话
    curl = curl_easy_init();
    if(curl) {
    
        // 设置URL
        curl_easy_setopt(curl, CURLOPT_URL, url);

        // 要求libcurl验证服务器的SSL证书
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);

        // 设置回调函数来处理接收到的数据
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, response_buffer);

        // 执行请求
        res = curl_easy_perform(curl);

        // 检查是否有错误
        if(res != CURLE_OK)
            fprintf(stderr, "curl_easy_perform() failed: %s\n",
                    curl_easy_strerror(res));

        // 清理curl
        curl_easy_cleanup(curl);
    }

    // 清理libcurl
    curl_global_cleanup();

    // 打印接收到的响应数据
    printf("Response: %s\n", response_buffer);

    return 0;
}
MQTT:

对于MQTT编程,可以使用Paho MQTT库,它提供了C和Python等版本。
下载并编译Paho MQTT库,然后在应用程序中包含相应的头文件并链接库。
使用Paho MQTT库提供的API进行发布、订阅和处理MQTT消息。
示例:使用Paho MQTT库,连接到MQTT服务器并发送/接收消息
首先,安装Paho MQTT C库。你可以从GitHub上下载源代码并编译安装:https://github.com/eclipse/paho.mqtt.c
示例中,首先创建了一个MQTT客户端,并连接到本地主机上的MQTT服务器(端口1883)。然后,设置了消息到达的回调函数,并订阅了指定的主题。
接下来,发送一个消息到该主题,并等待消息发送完成。最后,断开与MQTT服务器的连接,并销毁MQTT客户端。
示例只是一个基本的用法,实际使用时可能需要根据具体需求进行更多的配置和错误处理。例如,可能需要处理网络中断、重连等问题。

#include <stdio.h>
#include <stdlib.h>
#include "MQTTClient.h"

#define ADDRESS     "tcp://localhost:1883"
#define CLIENTID    "ExampleClient"
#define TOPIC       "example/topic"
#define PAYLOAD     "Hello MQTT!"

void messageArrived(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
    
    printf("Message arrived on topic: %s\n", topicName);
    printf("Payload: %s\n", (char*)message->payload);

    MQTTClient_freeMessage(&message);
    MQTTClient_free(topicName);
}

int main(int argc, char* argv[])
{
    
    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;

    MQTTClient_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL);

    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;

    if (MQTTClient_connect(client, &conn_opts) != MQTTCLIENT_SUCCESS)
    {
    
        printf("Failed to connect to MQTT broker.\n");
        exit(EXIT_FAILURE);
    }

    printf("Connected to MQTT broker.\n");

    // 设置回调函数来处理接收到的消息
    MQTTClient_setMessageCallback(client, messageArrived, NULL);

    // 订阅主题
    if (MQTTClient_subscribe(client, TOPIC, 0) != MQTTCLIENT_SUCCESS)
    {
    
        printf("Failed to subscribe to topic.\n");
        exit(EXIT_FAILURE);
    }

    printf("Subscribed to topic '%s'.\n", TOPIC);

    // 发送消息
    pubmsg.payload = PAYLOAD;
    pubmsg.payloadlen = strlen(PAYLOAD);
    pubmsg.qos = 0;
    pubmsg.retained = 0;

    if (MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token) != MQTTCLIENT_SUCCESS)
    {
    
        printf("Failed to publish message.\n");
        exit(EXIT_FAILURE);
    }

    printf("Waiting for publication of message with token %d\n", token);

    // 等待消息发送完成
    MQTTClient_waitForCompletion(client, token, 10000L);

    printf("Message with token %d published\n", token);

    // 断开连接
    MQTTClient_disconnect(client, 10000);

    MQTTClient_destroy(&client);

    return EXIT_SUCCESS;
}
485 和 232:

在嵌入式Linux中,串行通信接口(如RS-485和RS-232)通常通过TTY设备文件进行访问。
包含<termios.h>头文件,并使用open(), tcgetattr(), cfsetispeed(), cfsetospeed(), tcsetattr(), write(), read()等函数来配置和操作串口。

SIP:

实现SIP协议在嵌入式Linux中可能需要使用开源的SIP库,如oSIP或PJSIP。
下载、编译并安装所需的SIP库,然后在应用程序中链接库并使用其提供的API进行会话控制和媒体传输。
OpenSIP是一个开源的SIP(Session Initiation Protocol)协议栈,它可以在C语言环境下使用,用于实现VoIP(Voice over Internet Protocol)通话和其他实时通信应用。在C语言中使用OpenSIP处理SIP事务和建立VoIP通话涉及以下步骤:
安装OpenSIP:
首先,你需要在你的系统上安装OpenSIP。这通常涉及到下载源代码,配置,编译和安装。
配置OpenSIP:
OpenSIP需要配置文件来定义如何处理SIP消息和通话。这些配置文件通常是XML格式。
初始化OpenSIP:
在你的C代码中,你需要调用OpenSIP的API来初始化协议栈,加载配置文件,并启动必要的模块。
创建SIP用户代理:
用户代理(User Agent)是SIP事务中的终端点,代表用户参与通话。你需要使用OpenSIP的API来创建和配置用户代理。
处理SIP事务:
SIP事务包括INVITE, ACK, BYE等消息。你的C代码需要使用OpenSIP的API来发送和接收这些消息,并处理相应的响应。
建立VoIP通话:
一旦SIP事务被正确处理,VoIP通话就可以建立。这通常涉及到媒体流(音频,视频等)的协商和交换。
管理通话:
你的代码还需要能够管理通话的生命周期,包括通话的保持、转移、挂断等操作。
媒体处理:
VoIP通话中的媒体流(如音频)需要通过OpenSIP或其他音频处理库来处理编码、解码和传输。
安全性:
你可能还需要实现加密和认证机制来保护通话内容不被窃听,并确保通话双方的身份。
错误处理和日志:
你的应用程序需要能够妥善处理错误情况,并记录必要的日志以供调试和监控。
示例:使用OpenSIP在C语言中处理SIP事务和建立VoIP通话
示例创建了一个新的SIP INVITE请求,并将其发送到OpenSIPS服务器。然后,它等待并处理来自服务器的响应。
请注意,这只是一个基本的示例,实际的实现将需要处理更多的情况,包括错误处理、认证、媒体协商、对话管理等。你还需要根据你的具体需求和网络环境调整OpenSIPS服务器的配置和脚本。

#include <stdio.h>
#include <opensips/smi.h>

int main() {
    
    struct sip_msg *msg;
    struct sip_dst *dst;

    // 初始化OpenSIPS客户端库
    smi_init();

    // 创建一个新的SIP消息
    msg = smi_msg_create();

    // 设置SIP方法为INVITE
    smi_set_method(msg, "INVITE");

    // 设置源和目标地址
    dst = smi_dst_create("[email protected]", "your_sip_proxy_address");
    smi_set_request_uri(msg, "sip:[email protected]");
    smi_set_dst(msg, dst);

    // 添加SIP头信息(例如From、To、Contact等)
    smi_header_add(msg, "From", "<sip:[email protected]>");
    smi_header_add(msg, "To", "<sip:[email protected]>");
    smi_header_add(msg, "Contact", "<sip:[email protected]>");

    // 发送SIP消息
    int ret = smi_send(msg);
    if (ret != SMI_OK) {
    
        printf("Error sending SIP message: %s\n", smi_strerror(ret));
        return -1;
    }

    // 等待回应
    while ((msg = smi_recv()) != NULL) {
    
        // 处理收到的SIP消息
        printf("Received SIP message: %s %s\n", smi_get_method(msg), smi_get_request_uri(msg));

        // 在这里处理特定的SIP响应(例如180 Ringing、200 OK等)

        // 释放收到的SIP消息
        smi_msg_destroy(msg);
    }

    // 清理OpenSIPS客户端库
    smi_shutdown();

    return 0;
}
CAN:

在嵌入式Linux中,可以使用SocketCAN框架进行CAN接口编程。
配置和启用内核的CAN模块,然后使用socket(), bind(), send(), recv()等函数进行CAN消息的发送和接收。
SocketCAN是Linux内核中的一个模块,它提供了通过网络套接字接口访问CAN(控制器局域网)总线的功能。以下是一个使用SocketCAN API在C语言中发送和接收CAN消息的简单示例。
示例首先创建了一个CAN套接字,并将其绑定到指定的CAN接口(在这个例子中是"can0")。然后,它进入一个无限循环,接收CAN消息并打印出来。为了简单起见,这个示例还回显接收到的消息。
注意,这个示例假设系统已经配置了CAN接口并且启用了SocketCAN模块。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/can.h>
#include <linux/can/raw.h>

#define CAN_INTERFACE "can0"

int main() {
    
    int s;
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_frame frame;

    // 创建CAN套接字
    if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
    
        perror("Failed to create socket");
        return -1;
    }

    // 获取CAN接口名称
    strcpy(ifr.ifr_name, CAN_INTERFACE);

    // 将接口设为UP状态
    if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
    
        perror("Failed to get interface flags");
        close(s);
        return -1;
    }

    ifr.ifr_flags |= IFF_UP;
    if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
    
        perror("Failed to set interface flags");
        close(s);
        return -1;
    }

    // 设置CAN地址结构
    addr.can_family = AF_CAN;
    strcpy(addr.can_ifname, CAN_INTERFACE);

    // 绑定到CAN接口
    if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
    
        perror("Failed to bind socket");
        close(s);
        return -1;
    }

    printf("Listening on %s...\n", CAN_INTERFACE);

    while (1) {
    
        // 接收CAN消息
        if (recv(s, &frame, sizeof(frame), 0) < 0) {
    
            perror("Failed to receive message");
            continue;
        }

        // 打印接收到的CAN ID和数据
        printf("Received: ID=%#x, DLC=%d, Data=", frame.can_id & CAN_EFF_MASK, frame.can_dlc);
        for (int i = 0; i < frame.can_dlc; i++) {
    
            printf("%02x ", frame.data[i]);
        }
        printf("\n");

        // 发送CAN消息(这里只是一个简单的回显示例)
        frame.can_id |= CAN_EFF_FLAG; // 设置扩展帧标志
        if (send(s, &frame, sizeof(frame), 0) < 0) {
    
            perror("Failed to send message");
            continue;
        }
    }

    close(s);

    return 0;
}
USB:

嵌入式Linux中的USB编程可以使用LibUSB库或者直接操作USB设备文件。
如果使用LibUSB,需要下载、编译并安装库,然后在应用程序中链接库并使用其提供的API进行USB设备的枚举、配置和数据传输。

Ethernet:

以太网接口在嵌入式Linux中通常也是通过sockets API进行编程。
使用与TCP/IP编程类似的步骤,但需要注意的是,以太网接口可能还需要进行物理层和数据链路层的配置,如MAC地址设置、ARP处理等。

UVC和V4L2

UVC(USB Video Class)和V4L2(Video for Linux Two)是两个在视频采集和处理中常用的接口和技术。
UVC:
UVC是一种USB设备类规范,定义了如何通过USB接口传输视频数据。它是硬件级别的标准,旨在使USB摄像头和其他视频设备能够在各种操作系统上无需额外驱动就能工作。
UVC规范涵盖了设备的描述、控制和数据传输等方面,使得操作系统能够识别并使用符合UVC标准的设备。
UVC设备通常会将视频流编码为一种标准格式(如MJPEG或YUV)并通过USB接口发送。
V4L2:
V4L2是Linux内核中的一个视频捕获接口,它提供了一套API供应用程序访问视频捕获设备,包括摄像头、电视卡等。
V4L2不仅支持USB摄像头,还支持其他类型的视频输入设备,如PCI/PCIe接口的摄像头、电视调谐器等。
V4L2提供了对视频设备的各种控制功能,包括帧率调整、亮度对比度控制、缩放等,并且可以处理多种视频格式,包括RGB、YUV、MJPEG等。
应用程序可以通过V4L2 API打开设备、设置参数、读取视频帧等。
相同点:
都用于处理视频数据,尤其是从摄像头等设备捕获的视频数据。
不同点:
UVC是硬件级别的规范,主要关注USB视频设备的数据传输和设备描述,而V4L2是软件级别的接口,主要关注如何在Linux系统中访问和控制视频设备。
UVC是跨平台的标准,理论上可以在任何支持USB的系统上工作,而V4L2是Linux特有的接口。
虽然UVC设备通常可以直接在支持该规范的系统上工作,但为了充分利用设备的功能和优化性能,通常还需要配合操作系统的驱动程序,如Linux的V4L2驱动。
在实际应用中,UVC和V4L2通常是结合使用的:一个符合UVC标准的USB摄像头可以被Linux的V4L2驱动程序识别和管理,应用程序则通过V4L2 API来访问和控制这个摄像头。

RTSP、RTMP和WEBRTC的异同

RTSP、RTMP和WebRTC都是用于实时音视频流传输的协议,但它们在设计目标、应用场景和技术实现上有所不同:

RTSP (Real-Time Streaming Protocol):
RTSP是一种应用层协议,主要用于控制音频和视频数据的实时流传输。
它并不直接传输媒体数据,而是定义了如何初始化、控制(播放、暂停、停止等)和终止流媒体会话。
RTSP通常与RTP(Real-time Transport Protocol)和RTCP(Real-time Transport Control Protocol)配合使用,RTP负责实际的媒体数据传输,而RTCP负责服务质量监控和反馈。
RTMP (Real-Time Messaging Protocol):

RTMP最初由Adobe公司开发,主要用于将音视频数据从发布者传输到服务器,然后分发给多个订阅者。
它既可以用于直播,也可以用于点播,并且支持低延迟的实时通信。
RTMP可以直接传输音频、视频和数据,无需额外的控制协议。
WebRTC (Web Real-Time Communication):

WebRTC是一个开源项目,旨在实现在网页浏览器和其他现代平台上进行实时音视频通信而无需插件。
它包括一套API和相关的协议栈,如SDP(Session Description Protocol)用于会话描述和协商,ICE(Interactive Connectivity Establishment)用于网络穿透和NAT traversal,以及DTLS(Datagram Transport Layer Security)和SRTP(Secure Real-time Transport Protocol)用于安全的数据传输。
WebRTC主要应用于点对点或多方的实时通信,如视频会议、在线聊天等。
异同点:
目标和用途:
RTSP主要用于流媒体服务的控制,不直接传输媒体数据;RTMP主要用于实时音视频流的传输,特别适合直播场景;而WebRTC则专注于提供点对点或多方的实时通信能力,适用于各种互动应用。
技术实现:
RTSP基于文本协议,需要与其他协议(如RTP/RTCP)配合使用;RTMP是一种二进制协议,包含了流控制和数据传输的功能;WebRTC包含了一系列协议和API,能够处理复杂的网络环境和安全问题。
应用场景:
RTSP主要用于点对点或多播的流媒体传输,常用于实时视频监控、视频会议等场景。常见于传统的流媒体服务和设备中。
RTMP最初设计用于向Adobe Flash播放器传输音频、视频和其他数据,但随着Flash播放器的淘汰,其应用场景受到一定限制。目前,RTMP主要用作直播源推流、推流到直播CDN等场景。RTMP广泛应用于直播平台和一些专有的视频播放器;
WEBRTC主要应用于浏览器端的实时音视频通信,如视频会议、在线教育、互动直播等场景。WebRTC则主要用于网页和移动应用中的实时通信和协作工具。
传输方式:
RTSP(Real-Time Streaming Protocol)和RTMP(Real-Time Messaging Protocol)都是基于TCP的流媒体传输协议,而WEBRTC(Web Real-Time Communication)则主要基于UDP协议,可以实现更低延迟的音视频传输。
延时:
RTSP和RTMP的延时相对较低,一般在1-3秒左右。而WEBRTC的延时则更低,一般在200ms-500ms之间。
数据分段:
RTSP和RTMP都是采用数据分段的方式传输音视频数据,有利于实现实时传输和快速恢复。而WEBRTC则采用连续流的方式传输数据。
总的来说,RTSP、RTMP和WebRTC各有其特点和适用范围,选择哪种协议取决于具体的应用需求、网络环境和兼容性考虑。

逆向工程师

AI 工程师

AI平台的开发流程

需求分析与规划:
确定AI平台的目标和愿景,理解业务需求和用户期望。
分析市场趋势和技术现状,评估竞争环境和潜在机会。
系统架构设计:
设计AI平台的整体架构,包括数据处理、模型训练、推理服务、API接口、安全性和可扩展性等方面。
确定技术栈和工具链,选择合适的编程语言、框架和库。
数据管理与预处理:
设计和实现数据收集、清洗、标注和存储的解决方案。
实施数据隐私和安全策略,确保合规性。
模型开发与训练:
根据业务需求选择或开发AI模型,如深度学习、机器学习或规则引擎等。
划分数据集用于训练、验证和测试模型。
调整模型参数和优化算法以提高性能和准确性。
模型部署与集成:
将训练好的模型封装成服务,以便于在平台上进行调用和集成。
实现模型版本控制和灰度发布机制,支持模型的更新和回滚。
API和服务开发:
设计和实现API接口,允许外部应用和服务访问AI平台的功能。
开发客户端SDK和示例代码,简化开发者集成过程。
平台界面与用户体验:
设计和开发用户界面,提供直观的操作和监控功能。
关注用户体验设计,确保平台易用性和可访问性。
安全与权限管理:
实施身份验证和授权机制,确保平台资源的安全访问。
评估和缓解潜在的安全风险,如数据泄露、恶意攻击等。
测试与质量保证:
进行单元测试、集成测试和系统测试,确保平台各个组件的功能正确性和稳定性。
实施性能测试和压力测试,优化平台的响应时间和资源利用率。
运维与监控:
设计和实施监控和告警系统,实时跟踪平台的运行状态和性能指标。
制定运维策略和故障恢复计划,确保平台的高可用性和可靠性。
文档与培训:
编写详细的开发文档、用户手册和API参考指南。
提供培训和支持,帮助用户和开发者快速上手和使用AI平台。
持续迭代与优化:
收集用户反馈和使用数据,持续改进和优化平台功能。
跟踪最新的AI技术和行业动态,进行必要的升级和创新。
以上是一个大致的AI平台开发流程,具体的步骤可能会根据项目规模、团队结构和业务需求进行调整。在整个过程中,跨部门协作、敏捷开发方法和DevOps实践都是非常重要的。

大数据工程师

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yangzex/article/details/135098990

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法