[micropython k210] 基于 Socket 实现 MicroPython 的 HTTP 上传文件(multipart/form-data)

这篇具有很好参考价值的文章主要介绍了[micropython k210] 基于 Socket 实现 MicroPython 的 HTTP 上传文件(multipart/form-data)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

起因

下述内容需要具备 HTTP 的基础知识,如果不知道的可以过一遍 HTTP 协议详解

继上次在 K210 实现 HTTP Download 文件(https 也支持辣),现在就来说说直接基于 socket 的上传文件实现吧。

[micropython k210] 基于 Socket 实现 MicroPython 的 HTTP 上传文件(multipart/form-data)

首先准备一个 Server 文件服务器的 CPython 代码,这个是再简单不过了。

# coding=utf-8
from http.server import BaseHTTPRequestHandler
import cgi
import time

class PostHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        print(self.headers)
        form = cgi.FieldStorage(
            fp=self.rfile,
            headers=self.headers,
            environ={'REQUEST_METHOD': 'POST',
                     'CONTENT_TYPE': self.headers['Content-Type'],
                     }
        )
        self.send_response(200)
        self.end_headers()
        self.wfile.write(('Client: %s\n' % str(self.client_address)).encode())
        self.wfile.write(('User-agent: %s\n' %
                          str(self.headers['user-agent'])).encode())
        self.wfile.write(('Path: %s\n' % self.path).encode())
        self.wfile.write(b'Form data:\n')
        print(form.keys())
        for field in form.keys():
            field_item = form[field]
            filename = field_item.filename
            filevalue = field_item.value
            filesize = len(filevalue)  # 文件大小(字节)
            print(filename, filesize)
            with open('%s-' % time.time() + filename, 'wb') as f:
                f.write(filevalue)
        return

def StartServer():
    from http.server import HTTPServer
    sever = HTTPServer(("0.0.0.0", 8080), PostHandler)
    sever.serve_forever()

if __name__ == '__main__':
    StartServer()

可以看到实现处理了一个 post 的请求,然后对这个请求依次解析出来,通常情况下找找代码调用一下就成功了,比如下面的这份调用 request 的代码:

#coding=utf-8
import requests
url = "http://127.0.0.1:8080"
path = "./hfshttp.zip"
print(path)
files = {'file': open(path, 'rb')}
r = requests.post(url, files=files)
print (r.url)
print (r.text)

'''
Host: 127.0.0.1:8080
Connection: keep-alive
Accept: */*
User-Agent: python-requests/2.22.0
Accept-Encoding: gzip, deflate
Content-Length: 859783
Content-Type: multipart/form-data; boundary=c42a6d00053f74d5edd8c8b00a8318ef
['file']
hfshttp.zip 859636
'''

上传文件是不是很简单就实现了,这也是随处可见的代码,但这代码背后其实做了很多工作,你是无法直接在 MicroPython 上运行的。

没有 requests 怎么办?

不仅没有 requests 也没有 urllib ,那这可怎么办呢?

留给我们的只有如下模块。

from struct import pack
import socket
import uio as io

这个问题很早就年就有人在讨论了,但随着 CPython 的逐渐流行和成熟,基本上回复的都是赶紧换 requests ,也许谁也没想到后来还有个 MicroPython 吧,那没库了我们就不行了吗?

在这之前的文章,我已经在 K210 的 MicroPython 上基于 www.hc2.fr 的 MicroWebCli 实现了 HTTP 的基本操作,也就是我们常见的 GET 、POST 基础功能已经具备了,也就是如何包装一个上传数据包的问题。

如何选择上传文件协议呢?我们有两种请求的规范,前者是 PUT ,后者是 POST。

在 HTTP 1.1 之上有一个 WebDAV 协议,它和 FTP 一样,都拓展出了 PUT 、GET 、 Delete 等文件操作,与 FTP 的区别在于端口不同,因为一个是 80 另一个是 21,接着 WebDAV 是网络访问和文件请求并存的一种拓展协议,也就是说,最简单的文件上传协议就是实现一个 PUT,要求服务器开发 WebDAV 的实现就可以了,当然,本文的重点不在讲如何实现文件服务器的功能,只是提及一下 PUT 指令的内容,以此切入 POST 上传文件的基础知识铺垫。

那么 PUT 实现都有哪些内容呢?只需要客户端 socket 组装下述内容发送给开放了 HTTP1.1 PUT 指令的服务器就可以被接收处理了,以往经常有人会拿这个命令来试探服务器有没有漏洞 hah 所以现在的人都关了这个功能,或者上验证了。

PUT /bacon.htm HTTP/1.1
Content-Length: 31
Accept: */*
Accept-Language: en-US
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)
Host: 218.94.36.38:9010
Content-Length: text/plain

a piece of data

可以看出 PUT 的规范很简洁,下面提供一个 Python 的 Code 帮助你体会体会?

# File: httplib-example-2.py

import httplib

USER_AGENT = "httplib-example-2.py"

def post(host, path, data, type=None):

    http = httplib.HTTP(host)

    # write header
    http.putrequest("PUT", path)
    http.putheader("User-Agent", USER_AGENT)
    http.putheader("Host", host)
    if type:
        http.putheader("Content-Type", type)
    http.putheader("Content-Length", str(len(data)))
    http.endheaders()

    # write body
    http.send(data)

    # get response
    errcode, errmsg, headers = http.getreply()

    if errcode != 200:
        raise Error(errcode, errmsg, headers)

    file = http.getfile()
    return file.read()

if __name__ == "__main__":

    post("www.spam.egg", "/bacon.htm", "a piece of data", "text/plain")

实际上不难理解,想知道更多可以自行了解,可以看看这个 HTTP 协议入门 ,深入一点就要看标准协议辣,例如 Hypertext Transfer Protocol -- HTTP/1.1,示例 Code 来源在这里 The httplib module。

注意,为什么我要提及 PUT 实际上是有原因的,早期它的实现很简单,作为历史的车轮自然会依次淘汰掉不可靠的一些指令,尤其是复杂的指令要求服务器有对应的实现,后来 PUT 也因为服务器漏洞问题而被大部分场合禁用了,所以实现它已经没有什么意义了,那么接下来该怎么办呢?

这时候我们就要引出第二个上传协议 multipart/form-data 了。

multipart/form-data 是什么?如何实现?

multipart/form-data最初由 《RFC 1867: Form-based File Upload in HTML》文档定义。

Since file-upload is a feature that will benefit many applications, this proposes an
extension to HTML to allow information providers to express file upload requests uniformly, and a MIME compatible representation for file upload responses.

文档简介中说明文件上传作为一种常见的需求,在目前(1995年)的html中的form表单格式中还不支持,因此发明了一种兼容此需求的mime type。

The encoding type application/x-www-form-urlencoded is inefficient for sending large quantities of binary data or text containing non-ASCII characters. Thus, a new media type,multipart/form-data, is proposed as a way of efficiently sending the
values associated with a filled-out form from client to server.

文档中也写了为什么要新增一个类型,而不使用旧有的application/x-www-form-urlencoded:因为此类型不适合用于传输大型二进制数据或者包含非ASCII字符的数据。平常我们使用这个类型都是把表单数据使用url编码后传送给后端,二进制文件当然没办法一起编码进去了。所以multipart/form-data就诞生了,专门用于有效的传输文件。

现在我们假设服务器是一个黑盒子,符合上传文件的实现规范,而我们要做的就是在 MicroPython 上实现 HTTP 文件的上传文件,而且不是 PUT 指令,而是通过 POST 的实现,这里我们就要来实现它了。

首先我们在本文的最初就已经通过 requests 进行了一个上传文件的实践,接下来留给我们的挑战就是如何实现它,先看这篇文章 the-unfortunately-long-story-dealing-with ,可以不用细看,只是因为它提供了一份封装数据包的逻辑实现,我们只需要通过它就可以实现基础逻辑。

根据下图的逻辑,第一个是确定 HTTP 的 header 属于 POST 操作,同时提供了 Content-type 和 Content-length 文件信息。

import mimetools
import mimetypes
import io
import http
import json


form = MultiPartForm()
form.add_field("form_field", "my awesome data")

# Add a fake file
form.add_file(key, os.path.basename(filepath),
	fileHandle=codecs.open("/path/to/my/file.zip", "rb"))

# Build the request
url = "http://www.example.com/endpoint"
schema, netloc, url, params, query, fragments = urlparse.urlparse(url)

try:
	form_buffer =  form.get_binary().getvalue()
	http = httplib.HTTPConnection(netloc)
	http.connect()
	http.putrequest("POST", url)
	http.putheader('Content-type',form.get_content_type())
	http.putheader('Content-length', str(len(form_buffer)))
	http.endheaders()
	http.send(form_buffer)
except socket.error, e:
	raise SystemExit(1)

r = http.getresponse()
if r.status == 200:
	return json.loads(r.read())
else:
	print('Upload failed (%s): %s' % (r.status, r.reason))

于是这个请求的第一阶段就结束了,此时发起的 HTTP 数据如下:

  • CPython 的 requsets
POST / HTTP/1.0
User-Agent: python-requests/2.22.0
Accept-Encoding: gzip, deflate
Connection: keep-alive
Accept: */*
Content-Length: 859783
Content-Type: multipart/form-data; boundary=c76ef433019742a27e38c33455206c52
  • MicroPython 的 MicroWebCli
POST / HTTP/1.0
Content-Type: multipart/form-data; boundary=1590160638.3663664
Host: 127.0.0.1
User-Agent: MicroWebCli by JC`zic
Content-Length: 859808

这都是封装数据的过程,它只是发起了第一次的请求,接下来还有 form-data 的数据交互的请求,它格式满足如下,但这要如何理解呢?实际上在第一次的请求中就已经告知本次的 form-data 内容和长度了,所以在第一次的请求结束后,就需要继续发送这个 form-data 数据。

POST http://127.0.0.1:8080/
--1590161992.5543046
Content-Disposition: file; name="./"; filename="hfshttp.zip"
Content-Type: application/octet-stream

上述的 form-data 可以这样理解,其中 --1590161992.5543046 是第一次请求中提出的分隔符boundary,这个分隔符可不能在后续的文件中出现,因为是用来分批文件上传处理的,, form-data 标准格式是 name 和 value ,你也在这里面可以设计很多个协议内容,其他的就需要你看看服务器这一端的解析和实现了,例如下面这个是服务器的解析实现。

 def do_POST(self):
        form = cgi.FieldStorage(
            fp=self.rfile,
            headers=self.headers,
            environ={'REQUEST_METHOD': 'POST',
                     'CONTENT_TYPE': self.headers['Content-Type'],
                     }
        )
        self.send_response(200)
        self.end_headers()
        self.wfile.write(('Client: %s\n' % str(self.client_address)).encode())
        self.wfile.write(('User-agent: %s\n' %
                          str(self.headers['user-agent'])).encode())
        self.wfile.write(('Path: %s\n' % self.path).encode())
        self.wfile.write(b'Form data:\n')
        for field in form.keys():
            field_item = form[field]
            filename = field_item.filename
            filevalue = field_item.value
            filesize = len(filevalue)  # 文件大小(字节)
            print(filename, filesize)
            with open('%s-' % time.time() + filename, 'wb') as f:
                f.write(filevalue)
        return

当接收到路由(url)的 / 请求,先给个 200 应答表示接收到头了,再来解析 form-data 中的数据,其中 field 和 form[field].filename 对应的就是上述 Content-Disposition: file; name="./"; filename="hfshttp.zip" 中的 name 和 filename ,这样你就知道如何理解文件是如何被提取和下载的了。

[micropython k210] 基于 Socket 实现 MicroPython 的 HTTP 上传文件(multipart/form-data)

所以 form-data 的格式是这样的,参见 rfc1867 的 6. Examples 。

Content-type: multipart/form-data, boundary=AaB03x

--AaB03x
content-disposition: form-data; name="field1"

Joe Blow
--AaB03x
content-disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain

... contents of file1.txt ...
--AaB03x--

实际上 field_item.value 对应的内容它就在 ... contents of file1.txt ... 省略的位置里填充的,所以你可以理解 form-data 实际上就是一个被封装的多次 POST 请求,最后客户端传输完成后,会等待服务器的一个请求,告知传输完成,而这个应答也是对应 Server 的操作逻辑的。

http://127.0.0.1:8080/
Client: ('127.0.0.1', 64589)
User-agent: python-requests/2.22.0
Path: /
Form data:

实际上就是上述 server 代码写的应答,现在你应该知道要如何发送一个 multipart/form-data 的请求了吧,这实际上是和语言无关的,只需要用你手头的 socket 包装一下就可以实现。

做一些拓展内容

而关于这个 form-data 的封装过程,有很多优化要点和注意点。

先说 HTTP 数据包中的 boundary 和 mimetype 都是可以任意定义的,前者只要不和传输的内容有重合就行,所以我设置为 unix 时间字符串了,后者统一定义为 application/octet-stream 就可以了,因为这里不需要特别指定文件的解析逻辑操作,但如果你是写服务器的操作的话,则需要注意这些定义的区别。

class MultiPartForm(object):
	"""Accumulate the data to be used when posting a form."""

	def __init__(self):
		self.form_fields = []
		self.files = []
		self.boundary = mimetools.choose_boundary()
		return

	def get_content_type(self):
		return 'multipart/form-data; boundary=%s' % self.boundary

	def add_field(self, name, value):
		"""Add a simple field to the form data."""
		self.form_fields.append((name, value))
		return

	def add_file(self, fieldname, filename, fileHandle, mimetype=None):
		"""Add a file to be uploaded."""
		body = fileHandle.read()
		if mimetype is None:
			mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
		self.files.append((fieldname, filename, mimetype, body))
		return

	def get_binary(self):
		"""Return a binary buffer containing the form data, including attached files."""
		part_boundary = '--' + self.boundary

		binary = io.BytesIO()
		needsCLRF = False
		# Add the form fields
		for name, value in self.form_fields:
			if needsCLRF:
				binary.write('\r\n')
			needsCLRF = True

			block = [part_boundary,
			  'Content-Disposition: form-data; name="%s"' % name,
			  '',
			  value
			]
			binary.write('\r\n'.join(block))

		# Add the files to upload
		for field_name, filename, content_type, body in self.files:
			if needsCLRF:
				binary.write('\r\n')
			needsCLRF = True

			block = [part_boundary,
			  str('Content-Disposition: file; name="%s"; filename="%s"' % \
			  (field_name, filename)),
			  'Content-Type: %s' % content_type,
			  ''
			  ]
			binary.write('\r\n'.join(block))
			binary.write('\r\n')
			binary.write(body)


		# add closing boundary marker,
		binary.write('\r\n--' + self.boundary + '--\r\n')
		return binary

在 MultiPartForm 类设计的时候就是一个 StringIO 的缓冲区数据包装,注意我前面说过的,每一次发起的 form-data 请求都要知道它的帧大小,包括传输的文件内容的长度,而 MultiPartForm 在 MicroPython 执行 form_buffer = form.get_binary().getvalue() 的时候就会内存不足 。

wCli = MicroWebCli('http://192.168.123.4:8080', 'POST')
#wCli = MicroWebCli('http://127.0.0.1:8080', 'POST')
print('POST %s' % wCli.URL)

form = MultiPartForm()

# Add a fake file
form.add_file(filepath, filename, fileHandle=open(filepath + filename, "rb"))

form_buffer =  form.get_binary().getvalue()

wCli.OpenRequest(None, form.get_content_type(), str(len(form_buffer)))

wCli.RequestWriteData(form_buffer)

因为 MultiPartForm 在设计的时候没有考虑过内存不足的场合,它将预先载入所有文件的内容到 io.BytesIO() 对象中,然后获取长度只需要 len(get_binary()) 即可,这个的前提是机器内存足够大到放下这个 form-data ,但事实上在 MicroPython 中是不可能有这么多内存的,所以要改成边读边上传的模式,那么矛盾就出现了,我该如何在没有加载所有文件的情况下获取 form-data 的大小从而发起请求呢?

其实只需要改写两处地方,我们先看旧代码。

	def add_file(self, fieldname, filename, fileHandle, mimetype=None):
		"""Add a file to be uploaded."""
		body = fileHandle.read()
		if mimetype is None:
			mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
		self.files.append((fieldname, filename, mimetype, body))
		return

可以看到它此处是直接将整个文件都 read() 到 body 中,这在 MicroPython 中是不可能的,K210 通常只允申请 100K 的 BytesIO 对象,所以这里只需要提供文件的大小即可。

    def add_file(self, fieldname, filename, mimetype=None):
        """Add a file to be uploaded."""
        fileSize = os.stat(fieldname + filename)[6]
        if mimetype is None:
            mimetype = 'application/octet-stream'
        self.files.append((fieldname, filename, mimetype, fileSize))
        return

这是需要拆分两次过程,先动态计算 form-data 的 binary 的长度,再来提供操作函数,传递 socket.write 给 MultiPartForm 类代理执行 form.put_binary(wCli._write) 即可,所以改成如下接口。

form = MultiPartForm()
# form.add_field("form_field", "my awesome data")

# Add a fake file
form.add_file(filepath, filename)

wCli.OpenRequest(None, form.get_content_type(), str(form.len_binary()))

form.put_binary(wCli._write)

附赠 MicroPython 实现

惯例了,需要就直接拿去吧,记得结合 MicroWebCli 使用,有错误也都是一些 依赖库的错误,我都在 esp32 和 k210 的 micropython 上测试过辣。

class MultiPartForm(object):
    """Accumulate the data to be used when posting a form."""

    def __init__(self):
        self.form_fields = []
        self.files = []
        import time
        self.boundary = str(time.time()) # .encode()
        return

    def get_content_type(self):
        return 'multipart/form-data; boundary=%s' % self.boundary

    def add_file(self, fieldname, filename, mimetype=None):
        """Add a file to be uploaded."""
        fileSize = os.stat(fieldname + filename)[6]
        if mimetype is None:
            mimetype = 'application/octet-stream'
        self.files.append((fieldname, filename, mimetype, fileSize))
        return

    def len_binary(self):
        res = 0
        part_boundary = ('--' + self.boundary).encode()
        res += len(part_boundary)
        needsCLRF = False
        # Add the form fields
        for name, value in self.form_fields:
            if needsCLRF:
                res += len(b'\r\n')
            needsCLRF = True

            block = [part_boundary,
              ('Content-Disposition: form-data; name="%s"' % name).encode(),
              b'',
              value.encode()
            ]

            res += len(b'\r\n'.join(block))

        # Add the files to upload
        for fieldname, filename, content_type, fileSize in self.files:
            if needsCLRF:
                res += len(b'\r\n')
            needsCLRF = True

            block = [part_boundary,
              ('Content-Disposition: file; name="%s"; filename="%s"' % (fieldname, filename)).encode(),
              ('Content-Type: %s' % content_type).encode(),
              b'']

            res += len(b'\r\n'.join(block))
            res += len(b'\r\n')
            res += fileSize

        # add closing boundary marker,
        res += len(('\r\n--' + self.boundary + '--\r\n'))
        return res

    def put_binary(self, binary_write):
        """Return a binary buffer containing the form data, including attached files."""
        part_boundary = ('--' + self.boundary).encode()

        # binary = io.BytesIO()
        needsCLRF = False
        # Add the form fields
        for name, value in self.form_fields:
            if needsCLRF:
                binary_write('\r\n')
            needsCLRF = True

            block = [part_boundary,
              ('Content-Disposition: form-data; name="%s"' % name).encode(),
              b'',
              value.encode()
            ]

            binary_write(b'\r\n'.join(block))

        # Add the files to upload
        for fieldname, filename, content_type, fileSize in self.files:
            if needsCLRF:
                binary_write(b'\r\n')
            needsCLRF = True

            block = [part_boundary,
              ('Content-Disposition: file; name="%s"; filename="%s"' % (fieldname, filename)).encode(),
              ('Content-Type: %s' % content_type).encode(),
              b'']

            binary_write(b'\r\n'.join(block))
            binary_write(b'\r\n')
            #with open(fieldname + filename, 'rb') as fileObject:
                #binary_write(fileObject.read())
            with open(fieldname + filename, 'rb') as fileObject:
                # binary_write(fileObject.read())
                while True:
                    ch = fileObject.read(2048)
                    if not ch:
                        break
                    binary_write(ch)

        # add closing boundary marker,
        binary_write(('\r\n--' + self.boundary + '--\r\n').encode())

def test_http_upload():
    gc.collect()
    filename = 'F.zip'
    #filename = 'CH340.zip'
    filepath = '/sd/'
    fileObject = open(filepath + filename, 'rb')
    fileSize = os.stat(filepath + filename)[6]
    print(filepath, fileObject, fileSize)

    wCli = MicroWebCli('http://192.168.123.4:8080', 'POST')
    #wCli = MicroWebCli('http://127.0.0.1:8080', 'POST')
    print('POST %s' % wCli.URL)

    form = MultiPartForm()
    # form.add_field("form_field", "my awesome data")

    # Add a fake file
    form.add_file(filepath, filename)

    wCli.OpenRequest(None, form.get_content_type(), str(form.len_binary()))

    form.put_binary(wCli._write)

    # while True:
    #   data = fileObject.read(1024)
    #   if not data:
    #     break
    #   wCli.RequestWriteData(data)

    resp = wCli.GetResponse()
    if resp.IsSuccess() :
      o = resp.ReadContent()
      print('POST success', o)
    else :
      print('POST return %d code (%s)' % (resp.GetStatusCode(), resp.GetStatusMessage()))

#test_http_download()

#test_http_get()

while True:
    time.sleep(1)
    bak = time.ticks()
    try:
        test_http_upload()
    except Exception as E:
        print(E)
    print('total time ', (time.ticks() - bak) / 1000, ' s')

junhuanchen@qq.com 2020年5月23日留

注意:MicroWebCli 提供 application/x-www-form-urlencoded 的上传文件方法,需要配合服务器食用。文章来源地址https://www.toymoban.com/news/detail-711285.html

到了这里,关于[micropython k210] 基于 Socket 实现 MicroPython 的 HTTP 上传文件(multipart/form-data)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于MicroPython的智能火灾报警器系统的设计与实现

    最近闲来没事,总结一下自己的一个物联网综合课程设计的内容,将它上传到往上,一方面可以供自己日后的学习和回顾,另一方面希望帮助到其他有需要的人。 基于MicroPython的智能火灾报警器系统的设计与实现 硬件方面:主控板使用的是 ESP-WROOM-32 开发板,另外,传感器使

    2024年02月10日
    浏览(41)
  • 【雕爷学编程】MicroPython手册之内置模块 urequests:发送 HTTP 请求和处理响应

    MicroPython是为了在嵌入式系统中运行Python 3编程语言而设计的轻量级版本解释器。与常规Python相比,MicroPython解释器体积小(仅100KB左右),通过编译成二进制Executable文件运行,执行效率较高。它使用了轻量级的垃圾回收机制并移除了大部分Python标准库,以适应资源限制的微控制

    2024年01月17日
    浏览(28)
  • 基于K210的声源定位系统

    2022年全国大学生电子设计竞赛——声源定位跟踪系统(E题) 设计一种能够检测声音源的偏航角、俯仰角及距离来定位和指示声源的装置。水平方向上采用K210结合麦克风阵列采集不同方向麦克风的强度,从而计算偏航角和距离;从而控制舵机,将激光笔指到声源位置。 所需硬件

    2024年02月13日
    浏览(23)
  • 手把手使用Micropython+合宙Esp32c3(驱动安装,为合宙Esp32c3安装Micropython固件库,代码上传到ESP32C3中)含Thonny和vscode两种方法

    CH343驱动下载 下载最新版本就好 驱动下载好后,以管理员身份运行,并点击“安装” 安装完成之后连接设备就可以在设备管理器中看到自己设备及端口 MicroPython 是一种精简版的 Python 语言实现,专门用于嵌入式系统和物联网设备。它提供了一个适用于微控制器的交互式解释

    2024年02月10日
    浏览(41)
  • 八、W5100S/W5500+RP2040之MicroPython开发<HTTP Server示例>

      随着云计算的推广和普及,越来越多的网络设备和服务需要连接到网络,这意味着需要更多的IP地址和其他网络配置信息。DHCP服务器可以动态地分配IP地址和其他配置信息,简化了网络配置管理,提高了网络设备的可用性和效率。   W5100S/W5500是一款集成全硬件 TCP/IP 协

    2024年02月20日
    浏览(30)
  • 九、W5100S/W5500+RP2040之MicroPython开发<HTTP&OneNET示例>

      在这个智能硬件和物联网时代,MicroPython和树莓派PICO正以其独特的优势引领着嵌入式开发的新潮流。MicroPython作为一种精简优化的Python 3语言,为微控制器和嵌入式设备提供了高效开发和简易调试的   当我们结合WIZnet W5100S/W5500网络模块,MicroPython和树莓派PICO的开发潜力

    2024年02月03日
    浏览(37)
  • 使用esp32+micropython+microdot搭建web(http+websocket)服务器(超详细)第一部分

    microdot文档速查 什么是Microdot?Microdot是一个可以在micropython中搭建物联网web服务器的框架 micropyton文档api速查 Quick reference for the ESP32 演示视频链接 视频中我们简单的实现了 使用esp32搭建web服务器 实现get请求 上传网页到服务器 手机打开网址访问该网页 服务器处理请求,实现开

    2024年02月08日
    浏览(40)
  • 基于CW32的K210二维舵机视觉跟踪物体

    前言 最近想要做一个项目是涉及用国产MCU--CW32配合K210控制舵机实现跟踪物体的目的,我想要实现一个功能就是识别到目标并且把目标的坐标信息通过串口传输给单片机,单片机控制舵机进行控制,那么视觉方面目前我认为最好的选择就是使用k210了,它不仅成本低,性能好,

    2024年02月14日
    浏览(26)
  • OpenMV4 基于色块识别的图形+颜色+坐标识别代码(micropython)

    Hello大家好,最近竞赛需要开始研究OpenMV4,今天和大家分享一段基于 色块识别的图形+颜色+坐标识别代码 ,实测准确率高于90%哦,当然,需要在光线和距离都合适的情况下使用(假如你的识别结果不尽如人意,可以自行调节颜色阈值和目标与摄像头的距离),下面,话不多说

    2024年02月15日
    浏览(28)
  • 2023 电赛 E 题 K210 方案--K210实现矩形识别

    reset():重置并初始化单目摄像头 set_pixformat():设置摄像头输出格式,可选的帧格式有 GRAYSCALE ,  RGB565 ,  YUV422,需要根据自己的摄像头进行修改 set_framesize(): 设置摄像头输出帧大小,最大支持VGA格式,推荐设置为QVGA格式 skip_frames():跳过指定帧数或者跳过指定时间内的图像

    2024年02月14日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包