你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【STM32F769I-DISC1】对接deepseek本地模型

[复制链接]
lugl 发布时间:2025-2-27 16:19

【前言】

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

【程序框架】

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

image.png

下面就如何实现分享如下:

一、使用ollama部署本地deepseek大模型。

如何部署deepseek大模型,网上教程非常之名,本篇不做过多分享,我在部置好后,打开本地浏览器,部署成功,如下图:

image.png

二、部署转发服务器。

由于直接跟deepseek模型对象,返回的数据包含如下信息:

image.png

而我们需要的信息,每一行数据只占用了一小部分,所以我在本地搭建了转发服务器,将服务直接转发到8000端口,其代python代码如下:

import http.server
import socketserver
import http.client
import json
import re

OLLAMA_HOST = 'localhost'
OLLAMA_PORT = 11434
MODEL_NAME = "deepseek-r1:1.5b"

class OllamaProxyHandler(http.server.SimpleHTTPRequestHandler):
    def do_POST(self):
        content_length = int(self.headers['Content-Length'])
        post_data = self.rfile.read(content_length)
        try:
            request_json = json.loads(post_data)
            request_json["model"] = MODEL_NAME
            modified_post_data = json.dumps(request_json).encode('utf-8')

            # 只保留必要的请求头
            necessary_headers = {
                'Content-Type': 'application/json',
                'Content-Length': str(len(modified_post_data))
            }

            conn = http.client.HTTPConnection(OLLAMA_HOST, OLLAMA_PORT)
            print(f"Forwarding request: {self.path}, {necessary_headers}, {modified_post_data}")
            conn.request('POST', self.path, modified_post_data, necessary_headers)
            response = conn.getresponse()

            # 打印响应状态码和头信息
            print(f"Response status: {response.status}")
            print(f"Response headers: {response.getheaders()}")

            self.send_response(response.status)
            # 去掉分块传输编码头
            headers_to_send = [
                (header, value) for header, value in response.getheaders()
                if header.lower() != 'transfer-encoding'
            ]
            for header, value in headers_to_send:
                self.send_header(header, value)

            # 收集所有 response 字段的内容
            response_content = ""
            while True:
                line = response.readline()
                if not line:
                    break
                try:
                    json_obj = json.loads(line)
                    print(f"Parsed JSON: {json_obj}")
                    # 去除 <think> 和 </think> 标签
                    response_text = json_obj.get('response', "")
                    response_text = re.sub(r'<think>|</think>', '', response_text)
                    # 将 \n 或 \n\n 替换为 \r\n
                    response_text = re.sub(r'\n+', '\r\n', response_text)
                    response_content += response_text
                except json.JSONDecodeError:
                    print(f"Invalid JSON line: {line.decode('utf-8').strip()}")
                    continue  # 忽略非 JSON 行,继续处理后续数据

            # 计算内容长度
            response_content_bytes = response_content.encode('utf-8')
            content_length = len(response_content_bytes)
            self.send_header('Content-Length', str(content_length))
            self.end_headers()

            # 一次性发送 response 内容给客户端
            self.wfile.write(response_content_bytes)

            conn.close()
        except json.JSONDecodeError:
            self.send_error(400, "Bad Request: Invalid JSON data")
        except Exception as e:
            self.send_error(500, f"Internal Server Error: {str(e)}")

PORT = 8000
with socketserver.TCPServer(("", PORT), OllamaProxyHandler) as httpd:
    print(f"Serving at port {PORT}")
    httpd.serve_forever()
收藏 评论3 发布时间:2025-2-27 16:19

举报

3个回答
lugl 回答时间:2025-2-27 16:24:51

【本地代码实现】

在本地中,我们向shell添加一个http的指令,本地提取指令后,转发给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);
}

然后向命令列表添加指令:

image.png

这样我就可以使用http cmd向http_post发送数据了。

lugl 回答时间:2025-2-27 16:30:03

【http_post】代码实现

1、首先通过shell接收到用户的命令,通过sprintf来组装到发送data中:

 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);

发送到服务器后,注册一个void handle_response(struct netconn *conn)进程进行数据接收。

其完整码源码如下:

app_http.zip

c1.png
image.png
image.png
image.png
image.png
lugl 回答时间:2025-2-27 16:34:09

【实现效果】

源码编译好后下载到开发,进行shell命令,输入http hello返回数据如下:

image.png

输入中文:

image.png

输入1000*100等于多少:

image.png

【总结】

STM32F769通过lwip成功的实现与AI大模型的无缝结合。

下一期,我将驱动LCD屏,将对话结果,展示在屏上。

所属标签

ST中文论坛活动

即日起开启活动话题入口,之后的活动统一都放在此处,欢迎大家的加入!


最新内容

相似分享

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版