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

【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分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版