[project share][MAIXPY] to achieve K210 serial port picture transmission [POST submission picture]

Discussion and questions about boards that can run MicroPython but don't have a dedicated forum.
Target audience: Everyone interested in running MicroPython on other hardware.
Post Reply
User avatar
sipeed76
Posts: 7
Joined: Tue Mar 16, 2021 6:16 am
Contact:

[project share][MAIXPY] to achieve K210 serial port picture transmission [POST submission picture]

Post by sipeed76 » Tue Mar 16, 2021 7:47 am

Image

Write in front: When connected to the network, serial port diagram transmission cannot be called diagram transmission, but should be renamed diagram transmission. First, the bandwidth of ESP01S can no longer support high frame rate image transmission. Second, in practical application, it is not necessary to continuously transmit images. It is enough to transmit only one image when necessary. As the last part of serial port diagram transmission, this chapter will implement HTTP POST in two ways. This chapter requires you to have a POST interface to test, which requires a certain foundation. This chapter will end with the Django-based post interface code.

POST (theory implementation)

Before using post, let's take a look at HTTP.
What is HTTP? Hypertext Transfer Protocol (HTTP) is designed to ensure the communication between client and server. HTTP works as a request-response protocol between a client and a server. The web browser may be the client, and the network application on the computer may also act as the server. For example, the client (browser) submits an HTTP request to the server; The server returns a response to the client. The response contains status information about the request and what may be requested.
So what does HTTP have to do with TCP?
TCP is the underlying communication protocol. HTTP, which defines the mode of data transmission and connection, is the application layer protocol. The data in HTTP, which defines the content of data transmission, is transmitted using TCP protocol. Therefore, supporting HTTP must support TCP.
Therefore, we can simply implement HTTP based on TCP, which will be discussed in detail in this section.
Two HTTP request methods, GET and POST, are most commonly used when requesting-responding between client and server: GET and POST.GET - Requests data from a specified resource.POST - Submits data to the specified resource for processing.
POST Message

Simply put, GET and POST are also implemented by SOCK, and just like the preamble frames in previous chapters, they can be implemented by constructing a message. One of the simplest GET and POST messages is as follows:

Code: Select all

get_request_info = b"""GET /index HTTP/1.1
Host: 127.0.0.1:9000

"""
post_request_info = b'''POST /index HTTP/1.1
Host: 127.0.0.1:8888
Content-Type: application/x-www-form-urlencoded
Content-Length: 28

username=abc&hobby=1&hobby=2''
Referring to the above information, we can simply construct a message containing picture data:

Code: Select all

'''获取图片数据'''
img = sensor.snapshot()
img = img.compress(quality=50)#压缩图片为jpg,质量为10%
img_size=img.size()
img_bytes=img.to_bytes()
 '''Build a message'''
post_request_info = b'''POST /photo/ HTTP/1.1
Host: 127.0.0.1:80
FILENAME:JPG
Content-Type: application/multipart/form-data
Content-Length: '''
usrname=b'''

'''
post_bytes=post_request_info+str(img_size)+usrname
Post_Bytes is a stitched message, and once sent, the server knows we're ready to submit data.

''POST/photo/ HTTP/1.1'specifies the protocol method, post path, HTTP version.

"Host: 127.0.0.1:80" Specifies the host to access the server, usually by filling in the server top-level domain name directly, such as "www.baidu.com".

"FILENAME:JPG"Is the custom header content, optional.On my server side, it identifies the file format sent (which also means that any file can be submitted)

"Content-Type" specifies the format of the message content.For binary data, use "application/multipart/form-data".

"Content-Length:" is necessary to indicate the length of data we are sending next so that the connection is closed after receiving a fixed length of data (http is actually a short TCP link, so in order to maintain the connection, a heartbeat packet appears.)

"Usrname" is actually a line break/r/n that tells the server that the header is over and then the message content.

Connect sock

Socks are typically connected directly to fixed-end IP, but 99% of the time POST specifies a web address.Use hereSocket.getaddrinfoTo get the IP and port.

Code: Select all

sock = socket.socket()
addr = socket.getaddrinfo("localhost", 80)[0][-1]
sock.connect(addr)
For this example, it means connect 127.0.0.1:80 and POST to http://127.0.0.1/photo/useSock.send(post_Bytes) Submit a header.

Submit data

After the message has been sent out, the next step is to send the message content.As in the previous chapter, send directly using sock.

Code: Select all

block = int(len(img_bytes)/2048)
    for i in range(block):
       sock.send(img_bytes[i*2048:(i+1)*2048])
       sock.send(img_bytes[block*2048:])
Complete code and results demonstration

Complete Code

Code: Select all

# Untitled - By: Lithromantic - 周二 3月 9 2021
from fpioa_manager import fm
fm.register(10, fm.fpioa.UART1_TX, force=True)
fm.register(9, fm.fpioa.UART1_RX, force=True)
from machine import UART
import sensor, image, time,network
import socket
ssid="1234"
key="11111111"
ADDR = ("192.168.137.1", 80)
uart_A = UART(UART.UART1, 115200, 8, 1, 0, timeout=1000, read_buf_len=4096)
net=network.ESP8285(uart_A)
net.connect(ssid, key)
print(net.ifconfig())
sock = socket.socket()
sock.connect(ADDR)
sock.settimeout(10)

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 2000)

post_request_info = b'''POST /photo/ HTTP/1.1
Host: 127.0.0.1:80
FILENAME:JPG
Content-Type: application/multipart/form-data
Content-Length: '''
usrname=b'''

'''
while (True):
    img = sensor.snapshot()
    img = img.compress(quality=50)#压缩图片为jpg,质量为10%
Result demonstration:

Server side response:

Code: Select all

JPG
[14/Mar/2021 18:18:44] "POST /photo/ HTTP/1.1" 200 79
Image

view.py

Code: Select all

from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
import re
import os
import datetime
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@csrf_exempt
def hello(request):
    return HttpResponse("\r\nHello world ! \r\n")
@csrf_exempt
def runoob(request):
  if request.method == 'GET':
    return HttpResponse('请提交图片')  
  if request.method == 'POST':
    filename = request.META.get("HTTP_FILENAME")
    print(filename)
    myFile =request.body
    if not myFile:
        return HttpResponse("no files for upload!")
    if myFile:
        dir = os.path.join(BASE_DIR,'photo')
        nowtime=datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
        files = '{}.{}'.format(nowtime,filename)
        destination = open(os.path.join(dir, files),
                  'wb+')
        destination.write(myFile)
        destination.close()
    return HttpResponse('ok,图片储存在:{},图片名为:{}'.format(dir,filename))
@csrf_exempt
def runoob1(request):
    #name = request.POST.get("User-Agent")
    name = request.META.get("HTTP_USER_AGENT")
    return HttpResponse('姓名:{}'.format(name))
urls.py

Code: Select all

"""web URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from . import view
urlpatterns = [
    path('hello/', view.hello),
    path('post/',view.runoob1),
    path('photo/',view.runoob),
]

User avatar
sipeed76
Posts: 7
Joined: Tue Mar 16, 2021 6:16 am
Contact:

Implement K210 Serial Port Image Transfer(TCP Image Transfer

Post by sipeed76 » Tue Mar 16, 2021 4:23 pm

In this section we will use the tcp protocol of VOFA+.

Through tcp, we will be able to transfer the wireless graph to the upper computer through the network, which is very practical.

Since bloggers use bit boards and don't have their own network card firmware, we'll use an external network card in this section (esp01s, small and exquisite)

Image

Link esp01s to K210

First step, configure WiFi and socket

Code: Select all

# Untitled - By: Lithromantic - 周二 3月 9 2021
from fpioa_manager import fm
fm.register(10, fm.fpioa.UART1_TX, force=True)
fm.register(9, fm.fpioa.UART1_RX, force=True)
from machine import UART
import sensor, image, time
import network,socket,gc
from setuart import setUART as SU
perFrame=[0,0,320,240,27,0x7F800000,0x7F800000]
ssid="1234"
key="11111111"
def set_net(baudrate=1152000):
    #wifi config

    SU.setuart(baudrate,1)
    uart = UART(UART.UART1,baudrate, timeout=1000, read_buf_len=10240)#创建外部可访问的uart
    net=network.ESP8285(uart)
    net.disconnect()
    net.connect(ssid, key)
    #A=net.ifconfig()#cheak ip address. Your sock addr should be same to this
    ADDR={}
    ADDR[0]=net.ifconfig()[2]
    print(ADDR[0])
    #wifi config end

    #socket config
    ADDR = ("{}".format(ADDR[0]), 1347)
    return ADDR
    #socket config end
def set_img():
    #sensor.config
    sensor.reset()
    sensor.set_pixformat(sensor.RGB565)
    sensor.set_framesize(sensor.QVGA)
    sensor.skip_frames(time = 2000)
    #sensor.config end
def get_img(quality=70):
    img = sensor.snapshot()
    img = img.compress(quality)#压缩图片为jpg,质量为10%
    perFrame[1] = img.size()
    img_bytes = img.to_bytes() #转换图片为byte数组
    return img_bytes
def send_img(img_bytes):
    sendbuff=b''
    for i in perFrame:
        buff=i.to_bytes(4,'little')
        sendbuff=sendbuff+buff
    sock.send(sendbuff)#以数组形式发送图片
    block = int(len(img_bytes)/2048)
    for i in range(block):
       sock.send(img_bytes[i*2048:(i+1)*2048])
       sock.send(img_bytes[block*2048:])
clock = time.clock()

sock = socket.socket()
sock.connect(set_net(2048000))
sock.settimeout(10)
set_img()

while(True):
    send_img(get_img(50))
It should be noted here that the default serial baud rate of esp01s is 115200, which is very slow when transmitting data.

So I changed the baud rate to 204800 after linking to esp01s through setuart (using soft configuration here, it will restore to 115200 after restarting)

The address of the sock should be the same as the third number printed by ifconfig.

as follows

Code: Select all

('192.168.137.196', '255.255.255.0', '192.168.137.1', '0', '0', 'd2:57:7b:35:a7:e1', '1234')
Once the sock is connected, we can send data.Continue using the justfloat protocol here.

With a slight change, theUart.write Change toSock.send,It is ready to use.

Code: Select all

img = sensor.snapshot()
    img = img.compress(quality=10)#压缩图片为jpg,质量为10%
    perFrame[0] = 0
    perFrame[1] = img.size()
    perFrame[2] =img.width()   # 图片宽度
    perFrame[3] = img.height() # 图片高度
    perFrame[4] =27
    img_bytes = img.to_bytes() #转换图片为byte数组

    #####此处为vota+上位机的前导帧,使用其他接收时自行修改#####
    for i in perFrame:
        buff=i.to_bytes(4,'little')
        sock.send(buff)
    sock.send(bytes(prendFrame))
   ######################################################
    block = int(len(img_bytes)/2048)
    for i in range(block):
        sock.send(img_bytes[i*2048:(i+1)*2048])
        sock.send(img_bytes[block*2048:])#按块发送
It is important to note here that there is a data upper limit when TCP is dispatched, so cut the data into n smaller chunks to send.

When vofa+ is turned on, the settings are as follows:

Image

The effect is as shown in the diagram:

Image

Finally, show the source code of setuart:

Code: Select all

import time, network
from machine import UART
from fpioa_manager import fm
class setUART():
    def init():
        fm.register(10, fm.fpioa.UART1_TX, force=True)
        fm.register(9, fm.fpioa.UART1_RX, force=True)
        __class__.uart = UART(UART.UART1, 115200, timeout=1000, read_buf_len=8192)

    def _at_cmd(cmd="AT\r\n", resp="OK\r\n", timeout=20):
        __class__.uart.write(cmd) # "AT+GMR\r\n"
        time.sleep_ms(timeout)
        tmp = __class__.uart.read()
        # print(tmp)
        if tmp and tmp.endswith(resp):
            return True
        return False

    def setuart(baudrate=1152000, reply=5):
        __class__.init()
        for i in range(reply):
            print('set baudrate=%d...'%baudrate)
            time.sleep_ms(500) # at start > 500ms
            if __class__._at_cmd(timeout=500):
                break
        __class__._at_cmd()
        __class__._at_cmd('AT+UART_CUR=%d,8,1,0,0\r\n'%baudrate, "OK\r\n")#设置当前波特率,重启模块恢复到115200
if __name__ == "__main__":
    setUART.setuart()
    uart2 = UART(UART.UART1,1152000, timeout=1000, read_buf_len=10240)
    while(True):
        uart2.write("AT\r\n")
        B=uart2.read()
        print(B)

User avatar
sipeed76
Posts: 7
Joined: Tue Mar 16, 2021 6:16 am
Contact:

[Maixpy]K210 Serial Port Image transmission [2]

Post by sipeed76 » Tue Mar 16, 2021 4:27 pm

POST(requests)

Introducing requests
Requests is a library provided by micropyhton to bring into play the networking capabilities of IOT single-chip computers such as ESP8266/ESP32.Through requests, six common instructions for HTTP/HTTPS can be implemented.
In fact, on K210, the official firmware does not contain the requests module (after all, K210 does not have its own network card), but instead provides a library called MicroWebCli (which needs to be compiled on its own) Considering code universality (micropython can implement code using the official library, try not to use third-party code, which runs counter to micropython compatibility), use self-importing requests library implementation.

Use requests to build post message

Requests provides six http methods, all pointing to the request function:

Code: Select all

def head(url, **kw):
    return request("HEAD", url, **kw)

def get(url, **kw):
    return request("GET", url, **kw)

def post(url, **kw):
    return request("POST", url, **kw)

def put(url, **kw):
    return request("PUT", url, **kw)

def patch(url, **kw):
    return request("PATCH", url, **kw)

def delete(url, **kw):
    return request("DELETE", url, **kw)
In fact, these methods do not upload data, so let's start with request:

Code: Select all

def request(method, url, data=None, json=None, headers={}, stream=None):
Request provides methods, urls, data, headers, and the entrance we need, so we start here:

Code: Select all

method='POST'
url="http://192.168.137.1/photo/"
data=img.to_bytes()
headers={'FILENAME':'JPG'}
response = urequests.request(method=method,url=url,data=data,headers=headers)
Since Maixpy also does not have a USSL module, HTTPS is not available here (implemented in the MicroWebCli module). Headers note that you should use:, to split different custom headers.

For example, headers={'ID':'123', 'PW':'12345678'} Note that members of headers need to be capitalized, otherwise the server will not recognize them correctly

test

After you get the picture, call requests POST to submit the picture.You can also use urequest to get responses

Code: Select all

img=img = sensor.snapshot()
    img = img.compressed(100)#压缩图片为jpg,质量为10%
    data=img.to_bytes()
    response = urequests.request(method=method,url=url,data=data,headers=headers)
    print(response.status_code)
    print(response.reason)
Server response:

Code: Select all

JPG:
[14/Mar/2021 17:07:15] "POST /photo/ HTTP/1.0" 200 20
K210 Print Information:

Code: Select all

>>> init i2c2
[MAIXPY]: find ov2640
[MAIXPY]: find ov sensor
set baudrate=1152000...
200
b'OK'
ok,图片储存在:C:\Users\Lithromantic\Desktop\web\photo,图片名为:2021-03-15-10-30-00.JPG
Complete Code

Code: Select all

# Untitled - By: Lithromantic - 周二 3月 9 2021
from fpioa_manager import fm
fm.register(10, fm.fpioa.UART1_TX, force=True)
fm.register(9, fm.fpioa.UART1_RX, force=True)
from machine import UART
import sensor, image, time,network
import socket
import urequests
from setuart import setUART as SU
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.VGA)
sensor.skip_frames(time = 2000)
ssid="1234"
key="11111111"
method='POST'
url="http://192.168.137.1/photo/"
data=None
headers={'FILENAME':'JPG'}
def set_net(baudrate=1152000):
    #wifi config

    SU.setuart(baudrate,1)
    uart = UART(UART.UART1,baudrate, timeout=1000, read_buf_len=10240)
    net=network.ESP8285(uart)
    net.disconnect()
    net.connect(ssid, key)
#set_net(4096000)
while True:
    img=img = sensor.snapshot()
    img = img.compress(100)#压缩图片为jpg,质量为10%
    data=img.to_bytes()
    response = urequests.request(method=method,url=url,data=data,headers=headers)
    print(response.status_code)
    print(response.reason)
    print(response.text)
Request uses communication

In fact, while testing requests, I tested other methods one after another.However, when using get, the received data is garbled, which may be the reason why I have an ESP01S network card attached.While locating the problem, I noticed that there was a problem with sock.Read() (see another post from me), so request actually worked.Hope to have on-board network card under reply usage.Maixpy actually has a JSON interface, so it can actually quickly transplant the Miropython project of single-chip computers such as ESP32. It is not a big problem to directly deploy the Internet of Things project.

User avatar
sipeed76
Posts: 7
Joined: Tue Mar 16, 2021 6:16 am
Contact:

[vofa+] Implement K210 Serial port picture transfer

Post by sipeed76 » Wed Mar 17, 2021 7:20 am

VOFA+ is committed to lowering the threshold of graphical debugging: retaining the traditional byte stream debugging assistant style of data transmission and reception to meet the requirements of non-graphical debugging;
With intuitive and minimalist protocol design and convenient command binding, hardware engineers can gain powerful data visualization, data analysis, and command sending ability by using control without handling complex protocol logic, and only by programming for serial assistants.
With a tailorable system, thrift is human.By adding controls, engineers can customize an infinitely rich debugging interface, or they can simply use a minimal byte stream interface without worrying about heavy system footprint.
Have an open, open source plug-in system:Users can customize protocols and controls to suit their needs.Through the power of open source communities, VOFA+'s debugging ecosystem can continue to thrive.
Plug-in Driven High Degree of Freedom Upper Computer

There are always people who are not happy with the frame cache display in the IDE, or want to take a picture of K210 and send it to the computer, or some other place to take a picture of K210 and send it to the computer (don't like to follow the normal process anyway).

Essentially, pictures are sent and received through various communication protocols.Here is a simple way to send picture data to your computer through serial port.

The computer side can use python's pyserial or other languages to pass to opencv, pil, and so on for processing.Here is also the easiest way to do this first, using an existing PC for display.

The VOFA+host machine with high degree of freedom made in China is used here for various secondary development.

First look at the protocol engine provided by vofa:

Image

given three out-of-the-box protocol engines, and self-developed methods.I'm using firewater here.

Virewater specifies the format for picture transfer:

Code: Select all

数据格式#
先发送前导帧:
// 先发送前导帧
"image:IMG_ID, IMG_SIZE, IMG_WIDTH, IMG_HEIGHT, IMG_FORMAT\n"
image: - FireWater约定的前导帧开头,用于告诉协议引擎这是一个图片前导帧;
IMG_ID - 图片通道ID,用于标识不同图片;
IMG_SIZE - 即将发送过来的图片尺寸;
IMG_HEIGHT - 即将发送过来的图片高度;
IMG_WIDTH - 即将发送过来的图片宽度;
IMG_FORMAT - 图片格式,诸如8位灰度图、16位灰度图、jpg等等。
然后发送真正的图片数据,FireWater会根据前导帧里的IMG_FORMAT指定的格式将图片解析出来复制代码
After sending the lead frame in the protocol format, you can send the picture data, and vofa+ will display the received picture.

The routine here provides C code, and we've modified the Python code for maixpy:

Code: Select all

"image:%d,%d,%d,%d,%d\n"%(
            IMG_ID,     # 此ID用于标识不同图片通道
            IMG_SIZE,    # 图片数据大小
            IMG_WIDTH,   # 图片宽度
            IMG_HEIGHT, # 图片高度
            IMG_FORMAT# 图片格式复制代码
Then picture transfer.Because print sends content that is parsed into a natural language, causing data modifications (typically, print (img), which receives not picture data but picture information), we use serial output.

Before using serial ports, we should first convert images to bytes data.Use "img_Bytes =Img.to_Bytes() #Convert pictures to byte arrays"

Then the operation of the upper computer.

After the vofa is installed, select the protocol and link, configure the serial data properly (so vofa can also be used as a serial assistant), and select the firewater we use for the data protocol.

Find the control, find the image, and drag it onto the panel

Image

Then link to the single-chip computer, fill in the content according to the agreement, configure the serial port, after sending the picture data, right-click to bind the picture data

Image

Then we can start taking photos!

Image

Finally attach the source code:

Code: Select all

# Untitled - By: Lithromantic - 周日 2月 7 2021
#注意,复用uart后,ide将无法使用
from fpioa_manager import fm
fm.register(5, fm.fpioa.UART1_TX, force=True)
fm.register(4, fm.fpioa.UART1_RX, force=True)
from machine import UART
import sensor, image, time
uart_A = UART(UART.UART1, 115200, 8, 1, 0, timeout=1000, read_buf_len=4096)
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 2000)

clock = time.clock()
while(True):
    clock.tick()
    img = sensor.snapshot()
    img = img.compress(quality=10)#压缩图片为jpg,质量为10%
    IMG_ID = 0
    IMG_SIZE = img.size()
    IMG_WIDTH =img.width()   # 图片宽度
    IMG_HEIGHT = img.height() # 图片高度
    IMG_FORMAT =27
    img_bytes = img.to_bytes() #转换图片为byte数组
    #####此处为vota+上位机的前导帧,使用其他接收时自行修改#####
    uart_A.write("image:%d,%d,%d,%d,%d\n"%(
            IMG_ID,     # 此ID用于标识不同图片通道
            IMG_SIZE,    # 图片数据大小
            IMG_WIDTH,   # 图片宽度
            IMG_HEIGHT, # 图片高度
            IMG_FORMAT# 图片格式
            )
            )
    ######################################################
    uart_A.write(img_bytes,IMG_SIZE)#以数组形式发送图片

Post Reply