本文由米尔论坛优秀评测者“ALSET”提供。
1.项目背景介绍
DNN GAN VAE Diffusion
2.技术硬件方案

3.主要技术原理


OpenCV的DNN模块进行模型推理就显得比较简单,过程就是:
readNetFromCaffe readNetFromTensorflow readNetFromTorch readNetFromDarknet readNetFromONNX readNetFromModelOptimizer
这里所用风格迁移模型是开源的Torch/Lua的模型fast-neural-style。他们提供了十种风格迁移的模型,模型的下载脚本在:https://github.com/jcjohnson/fas ... _transfer_models.sh。这里使用OpenCV的readNetFromTorch函数加载PyTorch模型。
在OpenCV中输入给模型的图像需要首先被构建成一个4个区块的数据块(Blob),并且做如resize、归一化和缩放之类的一些预处理。
output = net.forward()
4.软件系统设计
5.软件运行效果
6.开发后记
在做本项目开发时,从资料中已知知名的内容生成项目stable diffusion已经在一些嵌入式开发板上移植运行成功,这个振奋的消息会不会又给自己挖了一个坑呢。
参考文献与资料
本文将介绍基于米尔电子MYD-JD9X开发板打造成开源的Thingsboard网关。


前面进行了ffmpeg的开发环境搭建以及性能测试,现在就可以进行具体的应用开发了。
这一篇就以一个网络视频播放的Demo来演示基于米尔MYD-YG2LX开发板快捷高效的多媒体应用开发。
过程
https://www.sample-videos.com/index.php#sample-mp4-video下下测试mp4视频
这里下载的是big_buck_bunny_720p_10mb.mp4。
导入视频文件到开发板的~目录下。
设置开发板和PC的IP地址在同一网段
我这里电脑是192.168.137.1,所以设置开发板为192.168.137.2
ifconfig eth0 192.168.37.2 root@myir-yg2lx:/usr/lib# ifconfig eth0 eth0: flags=-28605 mtu 1500 metric 1 inet 192.168.137.2 netmask 255.255.255.0 broadcast 192.168.137.255 inet6 fe80::ca9:54ff:fe57:8ed4 prefixlen 64 scopeid 0x20 ether 0e:a9:54:57:8e:d4 txqueuelen 1000 (Ethernet) RX packets 345675 bytes 422385328 (402.8 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 151051 bytes 11916402 (11.3 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 device interrupt 184
确认开发板和PC网口通讯OK。
从如下地址下载安装VLC
ffmpeg -re -i big_buck_bunny_720p_10mb.mp4 -an -vcodec copy -f rtp rtp://192.168.137.1:5004
打印如下
root@myir-yg2lx:~# ffmpeg -re -i big_buck_bunny_720p_10mb.mp4 -an -vcodec copy -f rtp rtp://192.168.137.1:5004 ffmpeg version 4.2.2 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 8.3.0 (GCC) configuration: --disable-stripping --enable-pic --enable-shared --enable-pthreads --cross-prefix=aarch64-poky-linux- --ld='aarch64-poky-linux-gcc -mtune=cortex-a55 -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/recipe-sysroot' --cc='aarch64-poky-linux-gcc -mtune=cortex-a55 -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/recipe-sysroot' --cxx='aarch64-poky-linux-g++ -mtune=cortex-a55 -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/recipe-sysroot' --arch=aarch64 --target-os=linux --enable-cross-compile --extra-cflags=' -O2 -pipe -g -feliminate-unused-debug-types -fmacro-prefix-map==/usr/src/debug/ffmpeg/4.2.2-r0 -fdebug-prefix-map==/usr/src/debug/ffmpeg/4.2.2-r0 -fdebug-prefix-map=/recipe-sysroot= -fdebug-prefix-map=/recipe-sysroot-native= -mtune=cortex-a55 -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/recipe-sysroot' --extra-ldflags='-Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed -fstack-protector-strong -Wl,-z,relro,-z,now' --sysroot=/recipe-sysroot --libdir=/usr/lib64 --shlibdir=/usr/lib64 --datadir=/usr/share/ffmpeg --disable-mipsdsp --disable-mipsdspr2 --cpu=generic --pkg-config=pkg-config --disable-static --enable-alsa --enable-avcodec --enable-avdevice --enable-avfilter --enable-avformat --enable-avresample --enable-bzlib --disable-libfdk-aac --enable-gpl --disable-libgsm --disable-indev=jack --disable-libvorbis --enable-lzma --disable-libmfx --disable-libmp3lame --disable-openssl --enable-postproc --disable-sdl2 --disable-libspeex --enable-swresample --enable-swscale --enable-libtheora --disable-vaapi --disable-vdpau --disable-libvpx --enable-libx264 --disable-libx265 --disable-libxcb --disable-outdev=xv --enable-zlib libavutil 56.31.100/56.31.100 libavcodec 58.54.100/58.54.100 libavformat 58.29.100/58.29.100 libavdevice 58.8.100/58.8.100 libavfilter 7.57.100/7.57.100 libavresample 4.0.0/4.0.0 libswscale 5.5.100/5.5.100 libswresample 3.5.100/3.5.100 libpostproc 55.5.100/55.5.100 Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'big_buck_bunny_720p_10mb.mp4': Metadata: major_brand : isom minor_version : 512 compatible_brands: isomiso2avc1mp41 creation_time : 1970-01-01T00:00:00.000000Z encoder : Lavf53.24.2 Duration: 00:01:02.32, start: 0.000000, bitrate: 1347 kb/s Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 959 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default) Metadata: creation_time : 1970-01-01T00:00:00.000000Z handler_name : VideoHandler Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, 5.1, fltp, 383 kb/s (default) Metadata: creation_time : 1970-01-01T00:00:00.000000Z handler_name : SoundHandler Output #0, rtp, to 'rtp://192.168.137.1:5004': Metadata: major_brand : isom minor_version : 512 compatible_brands: isomiso2avc1mp41 encoder : Lavf58.29.100 Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], q=2-31, 959 kb/s, 25 fps, 25 tbr, 90k tbn, 25 tbc (default) Metadata: creation_time : 1970-01-01T00:00:00.000000Z handler_name : VideoHandler SDP: v=0 o=- 0 0 IN IP4 127.0.0.1 s=No Name c=IN IP4 192.168.137.1 t=0 0 a=tool:libavformat 58.29.100 m=video 5004 RTP/AVP 96 b=AS:959 a=rtpmap:96 H264/90000 a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z01AH9oBQBbsBEAAAAMAQAAADIPGDKg=,aO88gA==; profile-level-id=4D401F Stream mapping: Stream #0:0 -> #0:0 (copy) Press [q] to stop, [?] for help frame= 1068 fps= 25 q=-1.0 Lsize= 5315kB time=00:00:42.68 bitrate=1020.2kbits/s speed= 1x video:5262kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 1.001308%
复制如下部分

在PC端新建demo.sdp文件,粘贴上述内容

然后使右键点击该sdp文件用VLC打开。
看到开始播放

固定帧率25帧
frame=496 fps=25 q=-1.0 size=2187kB time=00:00:19.80 bitrate= 905.0kbits/s speed=1x
总结
以上可以看到,得益于开发板优异的性能和完备的运行环境,开发多媒体应用非常便捷快速。所以基于本板进行二次开发是非常不错的选择。
D9有什么特点?
采用Cortex-A55 + R5组合架构
台积电16nm FinFET 制程工艺
22.6KDMIPS(4*A55)+3.2KDMPIS(2*R5)
车规级处理器,芯片结温范围从-40到125摄氏度
可同时运行多个不同操作系统,如安卓、Linux、FreeRTOS、裸系统等
可选: 单核D9-Lite、5核D9-Plus、六核D9-Pro。
基于D9开发难吗?
D9处理器:D9310、D9340、D9350、D9360等。
在使用 SDK 构建这个系统镜像过程中,还需要安装交叉工具链,米尔提供的这个 SDK 中除了包含各种源代码外还提供了必要的交叉工具链,可以直接用于编译应用程序等。
SDK 的安装步骤,如下:
sudo mkdir -p /tool
cd $HOME/MYD-JD9Xtar -xvf gcc_linaro_7.3.tar.gz -C /tool
为方便设置编译链,他们提供一个设置脚本 env.sh
第2步:快速编译镜像
这里我们需要使用米尔提供的 envsetup.sh 脚本进行环境变量的设置。
进入 SDK 目录,执行./build.sh config 工程配置, 选择 board、 project、 启动方式和内核版本。然后执行./build.sh 完成编译和打包, 镜像生成到 out 目录。
只需执行 build.sh 就可以一键编译镜像:
./build.sh
主要组件介绍:
images_d9_ref 板子的镜像主目录
binary 目录,包含所有组件的二进制文件
build_object 目录,包含 BSP 以及 buildroot 系统等组件的编译信息
images 目录,最终的镜像目录,.pac 格式的镜像烧录包
第3步:构建 SD 卡烧录器镜像
构建 sd 卡烧录镜像之前,需要先构建完 full 镜像。
准备 full 镜像,进入 SDK 目录,执行 build.sh config 命令,选择 D9 为 SD 卡启动。
然后,编译 SD 卡镜像,编译更新镜像包。
wujl-ubuntu1804:/media/wujl/D9/d9_ssdk$ ./build.sh ssdk
wujl-ubuntu1804:/media/wujl/D9/d9_ssdk$ ./build.sh uboot
root-jd9x:~# echo 0 > /sys/block/mmcblk0boot0/force_roroot-jd9x:~# echo 0 > /sys/block/mmcblk0boot1/force_roroot-jd9x:~# dd if=bootloader.img of=/dev/mmcblk0p26 bs=1M conv=sync 0+1 records in1+0 records out
wujl-ubuntu1804:/media/wujl/D9/d9_ssdk$ ./build.sh linux
root-jd9x:~# echo 0 > /sys/block/mmcblk0boot0/force_roroot-jd9x:~# echo 0 > /sys/block/mmcblk0boot1/force_roroot-jd9x:~# dd if=kernel.img of=/dev/mmcblk0p38 bs=1M conv=sync9+1 records in10+0 records outroot-jd9x:~# dd if=dtb.img of=/dev/mmcblk0p34 bs=1k conv=sync113+1 records in114+0 records out
最后
本篇测评由电子发烧友的优秀测评者“ALSET”提供。

什么是ncnn



set(CMAKE_SYSTEM_NAME Linux)set(CMAKE_SYSTEM_PROCESSOR arm)set(CMAKE_C_COMPILER "arm-poky-linux-gnueabi-gcc")set(CMAKE_CXX_COMPILER "arm-poky-linux-gnueabi-g++")set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)set(CMAKE_C_FLAGS "-march=armv7-a -mfloat-abi=hard -mfpu=neon --sysroot=/home/lutherluo/workspace/fsl-imx-fb/5.10-gatesgarth/sysroots/cortexa7t2hf-neon-poky-linux-gnueabi")set(CMAKE_CXX_FLAGS "-march=armv7-a -mfloat-abi=hard -mfpu=neon --sysroot=/home/lutherluo/workspace/fsl-imx-fb/5.10-gatesgarth/sysroots/cortexa7t2hf-neon-poky-linux-gnueabi")# cache flagsset(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "c flags")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" CACHE STRING "c++ flags")







板上运行测试ncnn
测试基于ncnn的应用


ncnn移植测试总结
由测评者HonestQiao提供
这篇分享,内容较长,且涉及到的知识点较多,需要耐心阅读,下面为具体内容的目录:
前言:
如果有心查看米尔MYC-J1028X核心板及开发板的官方界面:
![]()
今天,我们就在MYC-J1028X开发板上,搭建Node-RED环境,将开发板变身为工业物联网控制网关。
很多人了解或者知道Node-RED,是从智能家居或者小型物联网控制开始的,有的人认为只是一个类似的图形界面编程的工具。
其实,在工业互联网中,Node-RED可以用于数据收集、关联性触发控制、仪表呈现等,在低成本投入的情况下,也能获得较好的效果。
一、安装node-red
安装node-red,可以参考官方的文档:https://nodered.org/docs/getting-started/raspberrypi
虽然这篇文档,是针对树莓派的,但是对于Debian系的系统,都是可以参考的。
-
bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)
执行上述命令后,将自动下载安装脚本,启动安装进程,安装过程中的问题,一律选y即可。
![]()
安装过程可能耗时比较长,先沏一杯茶或者咖啡,等着吧:
![]()
首次安装,可能会遇到莫名其妙的Node-RED core没装上,核心都没装上,怎么玩?
不管,再次执行安装命令,就能好了:
![]()
如果提示那个shortcut有问题,先不管,不影响使用。
因为是首次安装,所以执行 node-red admin init 进行初始化:
![]()
初始化完成后,就可以执行 node-red start 启动Node-RED了。
![]()
正常启动后,就可以通过网址 http://开发板IP:1880/ 进行访问了:
![]()
输入初始化的时候设置的账户密码,登录后,按照下面的设置,即可切换到中文界面:
![]()
设置完成后,需要刷新页面,才能正式生效。
二、系统命令控制LED
现在,我们就可以开始在Node-RED中,开始进行外设控制了。
第一个控制逻辑,还是点灯,能点灯,我们就成功一大半了。
通过查看官方的手册,可以了解到:
![]()
![]()
我们可以先使用上面的指令,测试能否控制系统的D22-LED。
-
echo 0 | sudo tee /sys/class/leds/d22/brightness
-
echo 1 | sudo tee /sys/class/leds/d22/brightness
![]()
三、LED权限设置:
通过查看Node-RED的文档资料,可以了解到,有一个Exec Node,可以用于执行系统的命令,那我们正好可以使用其来执行上面手册中,控制D22的指令。
要使得普通用户运行的Node-RED能够控制系统LED,需要在udev中,添加对应的权限处理规则,否则操作时将会提示没有权限。
-
sudo groupadd -f -r leds
-
sudo usermod -a -G leds $USER
-
-
sudo vim /etc/udev/rules.d/99-leds.rules
-
-
SUBSYSTEM=="leds", ACTION=="add", RUN+="/bin/chgrp -R leds /sys%p", RUN+="/bin/chmod -R g=u /sys%p"
-
SUBSYSTEM=="leds", ACTION=="change", ENV{TRIGGER}!="none", RUN+="/bin/chgrp -R leds /sys%p", RUN+="/bin/chmod -R g=u /sys%p"
将上面的最后两行设置,添加到 /etc/udev/rules.d/99-leds.rules 后,需要重启系统才能生效。
友情提醒:类似在udev中进行普通用户权限的设置,后续还有很多次,一定要仔细设置。
重启后(sudo reboot),可以测试使用普通用户权限,来操作LED:
-
echo 0 | tee /sys/class/leds/d22/brightness
-
echo 1 | tee /sys/class/leds/d22/brightness
四、Node-RED控制系统LED
先在Node-RED界面中,参考下图,添加两个inject node,再添加两个Exec Node。
![]()
Exec Node的命令部分,参考下图填写,记住一个为echo 0,一个为echo 1,对应熄灭和点亮LED
![]()
两个injection Node的设置,分别如下:
![]()
![]()
上面的逻辑表示:
五、系统命令控制GPIO:
在官方手册上,有说明如何操作GPIO:
![]()
结合开发板和原理图上我们可以了解到,J18是我们可以方便使用的引脚区域:
![]()
![]()
J18上可供使用的GPIO对应关系如下:
类似系统LED,如果要在普通用户下控制GPIO,也需要在udev中进行一些设置,具体设置如下:
-
sudo groupadd -f -r gpio
-
sudo usermod -a -G gpio $USER
-
-
sudo vim /etc/udev/rules.d/99-gpio.rules
-
-
SUBSYSTEM=="gpio", KERNEL=="gpiochip*", ACTION=="add", RUN+="/bin/chgrp -R gpio /sys/class/gpio/export /sys/class/gpio/unexport", RUN+="/bin/chmod -R g=u /sys/class/gpio/export /sys/class/gpio/unexport"
-
SUBSYSTEM=="gpio*", KERNEL=="gpio*", ACTION=="add", RUN+="/bin/chgrp -R gpio /sys%p", RUN+="/bin/chmod -R g=u /sys%p"
-
SUBSYSTEM=="gpio*", KERNEL=="gpio*", ACTION=="change", ENV{TRIGGER}!="none", RUN+="/bin/chgrp -R gpio /sys%p", RUN+="/bin/chmod -R g=u /sys%p"
将上述最后三行内容写入/etc/udev/rules.d/99-gpio.rules,然后重启开发板即可生效。
重启完成后,我们就可以现在命令行下进行测试。
首先按照原理图,将可以用3.3V驱动的LED,连接到GPIO3_9引脚上。
然后使用下面的命令进行测试:
-
echo 425 > /sys/class/gpio/export
-
echo out > /sys/class/gpio/gpio425/direction
-
-
for i in {1..100};do
-
echo $((i%2)) > /sys/class/gpio/gpio425/value
-
sleep 1
-
done
执行完命令后,连接到GPIO3_9引脚上的LED,将每秒闪烁一次。
![]()
测试控制成功后,我们就可以到Node-RED中进行控制了。
六、Node-RED控制GPIO
要在Node-Red中控制GPIO,可以安装OPI-GPIO插件,按照以下步骤安装即可:
![]()
安装完成后,在右边组件选择栏,就会出现Orange Pi,选择其中的output pin,然后将之前设置的injection 0和1连接到新添加的控件上,再按照下面的步骤,设置对应的GPIO端口号。
![]()
因为GPIO3_9对应425,所以pin填写425即可。但Orange Pi在填写后,再次打开的时候,界面会变成初始状态,所以务必仔细填写。
填写完成,点击右上角的部署,连接到GPIO3_9上的LED,就会开始闪烁了,和板子上的D22-LED同步。
七、开发板上的S3按键使用:
在开发板上,有一个可供用户编程使用的按键S3:
![]()
![]()
S3按键,使用系统/dev/input/event0来监听接收,普通用户使用,需要做如下设置:
-
sudo usermod -a -G input $USER
-
-
sudo vim /etc/udev/rules.d/99-event.rules
-
-
KERNEL=="event[0-9]*", SUBSYSTEM=="input", TAG+="uaccess"
设置好以后,重启才能生效。
重启后,使用evtest指令测试:
![]()
S3按键后,就能够收到按键的消息了。
为了能够更好的接收到按键信息,我们需要使用Python去进行检测,这需要使用到python3-evdev库,先安装:
-
pip3 install evdev
然后,使用下面的python脚本进行测试:
-
from evdev import InputDevice
-
from select import select
-
dev = InputDevice('/dev/input/event0')
-
while True:
-
r,w,x=select([dev],[],[])
-
for event in dev.read():
-
print(event)
按下S3按键然后释放,就会收到信息了:
![]()
需要注意的是,S3按键使用系统Event接收,按下去和释放,都会触发一次。
按下去对应:
释放对应:
我们可以将程序完善一下,直接按键输出1,释放输出0:
-
from evdev import InputDevice
-
from select import select
-
dev = InputDevice('/dev/input/event0')
-
while True:
-
r,w,x=select([dev],[],[])
-
for event in dev.read():
-
if event.code == 2 and event.type == 1:
-
print("value: %d" % event.value)
-
按下S3按键然后释放,就会收到下面的信息了:
![]()
这样,我们就准备好将按键值提供给Node-RED使用了。
八、在Node-RED中使用S3按键控制LED:
要在Node-RED中,获取到Python提供的按键值,然后控制LED,需要使用下面的逻辑:
![]()
在Node-RED中的控制逻辑如下:
![]()
下面进行详细的说明。
首先,我们再次调整上面的监控脚本,使得监控按键的逻辑,符合实际需要,即:
具体代码如下:
-
from evdev import InputDevice
-
from select import select
-
dev = InputDevice('/dev/input/event0')
-
keypress_times = 0
-
print(keypress_times % 2)
-
while True:
-
r,w,x=select([dev],[],[])
-
for event in dev.read():
-
if event.value == 1:
-
keypress_times = keypress_times + 1
-
print(keypress_times % 2)
-
-
将上述代码保存到 /home/HonestQiao/Projects/event/evdev_key.py (具体路径,请根据你的实际情况设定),然后运行测试:
![]()
要在Node-RED中,启动运行上面的监听脚本,需要使用到node-red-node-daemon插件,安装即可:
![]()
![]()
因为evdev_key.py输出的内容,为【0或者1 并附带回车】,所以Node-RED收到后,需要进行一次转换,才能使用。
按照下面的步骤,添加转换控件,并设置好转换逻辑即可:
![]()
然后,添加GPIO3_10对应426引脚控制,并与转换为数字控件进行关联:
![]()
![]()
然后,在添加一个debug控件,以便能够查看到中间过程的数据信息:
![]()
点击部署后,在右边,就能看到调试输出信息了。
尝试一下按键,就能够看到脚本输出的字符串信息,已经转换为数字好的信息,并且GPIO3_10连接的黄色LED,也能够被正常控制了。
九、Node-RED使用I2C接口读取SHT30温湿度传感器数据:
前面的部分,都是LED控制、GPIO控制、按键读取,相对比较简单。
下面,再来一个感觉稍微提高一点点的实例,就是使用I2C接口读取SHT30温湿度传感器。
在之前看过的电路原理图中,J18接口上,就有I2C接口。
将I2C接口的SHT30,连接到J18对应的接口上:
![]()
要在普通用户权限下,使用i2c接口,我们又需要在udev中做一些设置:
-
sudo vim /etc/udev/rules.d/99-i2c.rules
-
-
KERNEL=="i2c-[0-9]*", GROUP="i2c"
将这行内容,写入到/etc/udev/rules.d/99-i2c.rules,然后重启生效。
重启完成后,在命令下下,测试是否能够找到SHT30:
先使用 i2cdetect -l 命令查看一下可供使用的i2c接口:
![]()
从上面可以看到,i2c-0、i2c-1可供使用。
然后,在使用 i2cdetect -y 0 和 i2cdetect -y 1 查看i2c挂载的设备: ![]()
在i2c-1中,有一个44的设备,这个正是 SHT30的默认i2c通讯地址。
可以尝试,把SHT30连线断开,再使用 i2cdetect -y 1 查看,有什么不同。
在Node-RED中,要读取SHT3X的数据,可以使用 node-red-contrib-sht 插件:
![]()
![]()
逻辑设置完成,点击右上角的部署后,调试区域,就会打印出来,实际读取到的温湿度信息了。
对着温湿度传感器哈哈气,就会发现读取到的数值,发生了变化。
十:Node-RED使用仪表盘显示温湿度传感器数据:
Node-RED还有一个迷人又强大的地方,那就是它不仅能够方便你用图形界面设计物联网设备的控制交互流程,还能够很方便的使用仪表盘,提供数据查看界面。
下面,我就在上一步读取到温湿度传感器的基础上,再做一个,可以在手机界面上查看访问的仪表盘。
首先,安装一个dashboard仪表盘控件:
![]()
然后,在SHT3X空间上,挂两个change,用于转换温湿度值:
![]()
![]()
而温湿度值,各挂一个gauge,用于显示最终的数据,其内容,参考如下设置即可:
温度gauge:
![]()
![]()
在第一次设置gauge的时候,如上面的温度gauge,要点 (4) 进入,设置group节点和Tab节点,具体如下:
![]()
![]()
经过以上设置,就能够将温湿度值,输出到仪表盘上了。
为了效果更好,我再添加一个当前日期时间转换,并使用文本控件显示。
先按照下面的步骤,添加一个function控件,别编写转换代码:
![]()
-
function formatDate(value) {
-
var date = new Date(value);
-
var y = date.getFullYear(),
-
m = date.getMonth() + 1,
-
d = date.getDate(),
-
h = date.getHours(),
-
i = date.getMinutes(),
-
s = date.getSeconds();
-
if (m < 10) { m = '0' + m; }
-
if (d < 10) { d = '0' + d; }
-
if (h < 10) { h = '0' + h; }
-
if (i < 10) { i = '0' + i; }
-
if (s < 10) { s = '0' + s; }
-
var t = y + '-' + m + '-' + d + ' ' + h + ':' + i + ':' + s;
-
return t;
-
}
-
-
return { payload: formatDate(msg.payload) };
其中的 formatDate() 函数,就是一个js中标准的把当前时间戳转换为YYYY-mm-dd HH:MM:SS格式的函数。
这也是Node-RED的一个强大之处,除了编写好的控件,还可以使用脚本进行中间过程的处理。
设置好了日期时间转换,再在其后挂一个text控件,用作显示即可:
![]()
全部设计完成后,点击右上角的部署,就可以使用手机进行查看了。
在手机上,访问网址 http://开发板IP:1880/ui ,就能看到如下的界面了:
![]()
十一、总结:
这篇分享,基于米尔MYC-J1028X开发板,结合Node-RED,将其变身为工业控制网关。
看到这里,看似分享了不少内容,但涉及到Node-RED的部分,只有Node-RED全部功能的万分之一都不到,而且都是非常基础的使用。
对于米尔MYC-J1028X开发板的使用,也是用到了实际功能的百分之一都不到。
Node-RED还有很多很多很多强大的功能,可以方便我们快速的获取外部设备的数据,并编写合适的逻辑处理流程来规整数据,并进行数据的呈现,或者对外部设备进行控制。
而这一切,并不需要特别专业的编程技能,就能够完成。
基于米尔MYC-J1028X开发板强劲的运算能力,以及丰富的外设接口,结合Node-RED所构建的工业控制网关,完全可以应用在实际场合中,发挥重大的作用。
将其部署在工业现场,用于收集、存储、处理和分析网络边缘的数据,能够减轻对云和数据中心的压力。
这套系统,可以通过灵活的I/O简化通信和控制,对数据进行主动采集、解析及过滤、汇聚,来可视化现场数据和控制逻辑,既能方便预测性维护,又能开展实时数据处理与决策。
此次的板卡测试,是米尔MYD-YT507H开发板的行车记录仪测试体验。

为了又快又好的开发行车记录仪的实际界面,以及后续进行各移动平台的App开发,选择了Flutter。事实证明,坑太多了。不过,跨平台特性,确实好。

# http服务器请求处理:网页、MJPEG数据流class CamHandler(BaseHTTPRequestHandler):def do_GET(self):# mjpeg推流if self.path.endswith('.mjpg'):self.send_response(200)self.send_header('Content-type','multipart/x-mixed-replace; boundary=--jpgboundary')self.end_headers()while True:if is_stop:breaktry:# rc,img = cameraCapture.read()rc,img = success,frameif not rc:continueif True:imgRGB=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)jpg = Image.fromarray(imgRGB)tmpFile = BytesIO()jpg.save(tmpFile,'JPEG')self.wfile.write(b"--jpgboundary")self.send_header(b'Content-type','image/jpeg')self.send_header(b'Content-length',str(tmpFile.getbuffer().nbytes))self.end_headers()jpg.save(self.wfile,'JPEG')else:img_fps = JPEG_QUALITY_VALUEimg_param = [int(cv2.IMWRITE_JPEG_QUALITY), img_fps]img_str = cv2.imencode('.jpg', img, img_param)[1].tobytes() # change image to jpeg formatself.send_header('Content-type','image/jpeg')self.end_headers()self.wfile.write(img_str)self.wfile.write(b"--jpgboundary") # end of this parttime.sleep(0.033)except KeyboardInterrupt:self.wfile.write(b"--jpgboundary--")breakexcept BrokenPipeError:continuereturn# 网页if self.path == '/' or self.path.endswith('.html'):self.send_response(200)self.send_header('Content-type','text/html')self.end_headers()self.wfile.write(b'Live video ')self.wfile.write(('' % self.headers.get('Host')).encode())
self.wfile.write(b'')return
# websocket服务请求处理async def CamTransmitHandler(websocket, path):print("Client Connected !")try :while True:# rc,img = cameraCapture.read()rc,img = success,frameif not rc:continueimg_fps = JPEG_QUALITY_VALUEimg_param = [int(cv2.IMWRITE_JPEG_QUALITY), img_fps]encoded = cv2.imencode('.jpg', img, img_param)[1]data = str(base64.b64encode(encoded))data = data[2:len(data)-1]await websocket.send(data)# cv2.imshow("Transimission", frame)# if cv2.waitKey(1) & 0xFF == ord('q'):# break# cap.release()except EXCEPTION_CONNECTION_CLOSE as e:print("Client Disconnected !")# cap.release()except:print("Someting went Wrong !")
cameraCapture = cv2.VideoCapture(CAMERA_NO)cameraCapture.set(cv2.CAP_PROP_FRAME_WIDTH, 320)cameraCapture.set(cv2.CAP_PROP_FRAME_WIDTH, 240)cameraCapture.set(cv2.CAP_PROP_SATURATION, 135)fps = 30size=(int(cameraCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),int(cameraCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))success,frame = cameraCapture.read()
...
while True:if is_stop:success = Falsebreak;success,frame = cameraCapture.read()if not success:continuetime_now = get_current_time()if time_now["time"] - time_record["time"] >= ROTATE_TIME:if time_record_prev:thubm_file = get_file_name(time_record_prev, 'thumbs', 'jpg')print("[Info] write to thumb: %s" % thubm_file)if not os.path.isfile(thubm_file):cv2.imwrite(thubm_file, frame)time_record = time_nowtime_record_prev = get_current_time()video_file = get_file_name(time_record_prev, 'videos', MEDIA_EXT)print("[Info] write to video: %s" % video_file)# encode = cv2.VideoWriter_fourcc(*"mp4v")encode = cv2.VideoWriter_fourcc(*'X264')# encode = cv2.VideoWriter_fourcc(*'AVC1')# encode = cv2.VideoWriter_fourcc(*'XVID')# encode = cv2.VideoWriter_fourcc(*'H264')videoWriter=cv2.VideoWriter(video_file, encode,fps,size) # mp4numFrameRemaining = ROTATE_TIME * fps #摄像头捕获持续时间while success and numFrameRemaining > 0:videoWriter.write(frame)success,frame = cameraCapture.read()numFrameRemaining -= 1cameraCapture.release()
encode = cv2.VideoWriter_fourcc(*'X264'),在不同的环境下面,提供的编码方式不完全相同。# -*- coding: utf-8 -*-import signalimport cv2import timefrom PIL import Imagefrom threading import Threadfrom http.server import BaseHTTPRequestHandler,HTTPServerfrom socketserver import ThreadingMixInfrom io import BytesIOimport osimport sysimport websocketsimport asyncioimport base64import ctypesimport inspectCAMERA_NO = 2ROTATE_TIME = 120MJPEG_ENABLE = 1WEBSOCKET_ENABLE = 1MJPEG_SERVER_PORT = 28888WEBSOCKET_PORT = 28889JPEG_QUALITY_VALUE = 65STORE_DIR = "./data/" if os.uname()[0] == 'Darwin' else "/sdcard/data/"MEDIA_EXT = "mkv"EXCEPTION_CONNECTION_CLOSE = websockets.exceptions.ConnectionClosed if sys.version[:3] == '3.6' else websockets.ConnectionCloseddef _async_raise(tid, exctype):"""raises the exception, performs cleanup if needed"""try:tid = ctypes.c_long(tid)if not inspect.isclass(exctype):exctype = type(exctype)res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))if res == 0:# passraise ValueError("invalid thread id")elif res != 1:# """if it returns a number greater than one, you're in trouble,# and you should call it again with exc=NULL to revert the effect"""ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)raise SystemError("PyThreadState_SetAsyncExc failed")except Exception as err:print(err)def stop_thread(thread):"""终止线程"""_async_raise(thread.ident, SystemExit)# 信号处理回调def signal_handler(signum, frame):# global cameraCapture# global thread# global server# global is_stop# global successprint('signal_handler: caught signal ' + str(signum))if signum == signal.SIGINT.value:print('stop server:')is_stop = Truesuccess = Falseprint("mjpeg server.socket.close...")server.socket.close()print("mjpeg server.shutdown...")server.shutdown()print("ws server.socket.close...")server_ws.ws_server.close()time.sleep(1)# print("ws server.shutdown...")# await server_ws.ws_server.wait_closed()print("mjpeg thread.shutdown...")thread_mjpeg.join()print("ws loop.shutdown...")# event_loop_ws.stop()event_loop_ws.call_soon_threadsafe(event_loop_ws.stop)time.sleep(1)# print("ws thread.shutdown...")# stop_thread(thread_ws)# time.sleep(1)# print(server)# print(server_ws)print(thread_mjpeg.is_alive())print(thread_ws.is_alive())print(event_loop_ws.is_running())# thread_ws.join()print("cameraCapture.release...")cameraCapture.release()print("quit...")# print(server_ws)sys.exit(0)# http服务器请求处理:网页、MJPEG数据流class CamHandler(BaseHTTPRequestHandler):def do_GET(self):# mjpeg推流if self.path.endswith('.mjpg'):self.send_response(200)self.send_header('Content-type','multipart/x-mixed-replace; boundary=--jpgboundary')self.end_headers()while True:if is_stop:breaktry:# rc,img = cameraCapture.read()rc,img = success,frameif not rc:continueif True:imgRGB=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)jpg = Image.fromarray(imgRGB)tmpFile = BytesIO()jpg.save(tmpFile,'JPEG')self.wfile.write(b"--jpgboundary")self.send_header(b'Content-type','image/jpeg')self.send_header(b'Content-length',str(tmpFile.getbuffer().nbytes))self.end_headers()jpg.save(self.wfile,'JPEG')else:img_fps = JPEG_QUALITY_VALUEimg_param = [int(cv2.IMWRITE_JPEG_QUALITY), img_fps]img_str = cv2.imencode('.jpg', img, img_param)[1].tobytes() # change image to jpeg formatself.send_header('Content-type','image/jpeg')self.end_headers()self.wfile.write(img_str)self.wfile.write(b"--jpgboundary") # end of this parttime.sleep(0.033)except KeyboardInterrupt:self.wfile.write(b"--jpgboundary--")breakexcept BrokenPipeError:continuereturn# 网页if self.path == '/' or self.path.endswith('.html'):self.send_response(200)self.send_header('Content-type','text/html')self.end_headers()self.wfile.write(b'Live video ')self.wfile.write(('' % self.headers.get('Host')).encode())
self.wfile.write(b'')returnclass ThreadedHTTPServer(ThreadingMixIn, HTTPServer):"""Handle requests in a separate thread."""# 启动MJPEG服务def mjpeg_server_star():global successglobal serverglobal thread_mjpegtry:server = ThreadedHTTPServer(('0.0.0.0', MJPEG_SERVER_PORT), CamHandler)print("mjpeg server started: http://0.0.0.0:%d" % MJPEG_SERVER_PORT)# server.serve_forever()thread_mjpeg = Thread(target=server.serve_forever);thread_mjpeg.start()except KeyboardInterrupt:print("mjpeg server stoping...")server.socket.close()server.shutdown()print("mjpeg server stoped")# websocket服务请求处理async def CamTransmitHandler(websocket, path):print("Client Connected !")try :while True:# rc,img = cameraCapture.read()rc,img = success,frameif not rc:continueimg_fps = JPEG_QUALITY_VALUEimg_param = [int(cv2.IMWRITE_JPEG_QUALITY), img_fps]encoded = cv2.imencode('.jpg', img, img_param)[1]data = str(base64.b64encode(encoded))data = data[2:len(data)-1]await websocket.send(data)# cv2.imshow("Transimission", frame)# if cv2.waitKey(1) & 0xFF == ord('q'):# break# cap.release()except EXCEPTION_CONNECTION_CLOSE as e:print("Client Disconnected !")# cap.release()except:print("Someting went Wrong !")# websocket服务器启动def websocket_server_start():global thread_wsglobal server_wsglobal event_loop_wsevent_loop_ws = asyncio.new_event_loop()def run_server():global server_wsprint("websocket server started: ws://0.0.0.0:%d" % WEBSOCKET_PORT)server_ws = websockets.serve(CamTransmitHandler, port=WEBSOCKET_PORT, loop=event_loop_ws)event_loop_ws.run_until_complete(server_ws)event_loop_ws.run_forever()thread_ws = Thread(target=run_server)thread_ws.start()# try:# yield# except e:# print("An exception occurred")# finally:# event_loop.call_soon_threadsafe(event_loop.stop)# 获取存储的文件名def get_file_name(time_obj, path, ext):file_name_time = "%04d-%02d-%02d_%02d-%02d-%02d" % (time_obj["year"], time_obj["month"], time_obj["day"], time_obj["hour"], time_obj["min"], 0)return '%s/%s/%s.%s' % (STORE_DIR, path, file_name_time, ext)# 获取当前整分时间def get_current_time():time_now = time.localtime()time_int = int(time.time())return {"year": time_now.tm_year,"month": time_now.tm_mon,"day": time_now.tm_mday,"hour": time_now.tm_hour,"min": time_now.tm_min,"sec": time_now.tm_sec,"time": time_int - time_now.tm_sec}# 设置信号回调signal.signal(signal.SIGINT, signal_handler)signal.signal(signal.SIGTERM, signal_handler)# 捕获摄像头cameraCapture = cv2.VideoCapture(CAMERA_NO)# 摄像头参数设置cameraCapture.set(cv2.CAP_PROP_FRAME_WIDTH, 320)cameraCapture.set(cv2.CAP_PROP_FRAME_WIDTH, 240)cameraCapture.set(cv2.CAP_PROP_SATURATION, 135)fps = 30size=(int(cameraCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),int(cameraCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))# 读取捕获的数据success,frame = cameraCapture.read()if not success:print("camera start failed.")quit()is_stop = Falseserver = Noneserver_ws = Noneevent_loop_ws = Nonethread_mjpeg = Nonethread_ws = Nonemjpeg_server_star()websocket_server_start()print("record server star:")thubm_file = Nonevideo_file = Nonetime_start = int(time.time())time_record = {"time":0}time_record_prev = Nonewhile True:if is_stop:success = Falsebreak;success,frame = cameraCapture.read()if not success:continuetime_now = get_current_time()if time_now["time"] - time_record["time"] >= ROTATE_TIME:if time_record_prev:thubm_file = get_file_name(time_record_prev, 'thumbs', 'jpg')print("[Info] write to thumb: %s" % thubm_file)if not os.path.isfile(thubm_file):cv2.imwrite(thubm_file, frame)time_record = time_nowtime_record_prev = get_current_time()video_file = get_file_name(time_record_prev, 'videos', MEDIA_EXT)print("[Info] write to video: %s" % video_file)# encode = cv2.VideoWriter_fourcc(*"mp4v")encode = cv2.VideoWriter_fourcc(*'X264')# encode = cv2.VideoWriter_fourcc(*'AVC1')# encode = cv2.VideoWriter_fourcc(*'XVID')# encode = cv2.VideoWriter_fourcc(*'H264')videoWriter=cv2.VideoWriter(video_file, encode,fps,size) # mp4numFrameRemaining = ROTATE_TIME * fps #摄像头捕获持续时间while success and numFrameRemaining > 0:videoWriter.write(frame)success,frame = cameraCapture.read()numFrameRemaining -= 1cameraCapture.release()


STATIC_URL = 'static/'STATICFILES_DIRS = [BASE_DIR / "static"]
# 媒体文件存放目录,以及缩略图和视频文件的后缀THUMB_HOME_DIR = "%s/%s/data/thumbs/" % (BASE_DIR, STATIC_URL)VIDEO_HOME_DIR = "%s/%s/data/videos/" % (BASE_DIR, STATIC_URL)IMG_FILTER = [".jpg"]MEDIA_FILTER = [ ".mkv"]import jsonfrom django.shortcuts import render, HttpResponsefrom rest_framework.response import Responsefrom rest_framework.permissions import AllowAnyfrom rest_framework.decorators import api_view, permission_classesimport osfrom django.conf import settingsTHUMB_HOME_DIR = settings.THUMB_HOME_DIRVIDEO_HOME_DIR = settings.VIDEO_HOME_DIRIMG_FILTER = settings.IMG_FILTERMEDIA_FILTER = settings.MEDIA_FILTER# Create your views here.def hello_django(request):str = '''[{"id": 1,"time": "2022-07-28 21:00","title": "2022-07-28 21:00","body": "videos/2022-07-28_2100.mp4"},{"id": 2,"time": "2022-07-28 23:00","title": "2022-07-28 23:00","body": "videos/2022-07-28_2300.mp4"},{"id": 3,"time": "2022-07-28 25:00","title": "2022-07-28 25:00","body": "videos/2022-07-28_2500.mp4"}]'''_json = json.loads(str)return HttpResponse(json.dumps(_json), content_type='application/json')def history_list(request):next = request.GET.get("next", '')print(f"thumb next = {next}")path = "/".join(request.path.split("/")[3:])print(f"thumb request.path= {request.path}")print(f"thumb path = {path}")#print os.listdir(FILE_HOME_DIR+".none/")data = {"files":[], "dirs":[]}print(data)child_path = THUMB_HOME_DIR+nextprint(f"child_path = {child_path}")data['cur_dir'] = path+nextprint(data)for dir in os.listdir(child_path):if os.path.isfile(child_path+"/"+dir):if os.path.splitext(dir)[1] in IMG_FILTER:data['files'].append(dir)else:data['dirs'].append(dir)print(data)data['files']=sorted(data['files'])data['files'].reverse()data['infos'] = []for i in range(0,len(data['files'])):thumb_name = data['files'][i]video_name = thumb_name.replace('.jpg', MEDIA_FILTER[0])file_time = thumb_name.replace('.jpg', '').replace('_', ' ')data['infos'].append({"id": i,"time": file_time,"title": file_time,"body": thumb_name,'thumb': thumb_name,'video': video_name})return Response(data['infos'], status = 200)
hello_django是最开始学习使用的,返回写死的json数据。history_list,则是自动遍历缩略图文件夹,获取缩略图文件信息,并生成所需要的json数据格式。下载代码,进入manage.py所在的目录后,执行下面的命令即可启动:



Scaffold来模拟手机/Pad的操作界面,具体界面如下:
实时画面:
历史记录列表:

fetch is widely supported enough to use · Issue #595 · dart-lang/http (github.com)最主要的,对米尔MYD-YT507开发板有了深入的了解,进行了实际的应用。作为一款车规级处理器T507的开发板,名不虚传!
此次的板卡测试,是米尔MYD-YT507H开发板的各项性能测试。






|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|








|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|




从CoreMark的基础性能测试,包括EMMC、RAM的存储性能测试,以及Qt的显示测试来看,米尔MYD-YT507H开发板的相关性能都是非常高的,可以满足边缘计算,人机交互,智能终端等各类高性能应用场景需求。
此次板卡的测试,是如何在Ubuntu建立xfce桌面环境,以及如何远程穿透访问家中的米尔MYD-YT507H开发板。
sudo apt updatesudo apt upgrade
2.安装locales:
# 安装sudo apt install locales# 设置:安装过程中,按照提示,选择 488. zh_CN.UTF-8 UTF-8、3. zh_CN.UTF-8sudo dpkg-reconfigure locales
# 设置系统全局locale,按照如下信息进行设置sudo vim /etc/environmentLANGUAGE="zh_CN.UTF-8:zh:en_US.UTF-8:en"LANG="zh_CN.UTF-8"# 设置当前用户locale,按照如下信息进行设置vim ~/.bashrcexport LC_ALL="zh_CN.UTF-8"export LANG="zh_CN.UTF-8"export LANGUAGE="zh_CN.UTF-8:zh:en_US.UTF-8:en"# 测试是否生效:设置生效后,执行data指令应返回中文source ~/.bashrcdate
4.安装文泉驿中文字体:
sudo apt install -y fonts-arphic-ukai fonts-arphic-gkai00mp fonts-arphic-bkai00mpsudo apt install -y xfonts-wqy ttf-wqy-microhei ttf-wqy-zenhei
5.安装xfce4:
# 安装过程中,按照提示,选择:56. 汉语、1. 汉语sudo apt install xfce4 xfce4-terminal
6.修改xorg配置:
vim /etc/X11/Xwrapper.config# 修改为如下行allowed_users = anybody
7.启动xfce4:
首先将开发板用HDMI线连接到显示器;我没有独立的显示器,所以连接到了电视;然后执行:
startxfce4
运行后,xfce4桌面会启动;没想到,电视分辨率太低,显示效果不佳:
既然本地显示效果不行,那就想办法弄成远程访问桌面。
我的电脑是macbook pro,分辨率够用,所以我安装了vnc,来提供远程桌面服务。
8.安装并配置vnc:
# 安装sudo apt install vnc4server# 设置密码vncserver # 启动后,根据提示,输入后续通过vnc访问远程桌面,需要的密码# 修改配置,使得vnc可以启动xfce4vim ~/.vnc/xstartup####添加以下内容unset DBUS_SESSION_BUS_ADDRESS# 下面两个任选其一# xfce4-session &startxfce4 &####添加以上内容vncserver -kill :1vncserver :1
最后一条命令执行后,就能够使用vnc工具,远程连接了。
该命令后,可以添加 -geometry 1600x900 参数,表示设置分辨率。
9.在其他电脑上,使用vnc viewer远程连接桌面:

vnc viewer是一个免费工具,可以在网上搜索下载RealVNC提供版本。
使用前面设置的vnc密码登录后,就能够正常进入访问了。
10.设置xfce4字体:
通过系统设置菜单,进入外观设置,选择对应的文泉驿微米黑字体即可。
到这一步,我们已经建立好了xfce4桌面环境,并且能够在内网访问该桌面环境了。
另外,我也在我的iPad Pro上,安装了vnc工具,通过iPad远程访问桌面,效果如下:
11.通过autossh,建立内网穿透,在公司也能够访问家里内网的米尔MYD-YT507H开发板:
要建立这个内网穿透,你首先需要一台可以在外网访问的Linux服务器,例如在云上的服务器,需要具有公网IP地址。autossh能够帮助你,米尔MYD-YT507H开发板连接到该服务器,并且可以让你通过该服务器,再来访问米尔MYD-YT507H开发板。
云上的Linux服务器设置:
# Linux服务器设置sudo vim /etc/ssh/sshd_config# 添加如下行,千万别修改错了GatewayPorts yes# 重启sshd/etc/init.d/sshd restart
米尔MYD-YT507H开发板设置:
sudo apt install autosshautossh -M 5911 -fNR 5901:127.0.0.1:5901 user@Linux服务器IP# 检查连接情况:出现一个autossh、一个ssh的执行行,表示正确ps -ef | grep ssh | grep 5901
云上的Linux服务器检查:
netstat -nlap | grep LISTEN | grep 5901# 执行后,应该可以看到5901对应的信息,否则没有设置正确,请重新设置
ssh -N -L 127.0.0.1:5901:127.0.0.1:5901 user@Linux服务器IP
127.0.0.1:5901,从而连到家里的米尔MYD-YT507H开发板上的5901端口,也就是VNC服务,并打开xfce4桌面环境了。使用同样的方法,也可以将米尔MYD-YT507H开发板的ssh的22端口,做内网穿透,在公司也能访问了。
想要了解优秀测评者“qinyunti”关于MYD-YT507H开发板测评原文的可以复制下方链接查看:
https://bbs.elecfans.com/jishu_2293739_1_1.html
想要了解米尔T507-H处理器开发板可以去米尔官网查看具体的产品介绍:
http://www.myir-tech.com/product/MYC-YT507H.htm
需要购买米尔MYD-YT507H开发板的可以复制下方链接购买:
https://detail.tmall.com/item.htm?id=673629085661