这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » 【STM32F769】AI之与本地deepseek对接

共3条 1/1 1 跳转至

【STM32F769】AI之与本地deepseek对接

工程师
2025-02-27 16:09:28   被打赏 50 分(兑奖)     打赏

【前言】

目前在拥抱AI的时代,我们的单片机如何也能跟上时代的潮流,体验如何与国产大模型deepseek交谈。笔者就此做了探讨,成功的在STM32F769上对接了deepseek本地大模型,实现了无缝交流。

【程序框架】

根据本地大模型的搭建,以及STM32F769的编程,我设计了程序框图如下:

image.png

1、STM32F769中使用nr_micro_sheel用来与用户做交流,用户可以使用指令来输入想要与模型交流的指令,同时也通过shell来展示,模型反馈的结果。

2、shell获取用户交互指令后,由lwip来实现对数据的转发,把指令通过http_post到转发服务端,同时接收反馈的数据,反馈给shell用于展示。

3、在服务端,使用转发服务,转发用户的指令,重新包装好请示指领,同时在获取用户指令后,把用户关心的数据过滤出来,转发给STM32F769

【程序实现】

1、 添加shell指令,在shell_uart.c中,编写用于shell用户输入的数据,使用 const char *data = &(argv[argv[1]]);

来提取用户指令。然后通过send_http_post_request(data);来把指令通过http post到服务器。

void shll_http_send_response(char argc, char *argv)
{
    if (argc < 2)
    {
        shell_printf("Usage: http <data>\r\n");
        return;
    }
 
    const char *data = &(argv[argv[1]]);
    if (data == NULL || strlen(data) == 0)
    {
        shell_printf("Error: Data is empty or NULL\n");
        return;
    }
 
 
    send_http_post_request(data);
}

2、在指令集中添加代码:

image.png

3、

编写app_http.c/h

在程中,首先创建一个tcp连接,使用netconn_connect(conn, &server_ip, SERVER_PORT);连接到转发服务器。使用char post_data[256]; 

            snprintf(post_data, sizeof(post_data), "{\"prompt\":\"%s\"}", data);

来拼接请求的数据。再通过组装post字符串,通过netconn_write(conn, request, strlen(request), NETCONN_COPY);

把请求数据发送给服务端

最后增加返回处理,其完成代码如下:

#include "lwip/api.h"
#include "lwip/err.h"
#include "lwip/sys.h" // 添加此头文件以使用 sys_timeout
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include "shell_uart.h"

#define SERVER_IP "192.168.3.231" // 替换为实际服务器IP
#define SERVER_PORT 8000
#define POST_URL "/api/generate" // 替换为实际的POST URL
int count = 0;

// 处理接收到的数据
void handle_response(struct netconn *conn)
{
    struct netbuf *inbuf;
    err_t err;
    char *buf;
    u16_t buflen;
    int is_chunked = false;
    char *chunk_size_str;
    int chunk_size = 0;

    // 读取响应头
    while (true)
    {
        err = netconn_recv(conn, &inbuf);
        if (err != ERR_OK)
        {
            shell_printf("Failed to receive response header, error: %d\n", err);
            return;
        }
        netbuf_data(inbuf, (void **)&buf, &buflen);

        // 检查是否为分块传输编码
        if (strstr(buf, "Transfer-Encoding: chunked") != NULL)
        {
            is_chunked = true;
        }

        // 打印响应头
        for (int i = 0; i < buflen; i++)
        {
            // shell_printf("%c", buf[i]);
            if (buf[i] == '\n')
            {
                break;
            }
        }

        netbuf_delete(inbuf);

        // 如果遇到空行,表示响应头结束
        if (buflen > 0 && buf[buflen - 2] == '\r' && buf[buflen - 1] == '\n')
        {
            break;
        }
    }

    // 处理分块传输编码的响应体
    if (is_chunked)
    {
        while (true)
        {
            count++;
            // 读取块大小
            err = netconn_recv(conn, &inbuf);
            if (err != ERR_OK)
            {
                // 修改为继续接收数据直到连接关闭
                if (err == ERR_CLSD)
                {
                    shell_printf("Connection closed by server\n");
                    break; // 连接关闭,结束接收
                }
                else
                {
                    shell_printf("Failed to receive response body, error: %d\n", err);
                    return;
                }
            }
            netbuf_data(inbuf, (void **)&buf, &buflen);

            // 解析块大小
            chunk_size_str = strtok(buf, "\r\n");
            if (chunk_size_str == NULL)
            {
                shell_printf("Invalid chunk size format\n");
                netbuf_delete(inbuf);
                return;
            }
            chunk_size = (int)strtol(chunk_size_str, NULL, 16);
            netbuf_delete(inbuf);

            // 如果块大小为0,表示所有块已接收完毕
            if (chunk_size == 0)
            {
                shell_printf("All chunks received\n");
                break;
            }

            // 读取并打印块数据
            char *chunk_buf = (char *)malloc(chunk_size + 1);
            if (chunk_buf == NULL)
            {
                shell_printf("Memory allocation failed\n");
                return;
            }
            err = netconn_recv(conn, &inbuf);
            if (err != ERR_OK)
            {
                shell_printf("Failed to receive chunk data, error: %d\n", err);
                free(chunk_buf);
                return;
            }
            netbuf_data(inbuf, (void **)&buf, &buflen);
            memcpy(chunk_buf, buf, chunk_size);
            chunk_buf[chunk_size] = '\0';

            shell_printf("%s\r\n", chunk_buf);
            free(chunk_buf);
            netbuf_delete(inbuf);

            // 跳过块末尾的CRLF
            err = netconn_recv(conn, &inbuf);
            if (err != ERR_OK)
            {
                shell_printf("Failed to receive CRLF after chunk, error: %d\n", err);
                return;
            }
            netbuf_delete(inbuf);
        }
    }
    else
    {
        // 处理非分块传输编码的响应体
        char *response_buf = NULL;
        size_t response_size = 0;
        size_t total_received = 0;
        while (true)
        {
            err = netconn_recv(conn, &inbuf);
            if (err != ERR_OK)
            {
                // 修改为继续接收数据直到连接关闭
                if (err == ERR_CLSD)
                {
                    break; // 连接关闭,结束接收
                }
                else
                {
                    shell_printf("Failed to receive response body, error: %d\n", err);
                    free(response_buf);
                    return;
                }
            }
            netbuf_data(inbuf, (void **)&buf, &buflen);

            // 调整缓冲区大小
            char *new_buf = (char *)realloc(response_buf, total_received + buflen + 1);
            if (new_buf == NULL)
            {
                shell_printf("Memory allocation failed\n");
                free(response_buf);
                return;
            }
            response_buf = new_buf;

            memcpy(response_buf + total_received, buf, buflen);
            total_received += buflen;
            response_buf[total_received] = '\0';

            netbuf_delete(inbuf);
        }
        shell_printf("%s\r\n", response_buf);
        free(response_buf);
    }

    // 关闭连接
    if (conn != NULL)
    {
       // shell_printf("Closing connection\n");
      //  netconn_close(conn);
      //  shell_printf("Deleting connection\n");
      //  netconn_delete(conn);
    }
}

// 发送HTTP POST请求
void send_http_post_request(const char *data)
{
    struct netconn *conn;
    err_t err;
    ip_addr_t server_ip;

    // 将字符串IP地址转换为ip_addr_t类型
    IP4_ADDR(&server_ip, 192, 168, 3, 231); // 替换为实际服务器IP

    // 创建一个TCP连接
    conn = netconn_new(NETCONN_TCP);
    if (conn != NULL)
    {
        // 连接到服务器
        err = netconn_connect(conn, &server_ip, SERVER_PORT);
        if (err == ERR_OK)
        {
            // 构建HTTP POST请求
            char request[4096];

            char post_data[256]; // 假设data不会超过256个字符
            snprintf(post_data, sizeof(post_data), "{\"prompt\":\"%s\"}", data);
            size_t data_length = strlen(post_data);

            snprintf(request, sizeof(request),
                     "POST %s HTTP/1.1\r\n"
                     "Host: %s:%d\r\n"
                     "Content-Type: application/json\r\n"
                     "Content-Length: %d\r\n"
                     "\r\n"
                     "%s",
                     POST_URL, SERVER_IP, SERVER_PORT, data_length, post_data);

            // 打印构建的 HTTP 请求以进行调试
            shell_printf("HTTP Request:\n%s\n", request);

            // 发送HTTP POST请求
            err = netconn_write(conn, request, strlen(request), NETCONN_COPY);
            if (err == ERR_OK)
            {
                // 处理响应
                handle_response(conn);
            }
            else
            {
                shell_printf("Failed to send HTTP POST request\n");
            }
        }
        else
        {
            shell_printf("Failed to connect to server\n");
        }

        // 关闭连接
        if (conn != NULL)
        {
          //  shell_printf("Closing connection\n");
            netconn_close(conn);
          //  shell_printf("Deleting connection\n");
            netconn_delete(conn);
        }
    }
    else
    {
        shell_printf("Failed to create TCP connection\n");
    }
}


最后通过shell把数据打印到串口终端之上,完成一次交互。

【实现效果】

image.pngimage.png由于本地模型是最小的,只能玩玩。如果需要更加准确的,还是得用api做在线的提问。




关键词: STM32F769     deepseek    

工程师
2025-02-27 16:45:55     打赏
2楼

学习拥抱AI


院士
2025-02-27 17:14:24     打赏
3楼

楼主的学习与行动力真快,点赞并谢谢分享,学习了。


共3条 1/1 1 跳转至

回复

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