数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

Screen是一个虚拟终端管理器。我们可以用它在后台管理终端界面,这样SSH断开后就不用怕正在进行的操作中断了。
一、安装
sudo apt-get update sudo apt-get install screen
二、使用
1、创建一个虚拟终端
使用putty登录树莓派后执行:
screen -S terminal1
这样就创建好一个名为terminal1的终端了。
此时我们可以随便执行操作了,比如执行sudo apt-get upgrade,或者其它消耗时间比较长的工作,像编译内核等等。
按ctrl+a后再按d这样就保存好一个虚拟终端了,系统会提示deatached。
SSH什么的可以完全断开不管了,让虚拟终端自己运行去吧。
2、访问已经创建好的终端
screen -ls #可以列出已经创建的正在后台运行的终端 screen -r #终端名称就可以了 #比如screen -r terminal1
3、彻底退出
如果一个虚拟终端中的程序执行完毕了,screen -r 进入这个终端后再执行exit就完全退出了。
这样以后通过SSH编译内核之类的长时间工作时,再也不怕因为断网造成的操作中断了。
在任何linux设备上都能安装Screen,操作也是一样的。

硬件开发
2015-03-20 06:59:00
Raspberry Pi (BCM2835): Device Information
Architecture ARMv6
CPU ARM11
RAM 256MB OR 512MB since October 2012 (shared with GPU)
GPU VideoCore IV
OpenGL OpenGL ES 2.0
Multimedia
Qt 5.0 (eglfs/QPA)
OpenMax IL 1.1.2
Supported, with OpenGL ES 2.0

Qt 5 port functional state (against Raspbian Wheezy (primary reference platform))
Feature State Additional info
Hardware accelerated cursor Done upstream
Wayland support Done upstream
Hardware decoding of images To Do
Scenegraph tailoring To Do
HardFP support Done Requires v8 patch
Qt Multimedia Webkit integration
Done To Do
Requires gst-omx webgl, tex mapper
开始
首先我们先创建一个目录来存放Qt5的源代码以及交叉编译所需要的所有文件,我选择在当前用户家目录下创建一个叫做“opt”的目录。
1
diveinedu @debian :~$ mkdir ~ /opt

2
diveinedu @debian :~$ cd ~ /opt
然后,下载以下文件:
下载Raspbian Wheezy 镜像 ( 这里 下载 [raspberrypi.org] ):
1
diveinedu @debian :~ /opt $ wget http: //downloads .raspberrypi.org /images/raspbian/2013-02-09-wheezy-raspbian/2013-02-09-wheezy-raspbian .zip

2
diveinedu @debian :~ /opt $unzip 2013-02-09-wheezy-raspbian.zip
下载解压完后挂载镜像:
1
diveinedu @debian :~ /opt $ sudo mkdir /mnt/rasp-pi-rootfs

2
diveinedu@debian:~ /opt $ sudo mount -o loop,offset=62914560 2013-03-09-wheezy-raspbian.img /mnt/rasp-pi-rootfs
我们这不介绍交叉工具链的编译,直接下载针对树莓派优化定制的交叉编译工具链(或者用github上树莓派的工具链 https://github.com/raspberrypi/tools ):
1
diveinedu@debian:~ /opt $ wget http: //blueocean .qmh-project.org /gcc-4 .7-linaro-rpi-gnueabihf.tbz

2
diveinedu@debian:~ /opt $ tar -xf gcc-4.7-linaro-rpi-gnueabihf.tbz
因为上面的交叉编译工具是32位Linux的,如果你所使用的是64位Linux的话,还需要安装32位的运行库软件包:
1
diveinedu@debian:~ /opt $ sudo apt-get install ia32-libs
如果用的是Debian Wheezy的64位系统,上面的行不通,因为Debian Wheezy 64位开启了multiarch-support ,需要执行:
1
diveinedu@debian:~/opt$ sudo apt-get install multiarch-support

2
diveinedu@debian:~/opt$ sudo dpkg --add-architecture i386

3
diveinedu@debian:~/opt$ sudo apt-get update

4
diveinedu@debian:~/opt$ sudo apt-get install ia32-libs

从远程仓库克隆一份cross-compile-tools到本地:
1
diveinedu@debian:~ /opt $ git clone git: //gitorious .org /cross-compile-tools/cross-compile-tools .git
从远程仓库克隆一份Qt5的源码库到本地:
1
diveinedu@debian:~ /opt $ git clone git: //gitorious .org /qt/qt5 .git

2
diveinedu@debian:~ /opt $ cd qt5

3
diveinedu@debian:~ /opt/qt5 $ . /init-repository
最后,把qtjsbackend子项目打补丁让其支持armv6指令集的树莓派:
1
diveinedu@debian:~ /opt/qt5 $ cd ~ /opt/qt5/qtjsbackend

2
diveinedu@debian:~ /opt/qt5 $ git fetch https: //codereview .qt-project.org /p/qt/qtjsbackend refs /changes/56/27256/4 && git cherry-pick FETCH_HEAD
如果有冲突的话就解决冲突的代码。

编译qtbase
现在我们已经准备好了为树莓派交叉编译Qt5所需要的全部资源,在正式编译之前只需要执行一个小脚本来修正一下符号链接和库文件路径设置:
1
diveinedu@debian:~ /opt/qt5 $ cd ~ /opt/cross-compile-tools

2
diveinedu@debian:~ /opt/qt5 $ sudo . /fixQualifiedLibraryPaths /mnt/rasp-pi-rootfs/ ~ /opt/gcc-4 .7-linaro-rpi-gnueabihf /bin/arm-linux-gnueabihf-gcc
进入qt5/qtbase目录执行以下脚本进行配置和编译工作:
1
diveinedu@debian:~ /opt/qt5 $ cd ~ /opt/qt5/qtbase

2
diveinedu@debian:~ /opt/qt5/qtbase $ . /configure -opengl es2 -device linux-rasp-pi-g++ -device-option CROSS_COMPILE=~ /opt/gcc-4 .7-linaro-rpi-gnueabihf /bin/arm-linux-gnueabihf- -sysroot /mnt/rasp-pi-rootfs -opensource -confirm-license -optimized-qmake -reduce-relocations -reduce-exports -release - make libs -prefix /usr/local/qt5pi -no-pch

3
diveinedu@debian:~ /opt/qt5/qtbase $ make -j 4

4
diveinedu@debian:~ /opt/qt5/qtbase $ sudo make install
编译其他模块
执行到这步的时候,你已经有了针对树莓派交叉编译的qmake工具了,你可以一一的去交叉编译Qt5的其他模块了,为里避免模块编译过程中可能 出现的依赖错误,建议按照这个模块顺序去编译: qtimageformats, qtsvg, qtjsbackend, qtscript, qtxmlpatterns, qtdeclarative, qtsensors, qt3d, qtgraphicaleffects,qtjsondb,qtlocation, qtdocgallery.
模块编译相关的类似命令:
1
diveinedu@debian:~ /opt/qt5 $ cd qtimageformats

2
diveinedu@debian:~ /opt/qt5/qtimageformats $ /usr/local/qt5pi/bin/qmake .

3
diveinedu@debian:~ /opt/qt5/qtimageformats $ make -j4

4
diveinedu@debian:~ /opt/qt5/qtimageformats $ sudo make install
把你所需要或者所想编译的模块都按顺序执行编译安装命令后,所有需要的东西都安装在了镜像文件(raspbain wheezy image)里面了。我们接下来就是把他烧到SD卡上去。 SD卡烧写命令:
1
diveinedu@debian:~ /opt/qt5 $ cd ~ /opt/

2
diveinedu@debian:~ /opt $ sync ; sudo umount /mnt/rasp-pi-rootfs

3
diveinedu@debian:~ /opt $ sudo dd bs=1M if =2013-02-09-wheezy-raspbian.img of= /dev/sdc ; sync
提示:/dev/sdc是我使用的SD的设备, 请根据自己的实际情况修改。

到这里,树莓派的Qt5运行库的编译移植过程就Done了。
后续会有例程Demo以及Qt5的QPA机制在树莓派上的eglfs平台插件的特点介绍和传统QtWidget程序在EGLFS环境下遇到的问题和解决分析。

C/C++开发,嵌入式Linux,嵌入式开发, Qt开发, Qt5移植 等, 尽在长沙戴维营教育 ,欢迎前来学习。
硬件开发
2015-03-20 06:46:00
要是近些年来你以任何一种方式参与过IT行业,恐怕就听过“物联网”(即IoT)这个术语。据知名调研机构Gartner声称,IoT正处于技术成熟度曲线(hype cycle)的顶峰,这意味着许多人因此而兴奋不已,但还没有出现太大的实质性进展。2009年,连接到互联网的设备数量不到10亿个,但Gartner预测,到2020年安装的物联网设备数量将多达260亿个,这将为生产商和服务提供商创造3000亿美元的收入,并且给全球经济带来1.9万亿美元的影响。
简而言之,物联网就是使用智能设备,收集数据,然后这些数据通过互联网传输到其他设备。它与机器对机器(M2M)技术密切相关。虽然“物联网”问世已有一段时日,但这个概念却由Kevin Ashton在1999年首次使用,那时他是宝洁公司的一名员工。
自此以后,物联网概念迅速广泛传播开来。ARM开展的一项调查发现,75%以上的企业已经在以某种方式使用物联网,或者在探究如何使用物联网。96%的调查对象预计会在2016年之前使用物联网。
之所以大家对物联网抱有浓厚的兴趣,一方面在于它大有潜力。Ashton在2006年的一篇文章中解释:“如果我们拥有无所不知的计算机――使用它们收集的数据,又不需要我们人类的任何帮助,我们就能够跟踪和统计一切设备,大大减少浪费、损耗和成本。我们知道设备何时需要更换、维护或召回,它们是全新还是越来越破旧。”他最后得出结论:“物联网有潜力改变这个世界,就像互联网那样。也许它的影响力还要大。”
物联网技术和标准方面的早期工作基本上出现在开源社区里面。我们将介绍一些比较值得关注的、目前在积极开发的开源物联网项目。虽然我们所列的开源项目通常侧重于软件,但也介绍了一大批开源硬件,业余爱好者能够低价买到其中好多硬件。
与往常一样,要是你觉得另外哪些项目应该榜上有名,欢迎留言交流。
开发工具篇
1. Arduino
Arduino既是面向交互式电子产品的硬件规范,又是一套软件,含有集成开发环境(IDE)和Arduino编程语言。官方网站解释,Arduino“是一款工具,用来制造比普通台式机更加能感知和控制物理世界的计算机。”背后的这家组织提供了许多供出售的板卡、入门套件、机器人及相关产品,另外许多机构已使用Arduino来制造自己的物联网相关软硬件产品。
相关网站: http://www.arduino.cc/
2. Eclipse IoT Project
Eclipse正在赞助物联网方面的几个不同项目。它们包括应用框架和服务;使用开源技术实现的物联网协议,包括MQTT CoAP、OMA-DM和OMA LWM2M;处理Lua的工具,Eclipse在大力宣传Lua,声称这是一种理想的物联网编程语言。Eclipse相关的项目包括Mihini、Koneki和Paho。官方网站上还有用于试用工具的沙箱环境和在线演示。
相关网站: http://iot.eclipse.org/
3. Kinoma
Kinoma软件平台归Marvell所有,它包括三个不同的开源项目。Kimona Create是一个DIY构造工具箱,可用于制作电子设备的原型。Kimona Studio是开发环境,可与Create和Kinoma Platform Runtime协同运行。Kimona Connect则是一款免费的iOS和安卓应用程序,将智能手机和平板电脑与物联网设备互联起来。
相关网站: http://www.marvell.com/kinoma/
4. M2MLabs Mainspring
Mainspring是为了开发远程监控、车队管理和智能网格等应用软件而设计的,它是一种开源框架,用于开发M2M应用软件。其功能包括:灵活的设备建模、设备配置、设备与应用软件之间的通信、数据的验证和规范化、数据的长期存储以及数据检索功能。它基于Java和Apache Cassandra NoSQL数据库。
相关网站: http://www.m2mlabs.com/framework
5. Node-RED
Node-RED立足于Node.js,自称是“用于连接物联网的可视化工具”。它让开发人员可以使用一种基于浏览器的流程编辑器,连接诸多设备、服务和API(应用编程接口)。它可以在Raspberry Pi上面运行,60000多个模板可用来扩展其功能。
相关网站: http://nodered.org/
硬件篇
6. Arduino Yún
这个微控制器将基于Arduino的板卡具有的易用性与Linux结合起来。它有两个处理器:ATmega32u4(支持Arduino)和Atheros AR9331(运行Linux)。其他特性包括:无线、以太网支持、USB端口、micro-SD卡槽、三个重置按键及更多。可以从Arduino官方网站购买。
相关网站: http://arduino.cc/en/Main/ArduinoBoardYun?from=Main.ArduinoYUN
7. BeagleBoard
BeagleBoard提供了信用卡大小的计算机,可以运行安卓和Linux。由于它们对耗电量的要求非常低,所以对物联网设备来说是个不错的选择。硬件设计和硬件运行的软件都是开源的,可以通过众多分销商购得BeagleBoard硬件(常常以BeagleBone这个品牌名来销售)。
相关网站: http://beagleboard.org/
8. Flutter
Flutter赖以成名的地方是传输距离远。这款基于Arduino的板卡有无线传输器,传输距离超过半英里。另外,你也不需要路由器;Flutter板卡可以彼此直接联系。它采用了256位AES加密技术,易于使用。硬件和软件都是完全开源的;基础板卡的价格只有20美元。
相关网站: http://www.flutterwireless.com/
9. Local Motors Connected Car
Local Motors是一家汽车公司,专门小规模设计开源汽车方案。它与IBM合作开发了一款与物联网连接的汽车,今年夏季的一次展会上展示了这款汽车。原型的开源软件和设计规范大部分可从下列链接下载。
相关网站: https://localmotors.com/awest/connected-car-project-internet-of-things/
10. Microduino
你也许能从名字中猜到,Microduino提供与Arduino兼容的超小板卡。实际上,这种板卡的大小如同硬币,可以堆叠起来,做成新的产品。所有硬件设计都是开源的,核心模块每个起价只有8美元。其资金来源是Kickstarter网站的一项众筹活动,到时筹到了134563美元。
相关网站: http://www.microduino.cc/
11. OpenPicus
这家公司提供一系列可编程模块和套件,用于将设备连接到云和物联网。其硬件和平台都是开源的,但其产品可用来制造闭源商用产品。该公司还提供供租用的开发服务。
相关网站: http://www.openpicus.com/
12. Pinoccio
与Arduino兼容的Pinnoccio板卡(公司称之为“Scouts”)可在低功耗网状网中彼此连接。板卡里面有内置的可充电电池,电池可以连接到太阳能电池板或任何USB电源装置。该组织还提供用于监控板卡活动的GUI:Pinoccio HQ和ScoutScript,而后者是一种易于使用的脚本语言,可用于控制设备。入门套件售价197美元。
相关网站: https://pinocc.io/
13. RasWIK
RasWIK由一家名为Ciseco的公司开发,其全称是Raspberry Pi Wireless Inventors Kit(Raspberry Pi无线发明家套件)。它让拥有Raspberry Pi的任何人都可以试着制作自己的与无线网络连接的设备。它含有29个不同项目的说明文档,你也可以自行编制说明文档。设备需要收费,但所有包含的代码都是开源的;当然,如果你愿意,可以用它来制作商用产品。
相关网站: http://shop.ciseco.co.uk/raswik/
14. SODAQ
SADAQ的全称是“太阳能供电的数据采集”,它提供了与Arduino兼容的板卡,还有类似乐高积木的插件模块。官方网站上有许多教程,因而很适合初学者学习。而太阳能电池板让它成为一个很好的选择,可以在可能没有电力和互联网连接的众多地方记录环境数据。基础板卡起价为39美元。
相关网站: http://www.sodaq.net/
15. Tessel
Tessel旨在借助这款能够支持JavaScript的、插入任何USB端口的微控制器,让软件开发人员更容易从事硬件开发。你还可以将它连接到额外的模块,从而添加方向感应器、环境光及环境声、摄像头、蓝牙、GPS及/或另外九种功能。一块板卡和一个模块起价99美元,额外模块也有售,售价25美元。所有软硬件设计都是完全开源的。
相关网站: https://tessel.io/
16. UDOO
这款与Arduino兼容的板卡还可以从第二个处理器来运行安卓或Linux(名为UDOObuntu的发行版)。它声称,功能很强大,四倍于Raspberry Pi。官方网站上有多个教程和项目,它还在网上开设了“Made by UDOOers”部分,人们可以在此展示自己的作品。基础板卡的起价为99美元。
相关网站: http://www.udoo.org/
家庭自动化软件篇
17. OpenHAB
OpenHAB让你家里已有的智能设备能够彼此联系。它与厂商无关、与硬件无关,可以在任何能够支持Java的系统上运行。它的目标之一就是,让用户可以为其设备添加新的功能特性,并以新的方式结合起来。它获得了几个奖项,还有一个配套的云计算服务,名为my.openHAB。
相关网站: http://www.openhab.org/
18. The Thing System
这个项目包括软件组件和网络协议。它承诺可以找到你家里面所有与互联网连接的物件,并结合起来,那样你就能控制它们。它支持一大批的设备,包括Nest恒温器、三星智能空调系统、Insteon LED灯泡、Roku、谷歌Chromecast、Pebble智能手表、Goji智能锁及其他众多设备。它用Node.js编写,可以装在Raspberry Pi上。
相关网站: http://thethingsystem.com/index.html
中间件篇
19. IoTSyS
这个物联网中间件为智能设备提供了一个通信堆栈。它支持多种标准和协议,包括IPv6、oBIX、6LoWPAN、受限应用协议(Constrained Application Protocol)和高效XML交换。官方网站上的几段视频演示了它是如何实际工作的。
相关网站: https://code.google.com/p/iotsys/
20. OpenIoT
OpenIoT网站解释,这个项目“是一个开源中间件,用于获取来自传感器云的信息,没必要操心具体使用什么传感器。”它旨在能够实现基于云的“感知即服务”,已开发了诸多用例,面向智慧农业、智能制造、城市群体感知、智慧生活和智能校园。其支持者包括:雅典信息技术学院(AIT)、洛桑联邦理工学院(EPFL)、弗劳恩霍夫光电系统技术和影像处理研究院、SENSAP Microsystems AE、AcrossLimits、联邦科学与工业研究组织、萨格勒布大学电气工程和计算机学院以及爱尔兰国立大学高威分校。
相关网站: http://openiot.eu/
操作系统篇
21. AllJoyn
这个面向物联网的开源操作系统最先由高通公司开发,现在得到了最负盛名的物联网组织之一AllSeen Alliance的支持,其成员包括Linux基金会、微软、LG、高通、夏普、松下、思科、赛门铁克及另外诸多知名公司。它包括一个框架和一套服务,让制造厂商得以制造出兼容设备。它具有跨平台的特点,拥有面向安卓、iOS、OS X、Linux及Windows 7的API。
相关网站: https://allseenalliance.org/developer-resources/alljoyn-open-source-project
22. Contiki
Contiki自诩为“面向物联网的开源操作系统”。它将低功耗微控制器连接到互联网,支持IPv6、6lowpan、RPL和CoAP等标准。其他主要功能包括:高效的内存分配、全IP联网、非常低的功耗、动态模块加载及更多特性。支持的硬件平台包括Redwire Econotags、Zolertia z1 motes、意法半导体开发工具包和德州仪器芯片及板卡。还提供收费的商业支持服务。
相关网站: http://www.contiki-os.org/
23. Raspbian
虽然Raspberry Pi其本意是作为一种教学设备,但许多开发人员已开始使用这种信用卡大小的计算机用于物联网项目。完整的硬件规范并非开源,但大部分软件和说明文档是开源的。Raspbian是一款流行的Raspberry Pi操作系统,基于Linux的Debian发行版。
相关网站: http://raspbian.org/
24. RIOT
RIOT号称是“面向物联网的友好的操作系统”。RIOS由FeuerWhere项目派生而来,于2013年首次亮相。它旨在既对开发人员友好,又对资源友好(即占用较少资源)。它支持多种架构,包括MSP430、ARM7、Cortex-M0、Cortex-M3、Cortex-M4和标准的x86 PC。
相关网站: http://riot-os.org/
25. Spark
Spark是一款基于云计算的分布式物联网操作系统。同一家公司还提供易于使用的硬件开发工具包及相关产品,起价只有39美元(硬件设计也是开源的)。它包括:基于Web的IDE、命令行接口、支持多种语言,以及可与许多不同的物联网设备兼容的代码库。它有一个非常活动的用户社区,还提供了大量说明文档和在线帮助。
相关网站: https://www.spark.io/
监测篇
26. Freeboard
Freeboard旨在让用户可以制作自己的仪表板,用于监测部署的物联网。代码在GitHub上可以免费获取;如果你将自己的仪表板公之于众,也可以免费试用服务。还为那些想确保数据私密的人提供了低价方案。网站上的示例仪表板显示了如何使用仪表板来跟踪空气质量、民用家电、酿酒厂运转状况或保湿器里面的环境条件。
相关网站: http://freeboard.io/
打印篇
27. Exciting Printer
Exciting提供了一套开源工具包,可用于体验物联网打印。有了它,你可以制作自己的小型打印机,然后用该打印机来打印从各种物联网设备获得的信息。比如说,它可以打印每日提醒和天气预报等信息。颇有意思的是,如果你想联系项目负责人,可以绘制图案,然后图案就能在对方办公室的物联网打印机上打印出来。
相关网站: http://exciting.io/printer/
平台和整合工具篇
28. DeviceHive
这个项目提供了一个机器对机器(M2M)通信框架,可用于将设备连接到物联网上。它含有易于使用的基于Web的管理软件,可用于构建网络、应用安全规则和监测设备。官方网站提供了用DeviceHub创建的示例项目,网站上还有一个“游乐场”部分,让用户可以在网上使用DeviceHub,看看它是如何工作的。
相关网站: http://www.devicehive.com/
29. Devicehub.net
Devicehub.net自称是“面向物联网的开源基石。”这项基于云的服务存储与物联网有关的数据,以可视化方式显示这些数据,并且让用户可以从网页来控制物联网设备。开发人员已使用该服务编写诸多应用程序,用来跟踪健康信息、监控孩子位置、自动化管理家用电气、跟踪车辆数据以及监测天气等。
相关网站: http://devicehub.net/dev/api#summary
30. IoT Toolkit
这个项目背后的组织正在开发一系列工具,用于将多个与物联网有关的传感器网络和协议整合起来。主要项目是Smart Object API,不过该组织还在开发HTTP-to-CoAP Semantic映射代理系统,这个应用框架包含嵌入式软件代理及更多组件。它还支持在硅谷举行的见面会,以便对物联网发展有兴趣的人士相互交流。
相关网站: http://iot-toolkit.com/
31. Mango
Mango自称是“世界上最受欢迎的开源机器对机器(M2M)软件”。它基于Web,支持多种平台。关键功能包括:支持多种语言和数据库、元点(meta point)、用户定义的事件、导入/导出及更多特性。
相关网站:
32. Nimbits
Nimbits可以存储和处理一种特定类型的数据――时间标记或地理标记的数据。提供公共平台即服务,你也可以下载软件,将软件部署到谷歌应用引擎、亚马逊EC2上的任何J2EE服务器或者Raspberry Pi上。它支持多种编程语言,包括Arduino、JavaScript、HTML或Nimbits.io Java库。
相关网站: http://forum.infiniteautomation.com/
33. OpenRemote
OpenRemote为基于家庭的业余爱好者、集成商、分销商和制造商提供了四种不同的整合工具。它支持几十种不同的现有协议,让用户可以制造出他们想象得到的几乎各种类型的智能设备,并使用支持Java的任何设备来控制。平台是开源的,但该公司还出售一系列支持服务、电子书及其他工具,旨在帮助设计和产品开发过程。
相关网站: http://www.openremote.com/
34. SiteWhere
这个项目提供了一个完整的平台,可用于管理物联网设备、收集数据,并将该数据与外部系统整合起来。SiteWhere发行版本可以下载,也可以在亚马逊的云上使用。它还与多个大数据工具整合起来,包括MongoDB和ApacheHBase。
相关网站: http://www.sitewhere.org/
35. ThingSpeak
ThingSpeak可以处理HTTP请求,并且存储及处理数据。这个开放数据平台的关键功能包括:开放式API、实时数据收集、地理位置数据、数据处理及可视化、设备状态消息及插件。它能整合多种硬件和软件平台,包括Arduino、Raspberry Pi、ioBridge/RealTime.io、Electric Imp、移动和互联网应用、社交网络及MATLAB数据分析工具。除了开源版本外,它还提供一项托管版服务。
相关网站: https://thingspeak.com/
硬件开发
2015-03-19 12:01:00
树莓派命令行: sudo apt-get install tightvncserver
安装好之后请一定先使用此命令设置一个VNC密码: vncpasswd
(先输入操作密码两次,然后会询问是否设置一个查看(view-only)密码,按自己喜欢,一般没必要。)
设置开机启动:
需要在/etc/init.d/中创建一个文件。例如tightvncserver(注:启动脚本的名称,有和程序名一致的习惯)。 sudo nano /etc/init.d/tightvncserver
内容如下:(putty窗口中按右键=粘贴) #!/bin/sh ### BEGIN INIT INFO # Provides: tightvncserver # Required-Start: $local_fs # Required-Stop: $local_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start/stop tightvncserver ### END INIT INFO # More details see: # http://www.penguintutor.com/linux/tightvnc ### Customize this entry # Set the USER variable to the name of the user to start tightvncserver under export USER='pi' ### End customization required eval cd ~$USER case "$1" in start) # 启动命令行。此处自定义分辨率、控制台号码或其它参数。 su $USER -c '/usr/bin/tightvncserver -depth 16 -geometry 800x600 :1' echo "Starting TightVNC server for $USER " ;; stop) # 终止命令行。此处控制台号码与启动一致。 su $USER -c '/usr/bin/tightvncserver -kill :1' echo "Tightvncserver stopped" ;; *) echo "Usage: /etc/init.d/tightvncserver {start|stop}" exit 1 ;; esac exit 0
注:少数玩家默认用户不是pi的请自行更改USER变量。
按Ctrl+X,回答Y(存盘)退出nano编辑器。
然后给tightvncserver文件加执行权限,并更新开机启动列表。 sudo chmod 755 /etc/init.d/tightvncserver sudo update-rc.d tightvncserver defaults
在Mac上访问,先安装VNC,然后连接时指定地址:vnc://192.168.1.123。
硬件开发
2015-03-19 10:10:00
MQTT 是轻量级的、灵活的物联网消息交换和数据传递协议,致力于为 IoT 开发人员实现灵活性与硬件/网络资源的平衡。
ESP8266 提供了⼀套⾼度集成的 Wi-Fi SoC 解决⽅案,其低功耗、 紧凑设计和⾼稳定性可以满⾜⽤户的需求。ESP8266 拥有完整的且⾃成体系的 Wi-Fi ⽹络功能,既能够独⽴应⽤,也可以作为从机搭载于其他主机 MCU 运⾏。
在此项目中我们将实现 ESP8266 连接到 EMQ X Cloud 运营和维护的免费公共 MQTT 服务器,并使用 Arduino IDE 来对 ESP8266 进行编程。 EMQ X Cloud 是由 EMQ 推出的安全的 MQTT 物联网云服务平台 ,它提供一站式运维代管、独有隔离环境的 MQTT 5.0 接入服务。
所需物联网组件 ESP8266 Arduino IDE MQTT X : 优雅的跨平台 MQTT 5.0 客户端工具 免费的公共 MQTT 服务器 Broker: broker.emqx.io TCP Port: 1883 Websocket Port: 8083
ESP8266 Pub/Sub 示意图
ESP8266 代码编写 首先我们将导入 ESP8266WiFi 和 PubSubClient 库,ESP8266WiFi 库能够将 ESP8266 连接到 Wi-Fi 网络,PubSubClient 库能使 ESP8266 连接到 MQTT 服务器发布消息及订阅主题。 #include #include 设置 Wi-Fi 名称和密码,以及 MQTT 服务器连接地址和端口 const char *ssid = "name"; // Enter your WiFi name const char *password = "pass"; // Enter WiFi password const char *mqtt_broker = "broker.emqx.io"; const int mqtt_port = 1883; 打开一个串行连接,以便于输出程序的结果并且连接到 Wi-Fi 网络 // Set software serial baud to 115200; Serial.begin(115200); // connecting to a WiFi network WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.println("Connecting to WiFi.."); } 设置 MQTT 服务器,并编写回调函数,同时将连接信息打印到串口监视器上 client.setServer(mqtt_broker, mqtt_port); client.setCallback(callback); while (!client.connected()) { Serial.println("Connecting to public emqx mqtt broker....."); if (client.connect("esp8266-client")) { Serial.println("Public emqx mqtt broker connected"); } else { Serial.print("failed with state "); Serial.print(client.state()); delay(2000); } } void callback(char *topic, byte *payload, unsigned int length) { Serial.print("Message arrived in topic: "); Serial.println(topic); Serial.print("Message:"); for (int i = 0; i < length; i++) { Serial.print((char) payload[i]); } Serial.println(); Serial.println("-----------------------"); } MQTT 服务器连接成功后,ESP8266 将向 MQTT 服务器发布消息和订阅主题 // publish and subscribe client.publish("esp8266/test", "hello emqx"); client.subscribe("esp8266/test"); 将主题名称打印到串行端口,然后打印收到消息的每个字节 void callback(char *topic, byte *payload, unsigned int length) { Serial.print("Message arrived in topic: "); Serial.println(topic); Serial.print("Message:"); for (int i = 0; i < length; i++) { Serial.print((char) payload[i]); } Serial.println(); Serial.println("-----------------------"); }
MQTT 服务器的连接和测试 请使用 Arduino IDE 将完整代码上传到 ESP8266,并打开串口监视器
建立 MQTT X 客户端 与 MQTT 服务器的连接, 并向 ESP8266 发送消息
在串口监视器查看 ESP8266 接收到的消息
完整代码 #include #include const char *ssid = "name"; // Enter your WiFi name const char *password = "pass"; // Enter WiFi password const char *mqtt_broker = "broker.emqx.io"; const int mqtt_port = 1883; WiFiClient espClient; PubSubClient client(espClient); void setup() { // Set software serial baud to 115200; Serial.begin(115200); // connecting to a WiFi network WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.println("Connecting to WiFi.."); } Serial.println("Connected to the WiFi network"); //connecting to a mqtt broker client.setServer(mqtt_broker, mqtt_port); client.setCallback(callback); while (!client.connected()) { Serial.println("Connecting to public emqx mqtt broker....."); if (client.connect("esp8266-client")) { Serial.println("Public emqx mqtt broker connected"); } else { Serial.print("failed with state "); Serial.print(client.state()); delay(2000); } } // publish and subscribe client.publish("esp8266/test", "hello emqx"); client.subscribe("esp8266/test"); } void callback(char *topic, byte *payload, unsigned int length) { Serial.print("Message arrived in topic: "); Serial.println(topic); Serial.print("Message:"); for (int i = 0; i < length; i++) { Serial.print((char) payload[i]); } Serial.println(); Serial.println("-----------------------"); } void loop() { client.loop(); }
总结
至此,我们已成功使 ESP8266 连接到 EMQ X Cloud 提供的公共 MQTT 服务器。 在本项目中我们简单的将 ESP8266 连接到 MQTT 服务器,这只是 ESP8266 较为基础的能力之一,ESP8266 其实还能与各类物联网传感器相连,并将传感器数据上报至 MQTT 服务器。
接下来我们将会陆续发布更多关于物联网开发及 ESP8266 的相关文章,尽情关注。 版权声明: 本文为 EMQ 原创,转载请注明出处。
原文链接: https://www.emqx.io/cn/blog/esp8266-connects-to-the-public-mqtt-broker
硬件开发
2020-05-21 10:16:09
开源的物联网技术平台thingsboard安装测试及使用步骤
1. 安装Java1.8
2. 安装Maven
3. 安装node js
4. 安装git
5. 安装npm依赖 以管理员权限运行cmd,然后分别执行如下命令: npm install -g cross-env npm install -g webpack
6. 安装IDEA
IntelliJ IDEA :http://www.jetbrains.com/idea/download/download-thanks.html?platform=windowsZip
7. 安装postgreSQL数据库
下载地址: https://get.enterprisedb.com/postgresql/postgresql-9.4.19-2-windows.exe
Postgresql数据库安装过程,请将用户postgres的密码设置成 postgres。方便后续操作。
Postgresql数据库安装完成后,创建数据库thingsboard,所有者为postgres,然后用数据库工具(我用的是Navicat Premium 12)分别运行D:\thingsboard-develop-1.5\dao\src\main\resources\sql目录下schema-entities.sql、schema-ts.sql、 system-data.sql
8. 下载 thingsboard 源码
https://github.com/thingsboard/thingsboard
9. 编译源码 mvn clean install -DskipTests
如下则代表编译成功:
如果一次不成功则多试几次。
10. 导入IDEA,运行 ThingsboardServerApplication
需要安装的插件:IntelliJ Lombok plugin, Protobuf Support

11. API测试工具安装及需要的测试文件 (win10下Ubuntu子系统)
ubuntu:win10应用商店 Ubuntu 18.04 LTS
1. 测试coap协议:安装 coap-cli sudo npm install coap-cli -g
测试脚本: coap-js.sh # Set ThingsBoard host to "demo.thingsboard.io" or "localhost" THINGSBOARD_HOST="localhost" # Replace YOUR_ACCESS_TOKEN with one from Device details panel. ACCESS_TOKEN="i5OqJGOWWV6vWku8v25B" # Publish serial number and firmware version attributes cat attributes-data.json | coap post coap://$THINGSBOARD_HOST/api/v1/$ACCESS_TOKEN/attributes # Publish timeseries data as an object without timestamp (server-side timestamp will be used) cat telemetry-data.json | coap post coap://$THINGSBOARD_HOST/api/v1/$ACCESS_TOKEN/telemetry
2. 测试http协议:安装cURL sudo apt-get update sudo apt install curl
测试脚本: curl.sh # Set ThingsBoard host to "demo.thingsboard.io" or "localhost" THINGSBOARD_HOST="localhost" # Set ThingsBoard port to 80 or 8080 THINGSBOARD_PORT=8080 # Replace YOUR_ACCESS_TOKEN with one from Device details panel. ACCESS_TOKEN="i5OqJGOWWV6vWku8v25B" # Publish serial number and firmware version attributes # replace $THINGSBOARD_PORT with 8080 (in case of local installation) or 80 (in case of live-demo). curl -v -X POST -d @attributes-data.json http://$THINGSBOARD_HOST:$THINGSBOARD_PORT/api/v1/$ACCESS_TOKEN/attributes --header "Content-Type:application/json" # Publish timeseries data as an object without timestamp (server-side timestamp will be used) # replace $THINGSBOARD_PORT with 8080 (in case of local installation) or 80 (in case of live-demo). curl -v -X POST -d @telemetry-data.json http://$THINGSBOARD_HOST:$THINGSBOARD_PORT/api/v1/$ACCESS_TOKEN/telemetry --header "Content-Type:application/json"
3. 测试mqtt协议:安装 mqtt npm install mqtt -g
测试脚本:mqtt-js.sh #!/bin/sh # Set ThingsBoard host to "demo.thingsboard.io" or "localhost" export THINGSBOARD_HOST="localhost" # Replace YOUR_ACCESS_TOKEN with one from Device details panel. export ACCESS_TOKEN="i5OqJGOWWV6vWku8v25B" # Read serial number and firmware version attributes ATTRIBUTES=$( cat attributes-data.json ) export ATTRIBUTES # Read timeseries data as an object without timestamp (server-side timestamp will be used) TELEMETRY=$( cat telemetry-data.json ) export TELEMETRY # publish attributes and telemetry data via mqtt client node publish.js
4. 测试mqtt协议:安装 mosquitto
测试脚本:mosquitto.sh resources/mosquitto.shCopy resources/mosquitto.sh to clipboard #!/bin/sh # Set ThingsBoard host to "demo.thingsboard.io" or "localhost" THINGSBOARD_HOST="localhost" # Replace YOUR_ACCESS_TOKEN with one from Device details panel. ACCESS_TOKEN="i5OqJGOWWV6vWku8v25B" # Publish serial number and firmware version attributes mosquitto_pub -d -h "$THINGSBOARD_HOST" -t "v1/devices/me/attributes" -u "$ACCESS_TOKEN" -f "attributes-data.json" # Publish timeseries data as an object without timestamp (server-side timestamp will be used) mosquitto_pub -d -h "$THINGSBOARD_HOST" -t "v1/devices/me/telemetry" -u "$ACCESS_TOKEN" -f "telemetry-data.json"
测试脚本:attributes-data.json {"firmware_version":"1.0.1", "serial_number":"SN-001"}
测试脚本:telemetry-data.json {"temperature":21, "humidity":55.0, "active": false}
测试脚本:publish.js var mqtt = require('mqtt'); console.log('Connecting to: %s using access token: %s', process.env.THINGSBOARD_HOST, process.env.ACCESS_TOKEN); var client = mqtt.connect('mqtt://'+ process.env.THINGSBOARD_HOST,{ username: process.env.ACCESS_TOKEN }); client.on('connect', function () { console.log('Client connected!'); client.publish('v1/devices/me/attributes', process.env.ATTRIBUTES); console.log('Attributes published!'); client.publish('v1/devices/me/telemetry', process.env.TELEMETRY); console.log('Telemetry published!'); client.end(); });
测试方法:
1. 运行ubuntu terminal
2. 新建thingsboads文件夹 mkdir thingsboard
3. 将上述测试脚本添加到thingsboard文件加下
4. 配置.sh文件可执行权限 chmod +x *.sh
5. 执行测试,比如测试coap协议上传 ./coap-js.sh
返回成功标识 如下:
服务器接收到消息:
详细测试步骤,请参见官方文档: https://thingsboard.io/docs/getting-started-guides/helloworld/
12. 官方文档
https://thingsboard.io/docs/getting-started-guides/helloworld/
https://thingsboard.io/docs/api/
想了解传感器相关知识,关注微信公众号: 物联网智能传感器
硬件开发
2019-01-16 14:43:00
LilyPad Arduino可穿戴技术和电子织物控制器板简介
第 1 章 LilyPad Arduino 概览
作为本书的第一章,在这里将为读者介绍 LilyPad Arduino 相关的基础知识。例如, LilyPad Arduino 是什么、它可以做什么。除此之外,还将介绍要完成后续学习需要预备的一些技能,例如缝纫基础和本书的写作思想。在读完本章之后,读者就可以成竹在胸地进行学习和创作了本文选自 Arduino可穿戴开发入门教程 。
1.1 可穿戴技术和电子织物
LilyPad Arduino 是为可穿戴技术和电子织物设计的微控制器板。所以在使用 LilyPad 之前,我们首先需要明确什么是可穿戴和电子织物。
1. 可穿戴技术
可穿戴技术主要探索和创造能直接穿在身上、或是整合进用户的衣服或配件的科学技术。它强调的是穿和戴。例如,最近流行的智能手环、智能手表、 Google Glass 都属于可穿戴技术。
2. 电子织物
电子织物与可穿戴技术的定义非常接近。它只强调在织物上集成微控制器、传感器或者致动器等外设,所以它不必是可穿戴的。例如,你可以自己使用 LilyPad 制作一个二进制时钟,并将它集成在刺绣壁画上,这就是电子织物 本文选自 Arduino可穿戴开发入门教程 。
1.2 LilyPad 各模块简介
LilyPad 家族有多种形式和各种相应的模块。所以,在读者选购之前,应该大致了解一下 LilyPad 家族的成员,以使自己可以按照需求进行购买。
1.2.1 控制器板
LilyPad 共有 4 种不同型号的控制器板,他们分别为 LilyPad Arduino USB (图 1.1 )、 LilyPad Arduino SimpleSnap (图 1.2 )、 LilyPad Arduino Simple (图 1.3 )和 LilyPad Arduino Main (图 1.4 )。
这四种型号的板子主体部分都是类似的,他们之间的差别从命名上也是可以看出一些端倪的。 LilyPad Arduino Main 是主要的微控制器板,其他三种都是在它的基础上进行了一些定制化以适应不同的需求:
q LilyPad Arduino Simple 与 LilyPad Arduino Main 的不同之处就是添加了外接电池的接口,但是接出的引脚有所减少;
q LilyPad Arduino USB 在 LilyPad Arduino Simple 的基础上集成了 USB 功能,这使得为它编程不再需要 FTDI 编程器;
q LilyPad Arduino SimpleSnap 引出的引脚数和 LilyPad Arduino Simple 一致,只是它的各个引脚不再是穿孔而是纽扣。并且, LilyPad Arduino SimpleSnap 已经板载了电池。
在本教程的编写过程中主要使用的是 LilyPad Arduino Main ,由于它是 LilyPad 家族主要的板子,所以在教程中我们简称他为 LilyPad 。其他三种板子由于接出的引脚都和 LilyPad Arduino Simple 是相同的,所以统称他们为 LilyPad Simple 本文选自 Arduino可穿戴开发入门教程 。
硬件开发
2015-03-31 10:14:00
疑问!
如何让嵌入式系统联网?
我们都知道,在我们使用电脑进行上网的话,我们是需要将我们的计算机通过网线或者通过无线网卡来与路由器相连,然后在通过猫的调制解调功能去与远程的服务器相连,从而进行我们上网的操作。那么,假如我们需要加嵌入式的机器联网,我们需要怎么做了??
其实原理和计算机也是一样的,首先我们需要一个能解网线或者网卡的相关外设,保证我们的嵌入式设备具有上网的硬件条件,然后通过厂家提供的硬件驱动进行驱动操作,这样就能够保证嵌入式的设备上网了,不过这仅仅是解决了我的一个问题,就是,现在设备能上网了,可是我的嵌入式设备如何通过远程来进行遥控操作了?也就是我们所说的指令传达!!!
这个问题一直困惑了我很久,不过在最近,我突然明白了好多,首先,先通过一张图来划一下这个嵌入式设备联网的过程:
其实 我认为,WIFI控制模块,大概就做了一个中间转换的功能,也就是说,他关键的作用就是将我们WIFI模块接收到的控制信号,转换成了串口信号(这个是常见嵌入式系统都是支持这个串口通讯的),也就是说,只要有芯片能够控制WIFI模块,就应该都能让设备连入因特网,不过是啊51单片机,pic,还是ARM,原理都是一样,只不过在处理的时候,速度的差别而已。

机智云的配置过程我就不在这里说了,因为文档上都有。那么 我们可以再谈谈如何基于机智云进行云上开发!
其实这个和我们开发微信的过程是一样的,首先,你得要有一个类似APPKEY一样的东西,这个需要去官方申请,一般都是能过的,然后,你可以写属于你自己的一些业务逻辑,当你的业务逻辑中碰到了诸如需要远程控制设备的时候,你此时就需要调用机智云给你的开放接口了,这样,你需要遥控时候,调用这个接口,这样就可以完成你的控制功能了,你设备上传的数据完全是可以放在自己的服务器上面的,这个我就不多说了,说白了,这个开发和开发其他云服务是一样一样的!!

好了 今天就写这么多,主要明白WIFI模块在嵌入式联网中起的作用就好了。为了支持我的说法,大家可以去某宝上面搜索“ WIFI模块”,你可以看到很多的类似的东西,他们都是透明的进行转换!!!
硬件开发
2015-03-30 13:18:00
简述
本文主要描述使用树莓派和L298N制作一个简单的遥控小车,遥控器使用简单的WEB来实现。
准备工作 树莓派,(本文使用的是Raspberry PI 2 B型,即2B) 8G以上TF卡 树莓派上可用和不可用的SD卡列表 四驱小车底(含电机, 注:本文中的小车底盘佩戴的是高扭矩直流电机) 母对母、公对母、公对公(可不用)杜邦线 L298N双HD桥电机驱动板 无线网卡(最好支持AP的) 充电电池组(7V以上的,镍氢或者18650充电电池皆可)
系统安装没什么说的,系统烧到SD就可以了,我使用的Raspbian,这货是基于Debian的,配置命令习惯几乎和ubuntu/debian一样,接上网线开机进入系统配置。 换上 中科大的源 ,再apt-get update一次; 为root用户设置密码; 配置无线网卡有两中方案,编辑/etc/network/interface,网上教程蛮多的: 自动连接到路由器,家里有无线路由器,小车在WIFI覆盖的地方; 树莓派上搭建WIFI热点,信号更好,可以在小区里面开,顺便勾搭妹子,哦哦哦,不对,应该是淘气小孩。唯一需要注意的是网卡芯片。
底盘组装 拼装没啥难度,亚克力板上的牛皮纸沾得简直是丧心病狂,马蛋,废了好一会儿功夫才撕干净(⊙﹏⊙)b; 马达那货就比较坑了,没有带线是裸机的,手中也恰巧没有电烙铁,当时我就懵逼啦。好吧,手中还有多余的公对公杜邦线,拔掉其中一头的接头,打火机烧一下(小时候学会的神技),拔掉一部分把铜线拧紧。最后铜线穿过马达接口的小洞里面,再拧紧。哎丫,没有胶带,最后用透明胶凑合凑合,绑紧。repeat 4次。
线路连接
之前一直没玩过硬件,毕竟我只是一个网工出身的程序猿,第一次拿到L298N研究了好久才明白,还是上图上图。 +12V接口 +5V~+35V, 如需要板内取电,则供电范围Vs:+7V~+35V 电机 OUT1、OUT2 为电机A输出;OUT3、OUT4为电机B输出 IO接口 ENA和ENA分别为A、B电机的 使能端 ,一开始ENA和ENB各自的上下两个针脚是用跳线帽连接起来的,拔掉就可以接线了。 IN1-IN4 为IO控制输入。
弄明白L298N之后,连接就方便多了, OUT1 OUT2 连接小车左侧前后两个电机(并联),之前电机接出来的杜邦线公头直接往接口里面塞(好羞羞),拧紧螺丝;同理,OUT3 OUT4 连接小车右侧前后两个电机; 便宜的镍氢电池组电源正负极分别连接12V、GND,电池组的接头是JST母头,比较好连接; 5V和GND连接到树莓派上的5V和GND用于供电。 ENA、ENB、IN1-IN4连接到树莓派I/O端口上。
接好之后是这样子:
摄像头模块
摄像头是好几年前台式机用的摄像头,分辨率是奇葩的470x640,而不是480x640,mjpg-streamer下,画面是花的,等到画面调正常之后,画面却如放幻灯片一样。我勒个去!摄像头连接到装了openwrt的wndr3800路由器上流畅的没朋友,在树莓派上调来调去却怎么也不行,认命了,放弃放弃,没有摄像头也能当遥控车开。
小车遥控器
终于到了编程部分,为了简单,使用python的RPI.GPIO模块来操作I/O,不过我发现功能蛮少的,没找到调速的API,文档在 这里 ,代码如下,轮子的参数视接的I/O口而定。 import RPi.GPIO as gpio class Wheel(object): def __init__(self, in_pin1, in_pin2, enable_pin1, enable_pin2): ''' :param in_pin1 in_pin2: IN1 IN2 or IN3 IN4 :param enable_pin1 enable_pin2: ENA or ENB ''' self.pin1 = in_pin1 self.pin2 = in_pin2 # setup I/O OUT gpio.setup(in_pin1, gpio.OUT) gpio.setup(in_pin2, gpio.OUT) gpio.setup(enable_pin1, gpio.OUT) gpio.setup(enable_pin2, gpio.OUT) # enable gpio.output(enable_pin1, True) gpio.output(enable_pin2, True) def forward(self): gpio.output(self.pin1, True) gpio.output(self.pin2, False) def backward(self): gpio.output(self.pin1, False) gpio.output(self.pin2, True) def stop(self): gpio.output(self.pin1, False) gpio.output(self.pin2, False) class Car(object): def __init__(self): gpio.setmode(gpio.BOARD) self.left_wheel = Wheel(13, 15, 7, 11) self.right_wheel = Wheel(16, 18, 22, 29) def forward(self): self.left_wheel.forward() self.right_wheel.forward() def backward(self): self.left_wheel.backward() self.right_wheel.backward() def left(self): self.left_wheel.stop() self.right_wheel.forward() def right(self): self.left_wheel.forward() self.right_wheel.stop() def stop(self): self.left_wheel.stop() self.right_wheel.stop() def shutdown(self): self.stop() gpio.cleanup()
最后用Flask模块实现Web(android真的好久都没摸了,偷笑),这需要在树莓派装Flask模块,在安装的时候发现树莓派的python环境是不完整的。这个代码就不贴了,直接上仓库地址。
小车遥控器部分代码: https://github.com/fordoo/rpi-car
小车演示GIF: http://ww2.sinaimg.cn/bmiddle/a3b0dabfgw1eqed43g2ivg20b20coqv5.gif
硬件开发
2015-03-30 10:57:00
预检(内容提要)

1. Ceph Node Setup(CEPH节点设置)
2. Ceph Deploy Setup (CEPH部署设置)
2.1 Advanced Package Tool (APT)
2.2 Red Hat Package Manager (RPM)
3. Summary (总结)
预检

新版本0.60。
感谢您尝试Ceph!深入钻研Ceph的之前,我们建议设立一个ceph-deploy管理节点和3个节点演示集群来探索一些Ceph功能。预检检查将帮助你准备使用 ceph-deploy一个管理节点和3个Ceph节点(或虚拟机)用来管理你的Ceph存储集群。
1.CEPH节点设置
执行以下的步骤:
1.在每个Ceph节点上创建一个用户。
ssh user@ceph-server
sudo useradd -d /home/ceph -m ceph
sudo passwd ceph
2.在每个Ceph节点中为用户增加 root 权限
echo "ceph ALL = (root) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/ceph
sudo chmod 0440 /etc/sudoers.d/ceph
3.安装一个SSH服务器 (如果必要):
sudo apt-get install openssh-server
sudo yum install openssh-server
4.用无密码的SSH连接到每个Ceph节点来配置你的 ceph-deploy 管理节点. 保留密码为空:
ssh-keygen
Generating public/private key pair.
Enter file in which to save the key (/ceph-client/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /ceph-client/.ssh/id_rsa.
Your public key has been saved in /ceph-client/.ssh/id_rsa.pub.
5.复制秘钥至每个Ceph节点.
ssh-copy-id ceph@ceph-server
6.修改你的ceph-deploy 管理节点的 ~/.ssh/config 文件使它能像你创建的用户(比如, ceph)一样记录至Ceph节点 .
Host ceph-server
Hostname ceph-server.fqdn-or-ip-address.com
User ceph
7.用和主机名 ping 来确定连接性 (比如, 不是IP地址). 必要的解决主机名解析问题和防火墙问题。
2.CEPH部署设置
增加Ceph资料库至 ceph-deploy 管理节点. 之后,安装 ceph-deploy.
重要:如果你是用不同的用户登录的,就不要用sudo或者root权限运行ceph-deploy,因为在远程的主机上不能发出sudo命令

2.1高级包管理工具(APT)
对于Debian和Ubuntu的发行版,请执行下列步骤:
1.添加发行密钥
wget -q -O- 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' | sudo apt-key add -
echo deb http://ceph.com/debian-dumpling/ $(lsb_release -sc) main | sudo tee /etc/apt/sources.list.d/ceph.list
sudo apt-get update
sudo apt-get install ceph-deploy
2.添加Ceph包到你的仓库,用一个稳定的Ceph发行版替换{ceph-stable-release}(如 cuttlefish, dumpling等),例如
echo deb http://ceph.com/debian-{ceph-stable-release}/ $(lsb_release -sc) main | sudo tee /etc/apt/sources.list.d/ceph.list
3.更新你的仓库并安装ceph-deploy
sudo apt-get update && sudo apt-get install ceph-deploy
2.2Red Hat软件包管理器(RPM)
对于Red Hat(rhel6), CentOS (el6), Fedora 17-19 (f17-f19), OpenSUSE 12 (opensuse12), 和 SLES (sles11)等平台按照下面的步骤:
1.添加包到你的仓库。打开一个文本编辑器和创建一个Yellowdog更新,修改(YUM)项,使用路径/etc/yum.repos.d/ceph.repo,例如:
sudo vim /etc/yum.repos.d/ceph.repo
粘贴下列示例代码。用一个稳定的Ceph发行版替换{ceph-stable-release}(如 dumpling等).R用你的Linux发行版替换{distro}(如el6 for CentOS 6, rhel6 for Red Hat 6, fc18 or fc19 for Fedora 18 or Fedora 19和sles11 for SLES 11)。最后,保存文件至/etc/yum.repos.d/ceph.repo。
[ceph-noarch]
name=Ceph noarch packages
baseurl=http://ceph.com/rpm-{ceph-stable-release}/{distro}/noarch
enabled=1
gpgcheck=1
type=rpm-md
gpgkey=https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc
2.更新你的仓库并安装ceph-deploy
sudo yum update && sudo yum install ceph-deploy
3.总结

完成了预检的快速入门,下面继续存储集群的快速入门。
硬件开发
2015-03-30 10:23:00
下载Nodejs
创建一个新的目录去存放下载的Nodejs文件,在这里我们创建了一个'nodes_download'目录。
注* 通过 http://nodejs.org/dist 查看最新版。
并不是所有最新版的NodeJS都能在树莓派上使用,因为有些没有正确地指定ARM的指令集。
树莓派二代已经可以完全正常使用了,本人亲测,把过程分享如下。
注意:树莓派一代使用0.12版本的nodejs,因为是ARM v6指令集的CPU,但Chriumun V8最版JavaScript引擎,可能使用了一些ARM v7的功能,比如内存保护等。
下载并解压'tar'包: wget http://nodejs.org/dist/v0.12.1/node-v0.12.1.tar.gz tar vxf node-v0.12.1.tar.gz cd node-v0.12.1
编译Nodejs
一旦源代码下载完成,编绎可能会花费好几个小时,所以请耐心等待。
注* 以前的版本编绎仅需要十几分钟,但随着Chriumun V8引擎变得越来越复杂,编绎时间也变得越来越长,应该是加入了越来越多对ECMAScript 6 支持的原因。想要提高编绎速度的,可以找到node.js0.8x版本进行编绎。
使用以下命令编绎Nodejs cd node-v0.12.1 ./configure make
安装编译好的Nodejs代码
一旦编绎完成,就可以安装进树莓派系统。这需要使用系统管理员登录系统,如 su 命令。 sudo make install
重启树莓派 sudo shutdown -r now
检查安装
一旦安装完成,可以使用以下命令检查版本: node -v npm -v
显示结果应该是:
Nodejs Version V0.12.1
NPM Version 2.5.1
如果结果正常那么Nodejs和NPM就安装完成了。
想用npm install phonegap -g,装上phonegap,有时网络慢出现错误,多来几次就好了...
启动phonegap服务
先启动Phonegap的服务:
phonegap create my-app cd my-app phonegap serve
然后到Iphone或者Android的手机上,下载安装phonegap developer app,输入: 192.168.1.10:3000 #把IP地址改为自己的,树莓派可使用 ifconfig 查看IP地址。3000为默认端口。
或者下载phonegap desktop app,可在Mac OS X和Windows上使用。连接方法与上面相同。
目前,还没有树莓派的客户端,所以用phonegap run 是运行不起来的。
OK,现在,已经创建了一个运行nodejs的phonegap服务器了,拿来做智能家居之类的,堪称神器!


硬件开发
2015-03-29 10:59:00
配置/etc/config/network文件 config switch 'eth1' option reset '0' option enable_vlan '0' config interface 'loopback' option ifname 'lo' option proto 'static' option ipaddr '127.0.0.1' option netmask '255.0.0.0' config interface 'lan' option ifname 'eth1' option proto 'static' option ipaddr '192.168.6.1' option netmask '255.255.255.0' config interface 'wan0' option ifname 'wlan0' option proto 'dhcp' config interface 'wan1' option ifname 'eth0' option proto 'dhcp' option ifname eth0 option proto dhcp
注意:wan0与wan1的配置,ifname的值要对应准确,此处interface的编号要被dhcp、wireless配置文件所使用。
配置/etc/config/dhcp
实现wan0、wan1自动dhcp获取IP地址功能
config dnsmasq option domainneeded '1' option boguspriv '1' option filterwin2k '0' option localise_queries '1' option rebind_protection '1' option rebind_localhost '1' option local '/lan/' option domain 'lan' option expandhosts '1' option nonegcache '0' option authoritative '1' option readethers '1' option leasefile '/tmp/dhcp.leases' option resolvfile '/tmp/resolv.conf.auto' config dhcp 'lan' option interface 'lan' option start '100' option limit '150' option leasetime '12h' option dhcpv6 'server' option ra 'server' config dhcp 'wan0' option interface 'wan0' option ignore '1' config dhcp 'wan1' option interface 'wan1' option ignore '1' config odhcpd 'odhcpd' option maindhcp '0' option leasefile '/tmp/hosts/odhcpd' option leasetrigger '/usr/sbin/odhcpd-update'
配置/etc/config/wireless config wifi-device 'radio0' option type 'mac80211' option channel '0' option hwmode '11g' option path 'platform/ar933x_wmac' option htmode 'HT20' config wifi-iface option device 'radio0' option network 'wan0' option mode 'sta' option ssid 'wifi名称' option encryption 'psk2' option key 'wifi密码'
‍ ‍ 注意:option network的值要与/etc/config/network中的interface编号对应。 ‍ ‍
‍ ‍ 配置/etc/config/firewall ‍ ‍ config defaults option syn_flood '1' option input 'ACCEPT' option output 'ACCEPT' option forward 'REJECT' config zone option name 'lan' option network 'lan' option input 'ACCEPT' option output 'ACCEPT' option forward 'ACCEPT' config zone option name 'wan' list network 'wan0' list network 'wan1' list network 'wan6' option input 'ACCEPT' option output 'ACCEPT' option forward 'ACCEPT' option masq '1' option mtu_fix '1' config forwarding option src 'lan' option dest 'wan' config rule option name 'Allow-DHCP-Renew' option src 'wan' option proto 'udp' option dest_port '68' option target 'ACCEPT' option family 'ipv4' config rule option name 'Allow-Ping' option src 'wan0' option proto 'icmp' option icmp_type 'echo-request' option family 'ipv4' option target 'ACCEPT' config rule option name 'Allow-DHCPv6' option src 'wan' option proto 'udp' option src_ip 'fe80::/10' option src_port '547' option dest_ip 'fe80::/10' option dest_port '546' option family 'ipv6' option target 'ACCEPT' config rule option name 'Allow-ICMPv6-Input' option src 'wan0' option proto 'icmp' option icmp_type 'echo-request echo-reply destination-unreachable packet-too-big time-exceeded bad-header unknown-header-type router-solicitation neighbout' option limit '1000/sec' option family 'ipv6' option target 'ACCEPT' config rule option name 'Allow-ICMPv6-Forward' option src 'wan' option dest '*' option proto 'icmp' option icmp_type 'echo-request echo-reply destination-unreachable packet-too-big time-exceeded bad-header unknown-header-type' option limit '1000/sec' option family 'ipv6' option target 'ACCEPT' config include option path '/etc/firewall.user'


一键配置脚本代码示例
所有的配置操作均使用OpenWRT系统提供的uci命令,也是OpenWRT开发中比较正统的操作方法,当然也并不局限于此,如果不怕麻烦的话,直接修改配置文件也能达到同样效果。
#!/bin/sh ENCRYPTION="psk2" #接入wifi加密方式 SSID="test" #接入wifi的SSID KEY="12345678" #接入wifi的密码 _wifi_sta_set_firewall(){ echo > /etc/config/firewall uci add firewall defaults 1>/dev/null uci set firewall.@defaults[0]=defaults uci set firewall.@defaults[0].syn_flood=1 uci set firewall.@defaults[0].input=ACCEPT uci set firewall.@defaults[0].output=ACCEPT uci set firewall.@defaults[0].forward=REJECT uci add firewall zone 1>/dev/null uci set firewall.@zone[0]=zone uci set firewall.@zone[0].name=lan uci set firewall.@zone[0].network=lan uci set firewall.@zone[0].input=ACCEPT uci set firewall.@zone[0].output=ACCEPT uci set firewall.@zone[0].forward=ACCEPT uci add firewall zone 1>/dev/null uci set firewall.@zone[1]=zone uci set firewall.@zone[1].name=wan uci add_list firewall.@zone[1].network=wan0 uci add_list firewall.@zone[1].network=wan1 #uci add_list firewall.@zone[1].network=wan6 uci set firewall.@zone[1].input=ACCEPT uci set firewall.@zone[1].output=ACCEPT uci set firewall.@zone[1].forward=ACCEPT uci set firewall.@zone[1].masq=1 uci set firewall.@zone[1].mtu_fix=1 uci add firewall forwarding 1>/dev/null uci set firewall.@forwarding[0]=forwarding uci set firewall.@forwarding[0].src=lan uci set firewall.@forwarding[0].dest=wan uci add firewall rule 1>/dev/null uci set firewall.@rule[0]=rule uci set firewall.@rule[0].name=Allow-DHCP-Renew uci set firewall.@rule[0].src=wan uci set firewall.@rule[0].proto=udp uci set firewall.@rule[0].dest_port=68 uci set firewall.@rule[0].target=ACCEPT uci set firewall.@rule[0].family=ipv4 uci add firewall rule 1>/dev/null uci set firewall.@rule[1]=rule uci set firewall.@rule[1].name=Allow-Ping uci set firewall.@rule[1].src=wan uci set firewall.@rule[1].proto=icmp uci set firewall.@rule[1].icmp_type=echo-request uci set firewall.@rule[1].family=ipv4 uci set firewall.@rule[1].target=ACCEPT uci add firewall rule 1>/dev/null uci set firewall.@rule[2]=rule uci set firewall.@rule[2].name=Allow-DHCPv6 uci set firewall.@rule[2].src=wan uci set firewall.@rule[2].proto=udp uci set firewall.@rule[2].src_ip=fe80::/10 uci set firewall.@rule[2].src_port=547 uci set firewall.@rule[2].dest_ip=fe80::/10 uci set firewall.@rule[2].dest_port=546 uci set firewall.@rule[2].family=ipv6 uci set firewall.@rule[2].target=ACCEPT #uci add firewall rule 1>/dev/null #uci set firewall.@rule[3]=rule #uci set firewall.@rule[3].name=Allow-ICMPv6-Input #uci set firewall.@rule[3].src=wan #uci set firewall.@rule[3].proto=icmp #uci set firewall.@rule[3].icmp_type='echo-request echo-reply destination-unreachable packet-too-big time-exceeded bad-header unknown-header-type router-solicitation neighbout' #uci set firewall.@rule[3].limit=1000/sec #uci set firewall.@rule[3].family=ipv6 #uci set firewall.@rule[3].target=ACCEPT #uci add firewall rule 1>/dev/null #uci set firewall.@rule[4]=rule #uci set firewall.@rule[4].name='Allow-ICMPv6-Forward' #uci set firewall.@rule[4].src=wan #uci set firewall.@rule[4].dest=* #uci set firewall.@rule[4].proto=icmp #uci set firewall.@rule[4].icmp_type='echo-request echo-reply destination-unreachable packet-too-big time-exceeded bad-header unknown-header-type' #uci set firewall.@rule[4].limit='1000/sec' #uci set firewall.@rule[4].family='ipv6' #uci set firewall.@rule[4].target='ACCEPT' echo OK uci add firewall include 1>/dev/null uci set firewall.@include[0]=include uci set firewall.@include[0].path='/etc/firewall.user' uci commit } _wifi_sta_set_network(){ echo > /etc/config/network uci set network.eth1=switch uci set network.eth1.reset=0 uci set network.eth1.enable_vlan=0 uci set network.loopback=interface uci set network.loopback.ifname=lo uci set network.loopback.proto=static uci set network.loopback.ipaddr=127.0.0.1 uci set network.loopback.netmask=255.0.0.0 uci set network.lan=interface uci set network.lan.ifname=eth1 uci set network.lan.proto=static uci set network.lan.ipaddr=192.168.6.1 uci set network.lan.netmask=255.255.255.0 uci set network.wan0=interface uci set network.wan0.ifname=wlan0 uci set network.wan0.proto=dhcp uci set network.wan1=interface uci set network.wan1.ifname=eth0 uci set network.wan1.proto=dhcp uci commit } #wifi在STA模式下设置dhcp参数 _wifi_sta_set_dhcp(){ uci delete dhcp.wan 2>/dev/null uci set dhcp.lan=dhcp uci set dhcp.lan.interface=lan uci set dhcp.lan.start=100 uci set dhcp.lan.limit=150 uci set dhcp.lan.leasetime=12h uci set dhcp.lan.dhcpv6=server uci set dhcp.lan.ra=server uci set dhcp.wan0=dhcp uci set dhcp.wan0.interface=wan0 uci set dhcp.wan0.ignore=1 uci set dhcp.wan1=dhcp uci set dhcp.wan1.interface=wan1 uci set dhcp.wan1.ignore=1 uci set dhcp.odhcpd=odhcpd uci set dhcp.odhcpd.maindhcp=0 uci set dhcp.odhcpd.leasefile=/tmp/hosts/odhcpd uci set dhcp.odhcpd.leasetrigger=/usr/sbin/odhcpd-update uci commit } #param: # 连接AP的SSID名称 # AP加密方式 # AP密码 wifi_connect_to(){ echo > /etc/config/wireless uci set wireless.radio0=wifi-device uci set wireless.radio0.type=mac80211 uci set wireless.radio0.channel=0 uci set wireless.radio0.hwmode=11g uci set wireless.radio0.path=platform/ar933x_wmac uci set wireless.radio0.htmode=HT20 if ! uci get wireless.@wifi-iface[0] 1>/dev/null 2>/dev/null then uci add wireless wifi-iface 1>/dev/null 2>/dev/null fi uci set wireless.@wifi-iface[0]=wifi-iface uci set wireless.@wifi-iface[0].device=radio0 uci set wireless.@wifi-iface[0].network=wan0 uci set wireless.@wifi-iface[0].mode=sta uci set wireless.@wifi-iface[0].ssid=$1 uci set wireless.@wifi-iface[0].encryption=$2 uci set wireless.@wifi-iface[0].key=$3 uci commit } _wifi_sta_set_firewall #设置firewall参数 _wifi_sta_set_network #设置network参数 _wifi_sta_set_dhcp #设置dhcp参数 wifi_connect_to $SSID $ENCRYPTION $KEY #连接wifi /etc/init.d/firewall enable /etc/init.d/firewall restart #/etc/init.d/network restart
‍ ‍

硬件开发
2015-03-27 16:53:00

英特尔的SoC开发套件提供了开发定制ARM快速和简单的方法*处理器的SoC设计。设计生产率是Arria 10 SoC架构的驱动理念之一。Arria 10 SoC提供与上一代SoC的完全软件兼容性,广泛的ARM软件和工具生态系统以及增强的FPGA和数字信号处理(DSP)硬件设计流程。Arria 10 SoC旨在满足中端应用的性能和功耗要求.

英特尔 Arria 10 SoC 结合了体系结构创新和 TSMC 的 20 nm 工艺技术,提高了性能,降低了功耗:

•处理器性能提升 65%,并且 CPU 运算/内核高达 1.5 GHz
•相比前一代性能提升 60%,具备超过 500 MHz 的 FPGA 逻辑内核性能(相比以前的 SoC 性能提升 15%)
•收发器带宽比前一代增大了 4 倍(带宽比以前的高端 FPGA 高出 2 倍)
•系统性能提升 4 倍(2,400 Mbps DDR4 SDRAM,混合内存立方体支持)
•超过 1,500 千兆次浮点运算/秒 (GFLOPs),而单个设备高达 50 GFLOPs/瓦特
•借助工艺技术改进和降低功耗的创新方法,使功耗降低了 40%

英特尔Arria 10 SoC FPGA支持使用 32位 DDR4 SDRAM 存储器,用于提升系统性能。
UMI UD408G5S1AF 256Mx32 8Gb DDR4 SDRAM的密度8Gb,32M字×32位×8组,数据速率为
3200Mbps/2933Mbps/2666Mbps/2400Mbps/2133Mbps/1866Mbps/1600Mbps。商业工作温度范围为TC=0℃至+95℃,工业工作温度范围是:TC=-40℃至+95℃。支持应用在Arria 10 SX SoC FPGA中,主要用在无线基础设备,计算和存储设备,广播设备,军用设备和智能设备,有线100G线路卡,40G GPON,测试测量设备以及医疗图像诊断设备。 SDRAM 存储器英尚微电子支持送样及测试。

设计效能是英特尔 Arria 10 SoC 体系结构发展的推动力量之一。英特尔 Arria 10 SoC与前一代 SoC 软件完全兼容,还提供庞大的很多 ARM 软件和工具生态系统,以及增强的 FPGA 和 DSP 硬件设计流程。
硬件开发
2020-05-20 13:40:00
【摘要】 IoT设备中嵌入AI能力实现产品的智能升级,已经是AIoT行业发展的重要通道,那怎样才能实现AIoT = AI + IoT呢?如何将AI模型塞到小小的IoT设备里,让它可以轻松运行起来呢?成为了AI开发者遇到的棘手难题。
你知道我们生活中常见的物联网智能设备融合AI技术后,会给我们带来什么样的智能交互体验?在我们指尖触碰的那一刹那背后隐藏的代码世界又是怎么样的呢?
今天就来和大家说说IoT智能设备轻松实现AI的奥秘!
AIoT,智能化升级的最佳通道
AIoT,对我们来说已经不是一个陌生的词汇了,随着深度学习的蓬勃发展和5G万物互联时代的到来,越来越多的人将AI与IoT结合到一起,而 AIoT已经成为传统行业智能化升级的最佳通道,是物联网发展的必然趋势。

AI和IoT相互交融产生的魔力,让许多智能产品及智能应用成为无穷想象的“潜力股”。比如智能音箱中的语音唤醒,家庭监控里的人脸识别,可穿戴设备上的AI计步等等,已经验证物联网产品融入人工智能,升级成了智能设备后的“威力”。
但,物联网AI 开发者的“痛”,你遇到过么?
很多AI开发者开发者在训练得到AI模型之后,必须得在设备上实现模型的推理才能获得相应的AI能力,但目前AI模型不能直接在设备上运行起来。这就意味着, 开发者还得有一套对应的推理框架才能真正实现AI与IoT设备的结合。

另外,目前深度学习虽然可以在很多领域超越传统算法,不过真正用到实际产品中却要面临计算量大,内存占用高,算法延时长的问题,而IoT设备又往往有算力低、内存小及实时性要求高的特点。 因此针对IoT资源受限的问题,AI模型的压缩及性能优化是AI模型在部署过程中必须解决的难点。

IoT设备中嵌入AI能力实现产品的智能升级,已经是AIoT行业发展的重要通道,那怎样才能实现AIoT = AI + IoT呢?如何将AI模型塞到小小的IoT设备里,让它可以轻松运行起来呢?成为了AI开发者遇到的棘手难题。
LiteAI四大"杀手锏",解锁物联网设备AI 开发难关 LiteOS轻量级AI推理框架LiteAI,从模型转换、优化及执行三个方面向开发者呈现如何在IoT设备上实现AI模型的推理全流程,并结合智能设备AI开发的案例,展示AI部署全过程。 针对IoT设备内存空间小的问题,LiteAI应用了模型量化技术,将模型参数从32比特浮点量化到8比特定点,实现75%模型压缩;实现更合理的内存管理算法,最大化内存复用率,绝大部分场景下达到内存使用下限值;提供模型压缩及聚类算法供开发者选择,进一步减少内存占用。 LiteAI采用算子融合、SIMD指令加速、循环分支细化及Cache分块等技术手段,优化AI网络算子性能,加速模型推理,充分发挥ARM CPU算力。 LiteAI推理引擎纯C语言实现,无第三方依赖,极为适合IoT产品部署;采用代码化模型执行函数设计,仅编译链接有用算子,完全剔除其他所有无用算子,基本无冗余代码,实现代码段空间占用最小化。
为了能让大家对于LiteAI有一个更深的认知,我们还准备了一堂专家线上直播课,详细解读IoT智能设备里的AI奥秘,如何利用华为LiteAI OS操作系统进行AIOT开发。 观看直播,听听专家怎么说IoT智能设备里的AI奥秘

点击关注,第一时间了解华为云新鲜技术~
硬件开发
2020-05-19 14:23:00
Thingsboard最让人头疼的就是项目的编译问题,其中在编译Thingsboard HTTP模块时我遇到的问题是:PKIX path building failed
FAILURE: Build failed with an exception. * What went wrong: A problem occurred configuring root project 'http'. > Could not resolve all dependencies for configuration ':classpath'. > Could not download gradle-ospackage-plugin.jar (com.netflix.nebula:gradle-ospackage-plugin:3.8.0) > Could not get resource 'https://jcenter.bintray.com/com/netflix/nebula/gradle-ospackage-plugin/3.8.0/gradle-ospackage-plugin-3.8.0.jar'. > Could not GET 'https://jcenter.bintray.com/com/netflix/nebula/gradle-ospackage-plugin/3.8.0/gradle-ospackage-plugin-3.8.0.jar'. > sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. BUILD FAILED Total time: 8.864 secs [INFO] Initialize build [INFO] Build [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 10.707 s [INFO] Finished at: 2020-05-19T11:12:44+08:00 [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.fortasoft:gradle-maven-plugin:1.0.8:invoke (default) on project http: org.gradle.tooling.BuildException: Could not execute build using Gradle distribution 'https://services.gradle.org/distributions/gradle-2.13-bin.zip'. -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. [ERROR] [ERROR] For more information about the errors and possible solutions, please read the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
尝试了很多关于maven PKIX path building failed的解决办法,包括参考:
忽略证书校验
https://blog.csdn.net/qq157538651/article/details/95811622
https://my.oschina.net/ghw/blog/3236131/print
配置证书
https://blog.csdn.net/i_like1/article/details/80334298
https://www.cnblogs.com/zdz8207/p/java-https-ssl-jsoup.html
以上无法都对我无效。。。。。
百般无奈只能从地址下手,找出https://jcenter.bintray.com的源头,是项目中gradle配置的默认仓库,将仓库修改为阿里的非https地址,到此问题解决。
找到Thingsboard项目/transport/http/build.gradle文件中的: repositories { jcenter() }
修改为: repositories { maven{url 'http://maven.aliyun.com/nexus/content/groups/public/'} jcenter() }
为了避免其他模块再出现相同问题,索性将所有的build.gradle全部修改一遍。
也可以一次性解决所有问题, 如果想一次更改所有的仓库地址,可以在 USER_HOME/.gradle/ 文件夹下添加 init.gradle 文件来配置,如: allprojects { repositories { maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } maven { url 'http://maven.aliyun.com/repository/public/' } maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' } maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'} all { ArtifactRepository repo -> if (repo instanceof MavenArtifactRepository) { def url = repo.url.toString() if (url.startsWith('https://repo.maven.apache.org/maven2/') || url.startsWith('https://repo.maven.org/maven2') || url.startsWith('https://repo1.maven.org/maven2') || url.startsWith('https://jcenter.bintray.com/')) { //project.logger.lifecycle "Repository ${repo.url} replaced by $REPOSITORY_URL." remove repo } } } } buildscript { repositories { maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } maven { url 'http://maven.aliyun.com/repository/public/'} maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' } maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'} all { ArtifactRepository repo -> if (repo instanceof MavenArtifactRepository) { def url = repo.url.toString() if (url.startsWith('https://repo1.maven.org/maven2') || url.startsWith('https://jcenter.bintray.com/')) { //project.logger.lifecycle "Repository ${repo.url} replaced by $REPOSITORY_URL." remove repo } } } } } }
硬件开发
2020-05-19 11:22:00
摘要
Qt是欧洲人创始的一个优秀的c++开发框架,API简单易用,社区庞大,资源丰富;但Qt太重量级了(需要很多的RAM和ROM,非常复杂)。为了解决这个问题,我开发了GOSP这个框架。GOSP在不依赖Qt的前提下,提供了类似Qt的API接口,仅需要几百KB的硬件资源(比Qt小的多),能运行在Qt不支持的低性能领域(对Qt形成补充),适用于嵌入式开发。
谨以此产品向Qt致敬,致敬Qt为世界做出的杰出贡献。
example效果演示:

v1.1更新内容
改进刷新机制,利用painter的translate。复杂item中还可以包含简单的item。
改进焦点机制,开发者使用GOSP开发时无需考虑焦点在控件间的切换问题。
完善了hasFocus、isFocusEnabled、setFocus接口。
更改初始化tabIndex部分
容器和item可以同时具有焦点。
主要特色 GOSP 是码云GVP项目,其产权 归全体贡献者共同所有,贡献者根据自身对项目的不同贡献而享有不等比例的产权。 史上最容易理解的GUI框架,史上最简单,没有之一。 100%使用图片来实现个性化的控件和界面。 提供了类似Qt的API。 基于别具一格的Giveda信号槽技术,各个模块代码之间无耦合。 无耦合的代码,史上最容易复用,没有之一。
开源协作
如果您觉得我们的开源软件对你有所帮助, 请加入我们、与我们同行 。 作为码云GVP项目,GOSP项目的知识产权归全体贡献者共有,贡献者根据自身对项目的不同贡献而享有不等比例的产权。
任何的合作或者建议均可发送邮件至 lei@giveda.com
硬件开发
2020-05-18 11:51:00
一、前言
在做各种各样的项目中,难免遇到需要设定自己界面风格样式的时候,而Qt提供的qss就是牛逼的为了实现定制各种各样的皮肤的,其实一个完美的UI界面,主要由两大块组成,一个是颜色搭配,一个是布局,这两个缺一不可,一般是交给专业的美工妹妹来做,大部分程序员审美不是很好,所以在没有美工妹妹的时候,需要去一些专业的UI界面网站寻找灵感。关于颜色搭配和布局这两点,很多人有个误区,以为只需要qss放进去就行,其实不行的,如果按钮文本框放的难看,一样看起来也是碍眼,还需要慢慢调整各种人机交互的控件的位置才行。
之前用Qt做过一个皮肤生成器,可以用它来快速制作皮肤,最快的时候一套皮肤只需要58秒钟,用皮肤生成器制作皮肤,基本上不超过一分钟就可以生成一套自己想要的皮肤,只要设置八种颜色即可。本人非常喜欢这套黑色风格样式皮肤,特意分享出来,其中还有其他两套皮肤,一套是淡蓝色风格,一套是白色扁平风格,欢迎各位拿去随意使用。
二、代码思路 //使用方法 void frmMain::initStyle() { //加载样式表 //QFile file(":/qss/psblack.css"); //QFile file(":/qss/flatwhite.css"); QFile file(":/qss/lightblue.css"); if (file.open(QFile::ReadOnly)) { QString qss = QLatin1String(file.readAll()); QString paletteColor = qss.mid(20, 7); qApp->setPalette(QPalette(QColor(paletteColor))); qApp->setStyleSheet(qss); file.close(); } }
三、效果图
四、开源主页
以上作品完整源码下载都在开源主页,会持续不断更新作品数量和质量,欢迎各位关注。 国内站点: https://gitee.com/feiyangqingyun/QWidgetDemo 国际站点: https://github.com/feiyangqingyun/QWidgetDemo 个人主页: https://blog.csdn.net/feiyangqingyun 知乎主页: https://www.zhihu.com/people/feiyangqingyun/
硬件开发
2020-05-18 08:58:07
一、前言
磁盘容量统计控件,说白了,就是用来统计本地盘符占用的容量,包括但不限于已用空间、剩余空间、总大小、已用百分比等,其中对应的百分比采用进度条显示,该进度条的前景色和背景色及文字颜色可以设置,在整体换肤的时候就需要用到。 本控件的基本上没有难点可言,就是兼容WIN和LINUX操作系统,在WIN上采用winapi去读取,linux采用QProcess去执行对应的命令(df -h)获取结果,然后定时器执行,关联信号槽获取返回的额数据解析即可,控件的应用场景主要是在一些嵌入式设备上面,方便用户查看当前还剩余多少空间。
主要功能: 可自动加载本地存储设备的总容量/已用容量 进度条显示已用容量 支持所有操作系统 增加U盘或者SD卡到达信号
二、代码思路 void DeviceSizeTable::load() { //清空原有数据 int row = this->rowCount(); for (int i = 0; i < row; i++) { this->removeRow(0); } #ifdef Q_OS_WIN QFileInfoList list = QDir::drives(); foreach (QFileInfo dir, list) { QString dirName = dir.absolutePath(); LPCWSTR lpcwstrDriver = (LPCWSTR)dirName.utf16(); ULARGE_INTEGER liFreeBytesAvailable, liTotalBytes, liTotalFreeBytes; if (GetDiskFreeSpaceEx(lpcwstrDriver, &liFreeBytesAvailable, &liTotalBytes, &liTotalFreeBytes)) { QString use = QString::number((double)(liTotalBytes.QuadPart - liTotalFreeBytes.QuadPart) / GB, 'f', 1); use += "G"; QString free = QString::number((double) liTotalFreeBytes.QuadPart / GB, 'f', 1); free += "G"; QString all = QString::number((double) liTotalBytes.QuadPart / GB, 'f', 1); all += "G"; int percent = 100 - ((double)liTotalFreeBytes.QuadPart / liTotalBytes.QuadPart) * 100; insertSize(dirName, use, free, all, percent); } } #else process->start("df -h"); #endif } void DeviceSizeTable::readData() { while (!process->atEnd()) { QString result = QLatin1String(process->readLine()); #ifdef __arm__ if (result.startsWith("/dev/root")) { checkSize(result, "本地存储"); } else if (result.startsWith("/dev/mmcblk")) { checkSize(result, "本地存储"); } else if (result.startsWith("/dev/mmcblk1p")) { checkSize(result, "SD卡"); QStringList list = result.split(" "); emit sdcardReceive(list.at(0)); } else if (result.startsWith("/dev/sd")) { checkSize(result, "U盘"); QStringList list = result.split(" "); emit udiskReceive(list.at(0)); } #else if (result.startsWith("/dev/sd")) { checkSize(result, ""); QStringList list = result.split(" "); emit udiskReceive(list.at(0)); } #endif } } void DeviceSizeTable::checkSize(const QString &result, const QString &name) { QString dev, use, free, all; int percent = 0; QStringList list = result.split(" "); int index = 0; for (int i = 0; i < list.count(); i++) { QString s = list.at(i).trimmed(); if (s == "") { continue; } index++; if (index == 1) { dev = s; } else if (index == 2) { all = s; } else if (index == 3) { use = s; } else if (index == 4) { free = s; } else if (index == 5) { percent = s.left(s.length() - 1).toInt(); break; } } if (name.length() > 0) { dev = name; } insertSize(dev, use, free, all, percent); } void DeviceSizeTable::insertSize(const QString &name, const QString &use, const QString &free, const QString &all, int percent) { int row = this->rowCount(); this->insertRow(row); QTableWidgetItem *itemname = new QTableWidgetItem(name); QTableWidgetItem *itemuse = new QTableWidgetItem(use); itemuse->setTextAlignment(Qt::AlignCenter); QTableWidgetItem *itemfree = new QTableWidgetItem(free); itemfree->setTextAlignment(Qt::AlignCenter); QTableWidgetItem *itemall = new QTableWidgetItem(all); itemall->setTextAlignment(Qt::AlignCenter); this->setItem(row, 0, itemname); this->setItem(row, 1, itemuse); this->setItem(row, 2, itemfree); this->setItem(row, 3, itemall); QProgressBar *bar = new QProgressBar; bar->setRange(0, 100); bar->setValue(percent); QString qss = QString("QProgressBar{background:%1;border-width:0px;border-radius:0px;text-align:center;}" "QProgressBar::chunk{border-radius:0px;}").arg(bgColor.name()); if (percent < 50) { qss += QString("QProgressBar{color:%1;}QProgressBar::chunk{background:%2;}").arg(textColor1.name()).arg(chunkColor1.name()); } else if (percent < 90) { qss += QString("QProgressBar{color:%1;}QProgressBar::chunk{background:%2;}").arg(textColor2.name()).arg(chunkColor2.name()); } else { qss += QString("QProgressBar{color:%1;}QProgressBar::chunk{background:%2;}").arg(textColor3.name()).arg(chunkColor3.name()); } bar->setStyleSheet(qss); this->setCellWidget(row, 4, bar); }
三、效果图
四、开源主页
以上作品完整源码下载都在开源主页,会持续不断更新作品数量和质量,欢迎各位关注。 国内站点: https://gitee.com/feiyangqingyun/QWidgetDemo 国际站点: https://github.com/feiyangqingyun/QWidgetDemo 个人主页: https://blog.csdn.net/feiyangqingyun 知乎主页: https://www.zhihu.com/people/feiyangqingyun/
硬件开发
2020-05-17 10:51:00
一、前言
在平时的写作过程中,经常需要将一些操作动作和效果图截图成gif格式,使得涵盖的信息更全面更生动,有时候可以将整个操作过程和运行效果录制成MP4,但是文件体积比较大,而且很多网站不便于上传,基本上都支持gif动图,一般一个5秒左右的gif,800*600分辨率,可以很好的控制在500KB内,这样就比较完美的支持各大网站上传动图。 最开始使用的是ScreenGif.exe,用了很久,感觉还可以,后面一个朋友推荐用LICEcap.exe,体积更小,压缩比更高,再到后来发现有个gif.h开源的类,调用其中的方法可以实现将多张图片合并到一张gif中去,而且还是跨平台的,本人亲自在WIN+UBUNTU测试成功。 最初的代码是倪大侠给的,我在此基础上重新完善了下,使得可以直接拖动窗体大小来改变录屏区域的大小。增加了对Qt4和其他编译器的支持。
主要功能: 可设置要录制屏幕的宽高,支持右下角直接拉动改变. 可设置变宽的宽度 可设置录屏控件的背景颜色 可设置录制的帧数 录制区域可自由拖动选择
二、代码思路 void GifWidget::saveImage() { if (!gifWriter) { return; } #if (QT_VERSION <= QT_VERSION_CHECK(5,0,0)) //由于qt4没有RGBA8888,采用最接近RGBA8888的是ARGB32,颜色会有点偏差 QPixmap pix = QPixmap::grabWindow(0, x() + rectGif.x(), y() + rectGif.y(), rectGif.width(), rectGif.height()); QImage image = pix.toImage().convertToFormat(QImage::Format_ARGB32); #else QScreen *screen = QApplication::primaryScreen(); QPixmap pix = screen->grabWindow(0, x() + rectGif.x(), y() + rectGif.y(), rectGif.width(), rectGif.height()); QImage image = pix.toImage().convertToFormat(QImage::Format_RGBA8888); #endif gif.GifWriteFrame(gifWriter, image.bits(), rectGif.width(), rectGif.height(), fps); count++; labStatus->setText(QString("正在录制 第 %1 帧").arg(count)); } void GifWidget::record() { if (btnStart->text() == "开始") { if (0 != gifWriter) { delete gifWriter; gifWriter = 0; } //先弹出文件保存对话框 //fileName = qApp->applicationDirPath() + "/" + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss.gif"); fileName = QFileDialog::getSaveFileName(this, "选择保存位置", qApp->applicationDirPath() + "/", "gif图片(*.gif)"); if (fileName.isEmpty()) { return; } int width = txtWidth->text().toInt(); int height = txtHeight->text().toInt(); fps = txtFps->text().toInt(); gifWriter = new Gif::GifWriter; bool bOk = gif.GifBegin(gifWriter, fileName.toLocal8Bit().data(), width, height, fps); if (!bOk) { delete gifWriter; gifWriter = 0; return; } count = 0; labStatus->setText("开始录制..."); btnStart->setText("停止"); //延时启动 timer->setInterval(1000 / fps); QTimer::singleShot(1000, timer, SLOT(start())); //saveImage(); } else { timer->stop(); gif.GifEnd(gifWriter); delete gifWriter; gifWriter = 0; labStatus->setText(QString("录制完成 共 %1 帧").arg(count)); btnStart->setText("开始"); QDesktopServices::openUrl(QUrl(fileName)); } }
三、效果图
四、开源主页
以上作品完整源码下载都在开源主页,会持续不断更新作品数量和质量,欢迎各位关注。 国内站点: https://gitee.com/feiyangqingyun/QWidgetDemo 国际站点: https://github.com/feiyangqingyun/QWidgetDemo 个人主页: https://blog.csdn.net/feiyangqingyun 知乎主页: https://www.zhihu.com/people/feiyangqingyun/
硬件开发
2020-05-16 09:41:00
垃圾分类(英文名为 Garbage classification )回收站,一般是指按一定规定或标准将垃圾分类储存、分类投放和分类搬运,从而转变成公共资源的一系列活动的总称。分类的目的是提高垃圾的资源价值和经济价值,力争物尽其用。
垃圾在分类储存阶段属于公众的私有品,垃圾经公众分类投放后成为公众所在小区或社区的区域性准公共资源,垃圾分类搬运到垃圾集中点或转运站后成为没有排除性的公共资源。从国内外各城市对生活垃圾分类的方法来看,大致都是根据垃圾的成分、产生量,结合本地垃圾的资源利用和处理方式来进行分类的。
进行垃圾分类收集可以减少垃圾处理量和处理设备,降低处理成本,减少土地资源的消耗,具有社会、经济、生态三方面的效益。
每个人每天都会扔出许多垃圾,在一些垃圾管理较好的地区,大部分垃圾会得到卫生填埋、焚烧、堆肥等无害化处理,而更多地方的垃圾则常常被简易堆放或填埋,导致臭气蔓延,并且污染土壤和地下水体。
垃圾无害化处理的费用是非常高的,根据处理方式的不同,处理一吨垃圾的费用约为一百元至几百元不等。人们大量地消耗资源,大规模生产,大量地消费,又大量地生产着垃圾。后果将不堪设想。
从国外各城市对生活垃圾分类的方法来看,大致都是根据垃圾的成分构成、产生量,结合本地垃圾的资源利用和处理方式来进行分类。如德国一般分为纸、玻璃、金属和塑料等;澳大利亚一般分为可堆肥垃圾,可回收垃圾,不可回收垃圾;日本一般分为塑料瓶类,可回收塑料,其他塑料,资源垃圾,大型垃圾,可燃垃圾,不可燃垃圾,有害垃圾等等。
垃圾分类是对垃圾收集处置传统方式的改革,是对垃圾进行有效处置的一种科学管理方法。人们面对日益增长的垃圾产量和环境状况恶化的局面,如何通过垃圾分类管理,最大限度地实现垃圾资源利用,减少垃圾处置的数量,改善生存环境状态,是当前世界各国共同关注的迫切问题。
推荐一款应用在垃圾回收站内火灾报警器中的一氧化碳传感器,由工采网从国外引进的纽扣式一氧化碳传感器 - TGS5141,TGS5141 一氧化碳传感器 CO 传感器是费加罗研发的可电池驱动的电化学式传感器,使用一个特殊的电极取代了储水器,由于去除了 TGS5042 中使用的储水器, TGS5141 与 TGS5042 相比,其外形尺寸缩减到只有后者的 10% 大小。超小型的体积使其可以成为诸如便携式一氧化碳检测仪、微型住宅一氧化碳检测仪、多用途火灾检测仪的理想选择 。 OEM 客户会发现,通过每个传感器的条形码,可以单独打印每个传感器的数据,使用户可以避免昂贵的气体校准程序,还允许对个别传感器进行追踪。
硬件开发
2020-05-16 09:03:00
摘要: 本文主要介绍物联网中目前较为常用的几款开发板,为大家带来不同开发板在功能和使用上的一些差异性功能。
1 引言
众所周知开发板是物联网架构中的感知层智能设备,这类设备通常有芯片、通信模组、以及操作系统组成。当然不同的开发版在功能以及使用上可能存在着一定的差别,下面为大家介绍几款常用的物联网开发板。
2 常用开发板
2.1 Arduino开发板
2.1.1 简介
Arduino开发板本着让用户简单使用而且开源的设计理念,具有灵活便捷、入手门槛低、同时具有丰富接口、多功能、易扩展等优点,因此在电子设计领域应用较广。目前市场上Arduino开发板的种类非常多,如Arduino UNO/UNO R3,Arduino101/Intel Curie,Arduino Micro,Arduino Ethernet,Intel Galileo 开发板等。该类开发板上手极快,操作简单,价格相对要便宜一点。
2.1.2独特优势 开放性,Arduino是起步较早的开源硬件项目,它的硬件电路和软件开发环境是完全公开的。 易用性:简单易用,无需安装额外驱动,采用类C语言,主函数中只涉及setup和loop连个模块。 易交流:Arduino已经划定了一个比较统一的框架,一些底层的初始化采用了统一的方法,对数字信号和模拟信号使用的端口也做了自己的标定,初学者在交流电路或程序时非常方便。
2.1.3 开发板性能分析
对于上述几款开发板的性能进行了简单的对比,Arduino UNO,该类开发板最为普通,功能较少,如无法实现插入SD卡进行扩展等,但时价格最便宜,仅需十几元;Arduino101/Intel Curie该开发板比较容易出现复位问题,价格较高,但是运算性能强大,对于Arduino Micro开发板,其底层编写容易出问题,但是尺寸最小,可做模拟键鼠;Arduino Ethernet 专注以太网,接口加网线长距离稳定,加tf卡可以储存更多资源。初始化速度比较慢,影响开发速度。Intel Galileo Gen 2代是一款功能比较强大的软件,下面对其简要介绍,Intel Galileo Gen 2代开发板兼容Arduino,具有一个32位处理器,可以将信息保存到SD卡进行记录,实现扩容,可与互联网收集的数据实现通信,具有USB主机连接器,可以使用网络摄像头,同时具有以太网接口,可以实现网线连接搭建局域网,同时具有低功耗的特点。工作电压为7-15V。具有14个数据引脚0-13(其中3、5、6、9、10、11可用于PWM输出),5个模拟引脚包括A0-A5,同时还具有其他接口。
2.2 小熊派开发板
与传统传感器不同的是,小熊派开发板没有板载传感器设计,为了满足不同的开发需求,该开发板采用可更换传感器扩展板设计,芯片指的是设备的主控芯片,该开发板搭载了一块低功耗的STM32L4单片机,通信模组 是数据传输的出入口,本开发板支持多种通信模组,采用可更换通信扩展板设计,包括WiFi,NBiot,2G ,4G等,liteos轻量级系统,支持串口,8MSPi flash,支持TF卡存储运行数据,240的分辨率lcd,显示传感器数据以及调试日志,AT-PC切换开关,电脑通过串口助手进行调试通信模组,右侧MCU模式,单片机MCU通过At指令与通信模组交互将传感器数据传输到云端。

为了便于开发调试,如图所示,该开发板板载了2.1版本的ST-Link,它具有在线调试烧录,拖拽下载以及虚拟串口等功能。开发板左上角内置了一个TF卡卡槽,TF卡可以存储运行数据,其左侧有一个8M的SPI Flash,使用其方便对程序进行远程升级。开发板中间板载一块分辨率为240 * 240的LCD屏幕,其主要用于显示传感器数据以及调试日志。LCD屏幕下方是一个基于STM32L431的超低功耗单片机作为开发板的主控芯片。开发板右上角具有一个拨码开关,将其拨至左侧AT-PC模式,通过电脑端的串口助手,发送AT指令调试通信模组。右边AT-MCU模式,通过MCU发送AT指令与通信模组进行交互,将采集到的传感器数据通过通信模组发送到云端。同时小熊派IoT开发板在主板上引出21个IO口,具备IIC、SPI、USART、ADC、DAC等功能,可最大程度地满足开发者的自主开发需求。
主要参数如下表:
2.3 STM32F429野火开发板
​STM32F429野火挑战者开发板,共有两个版本,二者存在一定差异,挑战者开发板是主控芯片是Cortex-M4系列, STM32F429IGTx,具有 1MB Flash,192kB SRAM+64CCM RAM,系统时钟达到 180MHz。板载 SDRAM:64M. SPI FLASH: 16M。EEPROM : 256 B。SD 卡:Micro SD卡接口,最大支持 32G 容量。
2.3.1 供电方式
5V 供电: 2个 MicroUSB 接口。
6-12V 供电:DC电源接口,可接 6-12V电源适配器。
2.3.2通讯方式
该开发板主要包括以太网接口,232串口通讯(带DB9母接口),USB转串口通讯,带Mini USB接口。USB主设备读取U盘文件,USB从设备,模拟U盘,CAN通讯、485通讯,皆带接线端子。 红外通讯:红外接收头接口。蓝牙串口。WIFI: EMW1062,可实现 wifi传输视频,板载天线。音频输出接口。
2.3.3 其他硬件
LCD:支持5寸屏,分辨率为800*480.
LED灯:1个,1个RGB全彩灯。
按键:3个实体按键,1个电容按键。
程序下载接口: JTAG接口、 SWD 接口:支持 DAP、JLink、ULink、STLink 下载器。
传感器:温湿度传感器、六轴重力加速度传感器、摄像头、光敏电阻。

讲了这么多,不知道大家对于这些开发板是否有一个大概的了解。希望大家根据自己的爱好和需要,选择合适的开发板产品,而不是盲目跟风。毕竟“只有最合适的,没有最好的”!

点击关注,第一时间了解华为云新鲜技术~
硬件开发
2020-05-15 17:12:00
一、前言
代码行数统计主要用来统计项目中的所有文件的代码行数,其中包括空行、注释行、代码行,可以指定过滤拓展名,比如只想统计.cpp的文件,也可以指定文件或者指定目录进行统计。写完这个工具第一件事情就是统计了一下自己写过的最大的项目大概多少行代码,看下是不是传说中的一行代码一块钱,这个最大的项目从2010年开始的,到现在差不多快10年了,是自己在现在公司写过的最大的项目,一直在升级更新完善,途中重构过两次,大的结构改动,统计了下好像有15W行左右的代码,纯代码大概在10W,其余是空行和注释行,着实把自己吓了一跳,还算是中型项目了,然后又统计了下自定义控件的所有代码,我勒个去,总代码23W行,纯代码17W行呢,哎呀我去!
主要功能: 可分别统计代码行/空行/注释行 支持指定过滤拓展名 支持指定文件或者指定目录进行统计 分步显示统计结果,不卡主界面 分别展示每个统计过的文件的大小/总行数/代码行数等
二、代码思路 void frmCountCode::countCode(const QString &filePath) { QDir dir(filePath); foreach (QFileInfo fileInfo , dir.entryInfoList()) { if (fileInfo.isFile()) { QString strFileName = fileInfo.fileName(); if (checkFile(strFileName)) { listFile << fileInfo.filePath(); } } else { if(fileInfo.fileName() == "." || fileInfo.fileName() == "..") { continue; } //递归找出文件 countCode(fileInfo.absoluteFilePath()); } } } void frmCountCode::countCode(const QStringList &files) { int lineCode; int lineBlank; int lineNotes; int count = files.count(); on_btnClear_clicked(); ui->tableWidget->setRowCount(count); quint32 totalLines = 0; quint32 totalBytes = 0; quint32 totalCodes = 0; quint32 totalNotes = 0; quint32 totalBlanks = 0; for (int i = 0; i < count; i++) { QFileInfo fileInfo(files.at(i)); countCode(fileInfo.filePath(), lineCode, lineBlank, lineNotes); int lineAll = lineCode + lineBlank + lineNotes; QTableWidgetItem *itemName = new QTableWidgetItem; itemName->setText(fileInfo.fileName()); QTableWidgetItem *itemSuffix = new QTableWidgetItem; itemSuffix->setText(fileInfo.suffix()); QTableWidgetItem *itemSize = new QTableWidgetItem; itemSize->setText(QString::number(fileInfo.size())); QTableWidgetItem *itemLine = new QTableWidgetItem; itemLine->setText(QString::number(lineAll)); QTableWidgetItem *itemCode = new QTableWidgetItem; itemCode->setText(QString::number(lineCode)); QTableWidgetItem *itemNote = new QTableWidgetItem; itemNote->setText(QString::number(lineNotes)); QTableWidgetItem *itemBlank = new QTableWidgetItem; itemBlank->setText(QString::number(lineBlank)); QTableWidgetItem *itemPath = new QTableWidgetItem; itemPath->setText(fileInfo.filePath()); itemSuffix->setTextAlignment(Qt::AlignCenter); itemSize->setTextAlignment(Qt::AlignCenter); itemLine->setTextAlignment(Qt::AlignCenter); itemCode->setTextAlignment(Qt::AlignCenter); itemNote->setTextAlignment(Qt::AlignCenter); itemBlank->setTextAlignment(Qt::AlignCenter); ui->tableWidget->setItem(i, 0, itemName); ui->tableWidget->setItem(i, 1, itemSuffix); ui->tableWidget->setItem(i, 2, itemSize); ui->tableWidget->setItem(i, 3, itemLine); ui->tableWidget->setItem(i, 4, itemCode); ui->tableWidget->setItem(i, 5, itemNote); ui->tableWidget->setItem(i, 6, itemBlank); ui->tableWidget->setItem(i, 7, itemPath); totalBytes += fileInfo.size(); totalLines += lineAll; totalCodes += lineCode; totalNotes += lineNotes; totalBlanks += lineBlank; if (i % 100 == 0) { qApp->processEvents(); } } //显示统计结果 listFile.clear(); ui->txtCount->setText(QString::number(count)); ui->txtSize->setText(QString::number(totalBytes)); ui->txtRow->setText(QString::number(totalLines)); ui->txtCode->setText(QString::number(totalCodes)); ui->txtNote->setText(QString::number(totalNotes)); ui->txtBlank->setText(QString::number(totalBlanks)); //计算百分比 double percent = 0.0; //代码行所占百分比 percent = ((double)totalCodes / totalLines) * 100; ui->labPercentCode->setText(QString("%1%").arg(percent, 5, 'f', 2, QChar(' '))); //注释行所占百分比 percent = ((double)totalNotes / totalLines) * 100; ui->labPercentNote->setText(QString("%1%").arg(percent, 5, 'f', 2, QChar(' '))); //空行所占百分比 percent = ((double)totalBlanks / totalLines) * 100; ui->labPercentBlank->setText(QString("%1%").arg(percent, 5, 'f', 2, QChar(' '))); } void frmCountCode::countCode(const QString &fileName, int &lineCode, int &lineBlank, int &lineNotes) { lineCode = lineBlank = lineNotes = 0; QFile file(fileName); if (file.open(QFile::ReadOnly)) { QTextStream out(&file); QString line; bool isNote = false; while (!out.atEnd()) { line = out.readLine(); //移除前面的空行 if (line.startsWith(" ")) { line.remove(" "); } //判断当前行是否是注释 if (line.startsWith("/*")) { isNote = true; } //注释部分 if (isNote) { lineNotes++; } else { if (line.startsWith("//")) { //注释行 lineNotes++; } else if (line.isEmpty()) { //空白行 lineBlank++; } else { //代码行 lineCode++; } } //注释结束 if (line.endsWith("*/")) { isNote = false; } } } }
三、效果图
四、开源主页
以上作品完整源码下载都在开源主页,会持续不断更新作品数量和质量,欢迎各位关注。 国内站点: https://gitee.com/feiyangqingyun/QWidgetDemo 国际站点: https://github.com/feiyangqingyun/QWidgetDemo 个人主页: https://blog.csdn.net/feiyangqingyun 知乎主页: https://www.zhihu.com/people/feiyangqingyun/
硬件开发
2020-05-15 09:38:00
当公司投资物联网时,首先要问的一个问题是,在物联网项目中,需要哪些关键人员? 以下是在2020年打造物联网梦想团队所需的7个关键参与者。 1、解决方案架构师 经验丰富的解决方案架构师是游戏规则的改变者。他或她是首席工程师,他或她将规划系统,协调整个技术堆栈的工作,并根据需要把没用的东西清理出去。 每个出色的物联网项目都始于可靠的设计,您的架构师是实现此目标的领导者。 2、软件工程师 软件是物联网的重要组成部分。数据模型、规则引擎、分析、连接协议、服务器基础设施和应用程序开发都随着软件开发而实现。在这个领域,关键是进行全堆栈开发,并着眼于在不同系统之间交换数据。 软件工程师致力于让您的物理产品成为智能的互联产品。通过连接到云,公司可以收集有价值的数据,并通过桌面和移动应用程序在控制面板中显示这些数据。 3、数据工程师 物联网就是利用数据的价值。它可以很简单,例如,知道产品的状态并发送软件更新以进行改进,或者是更高级的功能,比如自动驾驶汽车对物体检测做出反应。 这些场景的背后是数据大师,他建立了逻辑、规则和算法来实现这一点。 如果您想拥有一个强大且高收益的商业模式,请雇佣顶尖的数据工程师来挖掘其价值。 4、嵌入式工程师 据羿戓信息所了解,嵌入式工程师构建可在设备上运行的代理和其他软件。这些边缘软件系统可以收集数据,并准备好进行传输,并且在许多情况下还会进行本地处理(例如分析)。 只有当您必须在产品的传感器和为物联网应用程序收集数据的云平台之间建立软件桥梁时,才需要嵌入式工程师。 5、IT管理员 找到经验丰富的IT管理员对于确保业务的可靠性和安全性非常重要。 维护公司的IT网络、服务器和安全系统绝非易事。利用现有员工组建物联网团队的公司会发现,IT管理员扮演着非常重要的角色。 需要记住的一点是,IT管理员可以在构建和运行物联网应用中发挥作用。在构建方面,他们负责安装、设置基础设施(服务器、数据存储)、权限和控制以及安全性。(来自物联之家网)而在运行方面,他们将专注于授予平台访问权限、维护软件更新、扩展解决方案,以及使系统保持最佳状态的任何类型服务。 6、UI/UX设计师 UI/UX设计师在产品开发和产品设计的生命周期中扮演着重要角色。在开发的后期阶段,他们继续通过设计界面给团队带来价值,从而使产品焕发生机。 7、机电工程师 如果您正在开发新的智能产品,机电工程师是必不可少的。未来的智能设备需要电气和机械工程方面的专业知识。 除了这些宝贵的技能之外,机电工程师还应该了解无线网络、计算机和通信网络以及不同的编程语言。 随着新产品开发包括软件和硬件功能,机械和电气工程师正朝着物联网方向发展。 总结 构建端到端解决方案需要不同技能的专业人士参与。您的任务应该是通过分享物联网的爆发式增长、在前沿领域工作的机会,以及展示这是一个挑战自我和培养新技能的好地方,从而吸引他们加入团队。 当您进入这个领域,在组建物联网梦想团队时,请牢记这些核心参与者。 推荐阅读: 自助建站不利于优化原因在哪里
硬件开发
2020-05-14 16:10:00
一、前言
对于现在做前端开发人员来说,FlatUI肯定不陌生,最近几年扁平化的设计越来越流行,大概由于现在PC端和移动端的设备的分辨率越来越高,扁平化反而看起来更让人愉悦,而通过渐变色产生的质感色彩反而没有扁平化来得亲切。
Flat UI是基于Bootstrap之上进行二次开发的扁平化前端框架,他提供了动感、时尚的风格色调搭配,简洁、炫丽的功能组件,同时还提供了更为平滑的js交互动画,可以称得上前端扁平化设计框架的优秀代表之一。
既然是扁平化设计框架的优秀代表,当然需要在自己项目中应用应用,本人最早使用VB开发,而后转为C#开发,最后转为Qt开发,都是因为公司项目需要,根据需要不断学习新的编程框架,语言都是相通的,举一反三,以前用C#写的vista时钟控件和vista日历控件,稍微改改就转移成了Qt写的对应控件,非常方便,只要掌握了思想,熟练了一门语言和框架之后,其他的学起来特别快。
Qt中的qss机制,和css极为相似,感觉就是脱胎于css,用qss来实现Qt界面样式不是一般的方便,而是相当的爽,在看到FlatUI这样的精美的扁平化设计样式后,难以抑制手痒痒,就想用qss实现类似的风格。
二、代码思路 QString FlatUI::setPushButtonQss(QPushButton *btn, int radius, int padding, const QString &normalColor, const QString &normalTextColor, const QString &hoverColor, const QString &hoverTextColor, const QString &pressedColor, const QString &pressedTextColor) { QStringList list; list.append(QString("QPushButton{border-style:none;padding:%1px;border-radius:%2px;color:%3;background:%4;}") .arg(padding).arg(radius).arg(normalTextColor).arg(normalColor)); list.append(QString("QPushButton:hover{color:%1;background:%2;}") .arg(hoverTextColor).arg(hoverColor)); list.append(QString("QPushButton:pressed{color:%1;background:%2;}") .arg(pressedTextColor).arg(pressedColor)); QString qss = list.join(""); btn->setStyleSheet(qss); return qss; } QString FlatUI::setLineEditQss(QLineEdit *txt, int radius, int borderWidth, const QString &normalColor, const QString &focusColor) { QStringList list; list.append(QString("QLineEdit{border-style:none;padding:3px;border-radius:%1px;border:%2px solid %3;}") .arg(radius).arg(borderWidth).arg(normalColor)); list.append(QString("QLineEdit:focus{border:%1px solid %2;}") .arg(borderWidth).arg(focusColor)); QString qss = list.join(""); txt->setStyleSheet(qss); return qss; } QString FlatUI::setProgressQss(QProgressBar *bar, int barHeight, int barRadius, int fontSize, const QString &normalColor, const QString &chunkColor) { QStringList list; list.append(QString("QProgressBar{font:%1pt;background:%2;max-height:%3px;border-radius:%4px;text-align:center;border:1px solid %2;}") .arg(fontSize).arg(normalColor).arg(barHeight).arg(barRadius)); list.append(QString("QProgressBar:chunk{border-radius:%2px;background-color:%1;}") .arg(chunkColor).arg(barRadius)); QString qss = list.join(""); bar->setStyleSheet(qss); return qss; } QString FlatUI::setSliderQss(QSlider *slider, int sliderHeight, const QString &normalColor, const QString &grooveColor, const QString &handleBorderColor, const QString &handleColor) { int sliderRadius = sliderHeight / 2; int handleWidth = (sliderHeight * 3) / 2 + (sliderHeight / 5); int handleRadius = handleWidth / 2; int handleOffset = handleRadius / 2; QStringList list; list.append(QString("QSlider::horizontal{min-height:%1px;}").arg(sliderHeight * 2)); list.append(QString("QSlider::groove:horizontal{background:%1;height:%2px;border-radius:%3px;}") .arg(normalColor).arg(sliderHeight).arg(sliderRadius)); list.append(QString("QSlider::add-page:horizontal{background:%1;height:%2px;border-radius:%3px;}") .arg(normalColor).arg(sliderHeight).arg(sliderRadius)); list.append(QString("QSlider::sub-page:horizontal{background:%1;height:%2px;border-radius:%3px;}") .arg(grooveColor).arg(sliderHeight).arg(sliderRadius)); list.append(QString("QSlider::handle:horizontal{width:%3px;margin-top:-%4px;margin-bottom:-%4px;border-radius:%5px;" "background:qradialgradient(spread:pad,cx:0.5,cy:0.5,radius:0.5,fx:0.5,fy:0.5,stop:0.6 %1,stop:0.8 %2);}") .arg(handleColor).arg(handleBorderColor).arg(handleWidth).arg(handleOffset).arg(handleRadius)); //偏移一个像素 handleWidth = handleWidth + 1; list.append(QString("QSlider::vertical{min-width:%1px;}").arg(sliderHeight * 2)); list.append(QString("QSlider::groove:vertical{background:%1;width:%2px;border-radius:%3px;}") .arg(normalColor).arg(sliderHeight).arg(sliderRadius)); list.append(QString("QSlider::add-page:vertical{background:%1;width:%2px;border-radius:%3px;}") .arg(grooveColor).arg(sliderHeight).arg(sliderRadius)); list.append(QString("QSlider::sub-page:vertical{background:%1;width:%2px;border-radius:%3px;}") .arg(normalColor).arg(sliderHeight).arg(sliderRadius)); list.append(QString("QSlider::handle:vertical{height:%3px;margin-left:-%4px;margin-right:-%4px;border-radius:%5px;" "background:qradialgradient(spread:pad,cx:0.5,cy:0.5,radius:0.5,fx:0.5,fy:0.5,stop:0.6 %1,stop:0.8 %2);}") .arg(handleColor).arg(handleBorderColor).arg(handleWidth).arg(handleOffset).arg(handleRadius)); QString qss = list.join(""); slider->setStyleSheet(qss); return qss; } QString FlatUI::setRadioButtonQss(QRadioButton *rbtn, int indicatorRadius, const QString &normalColor, const QString &checkColor) { int indicatorWidth = indicatorRadius * 2; QStringList list; list.append(QString("QRadioButton::indicator{border-radius:%1px;width:%2px;height:%2px;}") .arg(indicatorRadius).arg(indicatorWidth)); list.append(QString("QRadioButton::indicator::unchecked{background:qradialgradient(spread:pad,cx:0.5,cy:0.5,radius:0.5,fx:0.5,fy:0.5," "stop:0.6 #FFFFFF,stop:0.7 %1);}").arg(normalColor)); list.append(QString("QRadioButton::indicator::checked{background:qradialgradient(spread:pad,cx:0.5,cy:0.5,radius:0.5,fx:0.5,fy:0.5," "stop:0 %1,stop:0.3 %1,stop:0.4 #FFFFFF,stop:0.6 #FFFFFF,stop:0.7 %1);}").arg(checkColor)); QString qss = list.join(""); rbtn->setStyleSheet(qss); return qss; } QString FlatUI::setScrollBarQss(QWidget *scroll, int radius, int min, int max, const QString &bgColor, const QString &handleNormalColor, const QString &handleHoverColor, const QString &handlePressedColor) { //滚动条离背景间隔 int padding = 0; QStringList list; //handle:指示器,滚动条拉动部分 add-page:滚动条拉动时增加的部分 sub-page:滚动条拉动时减少的部分 add-line:递增按钮 sub-line:递减按钮 //横向滚动条部分 list.append(QString("QScrollBar:horizontal{background:%1;padding:%2px;border-radius:%3px;min-height:%4px;max-height:%4px;}") .arg(bgColor).arg(padding).arg(radius).arg(max)); list.append(QString("QScrollBar::handle:horizontal{background:%1;min-width:%2px;border-radius:%3px;}") .arg(handleNormalColor).arg(min).arg(radius)); list.append(QString("QScrollBar::handle:horizontal:hover{background:%1;}") .arg(handleHoverColor)); list.append(QString("QScrollBar::handle:horizontal:pressed{background:%1;}") .arg(handlePressedColor)); list.append(QString("QScrollBar::add-page:horizontal{background:none;}")); list.append(QString("QScrollBar::sub-page:horizontal{background:none;}")); list.append(QString("QScrollBar::add-line:horizontal{background:none;}")); list.append(QString("QScrollBar::sub-line:horizontal{background:none;}")); //纵向滚动条部分 list.append(QString("QScrollBar:vertical{background:%1;padding:%2px;border-radius:%3px;min-width:%4px;max-width:%4px;}") .arg(bgColor).arg(padding).arg(radius).arg(max)); list.append(QString("QScrollBar::handle:vertical{background:%1;min-height:%2px;border-radius:%3px;}") .arg(handleNormalColor).arg(min).arg(radius)); list.append(QString("QScrollBar::handle:vertical:hover{background:%1;}") .arg(handleHoverColor)); list.append(QString("QScrollBar::handle:vertical:pressed{background:%1;}") .arg(handlePressedColor)); list.append(QString("QScrollBar::add-page:vertical{background:none;}")); list.append(QString("QScrollBar::sub-page:vertical{background:none;}")); list.append(QString("QScrollBar::add-line:vertical{background:none;}")); list.append(QString("QScrollBar::sub-line:vertical{background:none;}")); QString qss = list.join(""); scroll->setStyleSheet(qss); return qss; }
三、效果图
四、开源主页
以上作品完整源码下载都在开源主页,会持续不断更新作品数量和质量,欢迎各位关注。 国内站点: https://gitee.com/feiyangqingyun/QWidgetDemo 国际站点: https://github.com/feiyangqingyun/QWidgetDemo 个人主页: https://blog.csdn.net/feiyangqingyun 知乎主页: https://www.zhihu.com/people/feiyangqingyun/
硬件开发
2020-05-14 09:40:00
Prometheus 是由 SoundCloud 开源监控告警解决方案,支持多维 数据模型 (时序由 metric 名字和 k/v 的 labels 构成),具备灵活的查询语句( PromQL ),支持多种数据采集 exporters ;支持告警管理,基于指标实现告警监控;支持多种统计数据模型,图形化展示友好,图形展示除了内置的浏览器,也支持 Grafana 集成。
物联网 MQTT 服务器 EMQ X 提供 emqx_statsd 插件,用于将 EMQ X 运行指标及 Erlang 虚拟机状态数据输出到第三方的监控系统如 Prometheus 中。通过 Prometheus 自带的 node-exporter 还可以采集 Linux 服务器相关指标,实现服务器 + EMQ X 整体运维监控。
本文提供了 Prometheus + Grafana 整套 EMQ X 运维监控方案搭建过程。
安装与准备
Docker 镜像下载 # Docker 镜像包下载 docker pull prom/node-exporter docker pull prom/prometheus docker pull prom/pushgateway
启动 node-exporter
可选,用于收集服务器指标如 CPU、内存、网络等,如果使用 Docker 安装则需要映射目标服务器响应的状态文件: docker run -d -p 9100:9100 -v "/proc:/host/proc:ro" -v "/sys:/host/sys:ro" -v "/:/rootfs:ro" --net="host" prom/node-exporter
启动 pushgateway
pushgateway 用于接收 EMQ X 指标推送数据, 需要保证 EMQ X 能够访问 : docker run -d -p 9091:9091 prom/pushgateway
启动 Prometheus
指定配置文件与监听端口以启动 Prometheus: # 指定配置文件并启动 docker run -p 9090:9090 -v $PWD/prometheus.yaml:/etc/prometheus/prometheus.yaml -d prom/prometheus --config.file=/etc/prometheus/prometheus.yaml
Prometheus 配置文件 prometheus.yaml 样例如下,详细含义请参考 Prometheus 文档 : # prometheus.yaml global: scrape_interval: 10s # 默认抓取时间 evaluation_interval: 10s # 每10秒评估一次rules # 在本机上每一条时间序列上都会默认产生的,主要可以用于联合查询、远程存储、Alertmanager时使用。 external_labels: monitor: 'emqx-monitor' # 加载规则,依据 evaluation_interval 来定期评估rule rule_files: # - "first.rules" # - "second.rules" - "/etc/prometheus/rules/*.rules" # 数据拉取配置 scrape_configs: # 表示在这个配置内的时间序例,每一条都会自动添加上这个{job_name:"prometheus"}的标签 - job_name: 'prometheus' scrape_interval: 5s static_configs: - targets: ['127.0.0.1:9090'] # 服务器物理机监控 - job_name: 'node-exporter' scrape_interval: 5s static_configs: # node-exporter 根据实际情况填写 - targets: ['192.168.6.11:9100'] labels: instance: wivwiv-local # EMQ X Pushgateway 监控 - job_name: 'pushgateway' scrape_interval: 5s honor_labels: true static_configs: # pushgateway 根据实际情况填写 - targets: ['192.168.6.11:9091']
启动 EMQ X statsd 插件
打开 etc/emqx_statsd.conf ,确认以下配置: ## pushgateway 地址 statsd.push.gateway.server = http://127.0.0.1:9091 ## 数据采集/推送周期(毫秒) statsd.interval = 15000
启动插件:
./bin/emqx_ctl load plugins emqx_statsd
效果查看
通过 docker ps -a 命令查看组件是否成功运行,等待数个推送周期后,打开 http://localhost:9090 Prometheus 控制面板查看采集数据。 Prometheus 仅提供简单图表数据展示,如需更精美的可视化展示请结合 Grafana 使用。
集成 Grafana
Grafana 是一个开源、通用的度量分析与可视化展示工具,通过数据源(如各类数据库、开源组件),展示自定义报表、显示图表等。
启动 Grafana
通过 Docker 拉取并启动 Grafana 镜像: docker run -d --name=grafana -p 3000:3000 grafana/grafana
启动成功后,浏览器访问 http://127.0.0.1:3000 打开 Dashboard 控制台。
配置 Prometheus 数据源
在 Grafana 中添加数据源,选择 Prometheus 并填写正确的地址完成数据源添加。
导入 Grafana 模板数据
emqx_statsd 插件提供了 Grafana 的 Dashboard 的模板文件,这些模板包含了大部分 EMQ X 监控数据的展示。用户可直接导入到 Grafana 中,用以显示 EMQ X 的监控状态的图标。
模板文件位于 emqx_statsd/grafana_template 中, 因 EMQ X 版本差异问题,可能存在部分图表数据显示错误的情况,请用户手动调整适配。
点击 Upload.json file 按钮,导入后选择对应的文件夹与数据源即可。
效果展示
完成整套系统搭建并运行一段时间后,Prometheus 收集到的数据将展示在 Grafana 上,默认模板展示效果如下: EMQ Dashboard:包含连接、消息、主题、吞吐量历史统计 EMQ:包含客户端数、订阅数、主题数、消息数、报文数等业务信息历史统计 ErlangVM:每个 EMQ X 节点 Erlang 虚拟机进程/线程数量,ETS/Mnesia 数据库使用情况历史统计
如有其他需求,可以参照 「附:emqx-statsd 所有指标」并结合 Grafana 进行图标数据编排展示。

告警管理
Prometheus 与 Grafana 均支持指标告警功能,配置告警规则后,服务器会不断评估设置的规则与当前指标数据,在规则条件符合的时候发送出通知。
篇幅有限,告警相关配置与实践请关注后续文章。
附:emqx-statsd 所有指标
EMQ X MQTT 服务器通过 Prometheus push gateway 推送指标数据,支持的指标项如下: # TYPE erlang_vm_ets_limit gauge erlang_vm_ets_limit 256000 # TYPE erlang_vm_logical_processors gauge erlang_vm_logical_processors 4 # TYPE erlang_vm_logical_processors_available gauge erlang_vm_logical_processors_available NaN # TYPE erlang_vm_logical_processors_online gauge erlang_vm_logical_processors_online 4 # TYPE erlang_vm_port_count gauge erlang_vm_port_count 16 # TYPE erlang_vm_port_limit gauge erlang_vm_port_limit 1048576 # TYPE erlang_vm_process_count gauge erlang_vm_process_count 320 # TYPE erlang_vm_process_limit gauge erlang_vm_process_limit 2097152 # TYPE erlang_vm_schedulers gauge erlang_vm_schedulers 4 # TYPE erlang_vm_schedulers_online gauge erlang_vm_schedulers_online 4 # TYPE erlang_vm_smp_support untyped erlang_vm_smp_support 1 # TYPE erlang_vm_threads untyped erlang_vm_threads 1 # TYPE erlang_vm_thread_pool_size gauge erlang_vm_thread_pool_size 4 # TYPE erlang_vm_time_correction untyped erlang_vm_time_correction 1 # TYPE erlang_vm_statistics_context_switches counter erlang_vm_statistics_context_switches 20767 # TYPE erlang_vm_statistics_garbage_collection_number_of_gcs counter erlang_vm_statistics_garbage_collection_number_of_gcs 3924 # TYPE erlang_vm_statistics_garbage_collection_words_reclaimed counter erlang_vm_statistics_garbage_collection_words_reclaimed 6751048 # TYPE erlang_vm_statistics_garbage_collection_bytes_reclaimed counter erlang_vm_statistics_garbage_collection_bytes_reclaimed 54008384 # TYPE erlang_vm_statistics_bytes_received_total counter erlang_vm_statistics_bytes_received_total 23332 # TYPE erlang_vm_statistics_bytes_output_total counter erlang_vm_statistics_bytes_output_total 21266 # TYPE erlang_vm_statistics_reductions_total counter erlang_vm_statistics_reductions_total 18413181 # TYPE erlang_vm_statistics_run_queues_length_total gauge erlang_vm_statistics_run_queues_length_total 0 # TYPE erlang_vm_statistics_runtime_milliseconds counter erlang_vm_statistics_runtime_milliseconds 1782 # TYPE erlang_vm_statistics_wallclock_time_milliseconds counter erlang_vm_statistics_wallclock_time_milliseconds 68277 # TYPE erlang_vm_memory_atom_bytes_total gauge erlang_vm_memory_atom_bytes_total{usage="used"} 1507142 erlang_vm_memory_atom_bytes_total{usage="free"} 18787 # TYPE erlang_vm_memory_bytes_total gauge erlang_vm_memory_bytes_total{kind="system"} 63949544 erlang_vm_memory_bytes_total{kind="processes"} 45457848 # TYPE erlang_vm_dets_tables gauge erlang_vm_dets_tables 0 # TYPE erlang_vm_ets_tables gauge erlang_vm_ets_tables 115 # TYPE erlang_vm_memory_processes_bytes_total gauge erlang_vm_memory_processes_bytes_total{usage="used"} 45457696 erlang_vm_memory_processes_bytes_total{usage="free"} 152 # TYPE erlang_vm_memory_system_bytes_total gauge erlang_vm_memory_system_bytes_total{usage="atom"} 1525929 erlang_vm_memory_system_bytes_total{usage="binary"} 104504 erlang_vm_memory_system_bytes_total{usage="code"} 26779999 erlang_vm_memory_system_bytes_total{usage="ets"} 7685312 erlang_vm_memory_system_bytes_total{usage="other"} 27853800 # TYPE erlang_mnesia_held_locks gauge erlang_mnesia_held_locks 0 # TYPE erlang_mnesia_lock_queue gauge erlang_mnesia_lock_queue 0 # TYPE erlang_mnesia_transaction_participants gauge erlang_mnesia_transaction_participants 0 # TYPE erlang_mnesia_transaction_coordinators gauge erlang_mnesia_transaction_coordinators 0 # TYPE erlang_mnesia_failed_transactions counter erlang_mnesia_failed_transactions 21 # TYPE erlang_mnesia_committed_transactions counter erlang_mnesia_committed_transactions 128 # TYPE erlang_mnesia_logged_transactions counter erlang_mnesia_logged_transactions 3 # TYPE erlang_mnesia_restarted_transactions counter erlang_mnesia_restarted_transactions 0 # TYPE emqx_connections_count gauge emqx_connections_count 0 # TYPE emqx_connections_max gauge emqx_connections_max 0 # TYPE emqx_sessions_count gauge emqx_sessions_count 0 # TYPE emqx_sessions_max gauge emqx_sessions_max 0 # TYPE emqx_topics_count gauge emqx_topics_count 0 # TYPE emqx_topics_max gauge emqx_topics_max 0 # TYPE emqx_suboptions_count gauge emqx_suboptions_count 0 # TYPE emqx_suboptions_max gauge emqx_suboptions_max 0 # TYPE emqx_subscribers_count gauge emqx_subscribers_count 0 # TYPE emqx_subscribers_max gauge emqx_subscribers_max 0 # TYPE emqx_subscriptions_count gauge emqx_subscriptions_count 0 # TYPE emqx_subscriptions_max gauge emqx_subscriptions_max 0 # TYPE emqx_subscriptions_shared_count gauge emqx_subscriptions_shared_count 0 # TYPE emqx_subscriptions_shared_max gauge emqx_subscriptions_shared_max 0 # TYPE emqx_routes_count gauge emqx_routes_count 0 # TYPE emqx_routes_max gauge emqx_routes_max 0 # TYPE emqx_retained_count gauge emqx_retained_count 3 # TYPE emqx_retained_max gauge emqx_retained_max 3 # TYPE emqx_vm_cpu_use gauge emqx_vm_cpu_use 12.029950083194677 # TYPE emqx_vm_cpu_idle gauge emqx_vm_cpu_idle 87.97004991680532 # TYPE emqx_vm_run_queue gauge emqx_vm_run_queue 1 # TYPE emqx_vm_process_messages_in_queues gauge emqx_vm_process_messages_in_queues 0 # TYPE emqx_bytes_received counter emqx_bytes_received 0 # TYPE emqx_bytes_sent counter emqx_bytes_sent 0 # TYPE emqx_packets_received counter emqx_packets_received 0 # TYPE emqx_packets_sent counter emqx_packets_sent 0 # TYPE emqx_packets_connect counter emqx_packets_connect 0 # TYPE emqx_packets_connack_sent counter emqx_packets_connack_sent 0 # TYPE emqx_packets_connack_error counter emqx_packets_connack_error 0 # TYPE emqx_packets_connack_auth_error counter emqx_packets_connack_auth_error 0 # TYPE emqx_packets_publish_received counter emqx_packets_publish_received 0 # TYPE emqx_packets_publish_sent counter emqx_packets_publish_sent 0 # TYPE emqx_packets_publish_inuse counter emqx_packets_publish_inuse 0 # TYPE emqx_packets_publish_error counter emqx_packets_publish_error 0 # TYPE emqx_packets_publish_auth_error counter emqx_packets_publish_auth_error 0 # TYPE emqx_packets_publish_dropped counter emqx_packets_publish_dropped 0 # TYPE emqx_packets_puback_received counter emqx_packets_puback_received 0 # TYPE emqx_packets_puback_sent counter emqx_packets_puback_sent 0 # TYPE emqx_packets_puback_inuse counter emqx_packets_puback_inuse 0 # TYPE emqx_packets_puback_missed counter emqx_packets_puback_missed 0 # TYPE emqx_packets_pubrec_received counter emqx_packets_pubrec_received 0 # TYPE emqx_packets_pubrec_sent counter emqx_packets_pubrec_sent 0 # TYPE emqx_packets_pubrec_inuse counter emqx_packets_pubrec_inuse 0 # TYPE emqx_packets_pubrec_missed counter emqx_packets_pubrec_missed 0 # TYPE emqx_packets_pubrel_received counter emqx_packets_pubrel_received 0 # TYPE emqx_packets_pubrel_sent counter emqx_packets_pubrel_sent 0 # TYPE emqx_packets_pubrel_missed counter emqx_packets_pubrel_missed 0 # TYPE emqx_packets_pubcomp_received counter emqx_packets_pubcomp_received 0 # TYPE emqx_packets_pubcomp_sent counter emqx_packets_pubcomp_sent 0 # TYPE emqx_packets_pubcomp_inuse counter emqx_packets_pubcomp_inuse 0 # TYPE emqx_packets_pubcomp_missed counter emqx_packets_pubcomp_missed 0 # TYPE emqx_packets_subscribe_received counter emqx_packets_subscribe_received 0 # TYPE emqx_packets_subscribe_error counter emqx_packets_subscribe_error 0 # TYPE emqx_packets_subscribe_auth_error counter emqx_packets_subscribe_auth_error 0 # TYPE emqx_packets_suback_sent counter emqx_packets_suback_sent 0 # TYPE emqx_packets_unsubscribe_received counter emqx_packets_unsubscribe_received 0 # TYPE emqx_packets_unsubscribe_error counter emqx_packets_unsubscribe_error 0 # TYPE emqx_packets_unsuback_sent counter emqx_packets_unsuback_sent 0 # TYPE emqx_packets_pingreq_received counter emqx_packets_pingreq_received 0 # TYPE emqx_packets_pingresp_sent counter emqx_packets_pingresp_sent 0 # TYPE emqx_packets_disconnect_received counter emqx_packets_disconnect_received 0 # TYPE emqx_packets_disconnect_sent counter emqx_packets_disconnect_sent 0 # TYPE emqx_packets_auth_received counter emqx_packets_auth_received 0 # TYPE emqx_packets_auth_sent counter emqx_packets_auth_sent 0 # TYPE emqx_messages_received counter emqx_messages_received 0 # TYPE emqx_messages_sent counter emqx_messages_sent 0 # TYPE emqx_messages_qos0_received counter emqx_messages_qos0_received 0 # TYPE emqx_messages_qos0_sent counter emqx_messages_qos0_sent 0 # TYPE emqx_messages_qos1_received counter emqx_messages_qos1_received 0 # TYPE emqx_messages_qos1_sent counter emqx_messages_qos1_sent 0 # TYPE emqx_messages_qos2_received counter emqx_messages_qos2_received 0 # TYPE emqx_messages_qos2_sent counter emqx_messages_qos2_sent 0 # TYPE emqx_messages_publish counter emqx_messages_publish 0 # TYPE emqx_messages_dropped counter emqx_messages_dropped 0 # TYPE emqx_messages_dropped_expired counter emqx_messages_dropped_expired 0 # TYPE emqx_messages_dropped_no_subscribers counter emqx_messages_dropped_no_subscribers 0 # TYPE emqx_messages_forward counter emqx_messages_forward 0 # TYPE emqx_messages_retained counter emqx_messages_retained 2 # TYPE emqx_messages_delayed counter emqx_messages_delayed 0 # TYPE emqx_messages_delivered counter emqx_messages_delivered 0 # TYPE emqx_messages_acked counter emqx_messages_acked 0 # TYPE emqx_delivery_dropped counter emqx_delivery_dropped 0 # TYPE emqx_delivery_dropped_no_local counter emqx_delivery_dropped_no_local 0 # TYPE emqx_delivery_dropped_too_large counter emqx_delivery_dropped_too_large 0 # TYPE emqx_delivery_dropped_qos0_msg counter emqx_delivery_dropped_qos0_msg 0 # TYPE emqx_delivery_dropped_queue_full counter emqx_delivery_dropped_queue_full 0 # TYPE emqx_delivery_dropped_expired counter emqx_delivery_dropped_expired 0 # TYPE emqx_client_connected counter emqx_client_connected 0 # TYPE emqx_client_authenticate counter emqx_client_authenticate 0 # TYPE emqx_client_auth_anonymous counter emqx_client_auth_anonymous 0 # TYPE emqx_client_check_acl counter emqx_client_check_acl 0 # TYPE emqx_client_subscribe counter emqx_client_subscribe 0 # TYPE emqx_client_unsubscribe counter emqx_client_unsubscribe 0 # TYPE emqx_client_disconnected counter emqx_client_disconnected 0 # TYPE emqx_session_created counter emqx_session_created 0 # TYPE emqx_session_resumed counter emqx_session_resumed 0 # TYPE emqx_session_takeovered counter emqx_session_takeovered 0 # TYPE emqx_session_discarded counter emqx_session_discarded 0 # TYPE emqx_session_terminated counter emqx_session_terminated 0 版权声明: 本文为 EMQ 原创,转载请注明出处。
原文链接: https://www.emqx.io/cn/blog/emqx-prometheus-grafana
硬件开发
2020-05-13 17:04:00
存储器是由许多的存储单元集合所成,按照单元号顺序进行排列。每个单元由若干三进制位构成,以表示存储单元中所存放的数值,这种结构和数组的结构非常相似,故在VHDL语言中,通常是由数组描述存储器。存储器是用来存储程序和各种数据信息的记忆部件。


数据存储器 ram

这是个可以随时存取数据的一块存储器,也就是可以读(取)也可以写(存)的存储器,简称为RAM存储。

现在单片机里面所使用的RAM存储器,属于静态RAM或SRAM存储芯片,这个和电脑用的内存条有所不同。只要你把数据写入SRAM后,不断电或者不清除掉,这个数据就一直保存在那里。电脑用的是动态RAM,需要不断地给它加刷新脉冲才能保存数据。

因为MCU处理的信息量比电脑小很多,所以它带的RAM也比较少:从完全不带、带128、256、...1K、2K,再到4K,比ROM少多了。

因为实际上RAM只是作为数据临时存放的地方,除非进行图像处理需要存放大量的数据外。一般对于执行较简单任务的MCU单片机,有这么多也够用,如果实在不够用也只能采取外加 SRAM 如6116、6264等等来扩展。

为了对RAM存储单元存取8位二进制数,当然也得和ROM一样用“地址”来标示它的具体位置。假如某单片机有1K(1024)RAM,它的地址也是从0000到1024,或16进制数的0000H到03FFH。可见和ROM的地址是一样的。
硬件开发
2020-05-13 13:50:00
腾讯云loT应用创新大赛 自开启以来,收到了众多小伙伴们的投稿作品,本文是对其中的一篇优秀作品的摘录。LoRa作为一种远距离无线电技术,具有功耗低、传播距离远等优点,适合部署在机房、智慧建筑等物联网应用中。本期让我们跟随文章作者,通过LoRa技术实现对机房的环境检测与控制。
点击视频链接,查看详情
PPT讲解+实验演示+机房现场测试
一、概述

LoRa是semtech公司创建的低功耗局域网无线标准,我们知道,低功耗一般很难覆盖远距离,而远距离一般功耗高,LoRa的名字翻译就是远距离无线电(Long Range Radio),它最大特点就是在同样的功耗条件下比其他无线方式传播的距离更远,实现了低功耗和远距离的统一,它在同样的功耗下比传统的无线射频通信距离扩大3-5倍。

LoRa在物联网应用中的无线技术有多种,可组成局域网或广域网。LoRa网络主要由终端(可内置LoRa模块)、网关(或称基站)、Server和云四部分组成。

LoRaWAN的数据传输速率范围为0.3 kbps至37.5 kbps,为了最大化终端设备电池的寿命和整个网络容量,LoRaWAN网络服务器通过一种速率自适应(Adaptive Data Rate , ADR)方案来控制数据传输速率和每一终端设备的射频输出功率。

二、方案设计

本案例将通过LoRa技术实现对机房的环境检测与控制。

一般情况下,一个企业的机房很少连接网络,即使连接网络了,也要花费不小的费用进行设备和线路的安装。例如使用宽带时要走网线,距离短,机房少可能还好,如果机房较多,距离较长,那么安装成本和人工成本就会迅速增加。如果安装无线路由器,方法虽然可行,但是穿透力差,信号有时连接不上,想要增强信号,就要增加设备。

至于NB模组,如果信号良好,会是不错之选。目前仍然有许多城市或者乡镇没有覆盖NB-IoT信号。所以NB模组适用于一些大城市。我们要想知道是否覆盖物联网信号,就看这个城市是否有共享单车。比如我所在的海南省,除了地级市,其他县级市基本没有覆盖物联网,因为那里没有共享单车。

LoRa有远距离、低功耗以及低成本等优势。LoRa的传输距离范围长达15至20公里,低功耗的特性延长了电池使用寿命,免牌照的频段、基础设施以及节点/终端的低成本,以上特性都使LoRa的使用成本大幅降低。

所以就机房或者整栋建筑大楼这个特定区域来讲,采用LoRa技术来实现对机房的环境参数的采集与控制是最节省资源的办法。它的穿透力适合布局到整个办公大楼。从而把它变成智慧建筑是可行的。

LoRa网络拓补图

设计框架

智慧建筑

三、方案实现

首先参考教程 基于 TencentOS tiny 的 LoRaWAN 开发入门指南 [1] 的介绍完成开发环境搭建,包括 MDK 软件的安装及配置、ST-Link 驱动安装、串口软件的安装。

1. 硬件设计

(1)LoRa套件

本方案采用P-NUCLEO-LRWAN3套件,包括网关和节点,可用于评估LoRaWAN网络。使用该套件,用户可以轻松设置LPWAN网络,帮助用户学习LoRaWAN技术,了解如何在自己的应用程序中使用LoRaWAN技术。LoRa网关套件由ST Nucleo-F746ZG底板和瑞兴恒方基于SX1301的LRWAN_GS模块组成。

ST Nucleo LoRa节点套件由LRWAN_NS1扩展板和ST Nucleo-L073底板组成。其中 LRWAN_NS1扩展板集成瑞兴恒方的RHF0M003 LoRaWAN模组,并集成了温湿度传感器HTS221、气压传感器LPS22HB、3轴磁力传感器LIS3MDL、6轴姿态传感器LSM6DS3共4个I2C传感器件。

LoRa节点采集的数据通过LoRa网关将数据上传到物联网云平台,实现对终端设备的控制和数据监控。

LoRa套件

(2)LCD显示

液晶屏是ST7735R,用于显示实时采集的数据。例如温度度,压强,海拔等,以及控制的状态指示。采用模拟I2C的方式来实现写指令。

LCD显示采集的数据和控制状态

(3)继电器设计

继电器模块主要用于220V交流电的开关,实现对电机或电灯的电源控制。电路中采用的是光耦进行电气隔离,防止回流时对MCU的冲击。依据这个电路,可以扩展出多路继电器,实现控制各类设备。

继电器电路

继电器电路实物

(4)E53模块应用

采用E53 SC1模块,我是从小熊派物联网开发套件中拿来用的。这个扩展模块集成有LED路灯,光照强度传感器BH1750以及EEPROM芯片24Cxx。我的设计思想是,通过光照强度传感器检测机房亮暗度来决定是否开启应急灯的充电或开启机房灯光,保证应急抢修的需要。同时把相关控制信息存储到EEPROM,实现历史记录的查询。以下是模块原理图:

扩展模块接口原理图

光照强度传感器原理图

EEPROM原理图

E53智慧路灯扩展模块

(5)LoRa节点整体外观

右边的两个黑色按键和开发板上的蓝色按键分别实现LED路灯、继电器和LCD背光的本地控制。设计思路是脱离网络方便本地控制。

各个模块拼凑起来的LoRa控制电路

2. 软件设计

(1)LoRa源码实现

该套件可以很快实现上云,通过官方提供的教程 LoRa 温湿度传感器接入指引 [2] ,打通数据连接,随后就是修改TencentOS tiny源码中的LoRa案例。

以下是需要要上报云端的参数,需要注意的是参数的顺序要和云端解析顺序一致,否则会解码失败而读到错误的数据。
uint16_t report_period = 1; bool report_power_switch=0; bool report_motor_fan=0; float report_pressure=0; float report_height=0; float first_pressure=0; float first_height=0; extern float pressure_hPa; extern float temperature_degC; extern float height; ​ typedef struct device_data_st { uint8_t temperature; uint8_t humidity; uint16_t period; unsigned int quantity; bool power_switch; bool motor_fan; float pressure; float height; } __PACKED__ dev_data_t;

以下是按键任务,功能是进行普通的按键扫描,检测到相应按键后进行相应控制,同时把相关的控制状态通过LCD显示出来并反馈到云端。 void key_task(void *arg){ int lcd_back_flag=1; while(1) { tos_task_delay(10); if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET) { tos_task_delay(100); if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET) { lcd_back_flag=~lcd_back_flag; if(lcd_back_flag==1) { LCD_LED_CLR;//关闭LCD背光 } else { LCD_LED_SET;//开LCD背光 } } } if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET) { tos_task_delay(100); if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET) { if(report_power_switch==1) { report_power_switch=0; HAL_GPIO_WritePin(LD2_GPIO_Port,LD2_Pin,GPIO_PIN_RESET); printf("LED OFF: %d\n",report_power_switch); Gui_DrawFont_GBK16(5,125,RED,BLACK ,(uint8_t*)"LED:OFF"); } else { report_power_switch=1; HAL_GPIO_WritePin(LD2_GPIO_Port,LD2_Pin,GPIO_PIN_SET); printf("LED OFF: %d\n",report_power_switch); Gui_DrawFont_GBK16(5,125,GREEN,BLACK ,(uint8_t*)"LED:ON"); } } } if(HAL_GPIO_ReadPin(KEY3_GPIO_Port,KEY3_Pin)==GPIO_PIN_RESET) { tos_task_delay(100); if(HAL_GPIO_ReadPin(KEY3_GPIO_Port,KEY3_Pin)==GPIO_PIN_RESET) { if(report_motor_fan==1) { report_motor_fan=0; HAL_GPIO_WritePin(MOTOR_GPIO_Port,MOTOR_Pin,GPIO_PIN_RESET); printf("motor_fan OFF: %d\n", report_motor_fan); Gui_DrawFont_GBK16(5,140,RED,BLACK,(uint8_t*)"Motor:OFF "); } else { report_motor_fan=1; HAL_GPIO_WritePin(MOTOR_GPIO_Port,MOTOR_Pin,GPIO_PIN_SET); printf("motor_fan ON: %d\n", report_motor_fan); Gui_DrawFont_GBK16(5,140,GREEN,BLACK,(uint8_t*)"Motor:ON "); } } } } }

主任务函数,该任务实现LoRa连接到网关,检测连接状态,定时上报数据以及一些需要换算的参数。
void application_entry(void *arg) { printf("APP RUNNING...\r\n"); float report_temperature; int16_t temperature; int16_t report_humidity; unsigned int quantity=0; uint16_t sum1=0; char temp[]={0}; Lcd_Init(); LCD_LED_SET; Lcd_Clear(BLACK); Gui_DrawFont_GBK16(10,5,RED,BLACK ,"TencentOS Tiny"); Gui_DrawFont_GBK16(10,20,YELLOW,BLACK,"LoRa Node NO.1"); LPS22HB_Init(); HTS221_Init(); #ifdef LORA_REPORT Gui_DrawFont_GBK16(5,35,RED,BLACK,"LoRa Linking..."); rhf76_lora_init(HAL_UART_PORT_1); tos_lora_module_recvcb_register(recv_callback); if(tos_lora_module_join_otaa("8cf957200000f87e", "8cf957200000f87e9239aaaaad204a72")==-1) { Gui_DrawFont_GBK16(5,35,RED,BLACK, " Link GW Error! "); report_period=60; } else { Gui_DrawFont_GBK16(5,35,GREEN,BLACK," Link GW OK! "); } Gui_DrawFont_GBK16(5,125,RED,BLACK,"LED:OFF "); Gui_DrawFont_GBK16(5,140,RED,BLACK,"Motor:OFF "); #endif example_main_one_shot_lps22hb(); first_pressure=pressure_hPa; first_height=height; while (1) { printf("------LoRawan sensor board data------\n"); Gui_DrawFont_GBK16(5,50,YELLOW,BLACK,"---------------"); example_main_one_shot_lps22hb(); HTS221_Get_Temperature(&temperature); HTS221_Get_Humidity(&report_humidity); report_temperature= temperature_degC; report_height=(height-first_height)*1000; if(pressure_hPa<0) report_pressure=-pressure_hPa*100; else report_pressure=pressure_hPa*100; sprintf(temp,"Temp:%2.1f ",report_temperature); Gui_DrawFont_GBK16(5,65,WHITE,BLACK,temp); sprintf(temp,"Humi:%2.1f ", report_humidity / 10.0); Gui_DrawFont_GBK16(5,80,WHITE,BLACK,temp); sprintf(temp,"Pa:%2.2f ",pressure_hPa); Gui_DrawFont_GBK16(5,95,WHITE,BLACK,temp); sprintf(temp,"Height:%d ", (int)report_height); Gui_DrawFont_GBK16(5,110,WHITE,BLACK,temp); printf("LPS22HB_pressure[hPa]:%0.2f,height[mm]:%d\r\n", pressure_hPa,(int)report_height); printf("LPS22HB_temperature [degC]:%0.2f\r\n",report_temperature); printf("HTS221_temperature : %2.1f\n", temperature/10.0); printf("HTS221_humidity : %2.1f\n", report_humidity / 10.0); sum1++; printf("sum:%d\r\n",sum1); tos_task_delay(500); #ifdef LORA_REPORT if(sum1>=report_period) { sum1=0; quantity++; printf("quantity : %d\n", quantity); printf("LED_Status : %d\n",report_power_switch); printf("motor_Status: %d\n",report_motor_fan); dev_data_wrapper.u.dev_data.temperature = report_temperature; dev_data_wrapper.u.dev_data.humidity = report_humidity / 10; dev_data_wrapper.u.dev_data.period = report_period; dev_data_wrapper.u.dev_data.quantity = quantity; dev_data_wrapper.u.dev_data.power_switch= report_power_switch; dev_data_wrapper.u.dev_data.motor_fan = report_motor_fan; dev_data_wrapper.u.dev_data.pressure = report_pressure; dev_data_wrapper.u.dev_data.height = report_height; tos_lora_module_send(dev_data_wrapper.u.serialize, sizeof(dev_data_t)); } #endif } }

(2)云平台实现
登陆iotexplorer平台即可快速创建LoRa项目
建立项目

定义属性

数据解析

实时查看数据变化

(3)微信小程序实现

小程序集成了LoRa设备和NB设备,这样就可以切换页面查看并控制不同终端设备。这个小程序是通过官方提供demo进行二次开发的。

小程序主页面

LoRa模组小程序页面

NB模组小程序页面

四、总结

利用LoRa极强的穿透力,实现对楼层中的机房环境进行监控。它安装方便,节省了人力物力和财力。所以LoRa物联网应用于机房或建设智慧建筑有先天的优势。如果使用NB模组,每年都要换卡或者缴费。使用WIFI设备呢,穿透力不够强,距离没有LoRa传输得远。

本案例需要增强改进的地方是,检测机房断路器通断,交流接触器动作等器件的工作状态。也可以直接和PLC控制器进行通讯,直接读取PLC采集的数据,然后通过LoRa上传数据。

参考资料:
[1] 基于 TencentOS tiny 的 LoRaWAN 开发入门指南:
https://github.com/Tencent/TencentOS-tiny/blob/master/doc/16.TencentOS_tiny_LoRaWAN_Getting_Started_Guide.md

[2] LoRa 温湿度传感器接入指引:
https://cloud.tencent.com/document/product/1081/41112?from=10680

[3] LoRa节点源码和微信小程序源码及PPT链接:
https://share.weiyun.com/5u2vejl 密码:8szgyr

[4] 源码说明:源码中需要搭配TencentOS tiny的框架,只需要覆盖原来原来的lorawan案例的源码和rhf76的头文件。
硬件开发
2020-05-13 11:14:09
函数名 //int转字节数组 static QByteArray intToByte(int i); static QByteArray intToByteRec(int i); //字节数组转int static int byteToInt(const QByteArray &data); static int byteToIntRec(const QByteArray &data); static quint32 byteToUInt(const QByteArray &data); static quint32 byteToUIntRec(const QByteArray &data); //ushort转字节数组 static QByteArray ushortToByte(ushort i); static QByteArray ushortToByteRec(ushort i); //字节数组转ushort static int byteToUShort(const QByteArray &data); static int byteToUShortRec(const QByteArray &data);
函数体 QByteArray QUIHelper::intToByte(int i) { QByteArray result; result.resize(4); result[3] = (uchar)(0x000000ff & i); result[2] = (uchar)((0x0000ff00 & i) >> 8); result[1] = (uchar)((0x00ff0000 & i) >> 16); result[0] = (uchar)((0xff000000 & i) >> 24); return result; } QByteArray QUIHelper::intToByteRec(int i) { QByteArray result; result.resize(4); result[0] = (uchar)(0x000000ff & i); result[1] = (uchar)((0x0000ff00 & i) >> 8); result[2] = (uchar)((0x00ff0000 & i) >> 16); result[3] = (uchar)((0xff000000 & i) >> 24); return result; } int QUIHelper::byteToInt(const QByteArray &data) { int i = data.at(3) & 0x000000ff; i |= ((data.at(2) << 8) & 0x0000ff00); i |= ((data.at(1) << 16) & 0x00ff0000); i |= ((data.at(0) << 24) & 0xff000000); return i; } int QUIHelper::byteToIntRec(const QByteArray &data) { int i = data.at(0) & 0x000000ff; i |= ((data.at(1) << 8) & 0x0000ff00); i |= ((data.at(2) << 16) & 0x00ff0000); i |= ((data.at(3) << 24) & 0xff000000); return i; } quint32 QUIHelper::byteToUInt(const QByteArray &data) { quint32 i = data.at(3) & 0x000000ff; i |= ((data.at(2) << 8) & 0x0000ff00); i |= ((data.at(1) << 16) & 0x00ff0000); i |= ((data.at(0) << 24) & 0xff000000); return i; } quint32 QUIHelper::byteToUIntRec(const QByteArray &data) { quint32 i = data.at(0) & 0x000000ff; i |= ((data.at(1) << 8) & 0x0000ff00); i |= ((data.at(2) << 16) & 0x00ff0000); i |= ((data.at(3) << 24) & 0xff000000); return i; } QByteArray QUIHelper::ushortToByte(ushort i) { QByteArray result; result.resize(2); result[1] = (uchar)(0x000000ff & i); result[0] = (uchar)((0x0000ff00 & i) >> 8); return result; } QByteArray QUIHelper::ushortToByteRec(ushort i) { QByteArray result; result.resize(2); result[0] = (uchar)(0x000000ff & i); result[1] = (uchar)((0x0000ff00 & i) >> 8); return result; } int QUIHelper::byteToUShort(const QByteArray &data) { int i = data.at(1) & 0x000000FF; i |= ((data.at(0) << 8) & 0x0000FF00); if (i >= 32768) { i = i - 65536; } return i; } int QUIHelper::byteToUShortRec(const QByteArray &data) { int i = data.at(0) & 0x000000FF; i |= ((data.at(1) << 8) & 0x0000FF00); if (i >= 32768) { i = i - 65536; } return i; }
硬件开发
2020-04-19 16:44:00
一、前言
相信各位CS结构开发的程序员,多多少少都遇到过需要美化界面的事情,一般都不会采用系统的标题栏,这样就需要无边框标题栏窗体,默认的话无边框的标题栏都不支持拉伸和拖动的,毕竟去掉了标题栏则意味着失去了系统的窗体的属性,拉伸和拖动都需要自己写代码去实现,网上有很多类似的开源的方案,我也看过不少,总体来说复杂了些,对于初学者来说有可能看的云里雾里的,比如边框四周八个方位都可以自由拉伸这块,我的思路是针对设定的八个方位的区域进行识别鼠标是否按下,按下的哪个部位则执行什么拉伸策略,鼠标移到哪个位置则对应改变鼠标指针形状,更浅显易懂一些,至于拖动移动,还可以设置拖动的标题栏的高度等。
主要功能: 可以指定需要无边框的widget 边框四周八个方位都可以自由拉伸 可设置对应位置的边距,以便识别更大区域 可设置是否允许拖动 可设置是否允许拉伸
二、代码思路 bool FramelessWidget::eventFilter(QObject *watched, QEvent *event) { if (widget != 0 && watched == widget) { if (event->type() == QEvent::Resize) { //重新计算八个描点的区域,描点区域的作用还有就是计算鼠标坐标是否在某一个区域内 int width = widget->width(); int height = widget->height(); //左侧描点区域 rectLeft = QRect(0, padding, padding, height - padding * 2); //上侧描点区域 rectTop = QRect(padding, 0, width - padding * 2, padding); //右侧描点区域 rectRight = QRect(width - padding, padding, padding, height - padding * 2); //下侧描点区域 rectBottom = QRect(padding, height - padding, width - padding * 2, padding); //左上角描点区域 rectLeftTop = QRect(0, 0, padding, padding); //右上角描点区域 rectRightTop = QRect(width - padding, 0, padding, padding); //左下角描点区域 rectLeftBottom = QRect(0, height - padding, padding, padding); //右下角描点区域 rectRightBottom = QRect(width - padding, height - padding, padding, padding); } else if (event->type() == QEvent::HoverMove) { //设置对应鼠标形状,这个必须放在这里而不是下面,因为可以在鼠标没有按下的时候识别 QHoverEvent *hoverEvent = (QHoverEvent *)event; QPoint point = hoverEvent->pos(); if (resizeEnable) { if (rectLeft.contains(point)) { widget->setCursor(Qt::SizeHorCursor); } else if (rectRight.contains(point)) { widget->setCursor(Qt::SizeHorCursor); } else if (rectTop.contains(point)) { widget->setCursor(Qt::SizeVerCursor); } else if (rectBottom.contains(point)) { widget->setCursor(Qt::SizeVerCursor); } else if (rectLeftTop.contains(point)) { widget->setCursor(Qt::SizeFDiagCursor); } else if (rectRightTop.contains(point)) { widget->setCursor(Qt::SizeBDiagCursor); } else if (rectLeftBottom.contains(point)) { widget->setCursor(Qt::SizeBDiagCursor); } else if (rectRightBottom.contains(point)) { widget->setCursor(Qt::SizeFDiagCursor); } else { widget->setCursor(Qt::ArrowCursor); } } //根据当前鼠标位置,计算XY轴移动了多少 int offsetX = point.x() - lastPos.x(); int offsetY = point.y() - lastPos.y(); //根据按下处的位置判断是否是移动控件还是拉伸控件 if (moveEnable) { if (pressed) { widget->move(widget->x() + offsetX, widget->y() + offsetY); } } if (resizeEnable) { if (pressedLeft) { int resizeW = widget->width() - offsetX; if (widget->minimumWidth() <= resizeW) { widget->setGeometry(widget->x() + offsetX, rectY, resizeW, rectH); } } else if (pressedRight) { widget->setGeometry(rectX, rectY, rectW + offsetX, rectH); } else if (pressedTop) { int resizeH = widget->height() - offsetY; if (widget->minimumHeight() <= resizeH) { widget->setGeometry(rectX, widget->y() + offsetY, rectW, resizeH); } } else if (pressedBottom) { widget->setGeometry(rectX, rectY, rectW, rectH + offsetY); } else if (pressedLeftTop) { int resizeW = widget->width() - offsetX; int resizeH = widget->height() - offsetY; if (widget->minimumWidth() <= resizeW) { widget->setGeometry(widget->x() + offsetX, widget->y(), resizeW, resizeH); } if (widget->minimumHeight() <= resizeH) { widget->setGeometry(widget->x(), widget->y() + offsetY, resizeW, resizeH); } } else if (pressedRightTop) { int resizeW = rectW + offsetX; int resizeH = widget->height() - offsetY; if (widget->minimumHeight() <= resizeH) { widget->setGeometry(widget->x(), widget->y() + offsetY, resizeW, resizeH); } } else if (pressedLeftBottom) { int resizeW = widget->width() - offsetX; int resizeH = rectH + offsetY; if (widget->minimumWidth() <= resizeW) { widget->setGeometry(widget->x() + offsetX, widget->y(), resizeW, resizeH); } if (widget->minimumHeight() <= resizeH) { widget->setGeometry(widget->x(), widget->y(), resizeW, resizeH); } } else if (pressedRightBottom) { int resizeW = rectW + offsetX; int resizeH = rectH + offsetY; widget->setGeometry(widget->x(), widget->y(), resizeW, resizeH); } } } else if (event->type() == QEvent::MouseButtonPress) { //记住当前控件坐标和宽高以及鼠标按下的坐标 QMouseEvent *mouseEvent = (QMouseEvent *)event; rectX = widget->x(); rectY = widget->y(); rectW = widget->width(); rectH = widget->height(); lastPos = mouseEvent->pos(); //判断按下的手柄的区域位置 if (rectLeft.contains(lastPos)) { pressedLeft = true; } else if (rectRight.contains(lastPos)) { pressedRight = true; } else if (rectTop.contains(lastPos)) { pressedTop = true; } else if (rectBottom.contains(lastPos)) { pressedBottom = true; } else if (rectLeftTop.contains(lastPos)) { pressedLeftTop = true; } else if (rectRightTop.contains(lastPos)) { pressedRightTop = true; } else if (rectLeftBottom.contains(lastPos)) { pressedLeftBottom = true; } else if (rectRightBottom.contains(lastPos)) { pressedRightBottom = true; } else { pressed = true; } } else if (event->type() == QEvent::MouseMove) { //改成用HoverMove识别 } else if (event->type() == QEvent::MouseButtonRelease) { //恢复所有 pressed = false; pressedLeft = false; pressedRight = false; pressedTop = false; pressedBottom = false; pressedLeftTop = false; pressedRightTop = false; pressedLeftBottom = false; pressedRightBottom = false; widget->setCursor(Qt::ArrowCursor); } } return QObject::eventFilter(watched, event); }
三、效果图
四、开源主页
以上作品完整源码下载都在开源主页,会持续不断更新作品数量和质量,欢迎各位关注。 国内站点: https://gitee.com/feiyangqingyun/QWidgetDemo 国际站点: https://github.com/feiyangqingyun/QWidgetDemo 个人主页: https://blog.csdn.net/feiyangqingyun 知乎主页: https://www.zhihu.com/people/feiyangqingyun/
硬件开发
2020-05-21 09:05:00
函数名 //判断是否是IP地址 static bool isIP(const QString &ip); //判断是否是MAC地址 static bool isMac(const QString &mac); //判断是否是合法的电话号码 static bool isTel(const QString &tel); //判断是否是合法的邮箱地址 static bool isEmail(const QString &email); //16进制字符串转10进制 static int strHexToDecimal(const QString &strHex); //10进制字符串转10进制 static int strDecimalToDecimal(const QString &strDecimal); //2进制字符串转10进制 static int strBinToDecimal(const QString &strBin); //16进制字符串转2进制字符串 static QString strHexToStrBin(const QString &strHex); //10进制转2进制字符串一个字节 static QString decimalToStrBin1(int decimal); //10进制转2进制字符串两个字节 static QString decimalToStrBin2(int decimal); //10进制转16进制字符串,补零. static QString decimalToStrHex(int decimal);
函数体 bool QUIHelper::isIP(const QString &ip) { QRegExp RegExp("((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)"); return RegExp.exactMatch(ip); } bool QUIHelper::isMac(const QString &mac) { QRegExp RegExp("^[A-F0-9]{2}(-[A-F0-9]{2}){5}$"); return RegExp.exactMatch(mac); } bool QUIHelper::isTel(const QString &tel) { if (tel.length() != 11) { return false; } if (!tel.startsWith("13") && !tel.startsWith("14") && !tel.startsWith("15") && !tel.startsWith("18")) { return false; } return true; } bool QUIHelper::isEmail(const QString &email) { if (!email.contains("@") || !email.contains(".com")) { return false; } return true; } int QUIHelper::strHexToDecimal(const QString &strHex) { bool ok; return strHex.toInt(&ok, 16); } int QUIHelper::strDecimalToDecimal(const QString &strDecimal) { bool ok; return strDecimal.toInt(&ok, 10); } int QUIHelper::strBinToDecimal(const QString &strBin) { bool ok; return strBin.toInt(&ok, 2); } QString QUIHelper::strHexToStrBin(const QString &strHex) { uchar decimal = strHexToDecimal(strHex); QString bin = QString::number(decimal, 2); uchar len = bin.length(); if (len < 8) { for (int i = 0; i < 8 - len; i++) { bin = "0" + bin; } } return bin; } QString QUIHelper::decimalToStrBin1(int decimal) { QString bin = QString::number(decimal, 2); uchar len = bin.length(); if (len <= 8) { for (int i = 0; i < 8 - len; i++) { bin = "0" + bin; } } return bin; } QString QUIHelper::decimalToStrBin2(int decimal) { QString bin = QString::number(decimal, 2); uchar len = bin.length(); if (len <= 16) { for (int i = 0; i < 16 - len; i++) { bin = "0" + bin; } } return bin; } QString QUIHelper::decimalToStrHex(int decimal) { QString temp = QString::number(decimal, 16); if (temp.length() == 1) { temp = "0" + temp; } return temp; }
硬件开发
2020-04-19 16:42:00
函数名 //设置标签颜色 static void setLabStyle(QLabel *lab, quint8 type, const QString &bgColor = "", const QString &textColor = ""); //设置窗体居中显示 static void setFormInCenter(QWidget *frm); //设置翻译文件 static void setTranslator(const QString &qmFile = ":/image/qt_zh_CN.qm"); //设置编码 static void setCode(); //设置延时 static void sleep(int msec); //设置系统时间 static void setSystemDateTime(const QString &year, const QString &month, const QString &day, const QString &hour, const QString &min, const QString &sec); //设置开机自启动 static void runWithSystem(const QString &strName, const QString &strPath, bool autoRun = true);
函数体 void QUIHelper::setLabStyle(QLabel *lab, quint8 type, const QString &bgColor, const QString &textColor) { QString colorBg = bgColor; QString colorText = textColor; //如果设置了新颜色则启用新颜色 if (bgColor.isEmpty() || textColor.isEmpty()) { if (type == 0) { colorBg = "#D64D54"; colorText = "#FFFFFF"; } else if (type == 1) { colorBg = "#17A086"; colorText = "#FFFFFF"; } else if (type == 2) { colorBg = "#47A4E9"; colorText = "#FFFFFF"; } else if (type == 3) { colorBg = "#282D30"; colorText = "#FFFFFF"; } else if (type == 4) { colorBg = "#0E99A0"; colorText = "#FFFFFF"; } else if (type == 5) { colorBg = "#A279C5"; colorText = "#FFFFFF"; } else if (type == 6) { colorBg = "#8C2957"; colorText = "#FFFFFF"; } else if (type == 7) { colorBg = "#04567E"; colorText = "#FFFFFF"; } else if (type == 8) { colorBg = "#FD8B28"; colorText = "#FFFFFF"; } else if (type == 9) { colorBg = "#5580A2"; colorText = "#FFFFFF"; } } QStringList qss; //禁用颜色 qss << QString("QLabel::disabled{background:none;color:%1;}").arg(QUIConfig::BorderColor); //正常颜色 qss << QString("QLabel{border:none;background-color:%1;color:%2;}").arg(colorBg).arg(colorText); lab->setStyleSheet(qss.join("")); } void QUIHelper::setFormInCenter(QWidget *frm) { int frmX = frm->width(); int frmY = frm->height(); QDesktopWidget w; int deskWidth = w.availableGeometry().width(); int deskHeight = w.availableGeometry().height(); QPoint movePoint(deskWidth / 2 - frmX / 2, deskHeight / 2 - frmY / 2); frm->move(movePoint); } void QUIHelper::setTranslator(const QString &qmFile) { QTranslator *translator = new QTranslator(qApp); translator->load(qmFile); qApp->installTranslator(translator); } void QUIHelper::setCode() { #if (QT_VERSION <= QT_VERSION_CHECK(5,0,0)) #if _MSC_VER QTextCodec *codec = QTextCodec::codecForName("gbk"); #else QTextCodec *codec = QTextCodec::codecForName("utf-8"); #endif QTextCodec::setCodecForLocale(codec); QTextCodec::setCodecForCStrings(codec); QTextCodec::setCodecForTr(codec); #else QTextCodec *codec = QTextCodec::codecForName("utf-8"); QTextCodec::setCodecForLocale(codec); #endif } void QUIHelper::sleep(int msec) { if (msec > 0) { #if (QT_VERSION < QT_VERSION_CHECK(5,7,0)) QTime endTime = QTime::currentTime().addMSecs(msec); while (QTime::currentTime() < endTime) { QCoreApplication::processEvents(QEventLoop::AllEvents, 100); } #else QThread::msleep(msec); #endif } } void QUIHelper::setSystemDateTime(const QString &year, const QString &month, const QString &day, const QString &hour, const QString &min, const QString &sec) { #ifdef Q_OS_WIN QProcess p(0); p.start("cmd"); p.waitForStarted(); p.write(QString("date %1-%2-%3\n").arg(year).arg(month).arg(day).toLatin1()); p.closeWriteChannel(); p.waitForFinished(1000); p.close(); p.start("cmd"); p.waitForStarted(); p.write(QString("time %1:%2:%3.00\n").arg(hour).arg(min).arg(sec).toLatin1()); p.closeWriteChannel(); p.waitForFinished(1000); p.close(); #else QString cmd = QString("date %1%2%3%4%5.%6").arg(month).arg(day).arg(hour).arg(min).arg(year).arg(sec); system(cmd.toLatin1()); system("hwclock -w"); #endif } void QUIHelper::runWithSystem(const QString &strName, const QString &strPath, bool autoRun) { #ifdef Q_OS_WIN QSettings reg("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat); reg.setValue(strName, autoRun ? strPath : ""); #endif }
硬件开发
2020-04-19 16:40:00
函数名 //九宫格图片 horzSplit-宫格1/3/7/9宽度 vertSplit-宫格1/3/7/9高度 dstWidth-目标图片宽度 dstHeight-目标图片高度 static QPixmap ninePatch(const QString &picName, int horzSplit, int vertSplit, int dstWidth, int dstHeight); static QPixmap ninePatch(const QPixmap &pix, int horzSplit, int vertSplit, int dstWidth, int dstHeight);
函数体 QPixmap QUIHelper::ninePatch(const QString &picName, int horzSplit, int vertSplit, int dstWidth, int dstHeight) { QPixmap pix(picName); return ninePatch(pix, horzSplit, vertSplit, dstWidth, dstHeight); } QPixmap QUIHelper::ninePatch(const QPixmap &pix, int horzSplit, int vertSplit, int dstWidth, int dstHeight) { int pixWidth = pix.width(); int pixHeight = pix.height(); QPixmap pix1 = pix.copy(0, 0, horzSplit, vertSplit); QPixmap pix2 = pix.copy(horzSplit, 0, pixWidth - horzSplit * 2, vertSplit); QPixmap pix3 = pix.copy(pixWidth - horzSplit, 0, horzSplit, vertSplit); QPixmap pix4 = pix.copy(0, vertSplit, horzSplit, pixHeight - vertSplit * 2); QPixmap pix5 = pix.copy(horzSplit, vertSplit, pixWidth - horzSplit * 2, pixHeight - vertSplit * 2); QPixmap pix6 = pix.copy(pixWidth - horzSplit, vertSplit, horzSplit, pixHeight - vertSplit * 2); QPixmap pix7 = pix.copy(0, pixHeight - vertSplit, horzSplit, vertSplit); QPixmap pix8 = pix.copy(horzSplit, pixHeight - vertSplit, pixWidth - horzSplit * 2, pixWidth - horzSplit * 2); QPixmap pix9 = pix.copy(pixWidth - horzSplit, pixHeight - vertSplit, horzSplit, vertSplit); //保持高度拉宽 pix2 = pix2.scaled(dstWidth - horzSplit * 2, vertSplit, Qt::IgnoreAspectRatio); //保持宽度拉高 pix4 = pix4.scaled(horzSplit, dstHeight - vertSplit * 2, Qt::IgnoreAspectRatio); //宽高都缩放 pix5 = pix5.scaled(dstWidth - horzSplit * 2, dstHeight - vertSplit * 2, Qt::IgnoreAspectRatio); //保持宽度拉高 pix6 = pix6.scaled(horzSplit, dstHeight - vertSplit * 2, Qt::IgnoreAspectRatio); //保持高度拉宽 pix8 = pix8.scaled(dstWidth - horzSplit * 2, vertSplit); //生成宽高图片并填充透明背景颜色 QPixmap resultImg(dstWidth, dstHeight); resultImg.fill(Qt::transparent); QPainter painter; painter.begin(&resultImg); if (!resultImg.isNull()) { painter.drawPixmap(0, 0, pix1); painter.drawPixmap(horzSplit, 0, pix2); painter.drawPixmap(dstWidth - horzSplit, 0, pix3); painter.drawPixmap(0, vertSplit, pix4); painter.drawPixmap(horzSplit, vertSplit, pix5); painter.drawPixmap(dstWidth - horzSplit, vertSplit, pix6); painter.drawPixmap(0, dstHeight - vertSplit, pix7); painter.drawPixmap(horzSplit, dstHeight - vertSplit, pix8); painter.drawPixmap(dstWidth - horzSplit, dstHeight - vertSplit, pix9); } painter.end(); return resultImg; }
硬件开发
2020-04-19 16:38:00
函数名 //设置全局样式 static void setStyle(QUIWidget::Style style); static void setStyle(const QString &qssFile, QString &paletteColor, QString &textColor); static void setStyle(const QString &qssFile, QString &textColor, QString &panelColor, QString &borderColor, QString &normalColorStart, QString &normalColorEnd, QString &darkColorStart, QString &darkColorEnd, QString &highColor); //根据QSS样式获取对应颜色值 static void getQssColor(const QString &qss, QString &textColor, QString &panelColor, QString &borderColor, QString &normalColorStart, QString &normalColorEnd, QString &darkColorStart, QString &darkColorEnd, QString &highColor);
函数体 void QUIHelper::setStyle(QUIWidget::Style style) { QString qssFile = ":/qss/lightblue.css"; if (style == QUIWidget::Style_Silvery) { qssFile = ":/qss/silvery.css"; } else if (style == QUIWidget::Style_Blue) { qssFile = ":/qss/blue.css"; } else if (style == QUIWidget::Style_LightBlue) { qssFile = ":/qss/lightblue.css"; } else if (style == QUIWidget::Style_DarkBlue) { qssFile = ":/qss/darkblue.css"; } else if (style == QUIWidget::Style_Gray) { qssFile = ":/qss/gray.css"; } else if (style == QUIWidget::Style_LightGray) { qssFile = ":/qss/lightgray.css"; } else if (style == QUIWidget::Style_DarkGray) { qssFile = ":/qss/darkgray.css"; } else if (style == QUIWidget::Style_Black) { qssFile = ":/qss/black.css"; } else if (style == QUIWidget::Style_LightBlack) { qssFile = ":/qss/lightblack.css"; } else if (style == QUIWidget::Style_DarkBlack) { qssFile = ":/qss/darkblack.css"; } else if (style == QUIWidget::Style_PSBlack) { qssFile = ":/qss/psblack.css"; } else if (style == QUIWidget::Style_FlatBlack) { qssFile = ":/qss/flatblack.css"; } else if (style == QUIWidget::Style_FlatWhite) { qssFile = ":/qss/flatwhite.css"; } else if (style == QUIWidget::Style_Purple) { qssFile = ":/qss/purple.css"; } else if (style == QUIWidget::Style_BlackBlue) { qssFile = ":/qss/blackblue.css"; } else if (style == QUIWidget::Style_BlackVideo) { qssFile = ":/qss/blackvideo.css"; } QFile file(qssFile); if (file.open(QFile::ReadOnly)) { QString qss = QLatin1String(file.readAll()); QString paletteColor = qss.mid(20, 7); getQssColor(qss, QUIConfig::TextColor, QUIConfig::PanelColor, QUIConfig::BorderColor, QUIConfig::NormalColorStart, QUIConfig::NormalColorEnd, QUIConfig::DarkColorStart, QUIConfig::DarkColorEnd, QUIConfig::HighColor); qApp->setPalette(QPalette(QColor(paletteColor))); qApp->setStyleSheet(qss); file.close(); } } void QUIHelper::setStyle(const QString &qssFile, QString &paletteColor, QString &textColor) { QFile file(qssFile); if (file.open(QFile::ReadOnly)) { QString qss = QLatin1String(file.readAll()); paletteColor = qss.mid(20, 7); textColor = qss.mid(49, 7); getQssColor(qss, QUIConfig::TextColor, QUIConfig::PanelColor, QUIConfig::BorderColor, QUIConfig::NormalColorStart, QUIConfig::NormalColorEnd, QUIConfig::DarkColorStart, QUIConfig::DarkColorEnd, QUIConfig::HighColor); qApp->setPalette(QPalette(QColor(paletteColor))); qApp->setStyleSheet(qss); file.close(); } } void QUIHelper::setStyle(const QString &qssFile, QString &textColor, QString &panelColor, QString &borderColor, QString &normalColorStart, QString &normalColorEnd, QString &darkColorStart, QString &darkColorEnd, QString &highColor) { QFile file(qssFile); if (file.open(QFile::ReadOnly)) { QString qss = QLatin1String(file.readAll()); getQssColor(qss, textColor, panelColor, borderColor, normalColorStart, normalColorEnd, darkColorStart, darkColorEnd, highColor); qApp->setPalette(QPalette(QColor(panelColor))); qApp->setStyleSheet(qss); file.close(); } } void QUIHelper::getQssColor(const QString &qss, QString &textColor, QString &panelColor, QString &borderColor, QString &normalColorStart, QString &normalColorEnd, QString &darkColorStart, QString &darkColorEnd, QString &highColor) { QString str = qss; QString flagTextColor = "TextColor:"; int indexTextColor = str.indexOf(flagTextColor); if (indexTextColor >= 0) { textColor = str.mid(indexTextColor + flagTextColor.length(), 7); } QString flagPanelColor = "PanelColor:"; int indexPanelColor = str.indexOf(flagPanelColor); if (indexPanelColor >= 0) { panelColor = str.mid(indexPanelColor + flagPanelColor.length(), 7); } QString flagBorderColor = "BorderColor:"; int indexBorderColor = str.indexOf(flagBorderColor); if (indexBorderColor >= 0) { borderColor = str.mid(indexBorderColor + flagBorderColor.length(), 7); } QString flagNormalColorStart = "NormalColorStart:"; int indexNormalColorStart = str.indexOf(flagNormalColorStart); if (indexNormalColorStart >= 0) { normalColorStart = str.mid(indexNormalColorStart + flagNormalColorStart.length(), 7); } QString flagNormalColorEnd = "NormalColorEnd:"; int indexNormalColorEnd = str.indexOf(flagNormalColorEnd); if (indexNormalColorEnd >= 0) { normalColorEnd = str.mid(indexNormalColorEnd + flagNormalColorEnd.length(), 7); } QString flagDarkColorStart = "DarkColorStart:"; int indexDarkColorStart = str.indexOf(flagDarkColorStart); if (indexDarkColorStart >= 0) { darkColorStart = str.mid(indexDarkColorStart + flagDarkColorStart.length(), 7); } QString flagDarkColorEnd = "DarkColorEnd:"; int indexDarkColorEnd = str.indexOf(flagDarkColorEnd); if (indexDarkColorEnd >= 0) { darkColorEnd = str.mid(indexDarkColorEnd + flagDarkColorEnd.length(), 7); } QString flagHighColor = "HighColor:"; int indexHighColor = str.indexOf(flagHighColor); if (indexHighColor >= 0) { highColor = str.mid(indexHighColor + flagHighColor.length(), 7); } }
硬件开发
2020-04-19 16:32:00
函数名 //初始化数据库 static void initDb(const QString &dbName); //初始化文件,不存在则拷贝 static void initFile(const QString &sourceName, const QString &targetName); //新建目录 static void newDir(const QString &dirName); //写入消息到额外的的消息日志文件 static void writeInfo(const QString &info, bool needWrite = false, const QString &filePath = "log"); static void writeError(const QString &info, bool needWrite = false, const QString &filePath = "log"); //设置无边框窗体 static void setFramelessForm(QWidget *widgetMain, QWidget *widgetTitle, QLabel *labIco, QPushButton *btnClose, bool tool = true);
函数体 void QUIHelper::initDb(const QString &dbName) { initFile(QString(":/%1.db").arg(appName()), dbName); } void QUIHelper::initFile(const QString &sourceName, const QString &targetName) { //判断文件是否存在,不存在则从资源文件复制出来 QFile file(targetName); if (!file.exists() || file.size() == 0) { file.remove(); QUIHelper::copyFile(sourceName, targetName); } } void QUIHelper::newDir(const QString &dirName) { QString strDir = dirName; //如果路径中包含斜杠字符则说明是绝对路径 //linux系统路径字符带有 / windows系统 路径字符带有 :/ if (!strDir.startsWith("/") && !strDir.contains(":/")) { strDir = QString("%1/%2").arg(QUIHelper::appPath()).arg(strDir); } QDir dir(strDir); if (!dir.exists()) { dir.mkpath(strDir); } } void QUIHelper::writeInfo(const QString &info, bool needWrite, const QString &filePath) { if (!needWrite) { return; } QString fileName = QString("%1/%2/%3_runinfo_%4.txt").arg(QUIHelper::appPath()) .arg(filePath).arg(QUIHelper::appName()).arg(QDate::currentDate().toString("yyyyMM")); QFile file(fileName); file.open(QIODevice::WriteOnly | QIODevice::Append | QFile::Text); QTextStream stream(&file); stream << DATETIME << " " << info << NEWLINE; file.close(); } void QUIHelper::writeError(const QString &info, bool needWrite, const QString &filePath) { if (!needWrite) { return; } QString fileName = QString("%1/%2/%3_runerror_%4.txt").arg(QUIHelper::appPath()) .arg(filePath).arg(QUIHelper::appName()).arg(QDate::currentDate().toString("yyyyMM")); QFile file(fileName); file.open(QIODevice::WriteOnly | QIODevice::Append | QFile::Text); QTextStream stream(&file); stream << DATETIME << " " << info << NEWLINE; file.close(); } void QUIHelper::setFramelessForm(QWidget *widgetMain, QWidget *widgetTitle, QLabel *labIco, QPushButton *btnClose, bool tool) { labIco->setFixedWidth(TitleMinSize); btnClose->setFixedWidth(TitleMinSize); widgetTitle->setFixedHeight(TitleMinSize); widgetTitle->setProperty("form", "title"); widgetMain->setProperty("form", true); widgetMain->setProperty("canMove", true); if (tool) { widgetMain->setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); } else { widgetMain->setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); } IconHelper::Instance()->setIcon(labIco, QUIConfig::IconMain, QUIConfig::FontSize + 2); IconHelper::Instance()->setIcon(btnClose, QUIConfig::IconClose, QUIConfig::FontSize); }
硬件开发
2020-04-19 16:30:00
函数名 //桌面宽度高度 static int deskWidth(); static int deskHeight(); //程序文件名称+当前所在路径 static QString appName(); static QString appPath(); //初始化随机数种子 static void initRand();
函数体 int QUIHelper::deskWidth() { //没有必要每次都获取,只有当变量为空时才去获取一次 static int width = 0; if (width == 0) { width = qApp->desktop()->availableGeometry().width(); } return width; } int QUIHelper::deskHeight() { //没有必要每次都获取,只有当变量为空时才去获取一次 static int height = 0; if (height == 0) { height = qApp->desktop()->availableGeometry().height(); } return height; } QString QUIHelper::appName() { //没有必要每次都获取,只有当变量为空时才去获取一次 static QString name; if (name.isEmpty()) { name = qApp->applicationFilePath(); QStringList list = name.split("/"); name = list.at(list.count() - 1).split(".").at(0); } return name; } QString QUIHelper::appPath() { #ifdef Q_OS_ANDROID return QString("/sdcard/Android/%1").arg(appName()); #else return qApp->applicationDirPath(); #endif } void QUIHelper::initRand() { //初始化随机数种子 QTime t = QTime::currentTime(); qsrand(t.msec() + t.second() * 1000); }
硬件开发
2020-04-19 16:23:00
一、前言
之前做获取边界点的时候,主要采用的是在线地图的方式,因为在线地图中直接内置了函数可以根据行政区域的名称来自动获取边界,其实这些边界就是一些点坐标集合连接起来的平滑线,然后形成的轮廓图,这种方式有个弊端就是只能在线的时候使用,而我们大部分的应用场景应该是离线的,甚至很多设备永远是离线的,根本不可能去联网获取信息,但是又想要这个各省市区域的轮廓图怎办呢,只能事先拿到下载到这些需要的轮廓图文件才行,这些文件存储的就是经纬度坐标集合,在离线地图中只需要定义不规则线条绘制传入这些经纬度坐标集合即可。
Qt的浏览器控件的交互机制非常方便,所以在在线地图的时候可以对每个区域的经纬度坐标集合发给Qt程序,让他去存储到文件,在实际的测试过程中,发现有部分地图有多个封闭的曲线的,比如散落的岛屿和飞地,这些可不能遗漏呢,所以存储经纬度坐标信息,要按照数组的形式存储,最开始做的时候按照一个字符串集合存储的,后面发现部分地方少了甚至不规则,原来是有多个曲线集合,解析的时候根据数组来实例化不规则线条的类即可。
在线地图默认只能精确到县城,如果还要更精确的话,就需要自己手动调整边界点拉动好,然后主动获取当前边界点的经纬度坐标集合,存储起来,这就需要一开始设定一个基本的边界点的形状,开启允许编辑属性,然后自行去调整好位置,最后单击获取边界点坐标,保存文件即可,如果需要很多的乡镇的轮廓图,那只能很有耐心的慢慢的调整获取咯,当然这种无聊的没有技术含量的事情也可以交给小姑娘去做啦。
二、功能特点 同时支持在线地图和离线地图两种模式。 同时支持webkit内核、webengine内核、IE内核。 支持设置多个标注点,信息包括名称、地址、经纬度。 可设置地图是否可单击、拖动、鼠标滚轮缩放。 可设置协议版本、秘钥、主题样式、中心坐标、中心城市、地理编码位置等。 可设置地图缩放比例和级别,缩略图、比例尺、路况信息等控件的可见。 支持地图交互,比如鼠标按下获取对应位置的经纬度。 支持查询路线,可设置起点位置、终点位置、路线模式、路线方式、路线方案(最少时间、最少换乘、最少步行、不乘地铁、最短距离、避开高速)。 可显示点线面工具,可直接在地图上划线、点、矩形、圆形等。 可设置行政区划,指定某个城市区域绘制图层,在线地图自动输出行政区划边界点集合到js文件给离线地图使用。 可静态或者动态添加多个覆盖物。支持点、折线、多边形、矩形、圆形、弧线、点聚合等。 函数接口友好和统一,使用简单方便,就一个类。 支持js动态交互添加点、删除点、清空点、重置点,不需要刷新页面。 支持任意Qt版本、任意系统、任意编译器。
三、体验地址 体验地址: https://pan.baidu.com/s/1uQsDQO5E5crUBN2J-nPeLQ 提取码:1jkp 文件名:bin_map.zip 国内站点: https://gitee.com/feiyangqingyun 国际站点: https://github.com/feiyangqingyun 个人主页: https://blog.csdn.net/feiyangqingyun 知乎主页: https://www.zhihu.com/people/feiyangqingyun/
四、效果图
五、相关代码 QStringList MapBoundary::getResult(const QByteArray &data, quint8 type, const QString &provinceName, const QString &cityName) { //处理数据 QStringList result; if (type == 1 && provinceName.isEmpty()) { return result; } else if (type == 2 && (provinceName.isEmpty() || cityName.isEmpty())) { return result; } #if (QT_VERSION > QT_VERSION_CHECK(5,0,0)) //采用qt内置的json方法解析 QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); if (error.error == QJsonParseError::NoError) { QJsonObject rootObj = jsonDoc.object(); //qDebug() << rootObj.keys(); if (rootObj.contains("province")) { QJsonArray province = rootObj.value("province").toArray(); for (int i = 0; i < province.count(); i++) { QJsonObject subObj = province.at(i).toObject(); if (subObj.contains("name")) { QString name = subObj.value("name").toString(); if (type == 0) { result << name; } else if (type == 1) { if (name == provinceName) { QJsonArray city = subObj.value("city").toArray(); for (int j = 0; j < city.count(); j++) { QJsonObject nodeObj = city.at(j).toObject(); if (nodeObj.contains("cityname")) { QString cityname = nodeObj.value("cityname").toString(); result << cityname; } } //退出查找 break; } } else if (type == 2) { if (name == provinceName) { bool exist = false; QJsonArray city = subObj.value("city").toArray(); for (int j = 0; j < city.count(); j++) { QJsonObject nodeObj = city.at(j).toObject(); if (nodeObj.contains("cityname")) { QString cityname = nodeObj.value("cityname").toString(); if (cityname == cityName) { QJsonArray countyname = nodeObj.value("countyname").toArray(); for (int k = 0; k < countyname.count(); k++) { QString county = countyname.at(k).toString(); //数据中带了县城所在镇,要过滤 if (!county.endsWith("镇")) { result << county; } } exist = true; break; } } } //退出查找 if (exist) { break; } } } } } } } #else //采用字符串分割方法解析 QString temp = data; QStringList provice = temp.split("\n"); QString name, cityname; for (int i = 0; i < provice.count(); i++) { QString value = provice.at(i); if (value.contains("\"name\"")) { name = getValue(value); if (type == 0) { result << name; } } else if (value.contains("\"cityname\"")) { cityname = getValue(value); bool exist = false; if (name == provinceName) { exist = true; } if (type == 1) { //检测到是当前省份则来提取市区 if (exist) { result << cityname; //当是新的省份以后立即退出 if (name != provinceName) { break; } } } } else if (value.contains("\"countyname\"")) { if (type == 2) { if (name == provinceName && cityname == cityName) { QString county = getValue(value); county = county.mid(1, county.length() - 2); QStringList countys = county.split(" "); foreach (QString county, countys) { //数据中带了县城所在镇,要过滤 if (!county.endsWith("镇")) { result << county; } } break; } } } } #endif return result; }
硬件开发
2020-04-19 13:44:00
一、前言
在地图应用的相关项目中,在地图上标识一些设备点,并对点进行交互这个功能用的最多的,于是需要一套机制可以动态的添加、删除、清空、重置,重置的意思是将地图中的所有点的经纬度重新设置,其实就是先清空然后挨个重新添加所有点的信息,JS的异步交互功能非常强大,直接执行对应的JS函数就可以,没有必要刷新网页,最开始很多年前做的时候还不会JS,那时候想的最糟糕的办法就是写死在代码中,这样每次变动需要重新加载网页,后面发现那真是糟糕的办法,既然有异步刷新的办法为何不用呢,自从学会了JS异步刷新方法以后,索性将各种方法都改成了JS函数,传入对应的参数即可,参数尽可能的考虑到已知的各种各样的情况,方便用户自己添加。
在学习JS语法的时候发现其实程序都大同小异,正所谓一通百通,熟悉各大概的语法以后基本都可以上手,和C++最大的不同就是他没有数据类型的概念,作为解释性的语言,是在执行的时候自动去转换数据类型,工作都交给解释器做掉了,这样就大大方便了程序员,到处var即可,哪怕是数组啊对象啊,万物皆wav,只有当真正赋值的时候,才知道具体的数据类型。
二、功能特点 同时支持在线地图和离线地图两种模式。 同时支持webkit内核、webengine内核、IE内核。 支持设置多个标注点,信息包括名称、地址、经纬度。 可设置地图是否可单击、拖动、鼠标滚轮缩放。 可设置协议版本、秘钥、主题样式、中心坐标、中心城市、地理编码位置等。 可设置地图缩放比例和级别,缩略图、比例尺、路况信息等控件的可见。 支持地图交互,比如鼠标按下获取对应位置的经纬度。 支持查询路线,可设置起点位置、终点位置、路线模式、路线方式、路线方案(最少时间、最少换乘、最少步行、不乘地铁、最短距离、避开高速)。 可显示点线面工具,可直接在地图上划线、点、矩形、圆形等。 可设置行政区划,指定某个城市区域绘制图层,在线地图自动输出行政区划边界点集合到js文件给离线地图使用。 可静态或者动态添加多个覆盖物。支持点、折线、多边形、矩形、圆形、弧线、点聚合等。 函数接口友好和统一,使用简单方便,就一个类。 支持js动态交互添加点、删除点、清空点、重置点,不需要刷新页面。 支持任意Qt版本、任意系统、任意编译器。
三、体验地址 体验地址: https://pan.baidu.com/s/1uQsDQO5E5crUBN2J-nPeLQ 提取码:1jkp 文件名:bin_map.zip 国内站点: https://gitee.com/feiyangqingyun 国际站点: https://github.com/feiyangqingyun 个人主页: https://blog.csdn.net/feiyangqingyun 知乎主页: https://www.zhihu.com/people/feiyangqingyun/
四、效果图
五、相关代码 void MapBaiDu::addMarker(QStringList &list) { //动态添加点 //name 表示文本文字 //addr 表示地址 //point 表示经纬度坐标 //action 表示单击以后触发什么动作 0-不处理 1-自己弹框 2-发送信号 //animation 表示是否设置动画效果 0-不处理 1-跳动 2-坠落 //iconfile 表示图标文件路径,不设置则采用默认图标,注意图片的尺寸 //iconindex 表示图标对应在图片中的索引 list << QString(" function addMarker(name, addr, point, action, animation, iconfile, iconindex) {"); list << QString(" var list = point.split(',');"); //设置点经纬度坐标 list << QString(" var pot = new BMap.Point(list[0], list[1]);"); //设置文本文字 offset为对应标签显示的位置偏移值 list << QString(" var label = new BMap.Label(name, {\"offset\":new BMap.Size(20, -10)});"); //设置图标,不设置则采用默认图标 list << QString(" if (!iconfile) {"); list << QString(" var marker = new BMap.Marker(pot);"); list << QString(" } else if (iconfile == 'http://lbsyun.baidu.com/jsdemo/img/fox.gif') {"); list << QString(" var icon = new BMap.Icon(iconfile, new BMap.Size(300, 157));"); list << QString(" var marker = new BMap.Marker(pot, {icon: icon});"); list << QString(" } else if (iconfile == 'http://api.map.baidu.com/img/markers.png') {"); list << QString(" var icon = new BMap.Icon(iconfile, new BMap.Size(23, 25), {offset: new BMap.Size(10, 25), imageOffset: new BMap.Size(0, 0 - iconindex * 25)});"); list << QString(" var marker = new BMap.Marker(pot, {icon: icon});"); list << QString(" }"); list << QString(" map.addOverlay(marker);"); list << QString(" marker.setLabel(label);"); list << QString(" addClick(marker, name, addr, action);"); //弹跳效果-BMAP_ANIMATION_BOUNCE 坠落效果-BMAP_ANIMATION_DROP list << QString(" if (animation == 1) {"); list << QString(" marker.setAnimation(BMAP_ANIMATION_BOUNCE);"); list << QString(" } else if (animation == 2) {"); list << QString(" marker.setAnimation(BMAP_ANIMATION_DROP);"); list << QString(" }"); list << QString(" }"); } void MapBaiDu::deleteMarker(QStringList &list) { //动态删除点,如果name为空则删除所有 list << QString(" function deleteMarker(name) {"); list << QString(" var allOverlay = map.getOverlays();"); list << QString(" var len = allOverlay.length;"); list << QString(" for (var i = 0; i < len; i++) {"); list << QString(" if (name.length == 0) {"); list << QString(" map.removeOverlay(allOverlay[i]);"); list << QString(" } else if (allOverlay[i].getLabel().content == name) {"); list << QString(" map.removeOverlay(allOverlay[i]);"); list << QString(" break;"); list << QString(" }"); list << QString(" }"); list << QString(" }"); }
硬件开发
2020-04-18 09:50:00
几乎家家都有微波炉,但许多主妇都说用得不方便、不顺手,于是本应大有作为的微波炉却往往成了热剩菜剩饭的加热工具。实际上微波炉在会用的人手里,却像一个多变 “ 百宝箱 ” ,方便、省事、省时,而且用微波炉烹调食品不仅快捷还可以最大限度地保留食物中的营养成分。于是有了微波食品。
国家科学研究委员会的研究人员测量了用蒸、高压锅、煮或微 波炉烹煮的椰菜中的抗氧化剂含量。通过测试发现,在蒸的过程中,蔬菜中的抗氧化 剂几乎没有被破坏,但微波炉烹煮的椰菜中的抗氧化剂几乎消失殆尽,其他烹煮方法 对抗氧化剂的破坏程度介于两者之间。而抗氧化剂是通过破坏活性化学物质来保护 营养细胞的一种混合物。
微波食品是依靠微波加热或微波烹调即食的一类预制食品。微波食品为应用现代加工技术,对食品原料采用科学的配比和组合,预先加工成微波炉加热或调制使于食用的食品 , 即可用微波州加热烹制的食品。随着我国生活水平提高、社会老龄化趋势加速以及传统家庭模式及消费观念的变革,人们追求更高的生活品质 , 要求大幅降低家庭烹饪劳动强度 , 缩短烹饪时间,微波食品能减少人们对于饮食计划和准备的时间投入,从而备受众多消费者的青睐。目前,微波在我国食品行业的应用较多,但与欧美等发达国家相比 , 我国微波食品标准和规范参差不齐 , 品种比较单一,市场开发不足 , 缺乏行业弓 | 领性的规模企业和知名品牌 , 在军民融合发展等方面有显著差距。
由于微波烹调的速度很快,所以能较好地保存一些对热敏感和水溶性的维生素,如维生素 B 和 C ,矿物质和氨基酸的存有率也比其他烹任方法高。微波烹调是一种快速的烹调方法,加热时热在食品内部,所以加热均匀,不需翻炒,也不会使食物中的水分蒸发过多,因此,能保持食物的原色原味。
腌肉、腊肉、咸鱼和熏鸭等食品在加工过程中会产生亚硝胺,可能引起细胞的癌变。美国药理学家研究发现,将腌肉放在微波炉内烤 4-5 分钟,取出来时既香又脆、味美可口,而且用化学方法分析,找不到一点亚硝胺的痕迹。
食品在微波加热过程中,影响其加热特性的主要因素为 : 食品的介电特性,食品以及包装容器的大小、形状以及微波炉内的电场分布等。对于微波食品的开发而言,微波炉作为加热器具其内部的电场强度分布是由设计制造所确定的,而食品的介电性质、食品和容器的大小、形状则是由微波食品设计与开发决定的。
最后推荐两款应用在微波食品测试中专用的光纤传感器,由工采网从国外引进的高质量光纤温度传感器 - FOT-L-BA 和光纤温度传感器 - FOT-L-SD , FOT-L-SD 和 FOT-L-BA 是一类非常适合在极端环境下测量温度的光纤温度传感器,这种极端环境包括低温、核环境、微波和高强度的 RF 等。 FOT-L 集所有您期望从理想传感器器身获取的优良特性于一体。因此,即使在极端温度和不利的环境下,这类传感器依然能够提供高精度和可靠的温度测量。两种 FOT-L 温度传感器的主要特征都是完全不受 EMI 和 RFI 影响,同时,它们的尺寸小、针对危险环境内置安全装置、耐高温、耐腐蚀并且具备较高的精度。
硬件开发
2020-04-18 08:56:00
背景: 设备支持MQTT协议,主动推送设备消息到MQTT服务器。 设备定义了上线消息和下线消息,下线消息由"遗嘱"消息来实现,上线消息由设备发送,当客户端重连时,会主动发送上线请求。 业务服务器订阅MQTT服务器,来接受设备的消息。
问题: 发现上下线消息时混乱的,即业务服务器收到设备的下线消息,但是设备仍然在推送消息,似乎并没有下线。
思考: 谁来发送"遗嘱"下线消息?
MQTT服务器 MQTT服务器如何判断是否下线? 遗嘱消息发布的条件,包括但不限于: 服务端检测到了一个I/O错误或者网络故障。 客户端在保持连接(Keep Alive)的时间内未能通讯。 客户端没有先发送DISCONNECT报文直接关闭了网络连接。 由于协议错误服务端关闭了网络连接。 重启设备判断什么条件才会触发"遗嘱"?
断开设备后,MQTT服务器并不会 立刻发送 "遗嘱"消息,而是在一段时间后,即心跳时间(上述第二种)后才会触发"遗嘱"消息,这很好理解,断电后服务器啥消息都没收到,就会认为客户端其实没有断开。 wireshark能拯救世界!
重启设备后,一段时间,MQTT服务器下发了一条“下线”消息,我通过wireshark仔细分析,结果有一条非常有意思的消息!
MQTT服务器一直向设备发送"挥手“报文(断开连接第一步),但设备压根不管,仍然一直向MQTT服务器发送数据,而且MQTT服务器仍然一直接收数据。
思考: 这是谁的问题?
我知道"挥手"报文是tcp协议规定,也就是“挥手“的处理层是传输层处理,而传输层的处理肯定和MQTT服务器无关。至于传输层会出现bug这种事情,我简直不敢想。 也就是最大的可能是客户端。但是传输层出现问题,这怎么可能???? 定睛一看(每次在这种细节上,我总是不够细心,或许这是我的一个属性吧)
我观察了很久,突然发现一个非常“震惊”的消息。
我发现MQTT服务器回客户端"挥手"消息(灰色的那条)的端口是3340,而MQTT服务器回客户端ack消息的端口是3344,这怎么可能?
深思熟虑后,以及对比多家开源MQTT服务器后,我终于发现真正问题 。
MQTT的遗嘱判断非常简单,只要判断连接断开就立刻发送遗嘱消息,不需要判断全部存活的连接中,是否包含相同的clientID 对整个bug流程梳理。
设备在某段时间掉电,然后又重新连接了。此时设备发送上线消息,但是MQTT服务器在一段时间后,终于判断连接断开,从而发送下线消息。
解决方法: 放弃"遗嘱"消息,通过一段时间是否收到业务据来判断客户端是否“宕机”。 自己实现MQTT服务器,每次发送遗嘱消息时,对整个active连接判断是否有相同的clientID,我一直都是这么做的。 生成一个随机值A,对遗嘱消息进行修改,遗嘱消息都带上一个A。对上线消息进行修改,带上一个A。
当业务服务器,收到上线消息时,要把clientID和随机值A绑定存下来,获得下线消息时,从里面取出随机值,和当前的clientID绑定的随机值对比,如果相同则说明确实下线,如果不相同,则说明这是上个连接的值。
硬件开发
2020-04-17 11:20:00
一、前言
网上其实有很多各种各样的离线地图下载器,大部分都是要收费的,免费的要么是限制了下载的瓦片数量或者级别,要么是下载的瓦片图打上了水印,看起来很难看,由于经常需要用到离线地图,摆脱这个限制,特意花了点时间重新研究了瓦片地图的原理,做了个离线地图下载器,其实瓦片地图下载没有那么复杂,其实就是从开放的几个服务器地址组建要请求的瓦片地图的地址,发送请求以后会自动将图片返回给你,你只需要拿到图片数据保存成图片即可。
瓦片地图下载流程步骤如下: 获取可视区域或者行政区域的范围 拿到区域的左下角右上角经纬度坐标 根据层级数计算对应层级的瓦片数 自动生成下载瓦片地图的地址并发出请求 解析收到的数据保存成图片 更新对应界面的下载数量和进度 可选择对应保存的目录、全选层级、中途停止下载等 可选择是下载街道图还是卫星图等
二、功能特点 多线程同步下载多级别瓦片地图,不卡界面。 内置多个离线地图下载请求地址,自动随机选择一个发送请求。 下载地图类型同时支持街道图和卫星图。 自动计算可视区域或者行政区域的下载瓦片数量。 下载的级别可以自定义范围和选择。 每个瓦片下载完成都发送信号通知,参数包括下载用时。 可设置下载最大超时时间,超过了则丢弃跳到下一个下载任务。 实时显示下载进度,以及当前级别已经下载的瓦片数和总瓦片数。 下载过程中可以停止下载,下载完成自动统计总用时。 内置经纬度和屏幕坐标互相转换函数。 目前支持百度地图,其他地图比如谷歌地图、腾讯地图、高德地图可以定制。 函数接口友好和统一,使用简单方便,就一个类。 支持任意Qt版本、任意系统、任意编译器。
三、体验地址 体验地址: https://pan.baidu.com/s/1uQsDQO5E5crUBN2J-nPeLQ 提取码:1jkp 文件名:bin_map.zip 国内站点: https://gitee.com/feiyangqingyun 国际站点: https://github.com/feiyangqingyun 个人主页: https://blog.csdn.net/feiyangqingyun 知乎主页: https://www.zhihu.com/people/feiyangqingyun/
四、效果图
五、相关代码 void MapDownload::download(const QString &url, const QString &dirName, const QString &fileName, int zoom) { if (url.isEmpty()) { return; } //启动计时 QTime time; time.start(); //先判断文件夹是否存在,不存在则新建 QDir dir(dirName); if (!dir.exists()) { dir.mkpath(dirName); } //局部的事件循环,不卡主界面 QEventLoop eventLoop; QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url))); connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit())); //设置下载超时 QTimer timer; connect(&timer, SIGNAL(timeout()), &eventLoop, SLOT(quit())); timer.setSingleShot(true); timer.start(timeout); eventLoop.exec(); bool error = false; if (reply->bytesAvailable() > 0 && reply->error() == QNetworkReply::NoError) { //读取所有数据保存成文件 QByteArray data = reply->readAll(); QFile file(dirName + fileName); if (file.open(QFile::WriteOnly | QFile::Truncate)) { file.write(data); file.close(); } } else { //可以自行增加下载失败的统计 error = true; qDebug() << TIMEMS << "下载出错" << reply->errorString(); } int useTime = time.elapsed(); emit finsh(url, fileName, zoom, useTime, error); }
硬件开发
2020-04-17 08:50:00