http://mixdiy.com MixDiy Thu, 16 Jun 2022 05:38:56 +0000 zh-CN 1.2 http://mixdiy.com http://mixdiy.com 1 9 2 5 8 7 3 6 13 21 4 15 10 11 16 17 18 19 1 14 12 23 9 2 5 8 7 20 24 3 22 6 13 21 4 15 10 11 16 17 18 19 1 14 12 23nav_menu 24nav_menu https://wordpress.org/?v=6.0 http://mixdiy.com/wp-content/uploads/2021/12/cropped-mixdiy-11-logo-1-32x32.png http://mixdiy.com 32 32 <![CDATA[logo]]> http://mixdiy.com/logo/ Tue, 30 Nov 2021 11:21:03 +0000 http://mixdiy.com/wp-content/uploads/2021/11/logo.png 45 0 0 0 <![CDATA[cropped-logo.png]]> http://mixdiy.com/cropped-logo-png/ Tue, 30 Nov 2021 11:21:23 +0000 http://mixdiy.com/wp-content/uploads/2021/11/cropped-logo.png 46 0 0 0 <![CDATA[cropped-logo-1.png]]> http://mixdiy.com/cropped-logo-1-png/ Tue, 30 Nov 2021 11:21:55 +0000 http://mixdiy.com/wp-content/uploads/2021/11/cropped-logo-1.png 48 0 0 0 <![CDATA[cropped-cropped-logo-1.png]]> http://mixdiy.com/cropped-cropped-logo-1-png/ Tue, 30 Nov 2021 11:23:04 +0000 http://mixdiy.com/wp-content/uploads/2021/11/cropped-cropped-logo-1.png 50 0 0 0 <![CDATA[cropped-cropped-logo-1-1.png]]> http://mixdiy.com/cropped-cropped-logo-1-1-png/ Tue, 30 Nov 2021 11:23:43 +0000 http://mixdiy.com/wp-content/uploads/2021/11/cropped-cropped-logo-1-1.png 52 0 0 0 <![CDATA[cropped-cropped-logo-1-2.png]]> http://mixdiy.com/cropped-cropped-logo-1-2-png/ Tue, 30 Nov 2021 11:24:08 +0000 http://mixdiy.com/wp-content/uploads/2021/11/cropped-cropped-logo-1-2.png 53 0 0 0 <![CDATA[fritzing-0.9.6.linux_.AMD64.tar.bz2_]]> http://mixdiy.com/fritzing-0-9-6-linux_-amd64-tar-bz2_/ Thu, 16 Dec 2021 16:27:08 +0000 http://mixdiy.com/wp-content/uploads/2021/12/fritzing-0.9.6.linux_.AMD64.tar.bz2_.zip 116 0 0 0 <![CDATA[fritzing-0.9.6-175.64.msi_]]> http://mixdiy.com/fritzing-0-9-6-175-64-msi_/ Thu, 16 Dec 2021 16:27:14 +0000 http://mixdiy.com/wp-content/uploads/2021/12/fritzing-0.9.6-175.64.msi_.zip 117 0 0 0 <![CDATA[Fritzing-0.9.6-175.dmg_]]> http://mixdiy.com/fritzing-0-9-6-175-dmg_/ Thu, 16 Dec 2021 16:27:47 +0000 http://mixdiy.com/wp-content/uploads/2021/12/Fritzing-0.9.6-175.dmg_.zip 118 0 0 0 <![CDATA[fritzing-0.9.6.linux_.AMD64.tar.bz2_-1]]> http://mixdiy.com/fritzing-0-9-6-linux_-amd64-tar-bz2_-1/ Thu, 16 Dec 2021 16:39:39 +0000 http://mixdiy.com/wp-content/uploads/2021/12/fritzing-0.9.6.linux_.AMD64.tar.bz2_-1.zip 119 0 0 0 <![CDATA[logo3333]]> http://mixdiy.com/logo3333/ Mon, 27 Dec 2021 16:16:12 +0000 http://mixdiy.com/wp-content/uploads/2021/12/logo3333.jpg 188 0 0 0 <![CDATA[cropped-logo3333.jpg]]> http://mixdiy.com/cropped-logo3333-jpg/ Mon, 27 Dec 2021 16:16:47 +0000 http://mixdiy.com/wp-content/uploads/2021/12/cropped-logo3333.jpg 189 0 0 0 <![CDATA[cropped-cropped-logo3333.jpg]]> http://mixdiy.com/cropped-cropped-logo3333-jpg/ Mon, 27 Dec 2021 16:18:08 +0000 http://mixdiy.com/wp-content/uploads/2021/12/cropped-cropped-logo3333.jpg 191 0 0 0 <![CDATA[截屏2021-12-28-上午1.06.45]]> http://mixdiy.com/index.php/2021/12/28/gimp-2/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%881-06-45/ Mon, 27 Dec 2021 17:11:50 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午1.06.45.png 194 193 0 0 <![CDATA[截屏2021-12-28-上午1.06.45-1]]> http://mixdiy.com/index.php/2021/12/28/gimp-2/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%881-06-45-1/ Mon, 27 Dec 2021 17:16:08 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午1.06.45-1.png 195 193 0 0 <![CDATA[截屏2021-12-28-上午1.06.05]]> http://mixdiy.com/index.php/2021/12/28/gimp-2/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%881-06-05/ Mon, 27 Dec 2021 17:16:21 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午1.06.05.png 196 193 0 0 <![CDATA[abc]]> http://mixdiy.com/abc/ Mon, 27 Dec 2021 17:17:16 +0000 http://mixdiy.com/wp-content/uploads/2021/12/abc.jpg 197 0 0 0 <![CDATA[截屏2021-12-28 上午1.06.45]]> http://mixdiy.com/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%881-06-45-2/ Mon, 27 Dec 2021 17:17:30 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午1.06.45-2.png 198 0 0 0 <![CDATA[截屏2021-12-28 上午1.06.05]]> http://mixdiy.com/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%881-06-05-2/ Mon, 27 Dec 2021 17:17:39 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午1.06.05-1.png 199 0 0 0 <![CDATA[截屏2021-12-28 上午1.03.45]]> http://mixdiy.com/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%881-03-45/ Mon, 27 Dec 2021 17:17:47 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午1.03.45.png 200 0 0 0 <![CDATA[截屏2021-12-28 上午1.03.07]]> http://mixdiy.com/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%881-03-07/ Mon, 27 Dec 2021 17:17:54 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午1.03.07.png 201 0 0 0 <![CDATA[截屏2021-12-28 上午1.02.45]]> http://mixdiy.com/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%881-02-45/ Mon, 27 Dec 2021 17:18:00 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午1.02.45.png 202 0 0 0 <![CDATA[截屏2021-12-28 上午1.02.07]]> http://mixdiy.com/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%881-02-07/ Mon, 27 Dec 2021 17:18:06 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午1.02.07.png 203 0 0 0 <![CDATA[截屏2021-12-28 上午1.01.50]]> http://mixdiy.com/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%881-01-50/ Mon, 27 Dec 2021 17:18:13 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午1.01.50.png 204 0 0 0 <![CDATA[截屏2021-12-28 上午1.00.48]]> http://mixdiy.com/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%881-00-48/ Mon, 27 Dec 2021 17:18:29 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午1.00.48.png 205 0 0 0 <![CDATA[截屏2021-12-28 上午12.59.22]]> http://mixdiy.com/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%8812-59-22/ Mon, 27 Dec 2021 17:18:39 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午12.59.22.png 206 0 0 0 <![CDATA[截屏2021-12-28 上午12.58.31]]> http://mixdiy.com/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%8812-58-31/ Mon, 27 Dec 2021 17:18:46 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午12.58.31.png 207 0 0 0 <![CDATA[截屏2021-12-28-上午1.32.12]]> http://mixdiy.com/index.php/2021/12/28/gimp-2/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%881-32-12/ Mon, 27 Dec 2021 17:33:37 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午1.32.12.png 213 193 0 0 <![CDATA[截屏2021-12-28-上午1.32.30]]> http://mixdiy.com/index.php/2021/12/28/gimp-2/%e6%88%aa%e5%b1%8f2021-12-28-%e4%b8%8a%e5%8d%881-32-30/ Mon, 27 Dec 2021 17:33:47 +0000 http://mixdiy.com/wp-content/uploads/2021/12/截屏2021-12-28-上午1.32.30.png 214 193 0 0 <![CDATA[mixdiy-logo]]> http://mixdiy.com/mixdiy-logo/ Mon, 27 Dec 2021 17:36:14 +0000 http://mixdiy.com/wp-content/uploads/2021/12/mixdiy-logo.png 216 0 0 0 <![CDATA[cropped-mixdiy-logo.png]]> http://mixdiy.com/cropped-mixdiy-logo-png/ Mon, 27 Dec 2021 17:36:44 +0000 http://mixdiy.com/wp-content/uploads/2021/12/cropped-mixdiy-logo.png 217 0 0 0 <![CDATA[cropped-cropped-mixdiy-logo.png]]> http://mixdiy.com/cropped-cropped-mixdiy-logo-png/ Mon, 27 Dec 2021 17:37:30 +0000 http://mixdiy.com/wp-content/uploads/2021/12/cropped-cropped-mixdiy-logo.png 218 0 0 0 <![CDATA[mixdiy-1-logo]]> http://mixdiy.com/mixdiy-1-logo/ Mon, 27 Dec 2021 17:40:15 +0000 http://mixdiy.com/wp-content/uploads/2021/12/mixdiy-1-logo.jpg 220 0 0 0 <![CDATA[image]]> http://mixdiy.com/image/ Mon, 27 Dec 2021 17:40:35 +0000 http://mixdiy.com/wp-content/uploads/2021/12/image.jpeg 221 0 0 0 <![CDATA[image]]> http://mixdiy.com/image-2/ Mon, 27 Dec 2021 17:40:38 +0000 http://mixdiy.com/wp-content/uploads/2021/12/image-1.jpeg 222 0 0 0 <![CDATA[mixdiy-logo]]> http://mixdiy.com/mixdiy-logo-2/ Mon, 27 Dec 2021 17:43:17 +0000 http://mixdiy.com/wp-content/uploads/2021/12/mixdiy-logo-1.png 223 0 0 0 <![CDATA[cropped-mixdiy-logo-1.png]]> http://mixdiy.com/cropped-mixdiy-logo-1-png/ Mon, 27 Dec 2021 17:43:22 +0000 http://mixdiy.com/wp-content/uploads/2021/12/cropped-mixdiy-logo-1.png 224 0 0 0 <![CDATA[mixdiy-11-logo]]> http://mixdiy.com/mixdiy-11-logo/ Tue, 28 Dec 2021 05:45:45 +0000 http://mixdiy.com/wp-content/uploads/2021/12/mixdiy-11-logo.jpg 231 0 0 0 <![CDATA[cropped-mixdiy-11-logo.jpg]]> http://mixdiy.com/cropped-mixdiy-11-logo-jpg/ Tue, 28 Dec 2021 05:46:09 +0000 http://mixdiy.com/wp-content/uploads/2021/12/cropped-mixdiy-11-logo.jpg 232 0 0 0 <![CDATA[mixdiy-11-logo]]> http://mixdiy.com/mixdiy-11-logo-2/ Tue, 28 Dec 2021 05:50:22 +0000 http://mixdiy.com/wp-content/uploads/2021/12/mixdiy-11-logo.png 234 0 0 0 <![CDATA[cropped-mixdiy-11-logo.png]]> http://mixdiy.com/cropped-mixdiy-11-logo-png/ Tue, 28 Dec 2021 05:51:00 +0000 http://mixdiy.com/wp-content/uploads/2021/12/cropped-mixdiy-11-logo.png 235 0 0 0 <![CDATA[mixdiy-12-logo]]> http://mixdiy.com/mixdiy-12-logo/ Tue, 28 Dec 2021 05:52:48 +0000 http://mixdiy.com/wp-content/uploads/2021/12/mixdiy-12-logo.png 237 0 0 0 <![CDATA[mixdiy-12-logo]]> http://mixdiy.com/mixdiy-12-logo-2/ Tue, 28 Dec 2021 05:55:42 +0000 http://mixdiy.com/wp-content/uploads/2021/12/mixdiy-12-logo-1.png 240 0 0 0 <![CDATA[mixdiy-11-logo]]> http://mixdiy.com/mixdiy-11-logo-3/ Tue, 28 Dec 2021 05:56:32 +0000 http://mixdiy.com/wp-content/uploads/2021/12/mixdiy-11-logo-1.png 241 0 0 0 <![CDATA[cropped-mixdiy-11-logo-1.png]]> http://mixdiy.com/cropped-mixdiy-11-logo-1-png/ Tue, 28 Dec 2021 05:56:38 +0000 http://mixdiy.com/wp-content/uploads/2021/12/cropped-mixdiy-11-logo-1.png 242 0 0 0 <![CDATA[TV]]> http://mixdiy.com/index.php/2021/12/29/m3u/tv/ Wed, 29 Dec 2021 04:28:27 +0000 http://mixdiy.com/wp-content/uploads/2021/12/TV.txt 247 246 0 0 <![CDATA[IMG_20211127_131409]]> http://mixdiy.com/img_20211127_131409/ Wed, 29 Dec 2021 16:18:41 +0000 http://mixdiy.com/wp-content/uploads/2021/12/IMG_20211127_131409.jpg 254 0 0 0 <![CDATA[截屏2022-01-01-下午7.03.34]]> http://mixdiy.com/index.php/2022/01/01/astra/%e6%88%aa%e5%b1%8f2022-01-01-%e4%b8%8b%e5%8d%887-03-34/ Sat, 01 Jan 2022 11:10:44 +0000 http://mixdiy.com/wp-content/uploads/2022/01/截屏2022-01-01-下午7.03.34.png 259 258 0 0 <![CDATA[截屏2022-01-01-下午7.04.17]]> http://mixdiy.com/index.php/2022/01/01/astra/%e6%88%aa%e5%b1%8f2022-01-01-%e4%b8%8b%e5%8d%887-04-17/ Sat, 01 Jan 2022 11:11:01 +0000 http://mixdiy.com/wp-content/uploads/2022/01/截屏2022-01-01-下午7.04.17.png 260 258 0 0 <![CDATA[截屏2022-01-01-下午7.03.00]]> http://mixdiy.com/index.php/2022/01/01/astra/%e6%88%aa%e5%b1%8f2022-01-01-%e4%b8%8b%e5%8d%887-03-00/ Sat, 01 Jan 2022 11:14:16 +0000 http://mixdiy.com/wp-content/uploads/2022/01/截屏2022-01-01-下午7.03.00.png 261 258 0 0 <![CDATA[截屏2022-01-01-下午7.04.56]]> http://mixdiy.com/index.php/2022/01/01/astra/%e6%88%aa%e5%b1%8f2022-01-01-%e4%b8%8b%e5%8d%887-04-56/ Sat, 01 Jan 2022 11:14:56 +0000 http://mixdiy.com/wp-content/uploads/2022/01/截屏2022-01-01-下午7.04.56.png 262 258 0 0 <![CDATA[0AC001C4-711B-4991-A016-F347C9912363]]> http://mixdiy.com/index.php/2022/01/03/rip-shutdown/0ac001c4-711b-4991-a016-f347c9912363/ Mon, 03 Jan 2022 13:21:20 +0000 http://mixdiy.com/wp-content/uploads/2022/01/0AC001C4-711B-4991-A016-F347C9912363.jpeg 284 283 0 0 <![CDATA[截屏2022-01-05-上午11.43.27]]> http://mixdiy.com/index.php/2022/01/04/cockpit/%e6%88%aa%e5%b1%8f2022-01-05-%e4%b8%8a%e5%8d%8811-43-27/ Wed, 05 Jan 2022 03:50:51 +0000 http://mixdiy.com/wp-content/uploads/2022/01/截屏2022-01-05-上午11.43.27.png 325 298 0 0 <![CDATA[截屏2022-01-05-上午11.44.06]]> http://mixdiy.com/index.php/2022/01/04/cockpit/%e6%88%aa%e5%b1%8f2022-01-05-%e4%b8%8a%e5%8d%8811-44-06/ Wed, 05 Jan 2022 03:51:24 +0000 http://mixdiy.com/wp-content/uploads/2022/01/截屏2022-01-05-上午11.44.06.png 326 298 0 0 <![CDATA[截屏2022-01-05-上午11.55.07]]> http://mixdiy.com/index.php/2022/01/04/cockpit/%e6%88%aa%e5%b1%8f2022-01-05-%e4%b8%8a%e5%8d%8811-55-07/ Wed, 05 Jan 2022 03:55:56 +0000 http://mixdiy.com/wp-content/uploads/2022/01/截屏2022-01-05-上午11.55.07.png 329 298 0 0 <![CDATA[截屏2022-01-05-上午11.57.19]]> http://mixdiy.com/index.php/2022/01/04/cockpit/%e6%88%aa%e5%b1%8f2022-01-05-%e4%b8%8a%e5%8d%8811-57-19/ Wed, 05 Jan 2022 03:58:37 +0000 http://mixdiy.com/wp-content/uploads/2022/01/截屏2022-01-05-上午11.57.19.png 331 298 0 0 <![CDATA[IMG_2470]]> http://mixdiy.com/index.php/2022/01/07/power-off-and-on/img_2470/ Fri, 07 Jan 2022 06:47:02 +0000 http://mixdiy.com/wp-content/uploads/2022/01/IMG_2470.png 349 336 0 0 <![CDATA[IMG_2470-1]]> http://mixdiy.com/index.php/2022/01/07/power-off-and-on/img_2470-1/ Fri, 07 Jan 2022 06:50:38 +0000 http://mixdiy.com/wp-content/uploads/2022/01/IMG_2470-1.png 351 336 0 0 <![CDATA[IMG_2470-2]]> http://mixdiy.com/index.php/2022/01/07/power-off-and-on/img_2470-2/ Fri, 07 Jan 2022 06:50:42 +0000 http://mixdiy.com/wp-content/uploads/2022/01/IMG_2470-2.png 352 336 0 0 <![CDATA[IMG_2470-3]]> http://mixdiy.com/index.php/2022/01/07/power-off-and-on/img_2470-3/ Fri, 07 Jan 2022 07:04:08 +0000 http://mixdiy.com/wp-content/uploads/2022/01/IMG_2470-3.png 357 336 0 0 <![CDATA[IMG_2470]]> http://mixdiy.com/img_2470-4/ Fri, 07 Jan 2022 07:04:51 +0000 http://mixdiy.com/wp-content/uploads/2022/01/IMG_2470-4.png 358 0 0 0 <![CDATA[IMG_2470]]> http://mixdiy.com/img_2470-5/ Fri, 07 Jan 2022 07:05:23 +0000 http://mixdiy.com/wp-content/uploads/2022/01/IMG_2470.jpg 359 0 0 0 <![CDATA[bf8e5378e27b1c37437c2c78e045a0b3]]> http://mixdiy.com/bf8e5378e27b1c37437c2c78e045a0b3/ Fri, 07 Jan 2022 12:35:35 +0000 http://mixdiy.com/wp-content/uploads/2022/01/bf8e5378e27b1c37437c2c78e045a0b3.jpg 367 0 0 0 <![CDATA[2020WWDC-全球开发者大会-完整回放]]> http://mixdiy.com/index.php/2022/01/10/videotest/2020wwdc-%e5%85%a8%e7%90%83%e5%bc%80%e5%8f%91%e8%80%85%e5%a4%a7%e4%bc%9a-%e5%ae%8c%e6%95%b4%e5%9b%9e%e6%94%be/ Mon, 10 Jan 2022 14:21:15 +0000 http://mixdiy.com/wp-content/uploads/2022/01/2020WWDC-全球开发者大会-完整回放.mp4 384 383 0 0 <![CDATA[Fritzing-0.9.9b348]]> http://mixdiy.com/index.php/2022/01/16/filetype/fritzing-0-9-9b348/ Sat, 15 Jan 2022 16:36:51 +0000 http://mixdiy.com/wp-content/uploads/2022/01/Fritzing-0.9.9b348.dmg 418 417 0 0 <![CDATA[fritzing-0.9.9-l348-f0af53a9]]> http://mixdiy.com/index.php/2022/01/16/filetype/fritzing-0-9-9-l348-f0af53a9/ Sat, 15 Jan 2022 16:46:45 +0000 http://mixdiy.com/wp-content/uploads/2022/01/fritzing-0.9.9-l348-f0af53a9.appimage 422 417 0 0 <![CDATA[fritzing-0.9.9-l348-f0af53a9-win32]]> http://mixdiy.com/index.php/2022/01/16/filetype/fritzing-0-9-9-l348-f0af53a9-win32/ Sat, 15 Jan 2022 16:48:35 +0000 http://mixdiy.com/wp-content/uploads/2022/01/fritzing-0.9.9-l348-f0af53a9-win32.msi 423 417 0 0 <![CDATA[fritzing-0.9.9-l348-f0af53a9-win64]]> http://mixdiy.com/index.php/2022/01/16/filetype/fritzing-0-9-9-l348-f0af53a9-win64/ Sat, 15 Jan 2022 16:48:44 +0000 http://mixdiy.com/wp-content/uploads/2022/01/fritzing-0.9.9-l348-f0af53a9-win64.msi 424 417 0 0 <![CDATA[trim.8F13C59D-88AF-4387-A07C-53D334414612]]> http://mixdiy.com/trim-8f13c59d-88af-4387-a07c-53d334414612/ Sat, 15 Jan 2022 17:43:10 +0000 http://mixdiy.com/wp-content/uploads/2022/01/trim.8F13C59D-88AF-4387-A07C-53D334414612.mov 446 0 0 0 <![CDATA[服装设计]]> http://mixdiy.com/index.php/2022/01/16/test/%e6%9c%8d%e8%a3%85%e8%ae%be%e8%ae%a1/ Sun, 16 Jan 2022 10:57:13 +0000 http://mixdiy.com/wp-content/uploads/2022/01/服装设计.jpg 457 416 0 0 <![CDATA[debian-reference.zh-cn]]> http://mixdiy.com/index.php/2022/01/22/debian/debian-reference-zh-cn/ Sat, 22 Jan 2022 08:58:16 +0000 http://mixdiy.com/wp-content/uploads/2022/01/debian-reference.zh-cn.pdf 472 471 0 0 <![CDATA[2022-01-23-084225_686x793_scrot]]> http://mixdiy.com/index.php/2022/01/22/judge/2022-01-23-084225_686x793_scrot/ Sun, 23 Jan 2022 00:44:41 +0000 http://mixdiy.com/wp-content/uploads/2022/01/2022-01-23-084225_686x793_scrot.png 492 479 0 0 <![CDATA[2022-01-23-084914_974x1013_scrot]]> http://mixdiy.com/index.php/2022/01/22/judge/2022-01-23-084914_974x1013_scrot/ Sun, 23 Jan 2022 00:51:34 +0000 http://mixdiy.com/wp-content/uploads/2022/01/2022-01-23-084914_974x1013_scrot.png 494 479 0 0 <![CDATA[2022-01-23-084914_974x1013_scrot-1]]> http://mixdiy.com/index.php/2022/01/22/judge/2022-01-23-084914_974x1013_scrot-1/ Sun, 23 Jan 2022 00:52:02 +0000 http://mixdiy.com/wp-content/uploads/2022/01/2022-01-23-084914_974x1013_scrot-1.png 495 479 0 0 <![CDATA[2022-01-23-084931_500x534_scrot]]> http://mixdiy.com/index.php/2022/01/22/judge/2022-01-23-084931_500x534_scrot/ Sun, 23 Jan 2022 00:52:10 +0000 http://mixdiy.com/wp-content/uploads/2022/01/2022-01-23-084931_500x534_scrot.png 496 479 0 0 <![CDATA[2022-01-23-111245_500x500_scrot]]> http://mixdiy.com/index.php/2022/01/22/judge/2022-01-23-111245_500x500_scrot/ Sun, 23 Jan 2022 03:18:51 +0000 http://mixdiy.com/wp-content/uploads/2022/01/2022-01-23-111245_500x500_scrot.png 503 479 0 0 <![CDATA[2022-01-23-124840_500x500_scrot]]> http://mixdiy.com/index.php/2022/01/22/judge/2022-01-23-124840_500x500_scrot/ Sun, 23 Jan 2022 04:51:05 +0000 http://mixdiy.com/wp-content/uploads/2022/01/2022-01-23-124840_500x500_scrot.png 507 479 0 0 <![CDATA[2022-01-23-124840_500x500_scrot-1]]> http://mixdiy.com/index.php/2022/01/22/judge/2022-01-23-124840_500x500_scrot-1/ Sun, 23 Jan 2022 04:51:25 +0000 http://mixdiy.com/wp-content/uploads/2022/01/2022-01-23-124840_500x500_scrot-1.png 508 479 0 0 <![CDATA[1]]> http://mixdiy.com/index.php/2022/01/24/pyqt5-in-pycharm/attachment/1/ Mon, 24 Jan 2022 15:19:31 +0000 http://mixdiy.com/wp-content/uploads/2022/01/1.webp 515 514 0 0 <![CDATA[2]]> http://mixdiy.com/index.php/2022/01/24/pyqt5-in-pycharm/attachment/2/ Mon, 24 Jan 2022 15:19:49 +0000 http://mixdiy.com/wp-content/uploads/2022/01/2.webp 516 514 0 0 <![CDATA[3]]> http://mixdiy.com/index.php/2022/01/24/pyqt5-in-pycharm/attachment/3/ Mon, 24 Jan 2022 15:20:10 +0000 http://mixdiy.com/wp-content/uploads/2022/01/3.webp 517 514 0 0 <![CDATA[4]]> http://mixdiy.com/index.php/2022/01/24/pyqt5-in-pycharm/attachment/4/ Mon, 24 Jan 2022 15:20:22 +0000 http://mixdiy.com/wp-content/uploads/2022/01/4.webp 518 514 0 0 <![CDATA[5]]> http://mixdiy.com/index.php/2022/01/24/pyqt5-in-pycharm/attachment/5/ Mon, 24 Jan 2022 15:20:32 +0000 http://mixdiy.com/wp-content/uploads/2022/01/5.webp 519 514 0 0 <![CDATA[6]]> http://mixdiy.com/index.php/2022/01/24/pyqt5-in-pycharm/attachment/6/ Mon, 24 Jan 2022 15:20:43 +0000 http://mixdiy.com/wp-content/uploads/2022/01/6.webp 520 514 0 0 <![CDATA[PyCharm-2021.3.x临时激活码]]> http://mixdiy.com/pycharm-2021-3-x%e4%b8%b4%e6%97%b6%e6%bf%80%e6%b4%bb%e7%a0%81/ Tue, 25 Jan 2022 11:04:02 +0000 http://mixdiy.com/wp-content/uploads/2022/01/PyCharm-2021.3.x临时激活码.txt 530 0 0 0 <![CDATA[2022.1.25付款申请单明细-更新说明]]> http://mixdiy.com/2022-1-25%e4%bb%98%e6%ac%be%e7%94%b3%e8%af%b7%e5%8d%95%e6%98%8e%e7%bb%86-%e6%9b%b4%e6%96%b0%e8%af%b4%e6%98%8e/ Tue, 25 Jan 2022 11:04:42 +0000 http://mixdiy.com/wp-content/uploads/2022/01/2022.1.25付款申请单明细-更新说明.xlsx 532 0 0 0 <![CDATA[dial_1]]> http://mixdiy.com/index.php/2022/01/26/exchangefile/dial_1/ Wed, 26 Jan 2022 08:56:40 +0000 http://mixdiy.com/wp-content/uploads/2022/01/dial_1.zip 542 541 0 0 <![CDATA[gui_pin.py]]> http://mixdiy.com/index.php/2022/01/27/rpipin/gui_pin-py/ Wed, 26 Jan 2022 16:03:07 +0000 http://mixdiy.com/wp-content/uploads/2022/01/gui_pin.py.tar 550 549 0 0 <![CDATA[2thread]]> http://mixdiy.com/index.php/2022/01/28/thread/2thread/ Fri, 28 Jan 2022 03:11:44 +0000 http://mixdiy.com/wp-content/uploads/2022/01/2thread.zip 567 566 0 0 <![CDATA[gui_1280_400_random_dial.py]]> http://mixdiy.com/index.php/2022/01/31/gui1920-for-rpi/gui_1280_400_random_dial-py/ Sun, 30 Jan 2022 16:37:59 +0000 http://mixdiy.com/wp-content/uploads/2022/01/gui_1280_400_random_dial.py.tar 580 575 0 0 <![CDATA[54202DEF-DEE2-40BF-BDA5-6D5C026F183F]]> http://mixdiy.com/index.php/2022/02/01/linux-lite/54202def-dee2-40bf-bda5-6d5c026f183f/ Tue, 01 Feb 2022 05:12:58 +0000 http://mixdiy.com/wp-content/uploads/2022/02/54202DEF-DEE2-40BF-BDA5-6D5C026F183F.jpeg 584 582 0 0 <![CDATA[F9A37F89-7DE7-4C5C-AAFF-5FA909FB61FE]]> http://mixdiy.com/index.php/2022/02/01/linux-lite/f9a37f89-7de7-4c5c-aaff-5fa909fb61fe/ Tue, 01 Feb 2022 05:16:48 +0000 http://mixdiy.com/wp-content/uploads/2022/02/F9A37F89-7DE7-4C5C-AAFF-5FA909FB61FE.jpeg 588 582 0 0 <![CDATA[3B7578C0-C0E6-4662-BC6B-9443C60D9342]]> http://mixdiy.com/index.php/2022/02/05/64bit-rpi/3b7578c0-c0e6-4662-bc6b-9443c60d9342/ Sat, 05 Feb 2022 14:53:16 +0000 http://mixdiy.com/wp-content/uploads/2022/02/3B7578C0-C0E6-4662-BC6B-9443C60D9342.jpeg 595 594 0 0 <![CDATA[7D2C5A53-0237-4900-AAB6-8223AE31FC40]]> http://mixdiy.com/index.php/2022/02/05/64bit-rpi/7d2c5a53-0237-4900-aab6-8223ae31fc40/ Sat, 05 Feb 2022 14:53:45 +0000 http://mixdiy.com/wp-content/uploads/2022/02/7D2C5A53-0237-4900-AAB6-8223AE31FC40.jpeg 596 594 0 0 <![CDATA[87ACA436-9B18-431C-B3F5-D698D0C6EB41]]> http://mixdiy.com/index.php/2022/02/05/64bit-rpi/87aca436-9b18-431c-b3f5-d698d0c6eb41/ Sat, 05 Feb 2022 14:54:05 +0000 http://mixdiy.com/wp-content/uploads/2022/02/87ACA436-9B18-431C-B3F5-D698D0C6EB41.jpeg 597 594 0 0 <![CDATA[switcher]]> http://mixdiy.com/index.php/2022/01/31/gui1920-for-rpi/switcher/ Sun, 06 Feb 2022 02:51:46 +0000 http://mixdiy.com/wp-content/uploads/2022/02/switcher.pdf 600 575 0 0 <![CDATA[F9A37F89-7DE7-4C5C-AAFF-5FA909FB61FE]]> http://mixdiy.com/f9a37f89-7de7-4c5c-aaff-5fa909fb61fe-2/ Tue, 08 Feb 2022 01:46:02 +0000 http://mixdiy.com/wp-content/uploads/2022/02/F9A37F89-7DE7-4C5C-AAFF-5FA909FB61FE-edited.jpeg 617 0 0 0 <![CDATA[Pico]]> http://mixdiy.com/index.php/2022/02/08/rpipico-pcb/pico/ Tue, 08 Feb 2022 14:14:23 +0000 http://mixdiy.com/wp-content/uploads/2022/02/Pico.zip 624 623 0 0 <![CDATA[gui_1280_400_random_dial_autostart.py]]> http://mixdiy.com/index.php/2022/02/09/power-on-start-gui-gpio/gui_1280_400_random_dial_autostart-py/ Tue, 08 Feb 2022 17:21:02 +0000 http://mixdiy.com/wp-content/uploads/2022/02/gui_1280_400_random_dial_autostart.py.tar 631 630 0 0 <![CDATA[cmdline]]> http://mixdiy.com/index.php/2022/02/09/startup-rpi/cmdline/ Wed, 09 Feb 2022 04:06:16 +0000 http://mixdiy.com/wp-content/uploads/2022/02/cmdline.txt 640 635 0 0 <![CDATA[gui_1280_400_random_dial]]> http://mixdiy.com/index.php/2022/02/09/1920400program/gui_1280_400_random_dial/ Wed, 09 Feb 2022 07:56:18 +0000 http://mixdiy.com/wp-content/uploads/2022/02/gui_1280_400_random_dial.tar 655 654 0 0 <![CDATA[ico]]> http://mixdiy.com/index.php/2022/02/09/ico/ico/ Wed, 09 Feb 2022 10:29:24 +0000 http://mixdiy.com/wp-content/uploads/2022/02/ico.zip 660 659 0 0 <![CDATA[3892EB18-69AC-46A6-942C-48DB4FFC5B5C]]> http://mixdiy.com/index.php/2022/02/11/boot-from-internet/3892eb18-69ac-46a6-942c-48db4ffc5b5c/ Fri, 11 Feb 2022 14:01:27 +0000 http://mixdiy.com/wp-content/uploads/2022/02/3892EB18-69AC-46A6-942C-48DB4FFC5B5C.png 684 680 0 0 <![CDATA[CF5377F5-497E-4BDF-A7F7-ADCB294F2244]]> http://mixdiy.com/index.php/2022/02/11/boot-from-internet/cf5377f5-497e-4bdf-a7f7-adcb294f2244/ Fri, 11 Feb 2022 14:01:39 +0000 http://mixdiy.com/wp-content/uploads/2022/02/CF5377F5-497E-4BDF-A7F7-ADCB294F2244.png 685 680 0 0 <![CDATA[91B7553C-C825-488F-A539-7B8B237DAAED]]> http://mixdiy.com/index.php/2022/02/11/boot-from-internet/91b7553c-c825-488f-a539-7b8b237daaed/ Fri, 11 Feb 2022 14:01:47 +0000 http://mixdiy.com/wp-content/uploads/2022/02/91B7553C-C825-488F-A539-7B8B237DAAED.png 686 680 0 0 <![CDATA[3FEBF225-E821-4CB9-BBFF-2BEBDC490944]]> http://mixdiy.com/index.php/2022/02/11/boot-from-internet/3febf225-e821-4cb9-bbff-2bebdc490944/ Fri, 11 Feb 2022 14:01:55 +0000 http://mixdiy.com/wp-content/uploads/2022/02/3FEBF225-E821-4CB9-BBFF-2BEBDC490944.png 687 680 0 0 <![CDATA[C1C7EC38-A06A-4CB5-B79B-76D7F98DC73C]]> http://mixdiy.com/index.php/2022/02/11/boot-from-internet/c1c7ec38-a06a-4cb5-b79b-76d7f98dc73c/ Fri, 11 Feb 2022 14:02:06 +0000 http://mixdiy.com/wp-content/uploads/2022/02/C1C7EC38-A06A-4CB5-B79B-76D7F98DC73C.png 688 680 0 0 <![CDATA[EE3E45C1-567B-4373-BA40-F412C65FBA95]]> http://mixdiy.com/index.php/2022/02/11/boot-from-internet/ee3e45c1-567b-4373-ba40-f412c65fba95/ Fri, 11 Feb 2022 14:02:18 +0000 http://mixdiy.com/wp-content/uploads/2022/02/EE3E45C1-567B-4373-BA40-F412C65FBA95.png 689 680 0 0 <![CDATA[20160319151106360]]> http://mixdiy.com/index.php/2022/02/14/rpi-gpio/attachment/20160319151106360/ Mon, 14 Feb 2022 14:09:43 +0000 http://mixdiy.com/wp-content/uploads/2022/02/20160319151106360.png 696 693 0 0 <![CDATA[20160319151338995]]> http://mixdiy.com/index.php/2022/02/14/rpi-gpio/attachment/20160319151338995/ Mon, 14 Feb 2022 14:10:20 +0000 http://mixdiy.com/wp-content/uploads/2022/02/20160319151338995.png 697 693 0 0 <![CDATA[20160319151355449]]> http://mixdiy.com/index.php/2022/02/14/rpi-gpio/attachment/20160319151355449/ Mon, 14 Feb 2022 14:10:45 +0000 http://mixdiy.com/wp-content/uploads/2022/02/20160319151355449.png 698 693 0 0 <![CDATA[ab]]> http://mixdiy.com/index.php/2022/02/14/rpi-gpio/ab/ Mon, 14 Feb 2022 14:25:11 +0000 http://mixdiy.com/wp-content/uploads/2022/02/ab.jpeg 703 693 0 0 <![CDATA[7735599a90bd2b8237d34e0418e44cc5]]> http://mixdiy.com/7735599a90bd2b8237d34e0418e44cc5/ Fri, 18 Feb 2022 15:10:17 +0000 http://mixdiy.com/wp-content/uploads/2022/02/7735599a90bd2b8237d34e0418e44cc5.png 707 0 0 0 <![CDATA[652ed4b436e6adfdaa42130d3b31e543]]> http://mixdiy.com/652ed4b436e6adfdaa42130d3b31e543/ Fri, 18 Feb 2022 15:10:58 +0000 http://mixdiy.com/wp-content/uploads/2022/02/652ed4b436e6adfdaa42130d3b31e543.png 708 0 0 0 <![CDATA[842bb4ca351b8a728ee189cad73f0471]]> http://mixdiy.com/842bb4ca351b8a728ee189cad73f0471/ Fri, 18 Feb 2022 15:11:29 +0000 http://mixdiy.com/wp-content/uploads/2022/02/842bb4ca351b8a728ee189cad73f0471.png 709 0 0 0 <![CDATA[937bc3ff272b612b51241c615176be9b]]> http://mixdiy.com/937bc3ff272b612b51241c615176be9b/ Fri, 18 Feb 2022 15:12:09 +0000 http://mixdiy.com/wp-content/uploads/2022/02/937bc3ff272b612b51241c615176be9b.png 710 0 0 0 <![CDATA[997e8c4833ef77c90e09ce556af6c6fb]]> http://mixdiy.com/997e8c4833ef77c90e09ce556af6c6fb/ Fri, 18 Feb 2022 15:12:36 +0000 http://mixdiy.com/wp-content/uploads/2022/02/997e8c4833ef77c90e09ce556af6c6fb.png 711 0 0 0 <![CDATA[7735599a90bd2b8237d34e0418e44cc5-1]]> http://mixdiy.com/7735599a90bd2b8237d34e0418e44cc5-1/ Fri, 18 Feb 2022 15:12:55 +0000 http://mixdiy.com/wp-content/uploads/2022/02/7735599a90bd2b8237d34e0418e44cc5-1.png 712 0 0 0 <![CDATA[gui_1280_400_random_dial_autostart]]> http://mixdiy.com/index.php/2022/02/24/autodial/gui_1280_400_random_dial_autostart/ Thu, 24 Feb 2022 10:45:37 +0000 http://mixdiy.com/wp-content/uploads/2022/02/gui_1280_400_random_dial_autostart.tar 728 727 0 0 <![CDATA[2022-02-20-03.jpg]]> http://mixdiy.com/2022-02-20-03-jpg/ Thu, 24 Feb 2022 12:06:59 +0000 http://mixdiy.com/wp-content/uploads/2022/02/2022-02-20-03.jpg.tar 731 0 0 0 <![CDATA[gettimeab.py]]> http://mixdiy.com/index.php/2022/02/25/esp32-rpi-poweronoff/gettimeab-py/ Fri, 25 Feb 2022 07:29:45 +0000 http://mixdiy.com/wp-content/uploads/2022/02/gettimeab.py.zip 734 733 0 0 <![CDATA[2022-02-25-16-09.jpg]]> http://mixdiy.com/2022-02-25-16-09-jpg/ Fri, 25 Feb 2022 10:59:33 +0000 http://mixdiy.com/wp-content/uploads/2022/02/2022-02-25-16-09.jpg.tar 739 0 0 0 <![CDATA[main.py]]> http://mixdiy.com/index.php/2022/02/25/esp32-rpi-poweronoff/main-py/ Fri, 25 Feb 2022 16:24:44 +0000 http://mixdiy.com/wp-content/uploads/2022/02/main.py.zip 742 733 0 0 <![CDATA[main.py-1]]> http://mixdiy.com/index.php/2022/02/25/esp32-rpi-poweronoff/main-py-1/ Fri, 25 Feb 2022 16:31:18 +0000 http://mixdiy.com/wp-content/uploads/2022/02/main.py-1.zip 744 733 0 0 <![CDATA[main.py]]> http://mixdiy.com/index.php/2022/02/27/jj/main-py-2/ Sun, 27 Feb 2022 01:07:04 +0000 http://mixdiy.com/wp-content/uploads/2022/02/main.py.tar 756 755 0 0 <![CDATA[esp32readuart.py]]> http://mixdiy.com/index.php/2022/02/27/jj/esp32readuart-py/ Sun, 27 Feb 2022 02:33:53 +0000 http://mixdiy.com/wp-content/uploads/2022/02/esp32readuart.py.zip 758 755 0 0 <![CDATA[commuart.py]]> http://mixdiy.com/index.php/2022/02/27/jj/commuart-py/ Sun, 27 Feb 2022 04:36:46 +0000 http://mixdiy.com/wp-content/uploads/2022/02/commuart.py.tar 761 755 0 0 <![CDATA[main.py-1]]> http://mixdiy.com/index.php/2022/02/27/jj/main-py-1-2/ Sun, 27 Feb 2022 05:15:47 +0000 http://mixdiy.com/wp-content/uploads/2022/02/main.py-1.tar 767 755 0 0 <![CDATA[srchttp-suwish.com-resources-2017-raspi_use_10.pngreferhttp-suwish.comapp2002sizef999910000qa80n0g0nfmtjpeg]]> http://mixdiy.com/index.php/2022/02/27/jj/srchttp-suwish-com-resources-2017-raspi_use_10-pngreferhttp-suwish-comapp2002sizef999910000qa80n0g0nfmtjpeg/ Sun, 27 Feb 2022 05:41:19 +0000 http://mixdiy.com/wp-content/uploads/2022/02/srchttp-suwish.com-resources-2017-raspi_use_10.pngreferhttp-suwish.comapp2002sizef999910000qa80n0g0nfmtjpeg.jpeg 771 755 0 0 <![CDATA[2022-02-28-08-30.jpg]]> http://mixdiy.com/2022-02-28-08-30-jpg/ Mon, 28 Feb 2022 01:10:12 +0000 http://mixdiy.com/wp-content/uploads/2022/02/2022-02-28-08-30.jpg.tar 775 0 0 0 <![CDATA[2022-02-28-19-49.jpg]]> http://mixdiy.com/2022-02-28-19-49-jpg/ Wed, 02 Mar 2022 14:10:18 +0000 http://mixdiy.com/wp-content/uploads/2022/03/2022-02-28-19-49.jpg.tar 782 0 0 0 <![CDATA[321]]> http://mixdiy.com/321/ Wed, 02 Mar 2022 15:34:37 +0000 http://mixdiy.com/wp-content/uploads/2022/03/321.mp4 785 0 0 0 <![CDATA[微信图片_20220303154249]]> http://mixdiy.com/index.php/2022/03/03/esp32-c3-debug/%e5%be%ae%e4%bf%a1%e5%9b%be%e7%89%87_20220303154249/ Thu, 03 Mar 2022 07:44:25 +0000 http://mixdiy.com/wp-content/uploads/2022/03/微信图片_20220303154249.jpg 796 795 0 0 <![CDATA[微信图片_20220303154240]]> http://mixdiy.com/index.php/2022/03/03/esp32-c3-debug/%e5%be%ae%e4%bf%a1%e5%9b%be%e7%89%87_20220303154240/ Thu, 03 Mar 2022 07:44:37 +0000 http://mixdiy.com/wp-content/uploads/2022/03/微信图片_20220303154240.jpg 797 795 0 0 <![CDATA[2022-03-02-21-09.jpg]]> http://mixdiy.com/index.php/2022/03/02/one-day-in-the-street-conner/2022-03-02-21-09-jpg/ Fri, 04 Mar 2022 14:43:04 +0000 http://mixdiy.com/wp-content/uploads/2022/03/2022-03-02-21-09.jpg.tar 813 788 0 0 <![CDATA[esp32-c3快速参考]]> http://mixdiy.com/index.php/2022/03/13/esp32-refrance/esp32-c3%e5%bf%ab%e9%80%9f%e5%8f%82%e8%80%83/ Sun, 13 Mar 2022 14:46:05 +0000 http://mixdiy.com/wp-content/uploads/2022/03/esp32-c3快速参考.pdf 847 842 0 0 <![CDATA[ESP32-S2-快速参考手册]]> http://mixdiy.com/index.php/2022/03/13/esp32-refrance/esp32-s2-%e5%bf%ab%e9%80%9f%e5%8f%82%e8%80%83%e6%89%8b%e5%86%8c/ Sun, 13 Mar 2022 15:00:12 +0000 http://mixdiy.com/wp-content/uploads/2022/03/ESP32-S2-快速参考手册.pdf 849 842 0 0 <![CDATA[danci]]> http://mixdiy.com/index.php/2022/03/14/get-english-words/danci/ Mon, 14 Mar 2022 14:38:49 +0000 http://mixdiy.com/wp-content/uploads/2022/03/danci.txt 856 855 0 0 <![CDATA[2022-03-16-15-45]]> http://mixdiy.com/2022-03-16-15-45/ Wed, 16 Mar 2022 07:50:12 +0000 http://mixdiy.com/wp-content/uploads/2022/03/2022-03-16-15-45.jpg 861 0 0 0 <![CDATA[2022-03-16-15-43]]> http://mixdiy.com/2022-03-16-15-43/ Wed, 16 Mar 2022 07:50:28 +0000 http://mixdiy.com/wp-content/uploads/2022/03/2022-03-16-15-43.jpg 862 0 0 0 <![CDATA[2022-03-16-15-44]]> http://mixdiy.com/2022-03-16-15-44/ Wed, 16 Mar 2022 07:51:39 +0000 http://mixdiy.com/wp-content/uploads/2022/03/2022-03-16-15-44.jpg 865 0 0 0 <![CDATA[2022-03-16-15-45-1]]> http://mixdiy.com/2022-03-16-15-45-1/ Wed, 16 Mar 2022 07:51:51 +0000 http://mixdiy.com/wp-content/uploads/2022/03/2022-03-16-15-45-1.jpg 866 0 0 0 <![CDATA[WechatIMG68]]> http://mixdiy.com/index.php/2022/03/20/fritzing-user-manal/wechatimg68/ Sun, 20 Mar 2022 15:00:38 +0000 http://mixdiy.com/wp-content/uploads/2022/03/WechatIMG68.jpeg 875 872 0 0 <![CDATA[fritzing-preview-pcb-394c7b377466bcfde399554ccad2a4a9a6c85d15edcd618b072dd4da99799c5c]]> http://mixdiy.com/index.php/2022/03/20/fritzing-user-manal/fritzing-preview-pcb-394c7b377466bcfde399554ccad2a4a9a6c85d15edcd618b072dd4da99799c5c/ Sun, 20 Mar 2022 15:01:00 +0000 http://mixdiy.com/wp-content/uploads/2022/03/fritzing-preview-pcb-394c7b377466bcfde399554ccad2a4a9a6c85d15edcd618b072dd4da99799c5c.webp 876 872 0 0 <![CDATA[fritzing-preview-bb-d6833748595f9f6b12ab9dcd165886f8f7a9970e3a6e49a6face8196a83ab288]]> http://mixdiy.com/index.php/2022/03/20/fritzing-user-manal/fritzing-preview-bb-d6833748595f9f6b12ab9dcd165886f8f7a9970e3a6e49a6face8196a83ab288/ Sun, 20 Mar 2022 15:01:08 +0000 http://mixdiy.com/wp-content/uploads/2022/03/fritzing-preview-bb-d6833748595f9f6b12ab9dcd165886f8f7a9970e3a6e49a6face8196a83ab288.webp 877 872 0 0 <![CDATA[fritzing-preview-schem-9240bfebfd0e2384152ecec2b700c4aeee4adfa066f14b76e76c9f2c50ab8a2a]]> http://mixdiy.com/index.php/2022/03/20/fritzing-user-manal/fritzing-preview-schem-9240bfebfd0e2384152ecec2b700c4aeee4adfa066f14b76e76c9f2c50ab8a2a/ Sun, 20 Mar 2022 15:01:17 +0000 http://mixdiy.com/wp-content/uploads/2022/03/fritzing-preview-schem-9240bfebfd0e2384152ecec2b700c4aeee4adfa066f14b76e76c9f2c50ab8a2a.webp 878 872 0 0 <![CDATA[onoff-1.fzz_]]> http://mixdiy.com/index.php/2022/03/22/fritzing-onoff-pcb/onoff-1-fzz_/ Tue, 22 Mar 2022 09:37:57 +0000 http://mixdiy.com/wp-content/uploads/2022/03/onoff-1.fzz_.zip 891 890 0 0 <![CDATA[6Pin贴片_V1]]> http://mixdiy.com/index.php/2022/03/22/fritzing-onoff-pcb/6pin%e8%b4%b4%e7%89%87_v1/ Tue, 22 Mar 2022 09:44:01 +0000 http://mixdiy.com/wp-content/uploads/2022/03/6Pin贴片_V1.zip 895 890 0 0 <![CDATA[7Pin防呆贴片.fzz_]]> http://mixdiy.com/index.php/2022/03/22/fritzing-onoff-pcb/7pin%e9%98%b2%e5%91%86%e8%b4%b4%e7%89%87-fzz_/ Tue, 22 Mar 2022 09:44:13 +0000 http://mixdiy.com/wp-content/uploads/2022/03/7Pin防呆贴片.fzz_.zip 896 890 0 0 <![CDATA[u3]]> http://mixdiy.com/index.php/2022/03/24/video-in-city/u3/ Wed, 23 Mar 2022 17:12:28 +0000 http://mixdiy.com/wp-content/uploads/2022/03/u3.mp4 901 900 0 0 <![CDATA[u1]]> http://mixdiy.com/index.php/2022/03/24/video-in-city/u1/ Wed, 23 Mar 2022 17:13:25 +0000 http://mixdiy.com/wp-content/uploads/2022/03/u1.mp4 902 900 0 0 <![CDATA[onoff-2.fzz_]]> http://mixdiy.com/index.php/2022/03/22/fritzing-onoff-pcb/onoff-2-fzz_/ Thu, 24 Mar 2022 15:05:25 +0000 http://mixdiy.com/wp-content/uploads/2022/03/onoff-2.fzz_.zip 907 890 0 0 <![CDATA[onoff-2G.fzz_]]> http://mixdiy.com/index.php/2022/03/22/fritzing-onoff-pcb/onoff-2g-fzz_/ Thu, 24 Mar 2022 15:05:36 +0000 http://mixdiy.com/wp-content/uploads/2022/03/onoff-2G.fzz_.zip 908 890 0 0 <![CDATA[main.mpy_]]> http://mixdiy.com/index.php/2022/03/28/mpy/main-mpy_/ Mon, 28 Mar 2022 00:43:02 +0000 http://mixdiy.com/wp-content/uploads/2022/03/main.mpy_.tar 920 919 0 0 <![CDATA[10552179-3dff3f8ccda0a52d]]> http://mixdiy.com/index.php/2022/03/28/mpy/10552179-3dff3f8ccda0a52d/ Mon, 28 Mar 2022 00:51:49 +0000 http://mixdiy.com/wp-content/uploads/2022/03/10552179-3dff3f8ccda0a52d.webp 925 919 0 0 <![CDATA[2022-04-03]]> http://mixdiy.com/2022-04-03/ Sun, 03 Apr 2022 11:43:52 +0000 http://mixdiy.com/wp-content/uploads/2022/04/2022-04-03.avi 981 0 0 0 <![CDATA[2022-04-02]]> http://mixdiy.com/2022-04-02/ Sun, 03 Apr 2022 11:49:22 +0000 http://mixdiy.com/wp-content/uploads/2022/04/2022-04-02.mp4 982 0 0 0 <![CDATA[归档]]> http://mixdiy.com/%e5%bd%92%e6%a1%a3/ Mon, 04 Apr 2022 14:14:03 +0000 http://mixdiy.com/wp-content/uploads/2022/04/归档.zip 990 0 0 0 <![CDATA[onoff-6top-0603的副本.fzz_]]> http://mixdiy.com/index.php/2022/04/05/0603switcher/onoff-6top-0603%e7%9a%84%e5%89%af%e6%9c%ac-fzz_/ Tue, 05 Apr 2022 04:58:59 +0000 http://mixdiy.com/wp-content/uploads/2022/04/onoff-6top-0603的副本.fzz_.zip 994 992 0 0 <![CDATA[onoff-6top-0603-base-1]]> http://mixdiy.com/index.php/2022/04/05/npn-switcher/onoff-6top-0603-base-1/ Tue, 05 Apr 2022 07:52:28 +0000 http://mixdiy.com/wp-content/uploads/2022/04/onoff-6top-0603-base-1.zip 997 996 0 0 <![CDATA[onoff-6top-0603-base-2]]> http://mixdiy.com/index.php/2022/04/05/npn-switcher/onoff-6top-0603-base-2/ Tue, 05 Apr 2022 08:07:27 +0000 http://mixdiy.com/wp-content/uploads/2022/04/onoff-6top-0603-base-2.zip 1000 996 0 0 <![CDATA[time_controler-2.fzz_]]> http://mixdiy.com/index.php/2022/03/22/fritzing-onoff-pcb/time_controler-2-fzz_/ Sun, 10 Apr 2022 01:45:44 +0000 http://mixdiy.com/wp-content/uploads/2022/04/time_controler-2.fzz_.zip 1022 890 0 0 <![CDATA[bin]]> http://mixdiy.com/index.php/2022/04/16/bootloader/bin/ Sat, 16 Apr 2022 15:30:44 +0000 http://mixdiy.com/wp-content/uploads/2022/04/bin.tar 1083 1082 0 0 <![CDATA[image]]> http://mixdiy.com/index.php/2022/04/16/micropython-gujian/image-3/ Wed, 27 Apr 2022 11:06:44 +0000 http://mixdiy.com/wp-content/uploads/2022/04/image.png 1102 1078 0 0 <![CDATA[qqipsjk]]> http://mixdiy.com/qqipsjk/ Sat, 30 Apr 2022 00:56:40 +0000 http://mixdiy.com/wp-content/uploads/2022/04/qqipsjk.zip 1121 0 0 0 <![CDATA[84976219-7EE6-4B08-9550-CF294F4DF802]]> http://mixdiy.com/index.php/sample-page/84976219-7ee6-4b08-9550-cf294f4df802/ Sat, 30 Apr 2022 23:03:54 +0000 http://mixdiy.com/wp-content/uploads/2022/05/84976219-7EE6-4B08-9550-CF294F4DF802.jpeg 1126 2 0 0 <![CDATA[E2134A9B-B51E-4923-A233-40060787DAEE]]> http://mixdiy.com/index.php/sample-page/e2134a9b-b51e-4923-a233-40060787daee/ Sat, 30 Apr 2022 23:13:05 +0000 http://mixdiy.com/wp-content/uploads/2022/05/E2134A9B-B51E-4923-A233-40060787DAEE.jpeg 1128 2 0 0 <![CDATA[743491A2-2F02-4FFE-A89E-2161398988BD]]> http://mixdiy.com/index.php/sample-page/743491a2-2f02-4ffe-a89e-2161398988bd/ Sat, 30 Apr 2022 23:13:14 +0000 http://mixdiy.com/wp-content/uploads/2022/05/743491A2-2F02-4FFE-A89E-2161398988BD.gif 1129 2 0 0 <![CDATA[R-C.f45ed81b1c3f34c4aa31bb1e0f4701c4]]> http://mixdiy.com/index.php/sample-page/r-c-f45ed81b1c3f34c4aa31bb1e0f4701c4/ Sat, 30 Apr 2022 23:46:16 +0000 http://mixdiy.com/wp-content/uploads/2022/05/R-C.f45ed81b1c3f34c4aa31bb1e0f4701c4.jpeg 1138 2 0 0 <![CDATA[截屏2022-05-01-07.59.34]]> http://mixdiy.com/index.php/sample-page/%e6%88%aa%e5%b1%8f2022-05-01-07-59-34/ Sun, 01 May 2022 00:00:16 +0000 http://mixdiy.com/wp-content/uploads/2022/05/截屏2022-05-01-07.59.34.png 1139 2 0 0 <![CDATA[截屏2022-05-01-07.59.34-1]]> http://mixdiy.com/index.php/sample-page/%e6%88%aa%e5%b1%8f2022-05-01-07-59-34-1/ Sun, 01 May 2022 00:01:55 +0000 http://mixdiy.com/wp-content/uploads/2022/05/截屏2022-05-01-07.59.34-1.png 1141 2 0 0 <![CDATA[截屏2022-05-01-08.55.39]]> http://mixdiy.com/index.php/sample-page/%e6%88%aa%e5%b1%8f2022-05-01-08-55-39/ Sun, 01 May 2022 00:56:13 +0000 http://mixdiy.com/wp-content/uploads/2022/05/截屏2022-05-01-08.55.39.png 1143 2 0 0 <![CDATA[12V-5V-ONOFF.fzz_]]> http://mixdiy.com/index.php/2022/05/01/auto-water-pub/12v-5v-onoff-fzz_/ Sun, 01 May 2022 02:44:23 +0000 http://mixdiy.com/wp-content/uploads/2022/05/12V-5V-ONOFF.fzz_.zip 1150 1149 0 0 <![CDATA[12-51-onoff.fzz_]]> http://mixdiy.com/index.php/2022/05/01/auto-water-pub/12-51-onoff-fzz_/ Sun, 01 May 2022 02:44:37 +0000 http://mixdiy.com/wp-content/uploads/2022/05/12-51-onoff.fzz_.zip 1151 1149 0 0 <![CDATA[12v-5v-spx1117-布线.fzz_]]> http://mixdiy.com/index.php/2022/05/01/auto-water-pub/12v-5v-spx1117-%e5%b8%83%e7%ba%bf-fzz_/ Sun, 01 May 2022 07:11:12 +0000 http://mixdiy.com/wp-content/uploads/2022/05/12v-5v-spx1117-布线.fzz_.zip 1156 1149 0 0 <![CDATA[截屏2022-05-01-15.10.06]]> http://mixdiy.com/index.php/2022/05/01/auto-water-pub/%e6%88%aa%e5%b1%8f2022-05-01-15-10-06/ Sun, 01 May 2022 07:11:25 +0000 http://mixdiy.com/wp-content/uploads/2022/05/截屏2022-05-01-15.10.06.png 1157 1149 0 0 <![CDATA[12v-5v-spx11175.2.fzz_]]> http://mixdiy.com/index.php/2022/05/01/auto-water-pub/12v-5v-spx11175-2-fzz_/ Mon, 02 May 2022 10:59:25 +0000 http://mixdiy.com/wp-content/uploads/2022/05/12v-5v-spx11175.2.fzz_.zip 1170 1149 0 0 <![CDATA[12V-5V-ONOFF5.2.fzz_]]> http://mixdiy.com/index.php/2022/05/01/auto-water-pub/12v-5v-onoff5-2-fzz_/ Mon, 02 May 2022 10:59:36 +0000 http://mixdiy.com/wp-content/uploads/2022/05/12V-5V-ONOFF5.2.fzz_.zip 1171 1149 0 0 <![CDATA[12V-5V-spx11175.2]]> http://mixdiy.com/index.php/2022/05/01/auto-water-pub/12v-5v-spx11175-2/ Mon, 02 May 2022 11:36:10 +0000 http://mixdiy.com/wp-content/uploads/2022/05/12V-5V-spx11175.2.zip 1174 1149 0 0 <![CDATA[12v-5v-spx11175.2.fzz_-1]]> http://mixdiy.com/index.php/2022/05/01/auto-water-pub/12v-5v-spx11175-2-fzz_-1/ Mon, 02 May 2022 11:36:19 +0000 http://mixdiy.com/wp-content/uploads/2022/05/12v-5v-spx11175.2.fzz_-1.zip 1175 1149 0 0 <![CDATA[]]> http://mixdiy.com/index.php/2022/04/13/1056/ Wed, 13 Apr 2022 13:52:19 +0000 http://mixdiy.com/?p=1056 1056 0 1 0 <![CDATA[12V-5V-ONOFF5.2]]> http://mixdiy.com/index.php/2022/05/01/auto-water-pub/12v-5v-onoff5-2/ Mon, 02 May 2022 11:36:45 +0000 http://mixdiy.com/wp-content/uploads/2022/05/12V-5V-ONOFF5.2.zip 1176 1149 0 0 <![CDATA[pwof]]> http://mixdiy.com/pwof/ Thu, 05 May 2022 04:20:21 +0000 http://mixdiy.com/wp-content/uploads/2022/05/pwof.zip 1192 0 0 0 <![CDATA[截屏2022-05-05-23.17.25]]> http://mixdiy.com/index.php/2022/05/05/rewrite-copywriter/%e6%88%aa%e5%b1%8f2022-05-05-23-17-25/ Thu, 05 May 2022 15:39:49 +0000 http://mixdiy.com/wp-content/uploads/2022/05/截屏2022-05-05-23.17.25.png 1203 1202 0 0 <![CDATA[截屏2022-05-05-23.08.09]]> http://mixdiy.com/index.php/2022/05/05/rewrite-copywriter/%e6%88%aa%e5%b1%8f2022-05-05-23-08-09/ Thu, 05 May 2022 15:40:04 +0000 http://mixdiy.com/wp-content/uploads/2022/05/截屏2022-05-05-23.08.09.png 1204 1202 0 0 <![CDATA[截屏2022-05-05-23.07.48]]> http://mixdiy.com/index.php/2022/05/05/rewrite-copywriter/%e6%88%aa%e5%b1%8f2022-05-05-23-07-48/ Thu, 05 May 2022 15:40:18 +0000 http://mixdiy.com/wp-content/uploads/2022/05/截屏2022-05-05-23.07.48.png 1205 1202 0 0 <![CDATA[截屏2022-05-05-23.06.23]]> http://mixdiy.com/index.php/2022/05/05/rewrite-copywriter/%e6%88%aa%e5%b1%8f2022-05-05-23-06-23/ Thu, 05 May 2022 15:40:31 +0000 http://mixdiy.com/wp-content/uploads/2022/05/截屏2022-05-05-23.06.23.png 1206 1202 0 0 <![CDATA[Compress]]> http://mixdiy.com/index.php/2022/05/20/file/compress/ Wed, 18 May 2022 14:07:07 +0000 http://mixdiy.com/wp-content/uploads/2022/05/Compress.zip 1235 1234 0 0 <![CDATA[heartbeatmain32]]> http://mixdiy.com/index.php/2022/03/03/esp32-c3-debug/heartbeatmain32/ Fri, 20 May 2022 02:18:13 +0000 http://mixdiy.com/wp-content/uploads/2022/05/heartbeatmain32.zip 1239 795 0 0 <![CDATA[domain]]> http://mixdiy.com/index.php/2022/03/04/domain-reg/domain/ Mon, 30 May 2022 09:22:10 +0000 http://mixdiy.com/wp-content/uploads/2022/05/domain.zip 1294 807 0 0 <![CDATA[namedomain]]> http://mixdiy.com/index.php/2022/03/04/domain-reg/namedomain/ Mon, 30 May 2022 14:13:28 +0000 http://mixdiy.com/wp-content/uploads/2022/05/namedomain.tar 1298 807 0 0 <![CDATA[namedomain-1]]> http://mixdiy.com/index.php/2022/03/04/domain-reg/namedomain-1/ Mon, 30 May 2022 14:15:55 +0000 http://mixdiy.com/wp-content/uploads/2022/05/namedomain-1.tar 1299 807 0 0 <![CDATA[finddomain]]> http://mixdiy.com/index.php/2022/03/04/domain-reg/finddomain/ Sat, 04 Jun 2022 01:53:30 +0000 http://mixdiy.com/wp-content/uploads/2022/06/finddomain.zip 1314 807 0 0 <![CDATA[截屏2022-06-14-下午12.41.48]]> http://mixdiy.com/index.php/2022/06/14/wordpress-php-not-world/%e6%88%aa%e5%b1%8f2022-06-14-%e4%b8%8b%e5%8d%8812-41-48/ Tue, 14 Jun 2022 04:42:46 +0000 http://mixdiy.com/wp-content/uploads/2022/06/截屏2022-06-14-下午12.41.48.png 1357 1356 0 0 <![CDATA[截屏2022-06-14-下午12.47.11]]> http://mixdiy.com/index.php/2022/06/14/wordpress-php-not-world/%e6%88%aa%e5%b1%8f2022-06-14-%e4%b8%8b%e5%8d%8812-47-11/ Tue, 14 Jun 2022 04:47:49 +0000 http://mixdiy.com/wp-content/uploads/2022/06/截屏2022-06-14-下午12.47.11.png 1361 1356 0 0 <![CDATA[截屏2022-06-14-下午1.12.47]]> http://mixdiy.com/index.php/2021/12/23/zongjie/%e6%88%aa%e5%b1%8f2022-06-14-%e4%b8%8b%e5%8d%881-12-47/ Tue, 14 Jun 2022 05:13:07 +0000 http://mixdiy.com/wp-content/uploads/2022/06/截屏2022-06-14-下午1.12.47.png 1365 169 0 0 <![CDATA[截屏2022-06-14-下午1.12.47-1]]> http://mixdiy.com/index.php/2021/12/23/zongjie/%e6%88%aa%e5%b1%8f2022-06-14-%e4%b8%8b%e5%8d%881-12-47-1/ Tue, 14 Jun 2022 05:15:41 +0000 http://mixdiy.com/wp-content/uploads/2022/06/截屏2022-06-14-下午1.12.47-1.png 1366 169 0 0 <![CDATA[截屏2022-06-15-11.18.09]]> http://mixdiy.com/index.php/2022/06/05/backup-raspberrypi/%e6%88%aa%e5%b1%8f2022-06-15-11-18-09/ Wed, 15 Jun 2022 03:18:32 +0000 http://mixdiy.com/wp-content/uploads/2022/06/截屏2022-06-15-11.18.09.png 1395 1334 0 0 <![CDATA[截屏2022-06-15-11.18.09-1]]> http://mixdiy.com/index.php/2022/06/05/backup-raspberrypi/%e6%88%aa%e5%b1%8f2022-06-15-11-18-09-1/ Wed, 15 Jun 2022 03:38:00 +0000 http://mixdiy.com/wp-content/uploads/2022/06/截屏2022-06-15-11.18.09-1.png 1396 1334 0 0 <![CDATA[WordPress.2022-06-15.xml_]]> http://mixdiy.com/index.php/2022/06/15/wordpressxml/wordpress-2022-06-15-xml_/ Wed, 15 Jun 2022 14:15:49 +0000 http://mixdiy.com/wp-content/uploads/2022/06/WordPress.2022-06-15.xml_.zip 1423 1422 0 0 <![CDATA[关于我]]> http://mixdiy.com/index.php/sample-page/ Tue, 23 Nov 2021 03:42:37 +0000 http://localhost/?page_id=2

嗨,大家好!我白天是个外卖员,晚上就是个停不下来的打桩机㋡......^_^

。这是我的网站。我住在北京,养了条极通人性的狗叫小黑

,我喜欢艺术、旅行和美女顾客(我现在的女票就是我前顾客-^-)。

]]>
2 0 0 0
<![CDATA[隐私政策]]> http://mixdiy.com/index.php/privacy-policy/ Tue, 23 Nov 2021 03:42:37 +0000 http://localhost/?page_id=3

我们是一群喜欢美女顾客的外卖员

我们的站点地址是:http://www.mixdiy.com

评论

当访客留下评论时,我们会收集评论表单所显示的数据,和访客的IP地址及浏览器的user agent字符串来帮助检查垃圾评论。

由您的电子邮箱地址所生成的匿名化字符串(又称为哈希)可能会被提供给Gravatar服务确认您是否有使用该服务。Gravatar服务的隐私政策在此:https://automattic.com/privacy/。在您的评论获批准后,您的资料图片将在您的评论旁公开展示。

媒体

如果您向此网站上传图片,您应当避免上传那些有嵌入地理位置信息(EXIF GPS)的图片。此网站的访客将可以下载并提取此网站的图片中的位置信息。

Cookies

如果您在我们的站点上留下评论,您可以选择用cookies保存您的名字、电子邮箱地址和网站地址。这是通过让您可以不用在评论时再次填写相关内容而向您提供方便。这些cookies会保留一年。

如果您访问我们的登录页,我们会设置一个临时的cookie来确认您的浏览器是否接受cookies。此cookie不包含个人数据,且会在您关闭浏览器时被丢弃。

当您登录时,我们也会设置多个cookies来保存您的登录信息及屏幕显示选项。登录cookies会保留两天,而屏幕显示选项cookies会保留一年。如果您选择了“记住我”,您的登录状态则会保留两周。如果您注销登陆了您的账户,用于登录的cookies将会被移除。

如果您编辑或发布文章,我们会在您的浏览器中保存一个额外的cookie。这个cookie不包含个人数据而只记录了您刚才编辑的文章的ID。这个cookie会保留一天。

来自其他网站的嵌入内容

此站点上的文章可能会包含嵌入的内容(如视频、图片、文章等)。来自其他站点的嵌入内容的行为和您直接访问这些其他站点没有区别。

这些站点可能会收集关于您的数据、使用cookies、嵌入额外的第三方跟踪程序及监视您与这些嵌入内容的交互,包括在您有这些站点的账户并登录了这些站点时,跟踪您与嵌入内容的交互。

我们与谁共享您的信息

若您请求重置密码,您的IP地址将包含于密码重置邮件中。

我们保留多久您的信息

如果您留下评论,评论和其元数据将被无限期保存。我们这样做以便能识别并自动批准任何后续评论,而不用将这些后续评论加入待审队列。

对于本网站的注册用户,我们也会保存用户在个人资料中提供的个人信息。所有用户可以在任何时候查看、编辑或删除他们的个人信息(除了不能变更用户名外)、站点管理员也可以查看及编辑那些信息。

您对您的信息有什么权利

如果您有此站点的账户,或曾经留下评论,您可以请求我们提供我们所拥有的您的个人数据的导出文件,这也包括了所有您提供给我们的数据。您也可以要求我们抹除所有关于您的个人数据。这不包括我们因管理、法规或安全需要而必须保留的数据。

我们将您的信息发送到哪

访客评论可能会被自动垃圾评论监测服务检查。

]]>
3 0 0 0
<![CDATA[使用树莓派做一个“魔镜”]]> http://mixdiy.com/index.php/2021/11/23/mirror/ Tue, 23 Nov 2021 12:22:40 +0000 http://mixdiy.com/?p=18

第一部分——点子与镜子

作为一个男人,跟女友逛商城时,像是梅西百货那种的,难免会走神。我就是那号人,去年一月在纽约就发生过一回。长话短说。在我闲晃时我发现一面有灯光照明的镜子。我完全能自己做一面出来,而且比这更好。我想要属于我自己的魔镜!

magic_mirror1

一回到家,我开始构想我需要的东西:一面镜子,薄的那种,一个树莓派,一些木头和油漆,还需要大量空余时间。

关于镜子

普通的镜子是不行的。镜子是要那种单面透光那种,或更加精细功能的:后面屏幕黑屏时,它是一面镜子;而信息在屏幕上显示时就该像普通玻璃窗那样。

和警察局问询室内那面镜子原理一样,当只有一间房有光时,它就像一面镜子,其他时候它就是普通玻璃窗。

magic_mirror3

我所需要的是一面观察镜。现在,请相信我,当你问买玻璃的要一面观察镜时,会被反问一些奇怪的问题。他们那些人往往有更多新奇点子……嘿嘿,肮脏的思想永远是快乐的源泉。

终于我还是得到了一块不错的观察镜。开始找乐子吧!

第二部分——显示器

magic_mirror4

解决了镜子问题后,是时候为魔镜项目入手一台显示器了。

在为魔镜选择合适的显示器过程中,有一些纠结的决定。我是买一台新显示器还是捡一台呢?我需要什么尺寸?屏幕最薄会是多少?我如何移动控制按钮?屏幕是否够亮来盖过我的堂堂仪表?

因为我用显示器作为仪容之用,选择合适尺寸差不多就是选择合适的宽度(也就是镜子合适的高度)。在一些测量和尝试用胶带固定在墙上我想放置镜子的位置后,24英寸屏幕会是完美的选择。额外带来的好处是,绝大多数(便宜的)24英寸屏幕初始就带有1080P分辨率,正是我想在这个项目所使用的分辨率。

为了合适的屏幕类型和品牌,我几乎跑遍了方圆20公里内的电器铺。我敢肯定保安大叔在盯着我,而我正仔细检查所有屏幕背面和底部。

大多数品牌被立即否定了,因为它们的电源和视频连接线在背后。我需要转接线是在侧面的那种。

magic_mirror5

最后我选择了Iiyama显示器,它最接近我所希望的——价格便宜,边框窄小,简单触控按钮和正确转接器方向。

直到现在,我还是不清楚显示器外壳能否轻易被卸除,控制面板如何在显示器内部被连接上。销售人员不许我做出怪异的动作,打开显示器外壳,所以买到这台合适的显示器纯属运气。

magic_mirror6

我订购了一台24英寸Iiyama E2481HS-B1显示器。为确定显示器和镜子之间气密性良好,我拆下了外壳。在刚刚开箱就进行拆卸显示器够战战兢兢的,但……勉强成功。

这台Iiyama显示器的好处是,显示控制器(金属盒内的电子元件)为这面镜子的其他零件留下了足够的空间,而又保持了它原本的苗条外形。

magic_mirror7

实际的显示板仅有9毫米厚,而小小的边框也只是10毫米宽。加上显示控制器后,就是下列尺寸:556毫米x323毫米x46毫米(外加6毫米的镜子厚度)。这些尺寸构成了新的木头外壳尺寸。

第三部分——外壳

magic_mirror8

在量度新外壳所需尺寸后,就开始享受DIY的喜悦吧。我用松木做了个结实坚固的框架,用地板地脚线固定镜子和显示器的位置,那尺寸刚刚好(30毫米宽),还有圆角边框效果。

镜子很可能会发热,那就需要通风口了。此外,在外壳的背后还加了几个既美观又结实的挂点。

小剧透:产品最终重量6.5千克,由我做的两个挂载点所推测得来。

magic_mirror11

补充一下,我在外壳的底部开了条细缝作为电源线槽。

当然,它需要进行上色。在涂抹油灰(我在打磨抛光时后悔涂太多油灰了)、一些地漆及上土层后,新外壳完工。

magic_mirror14

有个木工小步骤我要去做的:我做了4个挂在块来安装显示器与镜子。现在准备好将它们上在镜框里面。

magic_mirror15

木工部分到此结束,进入魔镜的下一环节,安装硬件。

第四部分——安装硬件

magic_mirror16

魔镜开始成形。我订购了镜子,找到合适的显示器,完成打磨白色的外壳,是时候开始安装硬件了。

目前我手上有以下零件: · 显示器 · 树莓派 · 一条HDMI线(连接树莓派和显示器) · 一条USB转micro USB转接线(用来为树莓派供电) · 一条显示器电源线

但接着我遭遇到小小挫折。在找合适的显示器时,我完全没想到看看显示器有没有一个USB口,用来驱动树莓派。我以为现在的显示器都默认装有USB口,不过我错了,它没有。

这不是不能解决的问题,但我真心希望只用一条线来驱动魔镜。所以我只能将电源分为两条线,一条给显示器,一条给240伏USB降压器。补充一点,我希望用一条普通C13电源线就能驱动魔镜。

magic_mirror17

也就是说,我要做的是中间带有USB充电器的接插电源线。在翻了我几个闲置线材抽屉后,我找到了个旧USB充电器。

magic_mirror18

拆开充电器比想象中的容易,只要一点点焊接工序,一些胶和绝缘胶布即可。我要设法做个外观高大上的电源线。

magic_mirror20

我做了试运行,确保电源线正常工作。我将所有零件连接好,插好电源线,打开显示器。树莓派启动正常,USB充电器没有发热过量。不错!

magic_mirror21

接着组装所有零件。这次没有倒霉事件:所有东西完美吻合。

magic_mirror24

另一个阶段完成了,包括所有实体部分(除了还没把两颗螺丝钉在墙上)。进入戏肉的部分:安装树莓派。

Magic Mirror: Part V – Installing the Raspberry Pi

第五部分——安装树莓派

magic-mirror25

那么,做完所有硬件之后,就轮到安装树莓派了。要达到魔镜的所有需求,树莓派就要有以下功能:

· Wifi连接 · 屏幕旋转90度,符合照镜方向 · 本地网络服务维持界面 · 在全屏幕下运行的浏览器,用于显示界面

基本安装

因为Raspbian操作系统的灵活性和背后有开源社群支持,我就选了它。下载了操作系统映像后,就把它写入SD卡。

拷贝一个映像档需要很长时间,那么有三种选择:

  1. 用树莓派技术文档中提到的rdisk方法拷贝。
  2. 拿杯咖啡,坐下,在等待中享受咖啡因的感觉。
  3. 上面两者都选。

我选了3。完成拷贝后,我启动树莓派、登入,进入命令行的sudo raspi-config开始配置向导。在这个配置中,有几件重要事项需要配置:

· 确保系统启动到桌面(取代命令行模式或调试模式)。 · 调整时区,使魔镜显示正确时间。 · 在高级选项部分,确保显存超过128MB。 如果你想做和这个一样的镜子,尽管尝试选择所有其他选项。只是记住,如果选砸了,就再泡杯咖啡重装系统吧。

Wifi连接

我不想在魔镜上加任何多余的连接线,就选择了以Wifi连接因特网。安装Wifi接收器真的依赖频宽和种类,因此写下完全过程是多余的。有最好的替代吗?看看这个神奇的网站……

老实说,这部分安装确实很花时间。一旦装好了,就能工作正常。

旋转屏幕

魔镜在设计上是纵向肖像模式,所以我需要将屏幕顺时针旋转90度,最终显示分辨率为1080 x 1920。我曾担心这会是最大的问题,最终却很容易解决了。

树莓派的BIOS设置储存在系统启动分区中。在这分区中,有一个config.txt文件,载有所有设置。要旋转显示器,在这文件内加上以下一行代码:
display_rotate=1

要让显示器连接更加可靠,我就不解释为何加上下面允许HDMI线热插拔的代码了:
hdmi_force_hotplug=1

配置文件存盘后重启树莓派,我不禁会心一笑:这感觉真好,腰不疼颈不酸了!

网络服务器

要维持界面(很简单的一个网页),我需要在树莓派上Apache服务器。这在树莓派上是其中一个很常见的应用,安装过程简直行云流水。

首先我运行下列指令,确定我用的是最新系统软件。
sudo apt-get update && apt-get upgrade -y

现在是时候真的安装Apache了:
sudo apt-get install apache2 apache2-doc apache2-utils

完成了!就是所有这些。但要保证在服务器上能用到一些PHP脚本(以后会更多的),我也加上了PHP支持:
sudo apt-get install libapache2-mod-php5 php5 php-pear php5-xcache

又完成了!重启之后,网页服务器就挂载上线运行了!我在/var/www文件夹内放置了index.php文件,将浏览器首页指向树莓派的IP地址,发现成功了。

信息模式(kioskmode)

现在就是要确定树莓派能够显示我在信息模式(kioskmode)下用Chromium浏览器显示的网页。Chromium浏览器是个能在树莓派操作系统上运行的开源浏览器。

一如既往,安装很简单:
sudo apt-get install chromium x11-xserver-utils unclutter

但这一次,它需要一些额外设置,在信息模式下禁用屏幕保护和自动重启。我在/etc/xdg/lxsession/LXDE/autostart作了编辑,在前面加上了#号。
@xscreensaver -no-splash

另外我还加了以下代码:
@xset s off @xset -dpms @xset s noblank @chromium –kiosk –incognito http://localhost

这样就能完全禁用所有屏保功能,及Chromium浏览器在开机后自动启动,开启全屏模式并导向本地主页。

存盘并再一次重启,检查工作效果。树莓派并不是世界上最快的电脑,它需要花点时间,但最终测试页还是在旋转了90度的画面上显示出来。要得!

进入项目的最后部分,界面开发。

第六部分——界面开发

回顾一下,我买了合适的镜子、显示器,做了个新外壳,安装好硬件及配置完成树莓派,那就进入最后一步——界面开发。

需求和功能

我开始项目时,我发现不能通过镜子直接进行任何交互,要不然在铮亮的镜面上会抹上油脂和什么脏东西。但还有更重要的原因,还有别的更好设备可用于用户交互。我希望魔镜只是个被动信息来源。

而更更重要的是,镜子就是镜子,不应被大量(无用的)信息所填满。只有边上能用来作摘要性显示,给本帅哥留些足够自恋的空间吧。

这样我需要以下信息类别目录来满足我对信息的需求:

· 问候语 贴心问候,每日美好的开始。

· 天气 看到我穿的衬衫吗?不错吧!嗯?今天穿T恤衫不会鸡冻吧?

· 时钟和日历 现在赶时间吗?还有足够时间顾影自怜吧?

· 消息反馈 我只是担心自己形象帅不帅吗?还有别的需要上心吗?

当然,未来版本当中有大量的改进可能,现在列出的这些应该够用了。

基本安装

前面提过,程序界面不是一个在树莓派桌面上神奇的应用,仅仅是一个全屏幕网页。我能用HTML、CSS和Javascript开发外,还有一个额外的好处,在我将它放入魔镜之前,能在我的苹果电脑上开发测试。

magic-mirror28

在自己写的代码基础上,我用了一些开源库文件来优化它的运行速度:

Jquery

像大多数网站那样,魔镜使用Jquery来简化DOM操作方式,对我这种懒人最为适用。

Moment.js

它对我在时间安排操作上帮助不少,尤其是在争分夺秒时更节省不少时间。

FeedToJson

将RSS订阅用JSON数据转成javascript的方便工具。

iCal Parser

将iCal数据转化为JSON。可惜这个库文件并不完美,需要一些额外的优化。

以上这些库文件,加上我自己的HTML和Javascript足够使我的镜子成精了。

设计

我的魔镜需要个酷炫设计。我本人是个严重果粉,那么在设计上要遵从苹果系的设计指标

边框、倾角、阴影效果有时是用户界面厚重感的因素,甚至能盖过显示内容的光芒。所以,专注在内容设计,把用户交互界面摆在辅助位置上。

利用足够的实体空间,它是个重要内涵,在感官上更让人注意和易于理解。

怎么做到这一点?用Helvetica Neue字体,用天气图标对应显示天气信息。

magic-mirror29

镜子在背后没有光源时就只是一面镜子,用黑色背景是个重要设定。而为了有最好的对比度,显示内容字体应为白色,在加一些灰色阴影边缘,那灰度为50吧……

我大可以加上一些其他色彩,但目前为止我只想做到简洁的黑白界面。我可不想镜子比我的尊容还要出彩。(译者注:镜子:好吧,我不告诉你谁是世界上最帅的男人。镜子,碎。)

API设计

为了接收我想在镜子上显示的数据,我用了一些开源API和反馈代码。还有谁不太清楚什么是API,维基百科里面有详尽的解释:

“电脑操作系统(Operating system)’或‘程序库’提供给应用程序调用使用的代码。”

我心中的API就像你家的DVD播放器背后那样充满接口——如果你将其他设备连接到它的话,它就变得更加有用,这些接口本质上就是API。API让设备变得功能强大、有趣,尤其对我这种电脑狂人来说。

Openweathermap.org

Openweathermap有很好的API接口,能够免费得到天气预报信息。它允许你查询区域内的信息,定义你想要的信息类别。在这里我用了两个连接,一个是当前天气,另一个是预报天气。

iCal Calendar

iCloud允许你以iCal格式分享日历。因为javascript无法做到这一点(因为多站点脚本安全问题),我需要通过PHP代理服务器来开启数据。这其实很容易,只有三行代码而已:

12<!--?php <br ?-->    $url = "https://p01-calendarws.icloud.com/ca/subscribe/1/mysupersecreticloudhash";    echo file_get_contents($url);

只需查询calendar.php就能将在同一个服务器内日历表在界面上显示出来。

前面说过的iCal语法分析编辑帮我把信息解析成有用信息。可是,iCloud在他们的反馈中用了一些非标准标签,我要添加额外的代码行来将代码解析为javascript文件。

magic-mirror30

NOS新闻订阅

新闻订阅只是用到了荷兰公共广播公司的RSS订阅功能。可是,他们不支持JSON格式数据,因此我用了FeedToJson插件来把RSS数据转为JSON格式的。

其他内容,例如当前日期与实践,还有问候语都只是一些简单的javascript语句。

自动更新

你看了这篇博客这么久,应该能接受更进阶的信息了吧。那么瞪大眼看下去吧……

在开发当中,我遇到了一个很不爽的境地,就是没有键盘和鼠标连在魔镜上的问题。如果我更新界面,就没有一个简单方法在魔镜上更新页面的方法。最简单直接的方法是重启整个树莓派,但让我在调试每个更新上花时间太多了。

我用GIT分布控制系统更新文件,而每个更新版本都有本身的哈希校验提交码,我用PHP就能读取出来。这启发了我再代码行内加上一个片段,将本地页面和刚刚开发的页面进行哈希比对。如果两者不符,程序将重新载入网页,显示最新版本。

我用以下PHP代码获取当前网页上的哈希校验码:

1trim(`git rev-parse HEAD`)));

对比过程在javascript主文件内完成,但只有在HTML文件有当前版本的哈希校验值前提下才能实现:

1var gitHash = '';

现在,对比过程就很直观,每3秒进行一次:

1234567891011121314(function checkVersion()    {        $.getJSON('githash.php', {}, function(json, textStatus) {            if (json) {                if (json.gitHash != gitHash) {                    window.location.reload();                    window.location.href=window.location.href;                }            }        });        setTimeout(function() {            checkVersion();        }, 3000);    })();

所有这些神奇的操作,让更新魔镜界面变得简单。只需用SSH登入树莓派,进入合适的文件夹,然后处理一个git推送请求。

给我交出代码来!

得了得了……好奇了吧?所有代码在GitHub上都有。有件事情要说一声:代码很少标注,以面条式代码填充(译者注:就是大量复杂逻辑结构诸如if、when之类的条件语句,没想到老外还是有和国人程序猿一般的生物,反面例子啊),通篇都是TODO语句。哈,不怕晃晕菜的,尽管扎进去吧。

未来展望

魔镜还能做更多的功能,这是一个陆续发展过程。如有新功能上的建议,请不吝提供。让我知道你想在界面上的创意。不过请记住,少为美。:)

尽管有很多改进需要跟进,项目就总结到这儿吧。请关注我的博客,我会登出更多的照片来秀一下成品。

via org

2019-6-9 更新:
经过数年,无数 Maker 前赴后继地创造、加入到制作队伍,已经形成了一个专门 DIY 魔镜的社区。
网站不仅提供了完整的项目源码、一键安装脚本。还分享了项目中所用到的每个第三方模块,非常丰富,感觉已经发展成了一个平台化的系统软件。

]]>
18 0 0 0
<![CDATA[开始使用树莓派]]> http://mixdiy.com/index.php/2021/11/23/22/ Tue, 23 Nov 2021 12:47:17 +0000 http://mixdiy.com/?p=22

简介

在这个项目中,你将连接起一台树莓派电脑,并了解它可以做什么。

注: 本指南是对树莓派计算机的介绍,以及设置树莓派的详细指南使用您的树莓派 

你将能做什么

树莓派是一台可以做很多事情的小巧的计算机。 将其与和显示器、键盘和鼠标连接。

截屏

你将学到什么

本项目涵盖了树莓派数字制作课程如下几方面内容:

教师附加信息

如果您需要打印本项目文件,请使用适合打印版本

请使用页脚的链接访问此项目的GitHub版本库,文件夹’zh-CN/resources’里包含了所有资源(包括最终版示例) 。

你需要什么

硬件

  • 一个带有SD卡或Micro SD(TransFlash) 卡的树莓派
  • 带有连接线的显示器(可能还需要HDMI适配器)
  • USB键盘和鼠标
  • 一个电源
  • 耳机或扬声器(可选)
  • 以太网线(可选)

软件

  • Raspbian操作系统,通过NOOBS安装

认识树莓派

你将第一次见到树莓派! 你应该有一台树莓派电脑。 它应该没有连接任何东西。

  • 看一下你的树莓派。 你能找到图上标注的所有东西吗?
截图
  • USB端口 - 这些端口用于连接鼠标和键盘。 您还可以连接其他组件,例如U盘。
  • SD卡插槽 - 您可以将SD卡插入此处。 这是操作系统和文件存储的地方。
  • 以太网口 - 用于通过网线将树莓派连接到网络。 树莓派也能用wifi上网。
  • 音频插孔 - 您可以在此连接耳机或扬声器。
  • HDMI端口 - 用于连接显示器(或投影仪)将树莓派桌面显示出来。 如果您的显示器带有扬声器,您也可以使用它们播放声音。
  • Micro USB电源接口 - 这是连接电源的地方。 你应该永远在连接完所有其他组件后,最后再连接电源。
  • GPIO接口 - 这些接口允许您将电子组件(如LED和按钮)连接到树莓派。

连接你的树莓派

让我们连接你的树莓派并让它运行起来。

  • 检查树莓派底部的插槽,看看SD卡是否插入。 如果没有SD卡,则插入一个安装了Raspbian的SD卡(通过NOOBS)。截屏

注意: 许多micro Sd卡都是插在一个大号的卡套里的 - 您可以使用底部的凸起部分将卡取出。

sd 卡槽

用NOOBS安装Raspbian

  • 找到鼠标的USB插头,并将它连接到树莓派上的USB接口(随便哪个都可以)。截图
  • 以同样的方法连接好键盘。截屏
  • 确保您的显示器连接了电源并打开显示器开关。
  • 看看树莓派上的HDMI接口 - 请注意它顶部又大又平。
  • 使用HDMI线将显示器连接到树莓派的HDMI接口 - 如有必要,请使用适配器。树莓派4连接显示器到树莓派4的第一个HDMI接口 ,它标注了HDMI0.HDMI您可以以相同的方式连接的第二个屏幕。(可选)HDMI树莓派1,2,3代将屏幕连接到单HDMI端口。树莓派3 HDMI

注意: 现在屏幕上不会显示任何内容,因为树莓派尚未开机。

  • 如果要通过以太网将树莓派连接到因特网,请使用以太网网线将树莓派上的以太网接口连接到墙上或路由器上的以太网接口。 如果您要使用WiFi或者您不想连接到互联网,则无需执行此操作。
以太网
  • 如果您的屏幕有扬声器,您的树莓派可以通过显示器播放声音。 或者您可以将耳机或扬声器连接到音频端口。
耳机
  • 将电源插入插座并将其连接到树莓派的Micro USB电源接口上。
截屏

你应该看到树莓派上的红灯亮起并且显示器上显示出树莓图像。

然后,您的树莓派将引导到桌面。

截屏

完成设置

当您第一次启动树莓派时,将弹出 欢迎使用树莓派 应用程序并引导您完成初始设置。

树莓派设置向导
  • 单击 下一步 开始设置。
  • 设置您的 国家, 语言和 时区,然后再次单击 下一步 。
pi 设置国家向导
  • 为你的树莓派输入新密码,然后单击 下一步
pi设置密码向导
  • 选择要连接的WIFI网络名称,输入密码,然后点击 下一步完成连接。
pi 设置WIFI向导

注意: 如果您的树莓派型号没有无线连接,您将看不到这个屏幕。

  • 单击 下一步 让设置向导检查Raspbian的更新并安装它们(这可能需要一段时间)。
树莓派设置向导 更新
  • 单击 完成 或 重新启动 以完成设置。

注意: 如果需要完成更新,您只需要重新启动。

树莓派设置向导 完成

漫游树莓派

现在是时候去漫游树莓派了。

  • 你看到左上角的树莓图标了吗? 这就是你访问菜单的地方:点击它,可以找到很多应用程序。
  • 单击 Accessories 然后选择 Text Editor
截图
  • 在弹出的窗口输入我刚刚装好了一台树莓派电脑。
screenshot
  • 单击 File,然后选择 Save,然后选择 桌面 并将文件保存为 rp.txt
截图
  • 您应该看到名为 rp.txt 的图标出现在桌面上。
截图

您的文件已经保存到树莓派的SD卡上了。

  • 单击窗口右上角的 X 关闭文本编辑器。
  • 返回到树莓菜单,选择 Shutdown,然后选择 Reboot
  • 当树莓派重新启动后,您的文本文件应该仍然在桌面上。
  • 树莓派运行一种称为Linux的系统的一种发行版本(Windows和MacOS是其他操作系统)。 它允许您通过键入命令而不是单击菜单选项来完成各种任务。 点击屏幕顶部的 Terminal标志:
截图
  • 在出现的窗口中,输入:ls

然后按下键盘的 回车键。

您就可以在home中看到文件和文件夹的列表目录。

  • 现在,键入这个命令把目录更改(change directory )到Desktop:cd Desktop

您必须在每个命令后按 回车 键。

然后输入:

ls

你能看到你之前创建的文件吗?

  • 点击 X关闭终端窗口。
  • 现在将 rp.txt 拖动到桌面上的垃圾桶,以便树莓派可以被下一个人使用。截图

浏览网页

您可能想要将您的树莓派连接到互联网。 如果在安装过程中未插入以太网电缆或连接到WiFi网络,则可以现在连接。

  • 点击屏幕右上角带红色叉叉的图标,然后从下拉菜单中选择您要连接的网络。 您可能需要向老师询问您应该选择哪个网络。
Wifi未连接
  • 输入的无线网络密码,或请求大人为您输入密码,然后点击 确定
输入密码
  • 当您的树莓派连接到互联网时,您将看到无线局域网符号,而不是红色叉叉。
截屏
  • 点击网页浏览器图标并搜索 树莓派
截图

挑战:探索树莓派

浏览菜单 - 你能否找到:

  • 某一版本的Scratch?
  • 玩一个Python游戏?
  • 您可以编程的Minecraft版本?
]]>
22 0 0 0
<![CDATA[使用您的树莓派]]> http://mixdiy.com/index.php/2021/11/25/userpi/ Wed, 24 Nov 2021 16:14:42 +0000 http://mixdiy.com/?p=28

介绍

在这里,您将学习如何使用树莓派操作系统Raspbian和它的一些软件,以及如何根据您的需要调整一些关键设置。

如果您还没有运行树莓派,请查看我们的 设置您的树莓派 指南。

pi桌面

树莓派桌面

您的树莓派运行Raspbian,这是Linux操作系统(OS)的一种发行版本。 (Windows和macOS是其他类型的操作系统)。

Raspbian启动后,您将看到桌面出现。

pi桌面

左上角的树莓派图标是您访问菜单的入口。

  • 单击它可以找到许多应用程序,包括 编程 和 办公 应用程序。
  • 要打开文本编辑器,请单击附件并选择文本编辑器 。
截图
截图
  • 通过单击窗口右上角的 x, 关闭文本编辑器。
  • 浏览菜单中的一些其他应用程序,例如Python游戏 。

键盘和鼠标设置

要设置鼠标和键盘,请从菜单中选择 首选项 ,然后选择 鼠标和键盘

键盘和鼠标设置菜单

鼠标

您可以在此处更改鼠标移动速度和双击时间,如果您是左撇子,则可以交换左右键。

鼠标设置

键盘

您可以在此处调整键重复延迟和间隔值。

键盘设置

要更改键盘布局,请单击 键盘布局 ,然后从国家/地区列表中选择布局。

键盘布局

连接到互联网

如果您想将树莓派连接到互联网,可以将以太网电缆插入其中(如果你有Pi Zero,你也需要一个USB转以太网适配器)。

如果您的型号是Pi 4, 3 或Pi Zero W,您还可以连接到无线网络。

连接到无线网络

  • 单击屏幕右上角的无线网络图标,然后从下拉菜单中选择您的网络。
没有wifi
wifi
  • 输入无线网络的密码,然后单击 确定
输入密码
  • 一旦您的树莓派连接到互联网,您将看到无线LAN符号而不是红色叉叉。
wifi
  • 通过单击Web浏览器图标并在Web上搜索 树莓派来测试您的连接。
浏览器

设置声音

您的树莓派可以将声音通过 HDMI 传接到屏幕的内置扬声器(如果您的屏幕有扬声器)或者传送到 模拟 耳机插孔。

  • 右键单击右上角的扬声器图标,选择您的Pi是否应使用 HDMI 或 Analog 连接进行声音处理。
pi声音连接
  • 单击扬声器图标可通过向上或向下移动滑块来调节音量。
pi音量

安装软件

有很多很多软件和应用程序您可以在树莓派上下载和安装。

注: 你的树莓派要 已经连接到互联网 ,然后才能安装软件。

  • 在菜单中,单击 首选项 ,然后单击 推荐软件
推荐的软件菜​​单

您可以浏览所有推荐的软件,也可以按类别对其进行过滤。

推荐的软件
  • 要安装某个软件,请单击以选中其右侧的复选框。
选择要安装的软件
  • 然后单击 确定 以安装所选软件。
安装软件

除了树莓派推荐的软件之外,还有一个庞大的可用程序和应用程序库。

  • 单击 首选项 ,然后单击菜单中的 添加/删除软件
添加删除软件菜单

您可以搜索软件,也可以从左侧菜单中选择一个类别进行浏览。

添加删除软件

让我们尝试安装名为 Pinta的绘图应用程序。

  • 在搜索框中输入“pinta”,然后按 回车键
  • 在出现的列表中选择 Simple drawing/paint program
pinta安装
  • 单击 确定 开始安装过程。
  • 出现提示时,输入您的密码;如果您没有更改密码,它将是’raspberry’。
验证软件安装

Pinta现在将被下载并安装。

安装包
  • 完成此过程后,从菜单中选择 图像 然后选择 Pinta 打开Pinta。
pinta菜单

更新你的Pi

最好定期使用最新功能和修复程序更新Pi上的软件。

  • 您可以使用 添加/删除软件 应用程序更新Pi:通过从菜单的 首选项 选择它来打开。
添加删除软件菜单

在检查并安装任何更新之前,您应该刷新Pi上的软件包列表。

  • 单击左上角的 选项 ,然后选择 刷新软件包列表
刷新软件包列表

然后,您的Pi将更新所有包列表。

下载新的软件包列表
  • 完成后,单击 选项 然后选择 检查更新
检查更新

软件包更新程序 将打开并自动检查更新是否可用。 它将显示它在列表中找到的任何内容。

软件包更新程序
  • 单击 安装更新 以安装所有可用更新。
  • 出现提示时,输入您的密码;如果您没有更改密码,它将是’raspberry’。
验证软件安装
  • 然后将下载并安装更新。 您可以通过检查左下角的进度条来查看安装。
下载软件包

访问您的文件

树莓派上的所有文件(包括您自己创建的文件)都存储在SD卡上。 您可以使用 文件管理器 应用程序访问您的文件。

  • 单击 附件,然后单击菜单中的 文件管理器,或选择菜单栏上的 文件管理器 图标。
文件管理器菜单

当文件管理器打开时,您将看到 pi 目录 - 这是您可以存储文件和创建新子文件夹的位置。

文件管理器
  • 双击 Documents 图标打开目录并查看其中的文件。
文档

要打开文件,请双击其名称,或右键单击该文件以打开文件菜单以获取更多选项。

文件菜单

您可以在树莓派上使用USB硬盘和U盘。 这是备份文件并将其复制到其他计算机的便捷方式。

  • 将U盘插入树莓派。 将弹出一个窗口,询问您要执行的操作。
插入U盘
  • 单击 确定 到 在文件管理器打开。

文件管理器将打开并显示U盘上的文件。

U盘文件管理器

使用终端

终端 是一个非常有用的应用程序:它允许您使用键入的命令来访问文件目录并控制树莓派,而不是单击菜单选项。 它通常出现在许多教程和项目指南中,包括我们网站上的指南。

  • 要打开终端窗口,请单击屏幕顶部的 终端 图标,或者在菜单中选择 附件 ,然后选择 终端
截图

您可以在终端窗口中键入命令,然后按键盘上的 回车键 运行命令。

  • 在终端窗口中,键入:
ls
  • 然后按键盘上的回车键

ls命令列出当前文件目录中的所有文件和子目录。 默认情况下,终端在打开时访问的文件目录是名为 pi的文件目录。

pi终端ls
  • 现在输入此命令 change directory切换到桌面。
cd Desktop

每次命令后都必须按 回车键 。

  • 使用ls 命令列出Desktop目录中的文件。
ls
pi终端ls桌面

终端可以做比列出文件更多的事情 - 这是与树莓派进行交互的一种非常强大的方式!

  • 仅作为一个小例子,尝试命令 pinout
pinout

这将显示GPIO引脚的标记图,以及有关Pi的其他一些信息。

pinout
  • 单击 x 或使用命令 exit关闭终端窗口。

配置你的Pi

您可以通过菜单上 首选项 的 Raspberry Pi Configuration 应用程序控制大部分树莓派的设置,例如密码。

pi配置菜单

系统

在此选项卡中,您可以更改Pi的基本系统设置。

pi配置系统
  • 密码 - 设置 pi 用户的密码(最好更改出厂默认设置的密码’raspberry’)
  • Boot - 选择在Raspberry Pi启动时显示 Desktop 或 CLI (命令行界面)
  • 自动登录 - 启用此选项将使树莓派在启动时自动登录
  • 引导时连接网络 - 选择此选项将使树莓派等待网络连接可用,然后再启动
  • 启动画面 - 选择是否在Raspberry Pi启动时显示启动画面

接口

您可以使用许多不同类型的连接将设备和组件链接到树莓派。 “接口”选项卡用于打开或关闭这些不同的连接,以便Pi识别出您通过特定类型的连接将某些东西链接到它。

pi配置接口
  • 摄像头 - 启用 树莓派摄像头模块
  • SSH - 允许使用SSH从另一台计算机远程访问您的树莓派
  • VNC - 允许使用VNC从另一台计算机远程访问树莓派桌面
  • SPI - 启用SPI GPIO引脚
  • I2C - 启用I2C GPIO引脚
  • Serial - 使能串行(Rx,Tx)GPIO引脚
  • 1-Wire - 启用1-Wire GPIO引脚
  • 远程GPIO - 允许从另一台计算机访问Raspberry Pi的GPIO引脚

性能

如果您需要为要处理的特定项目执行此操作,可以在此选项卡中更改Pi的性能设置。

警告:更改Pi的性能设置可能会导致其表现不正常或无法正常工作。

pi配置性能
  • 超频 - 更改CPU速度和电压以提高性能
  • GPU内存 - 更改给予GPU的内存分配

本土化

pi配置本地化

此选项卡允许您将Raspberry Pi设置更改为特定于国家或地区。

  • 区域设置 - 设置树莓派使用的语言,国家/地区和字符集
  • 时区 - 设置时区
  • 键盘 - 更改键盘布局
  • WiFi国家 - 设置WiFi国家代码
]]>
28 0 0 0
<![CDATA[Elsonic推8.8吋长条状显示器:专为刷社交媒体准备]]> http://mixdiy.com/index.php/2021/11/26/tiaopin1/ Thu, 25 Nov 2021 21:30:50 +0000 http://mixdiy.com/?p=40

Elsonic 近日宣布推出 EK-MD088 长条状 8.8 英寸显示器,屏幕分辨率为 420*1920,是专门为刷社交媒体以及类似滚动体验而准备的。虽然这造型有点像是源自笑话的产物,但是该显示器将于明年 2 月份正式发售,售价为 130 美元。

对于那些无法在一个屏幕上容纳所有需要的东西,但又不想掏钱买另一个完整的显示器的用户来说,这些可能是一个巧妙的选择。但在垂直模式下几乎与 4K 显示器一样高。截图特别显示它可以显示Discord和Twitter的信息。

根据日本商店页面的机器翻译,它可以通过USB-C直接接收笔记本电脑或台式机的电源,并通过迷你HDMI接收其视频信号。商店页面说移动电池也可以使用。

]]>
40 0 0 0
<![CDATA[树莓派被曝明年开春上市]]> http://mixdiy.com/index.php/2021/11/30/shanghai/ Tue, 30 Nov 2021 11:38:02 +0000 http://mixdiy.com/?p=56

根据英国《每日电讯报》的报道,树莓派可能很快就会以超过 3.7 亿英镑(4.93 亿美元)的价格上市。

目前,树莓派已聘请 Stifel 和 Liberum 这两家投资银行的顾问为该公司在 2022 年春季上市提供建议。该消息是在树莓派从 Lansdowne Partners 和 Ezrah Charitable 获得 4500 万英镑(6000 万美元)投资后几个月发布的。据消息人士透漏,树莓派的估值溢价约为 5 亿美元。 

树莓派首席执行官 Eben Upton 在接受《每日电讯报》采访时表示,树莓派一直在研究如何为企业的未来提供资金。“显然,我们在9月份筹集的4500万美元消除了一些关于我们如何为未来融资的紧迫感。另一方面,我们对未来五年要做的事情有很好的计划。” 
据悉,这不是树莓派第一次考虑上市。今年3月,《每日电讯报》曾报道了树莓派上市的可能性。

树莓派发展历史树莓派创始人Eben Upton 在牛津大学教书时,发现学生们计算机的编程能力很差,因为当时计算机很贵且并不普及,为了解决这个问题,他便辞职进入博通工作,制作一个编程开发平台。
2012年3月,Eben Upton正式发售世界上最小的台式机,又称卡片式电脑,外形只有信用卡大小,却具有电脑的所有基本功能,这就是Raspberry Pi电脑板,中文译名"树莓派"。
这块树莓派基于ARM微型电脑主板制作,拥有PC所有基本功能,可以全部在一张信用卡大小的主板上。在Linux系统的加持下,树莓派只需要几行代码,就可以制作出有趣的项目。刚开始的树莓派配置不高,但一经推出后,受到了各界的喜爱,树莓派很快就卖完了,响应也特别好,后来为了维护 ,树莓派也在不断的升级,直到现在已经到了4B版本。

树莓派历代版本简介第一代的树莓派包括多个版本:A, A+, B, B+ ,国内常见的是 B 版本,A 版实际上没有发售A 版是 B 版的简化版。树莓派首先发行的是 B 版本。

树莓派 B

树莓派B型内置512MB内存,带两个USB端口,带100M有线网接口。配置:

BroadcomBCM2835

ARM1176JZF-S核心(ARM11)700MHz

内存:512MByte

双核多媒体协处理器(采用VideoCore IV技术)

1个SD/MMC/SDIO接口、1个0/100以太网接口、2个USB host 接口、1个3.5mm音频插孔,1个HDMI视频接口、1个RCA视频接口,1个26pin扩展口(支持SPI.I2C、UART)2013年2月国内厂商深圳韵动电子取得了该产品在国内的生产及销售权限,为了便于区分市场,树莓派基金会规定韵动电子在中国大陆销售的树莓派一律采用红色的PCB。

树莓派 A

Model A基本上可以认为是树莓派Model B的廉价版本,没有网络接口,内存容量也进一步缩小。树莓派A型(未发售)内置256MB内存,带一个USB端口,不带有线网接口。

树莓派B+

“树莓派B+”版本,依然采用 BCM2835 处理器以及和上一代树莓派相同的系统软件。内存也依然是512MB,但是在以下几处关键部件做了改进:

更多的 GPIO 针脚,40针!(老版本是26针)更多的USB接口,比老版本多2个,4个!并且对热插拔和过流保护做了改良。用Micro SD插口替换了老的SD插口。

更低的功耗,降低了功耗0.5~1W。音频优化,音频电路采用了专用的低噪音电源。

更简洁的外形,B+版本将USB接口和电路板边沿对齐了、移除AV接口,并在主板上做了4个固定孔,方便固定。从配置上来说,model B+使用了和model B相同的BCM2835芯片和512MB内存,但和前代产品相比较,B+版本的功耗更低,接口也更丰富。model B+将通用输入输出引脚增加到了40个,USB接口也从B版本的2个增加到了4个,除此之外,model B+的功耗降到了0.5W到1W,旧款的SD卡插槽被换成了更美观的推入式microSD卡槽,音频部分则采用了低噪供电。从外形上来看,USB接口被移到了主板的一边,复合视频移到了3.5mm音频口的位置,此外还增加了四个独立的安装孔。

树莓派A+

这款新型号支持同Model B一样的 MicroSD 卡读卡器和40-pin的GPI连接端口,其他的功能包括博通 BCM2385 ARM11处理器、256MB的内存和HDMI输出端口。Model A+完整的规格如下:配256MB内存的700MHz博通BCM2835处理器40针的扩展GPIO1个USB 2.0端口4通道立体声输出和复合视频端口全尺寸HDMI端口连接树莓派摄像头的CSI端口连接树莓派触控显示屏幕的DSI端口用于加载系统和储存数据的MicroSD端口Micro USB电源更小的主板尺寸和完全兼容HAT能够支持1080P高清视频输出

树莓派2B


树莓派2代的Model B采用Broadcom BCM2836 900MHz的四核SoC,1GB内存,是新一代开拓者,兼容1代B+。但相比之下,树莓派2的性能提升6倍,内存翻了一番。Raspberry Pi 2不仅能跑全系列ARM GNU/Linux发行版,而且支持Snappy Ubuntu Core及Windows 10  IoT Core(物联网版本)配置:搭载900MHz 的四核处理器(900MHz quad-core ARM Cortex-A7 CPU ),预计性能6倍于之前的B+版本。1GB LPDDR2 SDRAM,2倍于之前的B+版本。与1代完全兼容。

树莓派zero

配置:博通 BCM2835 芯片 1GHz ARM11 core (比树莓派1代快 40%)512MB LPDDR2 SDRAM一个 micro-SD 卡槽一个 mini-HDMI 接口,支持 1080p 60hz 视频输出Micro-USB 接口用于供电和数据传输1个 40Pin 的 GPIO 接口,同树莓派A+、B+、2B版本一样(引脚空置,需要自己焊接,这样在不需要使用到GPIO的时候会显得更加小巧更容易封装)空置的视频接口(用于连接电视输出视频,需要自己焊接)有史以来最小的树莓派尺寸,65mm x 30mm x 5mm

树莓派3B

配置:64-bit 1.2 GHz ARM Cortex A53 处理器,内置 WIFI ,兼容 2.4GHz 802.11 b/g/n;视频和 3D 性能进步明显;支持蓝牙 4.1 规范。4 个 USB 2.0 端口, 10/100 以太网, HDMI 端口, 3.5mm 音频口和视频输出,CSI 和 DSI 摄像机和显示连接器。microSD 插槽以及 GPIO 插槽。

树莓派Zero W


配置:BCM2835,1GHz,ARM11 CPU512MB RAMMini-HDMI 接口Micro-USB On-The-Go 接口Micro-USB 电源接口40-pin GPIO HAT 预留焊接口CSI 摄像头软排线接口802.11n 无线网络支持蓝牙 4.0 支持

树莓派3B+

树莓派3B+是采用 BCM2837B0 型号 CPU 构建,是之前 3B 上用的博通处理器的更新版本,这个处理器包含完整的性能优化和散热器。这允许更好的时钟频率,并能更准确地监控芯片温度。

双频无线网卡和蓝牙采用 Cypress CYW43455 “combo” 芯片,与上一代产品相比,3B+ 在2.4GHz 频带的数据传输表现更好,并且在 5GHz 频带的表现将更好。配置:1.4GHz 64位4核 ARM Cortex-A53 CPU双频 802.11ac 无线网卡和蓝牙 4.2更快的以太网(千兆以太网 over USB 2.0)1G LPDDR2PoE 支持(Power-over-Ethernet,with PoE HAT)改进 PXE 网络与 USB 大容量存储启动

树莓派4B

树莓派4代基于 BCM2711 构建,完全重新实现了 28nm 的 BCM283X。使用更强大的 Cortex-A72 内核取代 Cortex-A53,从而使性能较树莓派3B+提高了2到4倍(具体取决于测试基准)。
配置:

1.5GHz 4核心64位 ARM Cortex-A72 CPU (~3×倍性能)

1GB/2GB/4GB LPDDR4 SDRAM 内存

全吞吐量千兆以太网

双频 802.11ac 无线网络

蓝牙 5.0

两个 USB 3.0 和两个 USB 2.0 接口

双显示器支持,分辨率高达 4K

VideoCore VI 显卡,支持 OpenGL ES 3.x

HEVC 视频 4Kp60 硬解码

完全兼容早期的树莓派产品

树莓派各版本参数

]]>
56 0 0 0
<![CDATA[基于树莓派搭建个人服务器]]> http://mixdiy.com/index.php/2021/11/30/server/ Tue, 30 Nov 2021 11:42:02 +0000 http://mixdiy.com/?p=60

No.1

前言
由于本人在这段时候,看到了一个叫做树莓派的东东,初步了解之后觉得很有意思,于是想把整个过程记录下来。

No.2

树莓派是什么?

Raspberry Pi(中文名为树莓派,简写为 RPi,(或者 RasPi / RPI) 是为学习计算机编程教育而设计),只有信用卡大小的微型电脑,其系统基于 Linux。随着 Windows 10 IoT 的发布,我们也将可以用上运行 Windows 的树莓派。

自问世以来,受众多计算机发烧友和创客的追捧,曾经一“派”难求。别看其外表“娇小”,内“心”却很强大,视频、音频等功能通通皆有,可谓是麻雀虽小,五脏俱全。

1. 用我的话理解

用我的话理解就是树莓派就是一台主机,你可以外接显示器,键盘鼠标,u盘等等外设,因为它体积很小,而且又有很多串口和外接的口,可以直接调用很多底层硬件。

2. 市面上的型号

市面上大多是 3 代 B+ 型,淘宝一搜树莓派一大堆都是,价钱纯主板(不要任何外设)在 230+ 左右,有点小贵,超过我的预算,所以我继续寻找廉价的,终于让我发现了一款 100+ 的树莓派。

3. 树莓派 zero w

树莓派 zero w 是一款 mini 的树莓派,体质只有 3b+ 的 1/3。实际到手后,你会发现它真的超级小,超级可爱。以下是我的实物图,你可以看看大小到底有多 mini。

你可以看到,最上面是一根普通的黑色签字笔,接下来是一个即插即用型的外接 wifi 网卡,然后是一个 USB 读卡器,最底下的就是我们今天的主角 zero w。它真的超级小,有木有。真的是完美的诠释了那句“麻雀虽小,五脏俱全”的话。

zero w 这款树莓派的主要参数如下:•  BCM2835 处理器,1GHz 主频,512MB RAM•  BCM43438 WiFi / BT 芯片•  micro-USB 电源接口•  micro-USB OTG 接口•  miniHDMI 端口•  复合视频和重置扩展接口•  脆弱的 CSI 摄像头接口•  micro-SD 卡座,存放操作系统•  40-pin GPIO 扩展接口•  尺寸:65mm*30mm你别看它的 cpu 只有 1 核,内存只有 512MB,就觉得它可能什么都做不了,但是实际上它的性能还是很好的,用于跑一个网站真的是小 case。

4. 更多树莓派

关于更多树莓派型号或者使用教程你可以去树莓派实验室这个网站,上面有丰富的资源。

No.3

树莓派zero w安装系统

1. 准备

你可能提前需要准备的东西如下:

•  16GB or 32GB 的 SanDisk 内存卡(注意是以前那种放在手机上,很小的哦)

•  一根最普通不过的 usb 安卓数据线(not type-c)

•  u 盘格式化工具(推荐使用 SDFormatter)

•  系统烧写工具(Win32DiskImager)

•  树莓派系统(可以去官网下载)

我使用的是 Raspbian Stretch Lite 这个系统镜像,这个系统是官方制作的,lite 是无桌面版的,只有黑漆漆的控制台,优点是体积小,省性能和内存。

名字带有 desktop 的是有桌面 ui 的,对不熟悉 liunx 系统的朋友可能更友好,但是体积很大,占用的性能也会更高。

2. 第一步下载系统镜像

下载好你需要的系统镜像后,如下图

一开始只有一个 zip 的压缩包,大小大概 360MB 左右,你需要把它解压,得到上图的文件夹。

然后进入文件夹可以看到一个 img 的镜像,大小为 1.7GB 左右。

ps:这个官方的 Raspbian 镜像,如果是其他第三方的镜像,可能下载后的压缩包解压后不是 img 镜像,这种情况请另行百度解决。

3. 使用 Win32DiskImager 往内存卡中写入镜像

把内存卡插入读卡器后,插入电脑。

打开 Win32DiskImager 软件后,选择 img 镜像,设备选择你的 U 盘,然后点击写入就可以了,写入完成后会弹出成功的提示框。

ps: 我上图没有选择设备,因为的没插入读卡器,仅仅是示范而已

4. 修改 boot 分区的文件

先别急着拔出读卡器,此时,我们电脑可以看到 u 盘中只有一个名为 boot 的分区,大小可能只有 40MB 左右,不要着急,因为 window 不识别内存卡中 liunx 系统的其他分区。 

4.1 新建 ssh 文件

因为我们的 zero w 有一个 mini hdmi 的接口,但是我不需要屏幕,所以需要使用 ssh 连接到 zero w 中的系统,所以需要在第一次开机就能开启 ssh 功能。

我们进入 boot 分区内,然后新建一个名为 ssh 的文件,注意不要后缀名!!!!也不要往里面写任何东西!!

4.2 新建 wpa_supplicant.conf 文件

因为 ssh 连接是需要 ip 地址的,所以我们需要将 zero w 在第一次开机自动连接 wifi,使其和我们的电脑处于一个局域网,这样我们才可以通过 ssh 连接到 zero w 的系统。

同样的在 boot 分区内,新建一个名为 wpa_supplicant.conf 的文件,然后往里面写入如下内容后保存:

country=CN
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
ssid="你的wifi名字"
psk="你的wifi密码"
}

5. 组装我们的最小主机并连接

取出读卡器中的内存卡,然后插入到 zero w 中,使用一根 usb 安卓数据线连接电源(5V1A)即可。

等待几分钟,期间我们的 zero w 的指示灯会一直闪烁,很正常,等待指示灯常亮的时候,我们去路由器上,查看一下树莓派的 ip 地址。

可以看到我们 zero w 的 ip 为 192.168.0.104,然后使用 ssh 连接工具(推荐使用 putty)连接树莓派,初始账户为 pi,密码是 raspberry。

连接成功,如上图所示。这样我们的系统就正确无误的安装好了。

ps: 如果是手机开启热点当做一个路由器的话,咱们手机下载一个名叫 android terminal 的 app,然后输入 ip neigh 指令,就可以查到连接到手机的设备的 ip 信息了。

6. 优化咱们树莓派的系统

6.1 修改源

因为国外的源,咱们在国内的连接过去网速很慢,所以我们需要修改为国内的源,我修改的是中科大的源。

6.1.1 修改 sources.list 文件

sudo nano /etc/apt/sources.list
--注释其他内容,添加以下:
deb http://mirrors.ustc.edu.cn/raspbian/raspbian/raspbian stretch main contrib non-free rpi

6.1.2 修改 raspi.list 文件

sudo nano /etc/apt/sources.list.d/raspi.list
--注释其他内容,添加以下:
deb http://mirrors.ustc.edu.cn/archive.raspberrypi.org/debian stretch main ui
6.1.3 执行更新
sudo apt-get update
sudo apt-get upgrade

6.2 修改时区

sudo dpkg-reconfigure tzdata

找到亚洲 Asia,然后选择 shanghai 就可以了。

6.3 开机自启 ssh

第一种:

sudo raspi-config

进入选择找到 interfacing option 选择,然后找到 ssh,按回车使能 enable 就可以了。

第二种:

在终端命令行中启动 SSH 服务后,如果系统重启或关机后启动,SSH 服务默认是关闭的,依然需要手动启动,为了方便可以设置 SSH 服务开机自动启动,打开 /etc/rc.local 文件,在语句 exit 0 之前加入:/etc/init.d/ssh start

建议都试试,反之我的是可以了。

7. 安装 nginx

#安装
sudo apt-get install nginx
#启动
sudo /etc/init.d/nginx start
#重启
sudo /etc/init.d/nginx restart
#停止
sudo /etc/init.d/nginx stop

打开浏览器访问 192.168.0.104(你的树莓派 ip 地址),可以看到 nginx 的页面,说明安装好了。

可以正常的看到页面了,但是这样只能在内网(局域网中)看到,我想让所有人都可以访问怎么办?

8. 内网穿透

内网穿透,意思就是将内网(本地)的 web 应用通过 nat 穿透到公网上,从而让别人可以访问到。

内网穿透目前主要由 ngrok 和 frp 两种,都非常好用,国内 ngrok 免费的有 ittun、sunny 和 natapp,这三个都是免费的,前面两个可以自定义域名,后面的需要 vip 版本才可以自定义域名。

我这三种都试过,我发现 sunny 的 arm 版本的 ngrok 客户端在我的树莓派运行不了,ittun 的和 natpp 的 ngrok 都可以,由于需要自定义域名,我使用的是 ittun 的 ngrok_arm 版本的。

使用方法这三者官网都有详细说明,大家自行查看。

现在,树莓派网站已经搭建完成,可以访问http://mixdiy.com

因为需要 ngrok 在后台运行,所以我用的是 screen 会话使其可以在后台运行。但是开启自启,还没有实现,万一断电或者断网了,我必须手动去运行一下 ngrok,这是目前没有解决的痛点。

9. 更多

树莓派不仅仅只是可以用于运行一个网站,还有很多很多的功能等待你的开发,可以多去看看树莓派实验室里面,很多大神都写了很多实用的教程。

我的 zero w 状态信息如下:

在上面开启了一个 nginx 和 ngrok 服务,内存剩余还有 250MB,还是很舒服的,cpu 温度也不算高,运行两天了,基本在 37-39 之间。

]]>
60 0 0 0
<![CDATA[树莓派使用Lazarus]]> http://mixdiy.com/index.php/2021/12/01/lazarus-on-raspberry-pi/ Wed, 01 Dec 2021 02:15:53 +0000 http://mixdiy.com/?p=68

树莓派上的LazarusLazarus on Raspbian Wheezy

Raspberry Pi Logo.png

本文只适合 Raspberry Pi.

Raspberry Pi(数莓派)是一个信用卡大小的单板计算机。它由英国的Raspberry Pi基金为了激励在学校的计算机基础科学教学发起的。Raspberry Pis也可以用来作为媒体服务器以及其他用途机器人、控制工程等多种用途。 Raspberry Pi基金会推荐Raspbian Wheezy作为标准的操作系统。大家可以选择在RPI运行RISC OS、各种Linux发行版以及Android等操作系统。 Lazarus可以在原生的Raspbian操作系统下运行。

Contents

 [hide

安装与编译器Lazarus

在 Raspbian上简单安装

Raspberry Pi 1

在 Raspbian OS上,安装Lazarus与 Free Pascal很容易。打开命令行窗口输入下面的指令:

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install fpc
sudo apt-get install lazarus

这里安装的是为Raspberry Pi预编译的稳定版FPC与Lazarus 。当然,需要有网络连接。安装过程大概需要30分钟,这个过程是自动完成等的。安装完成以后,你就可以从LXDE的开始菜单的“Programming”启动Lazarus了。

  • 安装步骤 1
  • 安装步骤 2
  • 安装步骤 3
  • 安装步骤 4

如果你需要一个新的版本,或者抱怨Lazarus 的问题, 点击.

Raspberry Pi 2

自2015六月起,普通的“out of the box”安装方法也适用于安装树莓派2的B型。但是安装时获得的版本是相当旧的。想要获得新版FPC和Lazarus请看链接 相关文章.

  • Lazarus "out of the box" on Raspberry Pi 2

从Windows 对Raspberry Pi的交叉编译

1. 使用 fpcup

使用fpcup是实现交叉编译的一种方法,输入下面的指令: fpcup#Linux_ARM_cross_compiler

2. 使用脚本

可以使用手动批处理文件,按照以下步骤。

2.1 先决条件

FPC 2.7.1或更高的安装源代码
下载并安装Windows版本的Linaro binutils for linux gnueabihf到%FPCPATH%/bin/win32-armhf-linux [1]

2.2 创建脚本(需要匹配实际路径)

set PATH=C:\pp\bin\i386-win32;%PATH%;
set FPCMAKEPATH=C:/pp
set FPCPATH=C:/pp
set OUTPATH=C:/pp271
%FPCMAKEPATH%/bin/i386-win32/make distclean OS_TARGET=linux CPU_TARGET=arm  CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" FPC=%FPCPATH%/bin/i386-win32/ppc386.exe

%FPCMAKEPATH%/bin/i386-win32/make all OS_TARGET=linux CPU_TARGET=arm CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" FPC=%FPCPATH%/bin/i386-win32/ppc386.exe
if errorlevel 1 goto quit
%FPCMAKEPATH%/bin/i386-win32/make crossinstall CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" OS_TARGET=linux CPU_TARGET=arm FPC=%FPCPATH%/bin/i386-win32/ppc386.exe INSTALL_BASEDIR=%OUTPATH%

:quit
pause

使用生成的ppcrossarm.exe和 ARM RTL,你可以创建跨平台的Lazarus工程,生成Raspberry Pi和其他armhf设备的应用程序。 注意,这里不包括所有Window 库。

编译源代码

如果你想要从Subversion编译Lazarus源代码。请看 Michell Computing: Lazarus on the Raspberry Pi

  • How to start Lazarus in Raspbian Wheezy
  • Lazarus on Raspberry Pi
  • Numbering of GPIO pins

用Gentoo(及其他版本)在Raspberry上编译源码

  • Gentoo是一个基于Linux的自由操作系统

如果你想安装最新版的FPC及附件,或者是集成FPC编译器:请阅读下面的指南 这是关于Gentoo的例子,本指南适用其他版本:Install fpc on Raspberry with Gentoo

访问外部硬件

Raspberry Pi 外部连接引脚

Raspberry Pi的发展的目标之一是方便轻松地访问外部设备,如传感器和控制器。Lazarus有五种方法来访问I/O设备:

  1. 原生代码直接访问,使用BaseUnix单元
  2. 通过 封装外壳调用
  3. 通过 wiringPi 库.
  4. 使用单元 rpi_hal.
  5. 使用单元 PiGpio.
  6. 通过 PascalIO 库访问.

1. 原生代码直接访问

对Raspberry Pi的GPIO端口访问的简单测试程序程序访问GPIO的测试电路演示实现电路

这种访问外部硬件的方法,不需要额外的库。唯一的要求是baseunix库,这是Free Pascal的RTL部分。

通过GPIO端口控制开关

下面将演示一个简单程序。控制GPIO的17号引脚的电压,可以用来控制LED、晶体管或继电器。 程序包含GPIO17ToggleBox的实例ToggleBox和日志返回LogMemo类的实例TMemo

例如,一个LED的阳极连接到引脚11上(BCM2835 SoC对应的GPIO为17)和LED的阴极通过一个68欧姆的电阻连接引脚6(GND)。随后,LED会随着应用程序的toggle box切换点亮和熄灭。

该代码需要root权限运行,即从root帐号(不推荐)运行或通过“su '

控制单元:

unit Unit1;

{Demo application for GPIO on Raspberry Pi}
{Inspired by the Python input/output demo application by Gareth Halfacree}
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  Unix, BaseUnix;

type

  { TForm1 }

  TForm1 = class(TForm)
    LogMemo: TMemo;
    GPIO17ToggleBox: TToggleBox;
    procedure FormActivate(Sender: TObject);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure GPIO17ToggleBoxChange(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

const
  PIN_17: PChar = '17';
  PIN_ON: PChar = '1';
  PIN_OFF: PChar = '0';
  OUT_DIRECTION: PChar = 'out';

var
  Form1: TForm1;
  gReturnCode: longint; {stores the result of the IO operation}

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.FormActivate(Sender: TObject);
var
  fileDesc: integer;
begin
  { Prepare SoC pin 17 (pin 11 on GPIO port) for access: }
  try
    fileDesc := fpopen('/sys/class/gpio/export', O_WrOnly);
    gReturnCode := fpwrite(fileDesc, PIN_17[0], 2);
    LogMemo.Lines.Add(IntToStr(gReturnCode));
  finally
    gReturnCode := fpclose(fileDesc);
    LogMemo.Lines.Add(IntToStr(gReturnCode));
  end;
  { Set SoC pin 17 as output: }
  try
    fileDesc := fpopen('/sys/class/gpio/gpio17/direction', O_WrOnly);
    gReturnCode := fpwrite(fileDesc, OUT_DIRECTION[0], 3);
    LogMemo.Lines.Add(IntToStr(gReturnCode));
  finally
    gReturnCode := fpclose(fileDesc);
    LogMemo.Lines.Add(IntToStr(gReturnCode));
  end;
end;

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
var
  fileDesc: integer;
begin
  { Free SoC pin 17: }
  try
    fileDesc := fpopen('/sys/class/gpio/unexport', O_WrOnly);
    gReturnCode := fpwrite(fileDesc, PIN_17[0], 2);
    LogMemo.Lines.Add(IntToStr(gReturnCode));
  finally
    gReturnCode := fpclose(fileDesc);
    LogMemo.Lines.Add(IntToStr(gReturnCode));
  end;
end;

procedure TForm1.GPIO17ToggleBoxChange(Sender: TObject);
var
  fileDesc: integer;
begin
  if GPIO17ToggleBox.Checked then
  begin
    { Swith SoC pin 17 on: }
    try
      fileDesc := fpopen('/sys/class/gpio/gpio17/value', O_WrOnly);
      gReturnCode := fpwrite(fileDesc, PIN_ON[0], 1);
      LogMemo.Lines.Add(IntToStr(gReturnCode));
    finally
      gReturnCode := fpclose(fileDesc);
      LogMemo.Lines.Add(IntToStr(gReturnCode));
    end;
  end
  else
  begin
    { Switch SoC pin 17 off: }
    try
      fileDesc := fpopen('/sys/class/gpio/gpio17/value', O_WrOnly);
      gReturnCode := fpwrite(fileDesc, PIN_OFF[0], 1);
      LogMemo.Lines.Add(IntToStr(gReturnCode));
    finally
      gReturnCode := fpclose(fileDesc);
      LogMemo.Lines.Add(IntToStr(gReturnCode));
    end;
  end;
end;

end.

Main program:

program io_test;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Interfaces, // this includes the LCL widgetset
  Forms, Unit1
  { you can add units after this };

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

读取引脚的状态

读取一个GPIO引脚状态的演示程序程序访问GPIO的测试电路实际电路

当然也可以从GPIO端口读取一个开关的状态。

下面的简单例子非常类似于前一个例子。使用GPIO的18号引脚,控制二进制设备如开关输入,晶体管或继电器。这个程序包含一个名称为CheckBoxGPIO18CheckBox 与LogMemo的实例TMemo日志返回代码。

在实例中,第12引脚的一端连接一个按钮,按钮的一端(对应的GPIO引脚18的BCM2835 SoC)通过一个10欧姆的上拉电阻和引脚1连接(+3.3 V,看接线图)。按钮的另一端连接6号引脚(GND)。按钮的开关状态对应和程序中复选框的打开或关闭状态对应。

注意,当按钮释放时18脚的电位为高(由通过上拉电阻连接到引脚1),当按钮压下时电压为低(因为在这种情况下,引脚18是通过开关连接到GND)。因此,按钮按下时GPIO引脚信号为0,当释放时信号为1。

这个程序同样需要root权限

控制单元:

unit Unit1;

{Demo application for GPIO on Raspberry Pi}
{Inspired by the Python input/output demo application by Gareth Halfacree}
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}

{This application reads the status of a push-button}

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ButtonPanel, Unix, BaseUnix;

type

  { TForm1 }

  TForm1 = class(TForm)
    ApplicationProperties1: TApplicationProperties;
    GPIO18CheckBox: TCheckBox;
    LogMemo: TMemo;
    procedure ApplicationProperties1Idle(Sender: TObject; var Done: Boolean);
    procedure FormActivate(Sender: TObject);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  private
    { private declarations }
  public
    { public declarations }
  end;

const
  PIN_18: PChar = '18';
  IN_DIRECTION: PChar = 'in';

var
  Form1: TForm1;
  gReturnCode: longint; {stores the result of the IO operation}

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.FormActivate(Sender: TObject);
var
  fileDesc: integer;
begin
  { Prepare SoC pin 18 (pin 12 on GPIO port) for access: }
  try
    fileDesc := fpopen('/sys/class/gpio/export', O_WrOnly);
    gReturnCode := fpwrite(fileDesc, PIN_18[0], 2);
    LogMemo.Lines.Add(IntToStr(gReturnCode));
  finally
    gReturnCode := fpclose(fileDesc);
    LogMemo.Lines.Add(IntToStr(gReturnCode));
  end;
  { Set SoC pin 18 as input: }
  try
    fileDesc := fpopen('/sys/class/gpio/gpio18/direction', O_WrOnly);
    gReturnCode := fpwrite(fileDesc, IN_DIRECTION[0], 2);
    LogMemo.Lines.Add(IntToStr(gReturnCode));
  finally
    gReturnCode := fpclose(fileDesc);
    LogMemo.Lines.Add(IntToStr(gReturnCode));
  end;
end;

procedure TForm1.ApplicationProperties1Idle(Sender: TObject; var Done: Boolean);
var
  fileDesc: integer;
  buttonStatus: string[1] = '1';
begin
  try
    { Open SoC pin 18 (pin 12 on GPIO port) in read-only mode: }
    fileDesc := fpopen('/sys/class/gpio/gpio18/value', O_RdOnly);
    if fileDesc > 0 then
    begin
      { Read status of this pin (0: button pressed, 1: button released): }
      gReturnCode := fpread(fileDesc, buttonStatus[1], 1);
      LogMemo.Lines.Add(IntToStr(gReturnCode) + ': ' + buttonStatus);
      LogMemo.SelStart := Length(LogMemo.Lines.Text) - 1;
      if buttonStatus = '0' then
        GPIO18CheckBox.Checked := true
      else
        GPIO18CheckBox.Checked := false;
    end;
  finally
    { Close SoC pin 18 (pin 12 on GPIO port) }
    gReturnCode := fpclose(fileDesc);
    LogMemo.Lines.Add(IntToStr(gReturnCode));
    LogMemo.SelStart := Length(LogMemo.Lines.Text) - 1;
  end;
end;

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
var
  fileDesc: integer;
begin
  { Free SoC pin 18: }
  try
    fileDesc := fpopen('/sys/class/gpio/unexport', O_WrOnly);
    gReturnCode := fpwrite(fileDesc, PIN_18[0], 2);
    LogMemo.Lines.Add(IntToStr(gReturnCode));
  finally
    gReturnCode := fpclose(fileDesc);
    LogMemo.Lines.Add(IntToStr(gReturnCode));
  end;
end;

end.

上面的例子主程序是相同的,。

2. 通过封装的外壳访问硬件

访问硬件的另一种方法是通过封装终端命令。这是通过使用fpsystem功能。这种方法可以访问baseunix单元不支持的功能。下面的代码实现了一个具有相同功能的程序,该程序是从上面的第一个清单所产生的。

控制单元:

unit Unit1;

{Demo application for GPIO on Raspberry Pi}
{Inspired by the Python input/output demo application by Gareth Halfacree}
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Unix;

type

  { TForm1 }

  TForm1 = class(TForm)
    LogMemo: TMemo;
    GPIO17ToggleBox: TToggleBox;
    procedure FormActivate(Sender: TObject);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure GPIO17ToggleBoxChange(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form1: TForm1;
  gReturnCode: longint; {stores the result of the IO operation}

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.FormActivate(Sender: TObject);
begin
  { Prepare SoC pin 17 (pin 11 on GPIO port) for access: }
  gReturnCode := fpsystem('echo "17" > /sys/class/gpio/export');
  LogMemo.Lines.Add(IntToStr(gReturnCode));
  { Set SoC pin 17 as output: }
  gReturnCode := fpsystem('echo "out" > /sys/class/gpio/gpio17/direction');
  LogMemo.Lines.Add(IntToStr(gReturnCode));
end;

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  { Free SoC pin 17: }
  gReturnCode := fpsystem('echo "17" > /sys/class/gpio/unexport');
  LogMemo.Lines.Add(IntToStr(gReturnCode));
end;

procedure TForm1.GPIO17ToggleBoxChange(Sender: TObject);
begin
  if GPIO17ToggleBox.Checked then
  begin
    { Swith SoC pin 17 on: }
    gReturnCode := fpsystem('echo "1" > /sys/class/gpio/gpio17/value');
    LogMemo.Lines.Add(IntToStr(gReturnCode));
  end
  else
  begin
    { Switch SoC pin 17 off: }
    gReturnCode := fpsystem('echo "0" > /sys/class/gpio/gpio17/value');
    LogMemo.Lines.Add(IntToStr(gReturnCode));
  end;
end;

end.

主程序与上面的例子相同。这个程序必须用根权限执行。

3. wiringPi方法与函数

Alex Schaller封装的Gordon Henderson的Arduino兼容wiringpi库,提供了一种类似Arduino电路板的编码方案。

Function wiringPiSetup:longint: 使用wiringpi引脚编号方案初始化wiringpi系统

Procedure wiringPiGpioMode(mode:longint): 用Broadcom的GPIO引脚编号方案wiringpi初始化系统。

Procedure pullUpDnControl(pin:longint; pud:longint): 控制内部GPIO引脚上拉/下拉电阻。

Procedure pinMode(pin:longint; mode:longint): 设置一个PIN的模式:输入、输出或pwm_output。

Procedure digitalWrite(pin:longint; value:longint): 设置一个输出位。

Procedure pwmWrite(pin:longint; value:longint): 设置一个输出PWM(脉宽调制)值,0到1024之间。

Function digitalRead(pin:longint):longint: 读取一个给定引脚的值,返回1或0。

Procedure delay(howLong:dword): 等待多少毫秒。

Procedure delayMicroseconds(howLong:dword): 等待多少微秒。

Function millis:dword: 返回程序调用wiringpisetup函数花费的毫秒数。

4. rpi_hal-hardware抽象库 (GPIO, I2C and SPI 函数与方法)

这个单元大约1700行代码,由Stefan Fischer提供,提供程序和函数访问RPI硬件I2C,SPI和GPIO:

下面是函数和方法的摘录:

procedure gpio_set_pin (pin:longword;highlevel:boolean); { 设置 RPi GPIO 引脚速率为高或低; 速率 @ 700MHz -> 0.65MHz }

function gpio_get_PIN (pin:longword):boolean; { 获取 RPi GPIO 引脚等级,当Pin为'1'时返回True,为'0'时返回false; 速率 @ 700MHz -> 1.17MHz } 

procedure gpio_set_input (pin:longword); { 设置 RPi GPIO 引脚为直接输入}

procedure gpio_set_output(pin:longword); { 设置 RPi GPIO 引脚为直接输出 }

procedure gpio_set_alt (pin,altfunc:longword); { 设置 RPi GPIO 引脚为备用功能 0..5 }

procedure gpio_set_gppud (mask:longword); { 设置RPI GPIO拉上/下登记(gppud)}

...

function rpi_snr :string; { 信噪比: 0000000012345678 }

function rpi_hw  :string; { 处理器类型: BCM2708 }

function rpi_proc:string; { ARMv6-compatible processor rev 7 (v6l) }

...

function i2c_bus_write(baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : integer;

function i2c_bus_read (baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : integer;

function i2c_string_read(baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : string;

function i2c_string_write(baseadr,reg:word; s:string; testnr:integer) : integer;

...

procedure SPI_Write(devnum:byte; reg,data:word);

function SPI_Read(devnum:byte; reg:word) : byte;

procedure SPI_BurstRead2Buffer (devnum,start_reg:byte; xferlen:longword);

procedure SPI_BurstWriteBuffer (devnum,start_reg:byte; xferlen:longword); { 从缓冲区SPI的地址写入一段字节的内容 }

... 

测试程序 (testrpi.pas):

//Simple Test program, which is using rpi_hal;

  program testrpi;
  uses rpi_hal;
  begin
    writeln('Show CPU-Info, RPI-HW-Info and Registers:');
    rpi_show_all_info;
    writeln('Let Status LED Blink. Using GPIO functions:');
    GPIO_PIN_TOGGLE_TEST;
    writeln('Test SPI Read function. (piggy back board with installed RFM22B Module is required!)');
    Test_SPI;
  end.

5. 底层pascal单元 PiGpio(代替wiringpi C语言库的GPIO控制)

这个单元 (pigpio.pas[2]) 270 行代码是由Gabor Szollosi提供的 , 运行速度飞快(例如GPIO引脚输出开关频率达到8MHz) :

unit PiGpio;
{
 BCM2835 GPIO Registry Driver, also can use to manipulate cpu other registry areas

 This code is tested only Broadcom bcm2835 cpu, different arm cpus may need different
 gpio driver implementation

 2013 Gabor Szollosi
}
{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;

const
  REG_GPIO = $20000;//bcm2835 gpio register 0x2000 0000. new fpMap uses page offset, one page is 4096bytes
  // hex 0x1000 so simply calculate 0x2000 0000 / 0x1000  = 0x2000 0
  PAGE_SIZE = 4096;
  BLOCK_SIZE = 4096;
    // The BCM2835 has 54 GPIO pins.
//	BCM2835 data sheet, Page 90 onwards.
//	There are 6 control registers, each control the functions of a block
//	of 10 pins.

  CLOCK_BASE = (REG_GPIO + $101);
  GPIO_BASE =  (REG_GPIO + $200);
  GPIO_PWM =   (REG_GPIO + $20C);

     INPUT = 0;
     OUTPUT = 1;
     PWM_OUTPUT = 2;
     LOW = False;
     HIGH = True;
     PUD_OFF = 0;
     PUD_DOWN = 1;
     PUD_UP = 2;

   // PWM

  PWM_CONTROL = 0;
  PWM_STATUS  = 4;
  PWM0_RANGE  = 16;
  PWM0_DATA   = 20;
  PWM1_RANGE  = 32;
  PWM1_DATA   = 36;

  PWMCLK_CNTL =	160;
  PWMCLK_DIV  =	164;

  PWM1_MS_MODE    = $8000;  // Run in MS mode
  PWM1_USEFIFO    = $2000; // Data from FIFO
  PWM1_REVPOLAR   = $1000;  // Reverse polarity
  PWM1_OFFSTATE   = $0800;  // Ouput Off state
  PWM1_REPEATFF   = $0400;  // Repeat last value if FIFO empty
  PWM1_SERIAL     = $0200;  // Run in serial mode
  PWM1_ENABLE     = $0100;  // Channel Enable

  PWM0_MS_MODE    = $0080;  // Run in MS mode
  PWM0_USEFIFO    = $0020;  // Data from FIFO
  PWM0_REVPOLAR   = $0010;  // Reverse polarity
  PWM0_OFFSTATE   = $0008;  // Ouput Off state
  PWM0_REPEATFF   = $0004;  // Repeat last value if FIFO empty
  PWM0_SERIAL     = $0002;  // Run in serial mode
  PWM0_ENABLE     = $0001;  // Channel Enable


type

  { TIoPort }

  TIoPort = class // IO bank object
  private         //

    //function get_pinDirection(aPin: TGpIoPin): TGpioPinConf;

  public
   FGpio: ^LongWord;
   FClk: ^LongWord;
   FPwm: ^LongWord;
   procedure SetPinMode(gpin, mode: byte);
   function GetBit(gpin : byte):boolean;inline; // gets pin bit}
   procedure ClearBit(gpin : byte);inline;// write pin to 0
   procedure SetBit(gpin : byte);inline;// write pin to 1
   procedure SetPullMode(gpin, mode: byte);
   procedure PwmWrite(gpin : byte; value : LongWord);inline;// write pin to pwm value
  end;

  { TIoDriver }

  TIoDriver = class
  private

  public
    destructor Destroy;override;
    function MapIo:boolean;// creates io memory mapping
    procedure UnmapIoRegisrty(FMap: TIoPort);// close io memory mapping
    function CreatePort(PortGpio, PortClk, PortPwm: LongWord):TIoPort; // create new IO port
  end;

var

    fd: integer;// /dev/mem file handle
procedure delayNanoseconds (howLong : LongWord);


implementation

uses
  baseUnix, Unix;

procedure delayNanoseconds (howLong : LongWord);
var
  sleeper, dummy : timespec;
begin
  sleeper.tv_sec  := 0 ;
  sleeper.tv_nsec := howLong ;
  fpnanosleep (@sleeper,@dummy) ;
end;
{ TIoDriver }
//*******************************************************************************
destructor TIoDriver.Destroy;
begin
  inherited Destroy;
end;
//*******************************************************************************
function TIoDriver.MapIo: boolean;
begin
 Result := True;
 fd := fpopen('/dev/mem', O_RdWr or O_Sync); // Open the master /dev/memory device
  if fd < 0 then
  begin
    Result := False; // unsuccessful memory mapping
  end;
 //
end;
//*******************************************************************************
procedure TIoDriver.UnmapIoRegisrty(FMap:TIoPort);
begin
  if FMap.FGpio <> nil then
 begin
   fpMUnmap(FMap.FGpio,PAGE_SIZE);
   FMap.FGpio := Nil;
 end;
 if FMap.FClk <> nil then
 begin
   fpMUnmap(FMap.FClk,PAGE_SIZE);
   FMap.FClk := Nil;
 end;
 if FMap.FPwm <> nil then
 begin
   fpMUnmap(FMap.FPwm ,PAGE_SIZE);
   FMap.FPwm := Nil;
 end;
end;
//*******************************************************************************
function TIoDriver.CreatePort(PortGpio, PortClk, PortPwm: LongWord): TIoPort;
begin
  Result := TIoPort.Create;// new io port, pascal calls new fpMap, where offst is page sized 4096 bytes!!!
  Result.FGpio := FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortGpio); // port config gpio memory
  Result.FClk:= FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortClk);; // port clk
  Result.FPwm:= FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortPwm);; // port pwm
end;
//*******************************************************************************
procedure TIoPort.SetPinMode(gpin, mode: byte);
var
  fSel, shift, alt : byte;
  gpiof, clkf, pwmf : ^LongWord;
begin
  fSel := (gpin div 10)*4 ;  //Select Gpfsel 0 to 5 register
  shift := (gpin mod 10)*3 ;  //0-9 pin shift
  gpiof := Pointer(LongWord(Self.FGpio)+fSel);
  if (mode = INPUT) then
    gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift))  //7 shl shift komplemens - Sets bits to zero = input
  else if (mode = OUTPUT) then
  begin
    gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift)) or (1 shl shift);
  end
  else if (mode = PWM_OUTPUT) then
  begin
    Case gpin of
      12,13,40,41,45 : alt:= 4 ;
      18,19          : alt:= 2 ;
      else alt:= 0 ;
    end;
    If alt > 0 then
    begin
      gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift)) or (alt shl shift);
      clkf := Pointer(LongWord(Self.FClk)+PWMCLK_CNTL);
      clkf^ := $5A000011 or (1 shl 5) ;                  //stop clock
      delayNanoseconds(200);
      clkf := Pointer(LongWord(Self.FClk)+PWMCLK_DIV);
      clkf^ := $5A000000 or (32 shl 12) ;	// set pwm clock div to 32 (19.2/3 = 600KHz)
      clkf := Pointer(LongWord(Self.FClk)+PWMCLK_CNTL);
      clkf^ := $5A000011 ;                               //start clock
      Self.ClearBit(gpin);
      pwmf := Pointer(LongWord(Self.FPwm)+PWM_CONTROL);
      pwmf^ := 0 ;		 	        // Disable PWM
      delayNanoseconds(200);
      pwmf := Pointer(LongWord(Self.FPwm)+PWM0_RANGE);
      pwmf^ := $400 ;                             //max: 1023
      delayNanoseconds(200);
      pwmf := Pointer(LongWord(Self.FPwm)+PWM1_RANGE);
      pwmf^ := $400 ;                             //max: 1023
      delayNanoseconds(200);
      // Enable PWMs
      pwmf := Pointer(LongWord(Self.FPwm)+PWM0_DATA);
      pwmf^ := 0 ;                                //start value
      pwmf := Pointer(LongWord(Self.FPwm)+PWM1_DATA);
      pwmf^ := 0 ;                                //start value
      pwmf := Pointer(LongWord(Self.FPwm)+PWM_CONTROL);
      pwmf^ := PWM0_ENABLE or PWM1_ENABLE ;
    end;
  end;
end;
//*******************************************************************************
procedure TIoPort.SetBit(gpin : byte);
var
   gpiof : ^LongWord;
begin
  gpiof := Pointer(LongWord(Self.FGpio) + 28 + (gpin shr 5) shl 2);
  gpiof^ := 1 shl gpin;
end;
//*******************************************************************************
procedure TIoPort.ClearBit(gpin : byte);
var
   gpiof : ^LongWord;
begin
  gpiof := Pointer(LongWord(Self.FGpio) + 40 + (gpin shr 5) shl 2);
  gpiof^ := 1 shl gpin;
end;
//*******************************************************************************
function TIoPort.GetBit(gpin : byte):boolean;
var
   gpiof : ^LongWord;
begin
  gpiof := Pointer(LongWord(Self.FGpio) + 52 + (gpin shr 5) shl 2);
  if (gpiof^ and (1 shl gpin)) = 0 then Result := False else Result := True;
end;
//*******************************************************************************
procedure TIoPort.SetPullMode(gpin, mode: byte);
var
   pudf, pudclkf : ^LongWord;
begin
  pudf := Pointer(LongWord(Self.FGpio) + 148 );
  pudf^ := mode;   //mode = 0, 1, 2 :Off, Down, Up
  delayNanoseconds(200);
  pudclkf := Pointer(LongWord(Self.FGpio) + 152 + (gpin shr 5) shl 2);
  pudclkf^ := 1 shl gpin ;
  delayNanoseconds(200);
  pudf^ := 0 ;
  pudclkf^ := 0 ;
end;
//*******************************************************************************
procedure TIoPort.PwmWrite(gpin : byte; value : Longword);
var
   pwmf : ^LongWord;
   port : byte;
begin
  Case gpin of
      12,18,40 : port:= PWM0_DATA ;
      13,19,41,45 : port:= PWM1_DATA ;
      else exit;
  end;
  pwmf := Pointer(LongWord(Self.FPwm) + port);
  pwmf^ := value and $FFFFFBFF; // $400 complemens
end;
//*******************************************************************************
end.

Controlling Lazarus unit:(Project files[3])

unit Unit1;
 
{Demo application for GPIO on Raspberry Pi}
{Inspired by the Python input/output demo application by Gareth Halfacree}
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ExtCtrls, Unix, PiGpio;
 
type
 
  { TForm1 }
 
  TForm1 = class(TForm)
    GPIO25In: TButton;
    SpeedButton: TButton;
    LogMemo: TMemo;
    GPIO23switch: TToggleBox;
    Timer1: TTimer;
    GPIO18Pwm: TToggleBox;
    Direction: TToggleBox;
    procedure DirectionChange(Sender: TObject);
    procedure GPIO18PwmChange(Sender: TObject);
    procedure GPIO25InClick(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure GPIO23switchChange(Sender: TObject);
    procedure SpeedButtonClick(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;
 
const
     INPUT = 0;
     OUTPUT = 1;
     PWM_OUTPUT = 2;
     LOW = False;
     HIGH = True;
     PUD_OFF = 0;
     PUD_DOWN = 1;
     PUD_UP = 2;
 // Convert Raspberry Pi P1 pins (Px) to GPIO port
     P3 = 0;
     P5 = 1;
     P7 = 4;
     P8 = 14;
     P10 = 15;
     P11 = 17;
     P12 = 18;
     P13 = 21;
     P15 = 22;
     P16 = 23;
     P18 = 24;
     P19 = 10;
     P21 = 9;
     P22 = 25;
     P23 = 11;
     P24 = 8;
     P26 = 7;
 
var
  Form1: TForm1;
  GPIO_Driver: TIoDriver;
  GpF: TIoPort;
  PWM :Boolean;
  i, d : integer;
  Pin,Pout,Ppwm : byte;
 
implementation
 
{$R *.lfm}
 
{ TForm1 }
 
procedure TForm1.FormActivate(Sender: TObject);
begin
  if not GPIO_Driver.MapIo then
  begin
     LogMemo.Lines.Add('Error mapping gpio registry');
  end
  else
  begin
    GpF := GpIo_Driver.CreatePort(GPIO_BASE, CLOCK_BASE, GPIO_PWM);
  end ;
  Timer1.Enabled:= True;
  Timer1.Interval:= 25;     //25 ms controll interval
  Pin := P22;
  Pout := P16;
  Ppwm := P12;
  i:=1;
  GpF.SetPinMode(Pout,OUTPUT);
  GpF.SetPinMode(Pin,INPUT);
  GpF.SetPullMode(Pin,PUD_Up);    // Input PullUp High level
end;

procedure TForm1.GPIO25InClick(Sender: TObject);
begin
  If GpF.GetBit(Pin) then LogMemo.Lines.Add('In: '+IntToStr(1))
                     else LogMemo.Lines.Add('In: '+IntToStr(0));
end;

procedure TForm1.GPIO18PwmChange(Sender: TObject);
begin
  if GPIO18Pwm.Checked then
  begin
    GpF.SetPinMode(Ppwm,PWM_OUTPUT);
    PWM := True;                          //PWM on
  end
  else
  begin
    GpF.SetPinMode(Ppwm,INPUT);
    PWM := False;                          //PWM off
  end;
end;

procedure TForm1.DirectionChange(Sender: TObject);
begin
  if Direction.Checked then d:=10 else d:=-10;
end;

 
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
    GpF.SetPinMode(Ppwm,INPUT);
    GpF.ClearBit(Pout);
    GpIo_Driver.UnmapIoRegisrty(GpF);
end;
 
procedure TForm1.GPIO23switchChange(Sender: TObject);

Begin
  Timer1.Enabled := False;
  if GPIO23switch.Checked then
  begin
    GpF.SetBit(Pout); //Turn LED on
  end
  else
  begin
    GpF.ClearBit(Pout); //Turn LED off
  end;
  Timer1.Enabled := True;
end;

procedure TForm1.SpeedButtonClick(Sender: TObject);
var
  i,p,k,v: longint;
  ido:TDateTime;
begin
  ido:= Time;
  k:= TimeStampToMSecs(DateTimeToTimeStamp(ido));
  LogMemo.Lines.Add('Start: '+TimeToStr(ido));
  p:=10000000 ;
  For i:=1 to p  do Begin
    GpF.SetBit(P16);
    GpF.ClearBit(P16);
  end;
  ido:= Time;
  v:= TimeStampToMSecs(DateTimeToTimeStamp(ido));
  LogMemo.Lines.Add('Stop: '+TimeToStr(ido)+' Frequency: '+
                                IntToStr(p div (v-k))+' kHz');
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  If PWM then Begin
    If (d > 0) and (i+d < 1024) then begin
          i:=i+d;
          GpF.PwmWrite(Ppwm,i);
    end ;
    If (d < 0) and (i+d > -1) then begin
          i:=i+d;
          GpF.PwmWrite(Ppwm,i);
    end;
  end;
end;

 
end.
]]>
68 0 0 0
<![CDATA[树莓派最小桌面系统]]> http://mixdiy.com/index.php/2021/12/10/raspbian-lite-guide-gui/ Fri, 10 Dec 2021 14:05:02 +0000 http://mixdiy.com/?p=73

第1部分 - 建立基础

在这一部分,我们将专注于准备Raspbian Lite。

1、下载最新的Raspbian Lite图像。(https://www.raspberrypi.org/downloads/raspbian/
2、使用Win32DiskImager把Raspbian Lite刷SD中.
3、将SD / microSD卡插入Pi。
4、将Pi连接到Internet。以太网是最快的方式。否则我们必须使用Wi-Fi,则必须在Pi完成引导后。由于我们的WI-FI是隐藏的,所以需要修改:/etc/wpa_supplicant/wpa_supplicant.conf

#sudo nano /etc/wpa_supplicant/wpa_supplicant.conf

在文件中加入以下:

4.1也可以使用树莓派来配置WI-FI

    4.1.1运行:

          #sudo raspi-config 

          1. 选择 Network Options

          2. WI-FI(选择后输入SSID密码就可以了)

5、连接电视/显示器和键盘(此时鼠标是可选的)打开Pi,Pi应该成功启动并出现登录提示。
6、登录Raspbian。用户名是pi,密码是raspberry

第2部分 - 更新系统到最新

    1、安装更新,如果提示确认请输入Y.

      #sudo apt-get update   

      #sudo apt-get upgrade

      #sudo apt-get dist-upgrade

   2、运行完成后需要做一个清理。

      #sudo apt-get clean

第3部分 - 设定本地化

  1、首先们需安装中文的字体,不然会乱码    

sudo apt-get install ttf-wqy-zenhei ttf-wqy-microhei 安装文泉驿字体,若有中文字体则省略 
  2 、使用配置本地地址: sudo raspi-config 
  3、选择第四项 locallisation Options 
  4、I1 change locale 
  5、zh_cn.UTF8 和UTF-8 zh_GBK 空格键选择后 tab键确认 
  6、再次选择zh_cn.UTF-8 
  7、重启 sudo reboot

第4部分 - 开始安装桌面。

    下一部分重点介绍如何在Raspbian Lite上安装GUI。为了拥有GUI,我们需要以下4件事

     1.显示服务器
     2.桌面环境
     3.窗口管理器
     4.登录管理器

1、打开你的Pi并登录。我们将安装Xorg。要执行此操作,请键入:

   sudo apt-get install --no-install-recommends xserver-xorg

 然后按Enter键。将要安装许多依赖软件包,但必须安装这些软件包才能使GUI正常工作。安装中如果需要确认请输入Y。

如果只安装xserver-xorg,则无法从命令行启动Xorg Display Server。我们还需要安装xinit:

   sudo apt-get install --no-install-recommends xinit

2、Raspberry Pi Desktop(RPD)GUI 选择你想要安装的桌面。

    这里我选择的是LXDE GUI 它运行占用资源少。

    sudo apt-get install lxde-core lxappearance

    安装中如果需要确认请输入Y。

3、登录管理器安装:

     sudo apt-get install lightdm  

安装完成后我们就可以使用桌面系统了。

     startx

运行完成以上命令就可以看到你自己安装的桌面了。

4、Pi用户自动登录:

   sudo raspi-config

  1. 选择Boot Options
  2. Desktop / CLT
  3. Desktop Autologin Desktop GUI 桌面环境
  4. finish
  5. reboot

以上我们就完成了最小桌面系统。

#参考官方主页:

https://www.raspberrypi.org/forums/viewtopic.php?t=133691

下面我们来安装一些必要的软件。

1.安装Wi-FI 管理软件。     

sudo apt-get install network-manager-gnome

运行就可以看到图标了。

nm-applet

2.安装一个文本编辑器。

sudo apt-get install mousepad

3.安装Chromium.

sudo apt-get install chromium-browser

好了你现在可以上网了。

]]>
73 0 0 0
<![CDATA[树莓派开机启动GUI程序]]> http://mixdiy.com/index.php/2021/12/10/gup/ Fri, 10 Dec 2021 14:17:41 +0000 http://mixdiy.com/?p=77

1、如果是lite版,先安装界面,https://www.raspberrypi.org/forums/viewtopic.php?f=66&t=133691

参考上面界面,多种界面添加方法及比较。

2、将我们的程序拷贝到树莓派,依赖库之类的也添加好。

3、要想启动自己的图形界面,就需要启动X server,这个东西是在你登录树莓派输入用户名和密码之前启动的,所以我们可以在启动树莓派之前和启动X server之后启动自己的程序,/etc/profile 这个文件就是这两个程序的中间,我们直接更改这个配置文件就行了

sudo nano /etc/profile
在文件开头输入你程序的路径prg,再执行你的程序guiprogram,如下:

cd /home/pi/prg
./guiprogram

另一种方法是增加环境变量,程序guiprogram在/home/pi/Downloads/dist/下,直接执行即可

# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).

if [ "`id -u`" -eq 0 ]; then
  PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
else
  PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games:/home/pi/.local/bin:/home/pi/Downloads/dist"
fi
export PATH

guiprogram
]]>
77 0 0 0
<![CDATA[树莓派安装Ubuntu 18 64位系统]]> http://mixdiy.com/index.php/2021/12/11/ubuntu-18-64/ Fri, 10 Dec 2021 23:49:28 +0000 http://mixdiy.com/?p=88

开启强制HDMI输出:(很多现在的显示器在树莓派上并不能识别)
在TF卡分区,打开config.txt文件(开机后位置: /boot/config.txt),修改如下:

hdmi_safe=1
config_hdmi_boost=4
hdmi_ignore_edid=0xa5000080
hdmi_group=2
hdmi_mode=82
一些参数介绍:

项 含义
hdmi_safe=1 安全启动HDMI
config_hdmi_boost=4 开启热插拔
hdmi_group=1 CEA电视显示器
hdmi_group=2 DMT电脑显示器
hdmi_ignore_edid=0xa5000080 忽略自动探测的分辨率
输出分辨率:
hdmi_mode=4 640x480 60Hz
hdmi_mode=9 800x600 60Hz
hdmi_mode=16 1024x768 60Hz
hdmi_mode=82 1080p 60Hz
安装桌面方法
步骤一,安装Display Server。
sudo apt-get install --no-install-recommends xserver-xorg

sudo apt-get install --no-install-recommends xinit
步骤二,安装GUI桌面环境。
1,mate桌面环境

apt-get install ubuntu-mate-desktop

2,gnome桌面环境

apt-get install gnome

3,lxde桌面环境

apt-get install lxde

4,lxqt桌面环境

apt-get install lxqt

5,xubuntu桌面

apt-get install xubuntu-desktop

6,lubuntu桌面

apt-get install lubuntu-desktop

7,xfce桌面环境

apt-get install xfce4

8,cinnamon桌面环境

apt-get install cinnamon

9,bugie桌面环境

apt-get install ubuntu-budgie-desktop

这时,重启之后就完成了

]]>
88 0 0 0
<![CDATA[树莓派制作智能镜全过程]]> http://mixdiy.com/index.php/2021/12/12/yaode/ Sun, 12 Dec 2021 13:28:40 +0000 http://mixdiy.com/?p=93

第一部分——点子与镜子

作为一个男人,跟女友逛商城时,像是梅西百货那种的,难免会走神。我就是那号人,去年一月在纽约就发生过一回。长话短说。在我闲晃时我发现一面有灯光照明的镜子。我完全能自己做一面出来,而且比这更好。我想要属于我自己的智能镜!

magic_mirror1

一回到家,我开始构想我需要的东西:一面镜子,薄的那种,一个树莓派,一些木头和油漆,还需要大量空余时间。

关于镜子

普通的镜子是不行的。镜子是要那种单面透光那种,或更加精细功能的:后面屏幕黑屏时,它是一面镜子;而信息在屏幕上显示时就该像普通玻璃窗那样。

和警察局问询室内那面镜子原理一样,当只有一间房有光时,它就像一面镜子,其他时候它就是普通玻璃窗。

magic_mirror3

我所需要的是一面观察镜。现在,请相信我,当你问买玻璃的要一面观察镜时,会被反问一些奇怪的问题。他们那些人往往有更多新奇点子……嘿嘿,肮脏的思想永远是快乐的源泉。

终于我还是得到了一块不错的观察镜。开始找乐子吧!

第二部分——显示器

magic_mirror4

解决了镜子问题后,是时候为魔镜项目入手一台显示器了。

在为魔镜选择合适的显示器过程中,有一些纠结的决定。我是买一台新显示器还是捡一台呢?我需要什么尺寸?屏幕最薄会是多少?我如何移动控制按钮?屏幕是否够亮来盖过我的堂堂仪表?

因为我用显示器作为仪容之用,选择合适尺寸差不多就是选择合适的宽度(也就是镜子合适的高度)。在一些测量和尝试用胶带固定在墙上我想放置镜子的位置后,24英寸屏幕会是完美的选择。额外带来的好处是,绝大多数(便宜的)24英寸屏幕初始就带有1080P分辨率,正是我想在这个项目所使用的分辨率。

为了合适的屏幕类型和品牌,我几乎跑遍了方圆20公里内的电器铺。我敢肯定保安大叔在盯着我,而我正仔细检查所有屏幕背面和底部。

大多数品牌被立即否定了,因为它们的电源和视频连接线在背后。我需要转接线是在侧面的那种。

magic_mirror5

最后我选择了Iiyama显示器,它最接近我所希望的——价格便宜,边框窄小,简单触控按钮和正确转接器方向。

直到现在,我还是不清楚显示器外壳能否轻易被卸除,控制面板如何在显示器内部被连接上。销售人员不许我做出怪异的动作,打开显示器外壳,所以买到这台合适的显示器纯属运气。

magic_mirror6

我订购了一台24英寸Iiyama E2481HS-B1显示器。为确定显示器和镜子之间气密性良好,我拆下了外壳。在刚刚开箱就进行拆卸显示器够战战兢兢的,但……勉强成功。

这台Iiyama显示器的好处是,显示控制器(金属盒内的电子元件)为这面镜子的其他零件留下了足够的空间,而又保持了它原本的苗条外形。

magic_mirror7

实际的显示板仅有9毫米厚,而小小的边框也只是10毫米宽。加上显示控制器后,就是下列尺寸:556毫米x323毫米x46毫米(外加6毫米的镜子厚度)。这些尺寸构成了新的木头外壳尺寸。

第三部分——外壳

magic_mirror8

在量度新外壳所需尺寸后,就开始享受DIY的喜悦吧。我用松木做了个结实坚固的框架,用地板地脚线固定镜子和显示器的位置,那尺寸刚刚好(30毫米宽),还有圆角边框效果。

镜子很可能会发热,那就需要通风口了。此外,在外壳的背后还加了几个既美观又结实的挂点。

小剧透:产品最终重量6.5千克,由我做的两个挂载点所推测得来。

magic_mirror11

补充一下,我在外壳的底部开了条细缝作为电源线槽。

当然,它需要进行上色。在涂抹油灰(我在打磨抛光时后悔涂太多油灰了)、一些地漆及上土层后,新外壳完工。

magic_mirror14

有个木工小步骤我要去做的:我做了4个挂在块来安装显示器与镜子。现在准备好将它们上在镜框里面。

magic_mirror15

木工部分到此结束,进入魔镜的下一环节,安装硬件。

第四部分——安装硬件

magic_mirror16

魔镜开始成形。我订购了镜子,找到合适的显示器,完成打磨白色的外壳,是时候开始安装硬件了。

目前我手上有以下零件: 显示器 + 树莓派 + 一条HDMI线(连接树莓派和显示器) + 一条USB转micro USB转接线(用来为树莓派供电) + 一条显示器电源线

但接着我遭遇到小小挫折。在找合适的显示器时,我完全没想到看看显示器有没有一个USB口,用来驱动树莓派。我以为现在的显示器都默认装有USB口,不过我错了,它没有。

这不是不能解决的问题,但我真心希望只用一条线来驱动魔镜。所以我只能将电源分为两条线,一条给显示器,一条给240伏USB降压器。补充一点,我希望用一条普通C13电源线就能驱动智能镜。

magic_mirror17

也就是说,我要做的是中间带有USB充电器的接插电源线。在翻了我几个闲置线材抽屉后,我找到了个旧USB充电器。

magic_mirror18

拆开充电器比想象中的容易,只要一点点焊接工序,一些胶和绝缘胶布即可。我要设法做个外观高大上的电源线。

magic_mirror20

我做了试运行,确保电源线正常工作。我将所有零件连接好,插好电源线,打开显示器。树莓派启动正常,USB充电器没有发热过量。不错!

magic_mirror21

接着组装所有零件。这次没有倒霉事件:所有东西完美吻合。

magic_mirror24

另一个阶段完成了,包括所有实体部分(除了还没把两颗螺丝钉在墙上)。进入戏肉的部分:安装树莓派。

Magic Mirror: Part V – Installing the Raspberry Pi

第五部分——安装树莓派

magic-mirror25

那么,做完所有硬件之后,就轮到安装树莓派了。要达到智能镜的所有需求,树莓派就要有以下功能:

· Wifi连接 · 屏幕旋转90度,符合照镜方向 · 本地网络服务维持界面 · 在全屏幕下运行的浏览器,用于显示界面

基本安装

因为Raspbian操作系统的灵活性和背后有开源社群支持,我就选了它。下载了操作系统映像后,就把它写入SD卡。

拷贝一个映像档需要很长时间,那么有三种选择:

  1. 用树莓派技术文档中提到的rdisk方法拷贝。
  2. 拿杯咖啡,坐下,在等待中享受咖啡因的感觉。
  3. 上面两者都选。

我选了3。完成拷贝后,我启动树莓派、登入,进入命令行的sudo raspi-config开始配置向导。在这个配置中,有几件重要事项需要配置:

· 确保系统启动到桌面(取代命令行模式或调试模式)。 · 调整时区,使魔镜显示正确时间。 · 在高级选项部分,确保显存超过128MB。 如果你想做和这个一样的镜子,尽管尝试选择所有其他选项。只是记住,如果选砸了,就再泡杯咖啡重装系统吧。

Wifi连接

我不想在智能镜上加任何多余的连接线,就选择了以Wifi连接因特网。安装Wifi接收器真的依赖频宽和种类,因此写下完全过程是多余的。有最好的替代吗?看看这个神奇的网站……

老实说,这部分安装确实很花时间。一旦装好了,就能工作正常。

旋转屏幕

智能镜在设计上是纵向肖像模式,所以我需要将屏幕顺时针旋转90度,最终显示分辨率为1080 x 1920。我曾担心这会是最大的问题,最终却很容易解决了。

树莓派的BIOS设置储存在系统启动分区中。在这分区中,有一个config.txt文件,载有所有设置。要旋转显示器,在这文件内加上以下一行代码:
display_rotate=1

要让显示器连接更加可靠,我就不解释为何加上下面允许HDMI线热插拔的代码了:
hdmi_force_hotplug=1

配置文件存盘后重启树莓派,我不禁会心一笑:这感觉真好,腰不疼颈不酸了!

网络服务器

要维持界面(很简单的一个网页),我需要在树莓派上Apache服务器。这在树莓派上是其中一个很常见的应用,安装过程简直行云流水。

首先我运行下列指令,确定我用的是最新系统软件。
sudo apt-get update && apt-get upgrade -y

现在是时候真的安装Apache了:
sudo apt-get install apache2 apache2-doc apache2-utils

完成了!就是所有这些。但要保证在服务器上能用到一些PHP脚本(以后会更多的),我也加上了PHP支持:
sudo apt-get install libapache2-mod-php5 php5 php-pear php5-xcache

又完成了!重启之后,网页服务器就挂载上线运行了!我在/var/www文件夹内放置了index.php文件,将浏览器首页指向树莓派的IP地址,发现成功了。

信息模式(kioskmode)

现在就是要确定树莓派能够显示我在信息模式(kioskmode)下用Chromium浏览器显示的网页。Chromium浏览器是个能在树莓派操作系统上运行的开源浏览器。

一如既往,安装很简单:
sudo apt-get install chromium x11-xserver-utils unclutter

但这一次,它需要一些额外设置,在信息模式下禁用屏幕保护和自动重启。我在/etc/xdg/lxsession/LXDE/autostart作了编辑,在前面加上了#号。
@xscreensaver -no-splash

另外我还加了以下代码:
@xset s off @xset -dpms @xset s noblank @chromium –kiosk –incognito http://localhost

这样就能完全禁用所有屏保功能,及Chromium浏览器在开机后自动启动,开启全屏模式并导向本地主页。

存盘并再一次重启,检查工作效果。树莓派并不是世界上最快的电脑,它需要花点时间,但最终测试页还是在旋转了90度的画面上显示出来。要得!

进入项目的最后部分,界面开发。

第六部分——界面开发

回顾一下,我买了合适的镜子、显示器,做了个新外壳,安装好硬件及配置完成树莓派,那就进入最后一步——界面开发。

需求和功能

我开始项目时,我发现不能通过镜子直接进行任何交互,要不然在铮亮的镜面上会抹上油脂和什么脏东西。但还有更重要的原因,还有别的更好设备可用于用户交互。我希望智能镜只是个被动信息来源。

而更更重要的是,镜子就是镜子,不应被大量(无用的)信息所填满。只有边上能用来作摘要性显示,给本帅哥留些足够自恋的空间吧。

这样我需要以下信息类别目录来满足我对信息的需求:

· 问候语 贴心问候,每日美好的开始。

· 天气 看到我穿的衬衫吗?不错吧!嗯?今天穿T恤衫不会鸡冻吧?

· 时钟和日历 现在赶时间吗?还有足够时间顾影自怜吧?

· 消息反馈 我只是担心自己形象帅不帅吗?还有别的需要上心吗?

当然,未来版本当中有大量的改进可能,现在列出的这些应该够用了。

基本安装

前面提过,程序界面不是一个在树莓派桌面上神奇的应用,仅仅是一个全屏幕网页。我能用HTML、CSS和Javascript开发外,还有一个额外的好处,在我将它放入智能镜之前,能在我的苹果电脑上开发测试。

magic-mirror28

在自己写的代码基础上,我用了一些开源库文件来优化它的运行速度:

Jquery

像大多数网站那样,智能镜使用Jquery来简化DOM操作方式,对我这种懒人最为适用。

Moment.js

它对我在时间安排操作上帮助不少,尤其是在争分夺秒时更节省不少时间。

FeedToJson

将RSS订阅用JSON数据转成javascript的方便工具。

iCal Parser

将iCal数据转化为JSON。可惜这个库文件并不完美,需要一些额外的优化。

以上这些库文件,加上我自己的HTML和Javascript足够使我的镜子成精了。

设计

我的智能镜需要个酷炫设计。我本人是个严重果粉,那么在设计上要遵从苹果系的设计指标

边框、倾角、阴影效果有时是用户界面厚重感的因素,甚至能盖过显示内容的光芒。所以,专注在内容设计,把用户交互界面摆在辅助位置上。

利用足够的实体空间,它是个重要内涵,在感官上更让人注意和易于理解。

怎么做到这一点?用Helvetica Neue字体,用天气图标对应显示天气信息。

magic-mirror29

镜子在背后没有光源时就只是一面镜子,用黑色背景是个重要设定。而为了有最好的对比度,显示内容字体应为白色,在加一些灰色阴影边缘,那灰度为50吧……

我大可以加上一些其他色彩,但目前为止我只想做到简洁的黑白界面。我可不想镜子比我的尊容还要出彩。

API设计

为了接收我想在镜子上显示的数据,我用了一些开源API和反馈代码。还有谁不太清楚什么是API,维基百科里面有详尽的解释:

“电脑操作系统(Operating system)’或‘程序库’提供给应用程序调用使用的代码。”

我心中的API就像你家的DVD播放器背后那样充满接口——如果你将其他设备连接到它的话,它就变得更加有用,这些接口本质上就是API。API让设备变得功能强大、有趣,尤其对我这种电脑狂人来说。

Openweathermap.org

Openweathermap有很好的API接口,能够免费得到天气预报信息。它允许你查询区域内的信息,定义你想要的信息类别。在这里我用了两个连接,一个是当前天气,另一个是预报天气。

iCal Calendar

iCloud允许你以iCal格式分享日历。因为javascript无法做到这一点(因为多站点脚本安全问题),我需要通过PHP代理服务器来开启数据。这其实很容易,只有三行代码而已:

12<!--?php <br ?-->    $url "https://p01-calendarws.icloud.com/ca/subscribe/1/mysupersecreticloudhash";    echo file_get_contents($url);

只需查询calendar.php就能将在同一个服务器内日历表在界面上显示出来。

前面说过的iCal语法分析编辑帮我把信息解析成有用信息。可是,iCloud在他们的反馈中用了一些非标准标签,我要添加额外的代码行来将代码解析为javascript文件。

magic-mirror30

NOS新闻订阅

新闻订阅只是用到了荷兰公共广播公司的RSS订阅功能。可是,他们不支持JSON格式数据,因此我用了FeedToJson插件来把RSS数据转为JSON格式的。

其他内容,例如当前日期与实践,还有问候语都只是一些简单的javascript语句。

自动更新

你看了这篇博客这么久,应该能接受更进阶的信息了吧。那么瞪大眼看下去吧……

在开发当中,我遇到了一个很不爽的境地,就是没有键盘和鼠标连在智能镜上的问题。如果我更新界面,就没有一个简单方法在智能镜上更新页面的方法。最简单直接的方法是重启整个树莓派,但让我在调试每个更新上花时间太多了。

我用GIT分布控制系统更新文件,而每个更新版本都有本身的哈希校验提交码,我用PHP就能读取出来。这启发了我再代码行内加上一个片段,将本地页面和刚刚开发的页面进行哈希比对。如果两者不符,程序将重新载入网页,显示最新版本。

我用以下PHP代码获取当前网页上的哈希校验码:

1trim(`git rev-parse HEAD`)));

对比过程在javascript主文件内完成,但只有在HTML文件有当前版本的哈希校验值前提下才能实现:

1var gitHash = '';

现在,对比过程就很直观,每3秒进行一次:

1234567891011121314(function checkVersion()    {        $.getJSON('githash.php', {}, function(json, textStatus) {            if (json) {                if (json.gitHash != gitHash) {                    window.location.reload();                    window.location.href=window.location.href;                }            }        });        setTimeout(function() {            checkVersion();        }, 3000);    })();

所有这些神奇的操作,让更新智能镜界面变得简单。只需用SSH登入树莓派,进入合适的文件夹,然后处理一个git推送请求。

给我交出代码来!

得了得了……好奇了吧?所有代码在GitHub上都有。有件事情要说一声:代码很少标注,以面条式代码填充,通篇都是TODO语句。哈,不怕晃晕菜的,尽管扎进去吧。

未来展望

智能镜还能做更多的功能,这是一个陆续发展过程。如有新功能上的建议,请不吝提供。让我知道你想在界面上的创意。不过请记住,少为美。:)

]]>
93 0 0 0
<![CDATA[猫盘->群晖->网站]]> http://mixdiy.com/index.php/2021/12/14/net-disk/ Tue, 14 Dec 2021 06:13:39 +0000 http://mixdiy.com/?p=98

今天折腾群晖迷你Linux系统的时候遇到一个问题,Python写的程序基本上都会带上一些扩展包,今天在安装BeautifulSoup网页解析工具的时候遇到了麻烦。由于群晖迷你Linux系统阉割了很多功能,问题如下。

在安装BeautifulSoup网页解析包时,一般使用pip工具来安装,然而没有pip。

没有pip那就只好先安装pip工具了,要安装pip工具得先安装个epel-release,使用yum安装,提示没有yum。

安装yum提示没有rpm管理器,python缺失?

既然这些软件工具安装不了,那就只好下载yum源码编译安装,然而还是报错。

解决办法

直接下载pip源码编译安装即可。

首先通过群晖套件安装python3

然后安装setuptools工具,执行如下命令下载源码

wget --no-check-certificate https://files.pythonhosted.org/packages/6a/fa/5ec0fa9095c9b72cb1c31a8175c4c6745bf5927d1045d7a70df35d54944f/setuptools-59.6.0.tar.gz

然后解压

tar -zxvf setuptools-59.6.0.tar.gz

进入解压目录

cd setuptools-59.6.0

使用python3编译

python3 setup.py build

使用python3安装

python3 setup.py install

安装pip,执行下面命令下载到当前目录

wget --no-check-certificate https://files.pythonhosted.org/packages/da/f6/c83229dcc3635cdeb51874184241a9508ada15d8baa337a41093fab58011/pip-21.3.1.tar.gz

解压

tar -zxvf pip-21.3.1.tar.gz

进入pip目录

cd pip-21.3.1

python3编译

python3 setup.py build

python3安装

python3 setup.py install

设置环境变量

export PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/syno/sbin:/usr/syno/bin:/usr/local/sbin:/usr/local/bin:/volume1/@appstore/py3k/usr/local/bin"

由于每次关机环境变量会变化,所以大家最好一次将需要的扩展包下载完,不然以后再安装就需要再执行一下最后一句命令。

然后使用如下命令安装beautifulsoup扩展

pip3 install beautifulsoup4 datetime lxml pygame requests

安装完成后,运行python,然后执行下面代码不报错即可

from bs4 import BeautifulSoup

安装pip也可以按如下方式

执行sudo apt-get install python3-pip
若报 No module named ‘distutils.util’,执行 sudo apt-get install python3-distutils
若报E: Package python3-distutils has no installation candidate,执行: sudo apt update
再重新执行:sudo apt-get install python3-distutils
再执行:sudo apt-get install pyton3-pip

]]>
98 0 0 0
<![CDATA[猫盘装群晖系统建网站]]> http://mixdiy.com/index.php/2021/12/14/qunhui/ Tue, 14 Dec 2021 06:37:52 +0000 http://mixdiy.com/?p=105

1、安装自己的wordpress(下载最新wordpress并解压到web目录,修改目录权限方便更新)
2、设置php(phpmyadmin)(全选),否则无法链接数据库/创建wordpress数据库
3、安装wordpress
4、安装relative插件
5、下载cpolar,所在目录执行权限不够,修改目录权限或拷贝至有权限目录
5、创建脚本并设置群辉本身的计划任务(开机启动脚本)

]]>
105 0 0 0
<![CDATA[Fritzing(0.9.6版本)下载]]> http://mixdiy.com/index.php/2021/12/17/fritzing/ Thu, 16 Dec 2021 16:42:19 +0000 http://mixdiy.com/?p=123

Fritzing是一款在创客圈子和青少年教育领域非常受欢迎的电路设计软件,支持多国语言,软件包含4种视图,分别是——面包板原理图PCBCode。用鼠标单击就能轻松切换。对于前3种视图(面包板、原理图和PCB)而言,无论你在其中的哪一种进行电路设计,软件都会自动化同步到其他两种视图,并可以生成制板厂生产所需要的Greber文件、PDF文件和CAD格式文件。这一切都极大地方便了创客的使用。

目前网上可供下载的普遍都在0.9.3版本及更老的版本,新版本非常少见,为方便大家学习使用,这里提供了0.9.6版本供大家下载:

]]>
123 0 0 0
<![CDATA[推荐10个好用的PCB设计工具]]> http://mixdiy.com/index.php/2021/12/17/best-pcb/ Fri, 17 Dec 2021 09:09:37 +0000 http://mixdiy.com/?p=133

以下列出的是最值得推荐的10个印制电路板(PCB)设计软件:

  1. PCBWeb Designer
  2. ZenitPCB
  3. TinyCAD
  4. Osmond PCB
  5. BSch3V
  6. ExpressPCB
  7. KiCad
  8. Fritzing
  9. DesignSpark PCB
  10. gEDA

PCB设计软件工具比较标准

需要说明的是,这并不是一个最佳的排名列表,因为不同的任务和工作流程有不同的需要,我希望这个列表能让您了解在选择最适合您需求的设计时提供参考。

评估标准:

  • 用户界面(UI)设计:当我寻找PCB设计软件时,我首先关注的是这个工具是否设置了让设计者容易上手的导航栏。当我使用软件时,我会考虑与我交互的视觉按钮。直观的UI设计确保您可以立即开始设计电路,而无需花费大量时间学习或参考手册。
  • 受欢迎度:当更多的人使用一个软件工具时,是因为它有一些很棒的功能。例如,我查看掌握该工具的容易程度,以及其开发人员是否通过培训和易于理解的教程提供了出色的支持。
  • 集成:为了使PCB软件真正实现自动化并节省时间,它需要与您已经在使用的其他软件工具集成。因此,您选择的PCB软件能够与项目管理或其他PCB设计工具集成是很重要的。
  • 成本:大多数PCB设计软件工具都非常昂贵。这就是为什么我经常根据功能和成本来比较工具。虽然我接受工具会有不同的价格,因为它们有不同的功能。但我仍然想确保一个工具的价格对我的业务的价值和它的好处是值得的。

印制电路板(PCB)设计软件关键的特点

一个PCB设计工具要满足各种重要要求,就需要具备一定的基本特性。然而,重要的是要注意,对一个人来说很棒的功能可能对其他人来说并不相关。

以下这些是内容是我认为选择PCB设计软件需要关注的:

  • 仿真:为您提供电路原理是否正确、验证与分析PCB布局布线合理性以及是否满足可制造性。
  • 层数:这是一个基本功能,支持更多层数的PCB设计工具可以管理具有更复杂PCB设计要求的更高数量的组件。 3D建模:这可以让你了解PCB设计完成后的真实的样子。
  • 3D建模:这可以让你了解PCB设计完成后的真实的样子。
  • 复杂性和速度:PCB软件工具处理不同级别的复杂性和速度。更昂贵的工具通常能够处理更复杂的设计任务。这些工具通常提供更快的速度。
  • 库:PCB设计工具必须有一套相对完整的元件库文件。
  • Web和操作系统兼容性:检查软件是否需要下载到您的计算机上,或者您是否需要将其作为SaaS使用。此外,还要确定软件是否能与设备的操作系统兼容。

以下列出的是最值得推荐的10个印制电路板(PCB)设计软件的概要性的介绍:

下面,我提供了每个PCB设计软件工具的总结。我的描述集中在最佳用例和工具的主要特性上。

  1. PCBWeb Designer– 最好的,且免费的PCB设计软件

PCBWeb Designer是一个全方位服务的电子硬件设计和制造工具。它受欢迎的原因之一是它是免费的。这意味着对于一块板上使用的元器件数量没有限制。该软件也没有对板子大小的限制。

这个软件出现在最值得推荐的PCB设计软件列表上,因为它是免费的,且有一些很棒的功能。PCBWeb Designer易于使用的连线工具使设计多页原理图成为可能。此外,该软件允许布多层板,还支持DRC检查和敷铜。用户还可以使用材料清单管理器访问Arrow的元器件目录。

  1. ZenitPCB– 适合初学的PCB设计者

ZenitPCB出现在几乎所有最好的PCB设计软件工具列表中的一个原因是,该工具是直观的,非常容易学习,这使它成为一个优秀的程序。设计者不需要专门的培训就可以完成自己的PCB设计任务。

因为这是一个简单且实用的程序,你会注意到该工具主要是高中或大学的学生和他们的老师在使用。然而,这款软件的主要缺点是它没有自动布线器或自动布局等方便的功能。

如果您是个人或半专业用途使用这个程序,它是完全免费的,但有800pin的限制。

TinyCAD是一个用于绘制电路图(原理图)的开源程序。它支持标准元件库和支持用户创建自己的元件库。该工具还支持输出多种PCB网表,并可以生成SPICE网表。

尽管在TinyCAD中创建元件库相对容易,但这个过程非常耗时。然而,作为一个开源程序,它允许用户将他们认为对他人有用的符号上传到程序中,以便其他用户可以下载它们。

在TinyCAD网站上,您可以访问教程和其他支持材料,还可以创建和下载自定义元件库。如果您遇到了一些问题,您可以在网站上报告它们并获得帮助。

TinyCAD是一个免费的工具

  1. Osmond PCB– Best PCB design software for Macintosh 

Osmond PCB的开发人员承诺该工具必须具备灵活的特点。为了实现这一承诺,他们开发了允许用户使用公制或英制单位的软件。这两种单位,也可以用于相同的设计。该工具集成的元件编辑器使更改现有元件或定义新的元件变得容易。

Osmond PCB是一个优秀的程序,因为它没有任何限制。这意味着你可以设计任意大小和任意层数的PCB。一旦您完成了设计,Osmond将提供工具来帮助您检查完成的产品是否满足所有指定的设计规则。

Osmond PCB可免费使用。然而,也接受捐款以帮助支付持续支持和更新软件的费用。

  1. BSch3V– Best PCB design software for Windows

缩写“BSch”代表“Basic Schematic”。 BSch3V的设计者将其定义为Windows的原理图绘制程序。由于BSch3V的功能是基础的,所以它主要适用于项目不是很复杂的PCB设计。

BSch3V的主要功能包括一个元件库编辑器,PCB网表生成器和元件列表生成器。

BSch3V可以免费使用。

  1. ExpressPCB– Best PCB design software for reducing lead times 

ExpressPCB已经在市场上存在了20多年。因此,这是PCB设计团队的首选软件,他们想要节省从开始的一个想法到完成产品交付所花费的时间。此外,该软件多年来一直在不断改进,确保它既适合专业人士,也适合初学者。

您可以获得两个版本的ExpressPCB: ExpressPCB Classic和ExpressPCB Plus。Classic版本更适合需要易用性的团队。它的Schematic更容易上手,而它的快速设置意味着您可以尽可能快地开始工作。Plus版本是为需要灵活性的更复杂设计的团队设计的。

您可以免费使用ExpressPCB

  1. KiCad– Best for inspecting PSB designs in 3D 

KiCad是EDA(电子设计自动化)行业的开源软件,具有用于PCB设计的原理图、PCB布局和GERBER文件输出。该工具含有设计者希望的PCB设计软件中所有的标准功能,如软件自带的原理图符号库和内置的原理图符号编辑器等。

KiCad的最新版本5.1.10增强了一些功能,比如修复了关键bug。它还改进了3D模型库、PCB封装、原理图符号和文档。

KiCad是由包括法国格勒诺布尔大学在内的几个基金会和组织赞助的免费软件。

  1. Fritzing– Best for PCB design amateurs and hobbyists  

Fritzing是一个开源硬件项目,它借鉴Processing和Arduino平台,允许用户处理电路以进行快速原型开发,共享原型文档,并设计定制的PCB。该工具是由波茨坦应用科学大学开发的,以帮助PCB爱好者和业余爱好者。

Fritzing的最新版本0.9.6,允许通过在线数据库自动更新零件库。它还有助于关键错误的修复,高DPI显示支持,以及更容易的创建和自定义元件。

作为一个开源项目,fritzzing是一个免费的工具。

  1. DesignSpark PCB– Best for schematic capturing and layout improvement 

DesignSpark PCB是一个非常优秀的免费软件,它受欢迎的程度正在飙升。你会喜欢这个软件的主要因素之一是它的直观性,无论你是初学者还是有经验的专业人士,这个程序都很容易使用。该工具有助于设计PCB板和布局,原理图绘制,并进行布局改进。

将DesignSpark PCB列入最值得推荐的10个印制电路板(PCB)设计软件最重要的一个因素是,该工具无缝集成到PCB设计师已经使用的许多工具中。

DesignSpark PCB是一个免费工具。

  1. gEDA– Best for printed circuit board design 

gEDA是一个集多个开源软件包于一体的套件。它可以生成其他文件类型,如:*.ps或*.png。该软件具有自动布线功能和许多其他功能。软件即有强大的功能而且表现得很高效。特别推荐与gEDA原理图编辑器一起使用。

该工具主要面向印刷电路板设计(相对于集成电路设计),软件采取通用公共许可证(GPL)去完成电子设计。由于该项目的开放性,原理图符号、PCB封装和实用程序脚本可以在gEDA社区的一个附属网站(gedasymbols.org)上自由创建并共享。

gEDA提供丰富的库文件和专业支持社区且不收取额外费用。它是一个开源电子设计工具的集合。同时,也可以单独安装。

gEDA是免费使用的

Other PCB Design Software Tools 

其他被推荐的PCB设计软件

如果您不能在这上面介绍的工具中找到满足您所有需求的PCB软件,这里还有一些其他得工具可以推荐。

  • FreePCB:一个免费的,开源的,基于微软Windows操作系统开发的PCB设计软件,软件易于学习和使用,并且还能够专业,高质量的完成设计工作。
  • Eagle:一个容易使用的图形化界面的PCB布局编辑器(EAGLE),同时,软件具有强大的原理图功能和友好的界面,很多专业人士也在使用该工具。
  • Altium:这是一个具有高可靠性的PCB设计软件,软件将帮助设计者为实现多种真实的应用场景而创建令人惊叹的PCB。软件功能非常强大,并要求运行在Windows操作系统上。
  • DipTrace: 软件高效、快速地创建设计,从而节省设计时间。对于初学者或时间紧张的人来说,DipTrace非常理想,软件具有原理图绘制功能和PCB编辑以及布线功能,还能转换为多种格式。
  • Pulsonix:这是一个易于使用,提供一个完整且专业的PCB编辑器,用户还可选购极其优秀的自动布线器。该软件在一个设计环境中结合了原理图输入和PCB布局。
  • Sprint Layout:具有逻辑清晰且非常容易上手的PCB设计工具,软件适合任何人,特别是适合初学者且急于完成设计的人。软件支持集成Excel控件,也能输出专业的生产文件Gerber。
  • EasyEDA: 软件有自带的元件库超过100万,还允许用户创建或导入元件库。
  • PCB Artist:用户能够访问一个拥有超过50万个元器件库、设计多达28层的PCB设计文件,软件还具有人为干涉的自动布线器、能完成多页原理图绘制和自然导入Eagle格式的文件。
  • Ultiboard: 软件具有很多自动化的功能,使得设计者可以很快完成PCB设计。该工具其他的功能:如电子表格视图、工具箱和设计向导,使得管理PCB设计文件更容易。
  • Solidworks PCB易于将电子原理图集成到3D模型中,通过自动化重复任务节省您的时间。

下是在讨论PCB设计工具时经常遇到的两个问题:

什么是PCB设计软件?

PCB设计软件是设计工程师借助计算机来完成印刷电路板(PCB)的计算机程序。通常,该软件程序由一些软件模块工具组成,这些工具使用户能够在项目上进行协作,访问先前创建的元件库,并确定电路原理图设计的准确性等任务。

为什么需要PCB设计软件?

PCB设计软件对于PCB设计人员来说是至关重要的,因为它允许过程的自动化,否则需要很长时间才能完成。该软件还允许协作,使得将执行项目所需的所有信息放到单个文件仓库中成为可能,在该文件仓库中,所有项目相关人都可以实时看到变化。PCB设计软件对于管理质量也是至关重要的,因为它可以确定是否满足了所有的项目规格。

]]>
133 0 0 0
<![CDATA[树莓派上安装WordPress]]> http://mixdiy.com/index.php/2021/12/19/wordpress/ Sun, 19 Dec 2021 15:56:56 +0000 http://mixdiy.com/?p=139

WordPress 是一个非常受欢迎的开源博客平台和内容管理平台(CMS)。它很容易搭建,而且还有一个活跃的开发者社区构建网站、创建主题和插件供其他人使用。

虽然通过一键式 WordPress 设置获得托管包很容易,但也可以简单地通过命令行在 Linux 服务器上设置自己的托管包,而且树莓派是一种用来尝试它并顺便学习一些东西的相当好的途径。

一个经常使用的 Web 套件的四个部分是 Linux、Apache、MySQL 和 PHP。这里是你对它们每一个需要了解的。

Linux

树莓派上运行的系统是 Raspbian,这是一个基于 Debian,为运行在树莓派硬件上而优化的很好的 Linux 发行版。你有两个选择:桌面版或是精简版。桌面版有一个熟悉的桌面还有很多教育软件和编程工具,像是 LibreOffice 套件、Mincraft,还有一个 web 浏览器。精简版本没有桌面环境,因此它只有命令行以及一些必要的软件。

这篇教程在两个版本上都可以使用,但是如果你使用的是精简版,你必须要有另外一台电脑去访问你的站点。

Apache

Apache 是一个受欢迎的 web 服务器应用,你可以安装在你的树莓派上伺服你的 web 页面。就其自身而言,Apache 可以通过 HTTP 提供静态 HTML 文件。使用额外的模块,它也可以使用像是 PHP 的脚本语言提供动态网页。

安装 Apache 非常简单。打开一个终端窗口,然后输入下面的命令:

sudo apt install apache2 -y

Apache 默认放了一个测试文件在一个 web 目录中,你可以从你的电脑或是你网络中的其他计算机进行访问。只需要打开 web 浏览器,然后输入地址 <http://localhost>。或者(特别是你使用的是 Raspbian Lite 的话)输入你的树莓派的 IP 地址代替 localhost。你应该会在你的浏览器窗口中看到这样的内容:

这意味着你的 Apache 已经开始工作了!

这个默认的网页仅仅是你文件系统里的一个文件。它在你本地的 /var/www/html/index/html。你可以使用 Leafpad 文本编辑器写一些 HTML 去替换这个文件的内容。

cd /var/www/html/
sudo leafpad index.html

保存并关闭 Leafpad 然后刷新网页,查看你的更改。

MySQL

MySQL(读作 “my S-Q-L” 或者 “my sequel”)是一个很受欢迎的数据库引擎。就像 PHP,它被非常广泛的应用于网页服务,这也是为什么像 WordPress 一样的项目选择了它,以及这些项目是为何如此受欢迎。

在一个终端窗口中输入以下命令安装 MySQL 服务(LCTT 译注:实际上安装的是 MySQL 分支 MariaDB):

sudo apt-get install mysql-server -y

WordPress 使用 MySQL 存储文章、页面、用户数据、还有许多其他的内容。

PHP

PHP 是一个预处理器:它是在服务器通过网络浏览器接受网页请求是运行的代码。它解决那些需要展示在网页上的内容,然后发送这些网页到浏览器上。不像静态的 HTML,PHP 能在不同的情况下展示不同的内容。PHP 是一个在 web 上非常受欢迎的语言;很多像 Facebook 和 Wikipedia 的项目都使用 PHP 编写。

安装 PHP 和 MySQL 的插件:

sudo apt-get install php php-mysql -y

删除 index.html,然后创建 index.php

sudo rm index.html
sudo leafpad index.php

在里面添加以下内容:

<?php phpinfo(); ?>

保存、退出、刷新你的网页。你将会看到 PHP 状态页:

WordPress

你可以使用 wget 命令从 wordpress.org 下载 WordPress。最新的 WordPress 总是使用 wordpress.org/latest.tar.gz 这个网址,所以你可以直接抓取这些文件,而无需到网页里面查看,现在的版本是 5.8.2。

确保你在 /var/www/html 目录中,然后删除里面的所有内容:

cd /var/www/html/
sudo rm *

使用 wget 下载 WordPress,然后提取里面的内容,并移动提取的 WordPress 目录中的内容移动到 html 目录下:

sudo wget http://wordpress.org/latest.tar.gz
sudo tar xzf latest.tar.gz
sudo mv wordpress/* .

现在可以删除压缩包和空的 wordpress 目录了:

sudo rm -rf wordpress latest.tar.gz

运行 ls 或者 tree -L 1 命令显示 WordPress 项目下包含的内容:

.
├── index.php
├── license.txt
├── readme.html
├── wp-activate.php
├── wp-admin
├── wp-blog-header.php
├── wp-comments-post.php
├── wp-config-sample.php
├── wp-content
├── wp-cron.php
├── wp-includes
├── wp-links-opml.php
├── wp-load.php
├── wp-login.php
├── wp-mail.php
├── wp-settings.php
├── wp-signup.php
├── wp-trackback.php
└── xmlrpc.php

3 directories, 16 files

这是 WordPress 的默认安装源。在 wp-content 目录中,你可以编辑你的自定义安装。

你现在应该把所有文件的所有权改为 Apache 的运行用户 www-data

sudo chown -R www-data: .

WordPress 数据库

为了搭建你的 WordPress 站点,你需要一个数据库。这里使用的是 MySQL。

在终端窗口运行 MySQL 的安全安装命令:

sudo mysql_secure_installation

你将会被问到一系列的问题。这里原来没有设置密码,但是在下一步你应该设置一个。确保你记住了你输入的密码,后面你需要使用它去连接你的 WordPress。按回车确认下面的所有问题。

当它完成之后,你将会看到 “All done!” 和 “Thanks for using MariaDB!” 的信息。

在终端窗口运行 mysql 命令:

sudo mysql -uroot -p

输入你创建的 root 密码(LCTT 译注:不是 Linux 系统的 root 密码,是 MySQL 的 root 密码)。你将看到 “Welcome to the MariaDB monitor.” 的欢迎信息。在 “MariaDB [(none)] >” 提示处使用以下命令,为你 WordPress 的安装创建一个数据库:

create database wordpress;

注意声明最后的分号,如果命令执行成功,你将看到下面的提示:

Query OK, 1 row affected (0.00 sec)

把数据库权限交给 root 用户在声明的底部输入密码:

GRANT ALL PRIVILEGES ON wordpress.* TO 'root'@'localhost' IDENTIFIED BY 'YOURPASSWORD';

为了让更改生效,你需要刷新数据库权限:

FLUSH PRIVILEGES;

按 Ctrl+D 退出 MariaDB 提示符,返回到 Bash shell。

WordPress 配置

在你的 树莓派 打开网页浏览器,地址栏输入 http://localhost。选择一个你想要在 WordPress 使用的语言,然后点击“Continue”。你将会看到 WordPress 的欢迎界面。点击 “Let’s go!” 按钮。

按照下面这样填写基本的站点信息:

Database Name:      wordpress
User Name:          root
Password:           <YOUR PASSWORD>
Database Host:      localhost
Table Prefix:       wp_

点击 “Submit” 继续,然后点击 “Run the install”。

按下面的格式填写:为你的站点设置一个标题、创建一个用户名和密码、输入你的 email 地址。点击 “Install WordPress” 按钮,然后使用你刚刚创建的账号登录,你现在已经登录,而且你的站点已经设置好了,你可以在浏览器地址栏输入 http://localhost/wp-admin 查看你的网站。

永久链接

更改你的永久链接设置,使得你的 URL 更加友好是一个很好的想法。

要这样做,首先登录你的 WordPress ,进入仪表盘。进入 “Settings”,“Permalinks”。选择 “Post name” 选项,然后点击 “Save Changes”。接着你需要开启 Apache 的 rewrite 模块。

sudo a2enmod rewrite

你还需要告诉虚拟托管服务,站点允许改写请求。为你的虚拟主机编辑 Apache 配置文件:

sudo leafpad /etc/apache2/sites-available/000-default.conf

在第一行后添加下面的内容:

<Directory "/var/www/html">
    AllowOverride All
</Directory>

确保其中有像这样的内容 <VirtualHost *:80>

<VirtualHost *:80>
    <Directory "/var/www/html">
        AllowOverride All
    </Directory>
    ...

保存这个文件,然后退出,重启 Apache:

sudo systemctl restart apache2

下一步?

WordPress 是可以高度自定义的。在网站顶部横幅处点击你的站点名,你就会进入仪表盘。在这里你可以修改主题、添加页面和文章、编辑菜单、添加插件、以及许多其他的事情。

这里有一些你可以在树莓派的网页服务上尝试的有趣的事情:

  • 添加页面和文章到你的网站
  • 从外观菜单安装不同的主题
  • 自定义你的网站主题或是创建你自己的
  • 使用你的网站服务向你的网络上的其他人显示有用的信息

不要忘记,树莓派是一台 Linux 电脑。你也可以使用相同的结构在运行着 Debian 或者 Ubuntu 的服务器上安装 WordPress。

]]>
139 0 0 0
<![CDATA[exchangefile]]> http://mixdiy.com/index.php/2022/01/26/exchangefile/ Wed, 26 Jan 2022 08:56:49 +0000 http://mixdiy.com/?p=541 ]]> 541 0 0 0 <![CDATA[]]> http://mixdiy.com/index.php/2022/04/13/1055/ Wed, 13 Apr 2022 13:48:45 +0000 http://mixdiy.com/?p=1055 1055 0 1 0 <![CDATA[树莓派安装cURL]]> http://mixdiy.com/index.php/2021/12/21/curl/ Tue, 21 Dec 2021 14:04:00 +0000 http://mixdiy.com/?p=144

树莓派的部分插件需要cURL依赖,比如ssl,目前网上提供的方式非常单一,基本如下:

wget http://curl.haxx.se/download/curl-7.80.0.tar.gz

2. tar -xzvf curl-7.80.0.tar.gz

3. cd curl-7.80.0/

4. ./configure 耗时2分钟左右

5. make

6. sudo make install

以上方式实际使用安装过程麻烦而且易出问题,现提供一个简单的方式:

sudo apt-get install php7.4-curl (我用的是php7.4,各位根据自己的版本修改即可)

安装完成后需要重启apache

sudo service apache2 restart
]]>
144 0 0 0
<![CDATA[wordpress 网站安装ssl证书]]> http://mixdiy.com/index.php/2021/12/22/cpolar-ssl-https/ Wed, 22 Dec 2021 09:54:20 +0000 http://mixdiy.com/?p=153

首先需要通过wordpress后他安装ssl插件,这里选择realy simple ssl,安装完成后下载证书等文件到指定目录,然后执行以下命令即可

cpolar http -hostname=secure.example.com -key=/path/to/tls.key -crt=/path/to/tls.crt 80

]]>
153 0 0 0
<![CDATA[cpolar.yml]]> http://mixdiy.com/index.php/2021/12/23/cpolar-yml/ Wed, 22 Dec 2021 16:20:20 +0000 http://mixdiy.com/?p=158

GNU nano 5.4 /usr/local/etc/cpolar/cpolar.yml

authtoken: ZTcyNDg2NGUtZmI5NC00MGVmLWI4ZjAtOWY2YTJkODcxYWJm
tunnels:
custdomain:
addr: 80
proto: http
hostname: mixdiy.com
crt: /home/pi/cert/certificate.cert
key: /home/pi/cert/private.pem
redirect_https: true

custdomain1:
addr: 80
proto: http
hostname: www.mixdiy.com

在/etc/rc.local文件exit 0上添加:

cpolar start-all

]]>
158 0 0 0
<![CDATA[用树莓派搭建自定义域名的wordpress网站]]> http://mixdiy.com/index.php/2021/12/23/raspi-wordpress-cpolar/ Wed, 22 Dec 2021 16:30:42 +0000 http://mixdiy.com/?p=160

这非常适合设置您的第一个网站,不仅可以学习管理 wordpress 站点,还可以学习 Linux。您将需要一个树莓派(Raspberry Pi)、几个小时和一台计算机来下载映像。 树莓派(RPI) 是学习这些东西的完美设备。

还可以查看 RaspberryPi 的官方项目站点
来源: https://projects.raspberrypi.org/en/projects/lamp-web-server-with-wordpress

使用 Raspberry Pi Imager 安装 Raspberry Pi OS

下载适用于您的操作系统的树莓派镜像工具。它支持大多数操作系统(Windows、Mac 和 Linux)。

https://www.raspberrypi.org/downloads/

  • 运行并安装树莓派镜像工具
  • 选择 Raspberry Pi OS -> Raspberry Pi OS (32-bit)
  • 将 SD 卡插入计算机
  • 点击 Write 按钮
QQ20210630-204122@2x

设置 Apache Web 服务器

Apache 是一种流行的 Web 服务器应用程序,您可以将其安装在 Raspberry Pi 上以允许它为网页提供服务。

Apache 本身可以通过 HTTP 提供 HTML 文件。通过附加模块,它可以使用 PHP 等脚本语言提供动态网页。

sudo apt-get install apache2 -y
sudo service apache2 restart

测试 web 站点

默认情况下,Apache 将测试 HTML 文件放在 web 文件夹中,您可以从您的 Pi 或网络上的另一台计算机查看该页面。

在 Raspberry Pi 上打开 Apache 默认网页:

  • 通过从菜单中选择 Internet > Chromium Web 浏览器打开 Chromium。
  • 输入地址 http://localhost

您应该在浏览器窗口中看到:

apache-it-works

安装静态样例站点

为了让站点的内容更有意义,我们部署一个简单的静态演示站点–冥想放松站点。

cd /var/www/html/
sudo rm *

将web站点发布到公网

目前,这个站点只能在局域网站可以被访问,公网用户是访问不到的。要想所有人访问你创建的漂亮站点,我们需要做如下工作。

安装 Cpolar

cpolar是一款内网穿透工具,可以将您的内网站点暴露到公网上,使所有人可以访问到你的站点。

  • cpolar一键安装脚本:(国内用户)
curl -L https://www.cpolar.com/static/downloads/install-release-cpolar.sh | sudo bash
  • 或短链接安装方式:(国外用户)
curl -sL https://git.io/cpolar | sudo bash
  • 查看cpolar版本信息
cpolar version

如果正常显示,则安装成功

cpolar进行token认证

如果您还没有cpolar账号,请去cpolar官网注册并登录后台获取认证token

cpolar authtoken xxxxxxxxxxxxxxxxxx

生成cpolar随机域名网址

cpolar http -region=cn_vip 80

上图显示,cpolar将内网站点,发布到了一个随机域名网址:https://711d7522.vip.cpolar.cn

  • 我们打开浏览器试一下:https://711d7522.vip.cpolar.cn

现在,我们已经发布了内网的web站点到公网,任何人都可以访问到它。

回到终端窗口,按CTRL+C键,退出cpolar

此时,公网地址不再能被访问。

生成cpolar二级子域名

虽然已经发布到公网,但域名是随机变化的,只适合临时测试使用。如果要长期使用,我们要配置二级子域名。

操作步骤:

  • 升级到cpolar基础套餐
  • 登录到cpolar后台–>预留–>保留二级子域名,例如添加dev9,地区选择VIP China地区

在前台终端测试域名

cpolar http -subdomain=dev9 -region=cn_vip 80

ShellCopy

如果显示正常,则说明我们已经配置好了。

QQ20210701-033554@2x

用新的域名在浏览器里访问:https://dev9.vip.cpolar.cn

正常则说明我们的固定二级子域名配置好了。

将参数保存到cpolar配置文件中

刚刚我们在前台运行cpolar程序,关掉后,域名消失,现在我们将参数保存到配置文件中。以支持开机后台自启动运行。

  • 编辑配置文件
nano /usr/local/etc/cpolar/cpolar.yml

ShellCopy

如图:

QQ20210701-024834@2x

上图为样例配置文件,它会配置两个默认隧道:一个ssh隧道和一个website隧道。

参数说明:

authtoken: xxxxxxxxxxxx #认证token

tunnels:
  ssh:              #隧道名称,表示ssh,名称可以自定义
    addr: 22        #端口号为22
    proto: tcp      #协议tcp
    region: cn_vip  #地区,cn_vip,可选:us,hk,cn,cn_vip
  website:          #隧道名称,用户可以自定义,但多隧道时,不可重复
    addr: 8080      #本地Web站点端口
    proto: http     #协议http
    region: cn_vip  #地区,cn_vip,可选:us,hk,cn,cn_vip


YAMLCopy

本例中,我们需要修改如下内容:

  • 将website隧道的默认8080端口修改为80
  • 添加一行 subdomain: "你的二级子域名"

修改后的效果如图:

QQ20210701-040329@2x

注意: 配置文件是yaml格式的,缩进敏感,而且不能有TAB键

然后按CTRL+X,退出,提示你是否保存,回答Y,确认保存文件路径,回车

测试修改后配置文件

在前台启动所有隧道测试

cpolar start-all

ShellCopy

QQ20210701-035229@2x

如上图显示,则为正常,按CTRL+C退出

如果报错,会提示配置文件某行有错误,请重新修改。直到类似上图正确输出。

配置cpolar服务开机自启动

  • 配置cpolar开机自启动
sudo systemctl enable cpolar

ShellCopy

  • 守护进程方式,启动cpolar
sudo systemctl start cpolar

ShellCopy

  • 查看cpolar守护进程状态
sudo systemctl status cpolar

ShellCopy

如图说明,启动状态成功

QQ20210701-040009@2x
  • 重新启动
sudo reboot

ShellCopy

重启后,检查cpolar隧道是否仍在线

访问后台–>状态 https://dashboard.cpolar.com/status

如图说明配置成功

QQ20210701-042829@2x

安装 PHP

  • 更新到最新仓库
sudo apt-get update -y
  • 安装 apache2 php
sudo apt-get install apache2 php  -y
  • 重启 apache2 服务
sudo service apache2 restart

安装MySQL数据库

sudo apt-get install mariadb-server php-mysql -y
sudo service apache2 restart

安装 WordPress

cd /var/www/html/
  • 删除旧静态站点内容
sudo rm -rf *
  • 下载最新的wordpresss压缩包
sudo wget http://wordpress.org/latest.tar.gz
  • 解压
sudo tar xzf latest.tar.gz
sudo mv wordpress/* .
sudo rm -rf wordpress latest.tar.gz
  • 配置目录权限
sudo chown -R www-data: .

设置您的 WordPress 数据库

设置 MySQL/MariaDB

sudo mysql_secure_installation
  • 此时系统会询问你:Enter current password for root (enter for none): , 按回车(enter) 键,因为第一次登陆是没有密码的。
  • 然后会询问你: Set root password? —— 按 Y ,进行root帐号的密码设置
  • 此时,会提示 New password ,在此输入你的MySQL密码重要:请牢记这个密码,输入完成按回车,会提示re-enter new password此时再重复输入密码,回车即可。
  • 然后,询问你 Remove anonymous users ,按 Y 。
  • 然后,询问你 Disallow root login remotely ,按 Y 。
  • 然后,询问你 Remove test database and access to it ,按 Y 。
  • 然后,询问你 Reload privilege tables now ,按 Y 。
  • 最后,您将看到消息 All done! 和 Thanks for using MariaDB! 。表示已经设置完成了。

创建 WordPress 数据库

sudo mysql -uroot -p
  • 输入您创建的 root 密码。
create database wordpress;
  • 现在将数据库权限授予 root 用户。注意:您需要在 IDENTIFIED BY 后输入您自己的密码。
GRANT ALL PRIVILEGES ON wordpress.* TO 'root'@'localhost' IDENTIFIED BY 'YOURPASSWORD';

重要提示:将上面的 YOURPASSWORD 更改为您的密码。

  • 为了使更改生效,您需要刷新数据库权限:
FLUSH PRIVILEGES;
  • 退出 MariaDB :
exit

重新启动树莓派

sudo reboot

WordPress configuration

  • 在您的 Pi 上打开 Web 浏览器并转到 http://localhost,您应该会看到一个 WordPress 页面,要求选择您的语言。
1-QQ20210702-050009@2x
  • 选择您的语言,然后单击 Continue 按钮。

您将看到 WordPress 欢迎屏幕

2-QQ20210702-050632@2x
  • 点击 现在就开始! 按钮。
  • 现在填写网站基本信息如下:
Database Name:      wordpress
User Name:          root
Password:           <YOUR PASSWORD>
Database Host:      localhost
Table Prefix:       wp_
  • 单击 提交 继续。
  • 单击运行安装程序按钮。

现在你越来越近了!

3-QQ20210702-051441@2x

填写信息:为您的站点命名,创建用户名和密码,然后输入您的电子邮件地址。点击 安装 WordPress 按钮,然后使用您刚刚创建的帐户登录。

现在您已登录并设置了站点,您可以通过访问 http://localhost/wp-admin 来查看该网站。

登录管理后台:

4-QQ20210702-052346@2x

将WordPress站点发布到公网

我们用cpolar将WordPress发布到公网前,通常需要做两件事:

安装相对URL插件

您必须确保WordPress发布为相对URL。
您可以通过安装以下插件之一来完成此操作

  • https://github.com/optimizamx/odt-relative-urls
  • http://wordpress.org/plugins/relative-url/
  • http://wordpress.org/plugins/root-relative-urls/

本例中,我们安装Relative URL插件:

  • 登录WordPress仪表盘–>插件–>安装插件
6-QQ20210702-060810@2x
  • 在关键词搜索栏输入Relative URL 回车
7-QQ20210702-061149@2x
  • 找到插件后点击现在安装按钮
  • 当安装成功后,点击启用按钮,激活插件。

修改config.php配置

您必须确保Wordpress了解它是为了通过隧道主机名提供服务。 您可以通过修改wp-config.php来配置Wordpress以包含以下行:

define('WP_SITEURL', 'http://' . $_SERVER['HTTP_HOST']);
define('WP_HOME', 'http://' . $_SERVER['HTTP_HOST']);

CodeCopy

  • 修改wp-config.php文件
sudo nano /var/www/html/wp-config.php

ShellCopy

配置好以后如图:

8-QQ20210702-055708@2x

现在,我们的博客站点可以被公网正常访问啦!让我们看看效果:

9-QQ20210702-055852@2x

支持好友链接样式

建议您更改永久链接设置以使您的 URL 更友好。

为此,请登录 WordPress 并转到仪表盘

转到设置,然后转到固定链接

选择文章名选项,然后单击保存更改

5-QQ20210702-053322@2x

您需要启用 Apache 的重写模块:

sudo a2enmod rewrite

ShellCopy

您还需要告诉为该站点提供服务的虚拟主机允许覆盖请求。

  • 编辑虚拟主机的 Apache 配置文件:
sudo nano /etc/apache2/sites-available/000-default.conf

ShellCopy

  • 在第 1 行之后添加以下几行。
<Directory "/var/www/html">
    AllowOverride All
</Directory>

CodeCopy

  • 确保它在 <VirtualHost *:80> 中,如下所示:
<VirtualHost *:80>
    <Directory "/var/www/html">
        AllowOverride All
    </Directory>
    ...

CodeCopy

  • 保存文件并退出。
  • 重新启动Apache.
sudo service apache2 restart

ShellCopy

定制主题

WordPress 是非常可定制的。通过在页面顶部的 WordPress 横幅中单击您的站点名称(当您登录时),您将被带到仪表板。从那里,您可以更改主题、添加页面和帖子、编辑菜单、添加插件等等。这只是在 Raspberry Pi 的 Web 服务器上设置一些有趣的东西的品尝器。

下面,我们更换一个主题试试。

  • WordPress仪表盘–>外观–>主题
10-QQ20210702-063021@2x
  • 点击热门,选择一个自己喜欢的主题,点击安装按钮
11-QQ20210702-063128@2x
  • 主题安装成功后,点击启用按钮。
  • 让我们重新打开站点,看一下效果:
12-QQ20210702-063554@2x

现在,您的站点已经建好了,您可以进一步体验更多的主题,慢慢探索吧。

]]>
160 0 0 0
<![CDATA[树莓派安装WordPress总结]]> http://mixdiy.com/index.php/2021/12/23/zongjie/ Thu, 23 Dec 2021 03:16:52 +0000 http://mixdiy.com/?p=169

树莓派上安装wordpress主要包括以下几个步骤和注意点:

1、烧录树莓派系统到sd卡或emmc

2、更新系统

apt-get update

apt-get upgrade

3、安装php、mysql、wordpress

修改上传文件大小限制

php.ini

sudo nano /etc/php/7.4/apache2/php.ini

修改upload_max_filesize和post_max_size这两处
upload_max_filesize = 50M, 将后面的50M修改为自己想要的大小。
post_max_size = 20M, 将这个20M修改为自己想要的大小

sudo service apache2 restart

创建数据库

wordpress

修改版权信息

footer.php

4、安装cpolar(用于远程访问)

5、安装wordpress ssl 插件

6、修改rc.local设置开机启动

sudo nano /etc/rc.local

7、配置看门狗(非必要,但强烈建议,可以大幅减少不必要的维护)

以上步骤完成后就可以通过wordpress发博客了

附录:

Wordpress修改php.ini的方法:首先找到并打开php.ini文件;然后将内容配置为“file _ uploads=onupload _ tmp _ dir等等。最后,保存更改。

wordpressphp . ini修改PHP上传文件大小限制方法详解

打开php.ini,先找到它

file _ uploads=on是否允许通过HTTP上传文件的开关。默认值为开,这意味着开

upload _ tmp _ dir文件将被上传到临时文件在服务器的存储位置。如果未指定,将使用系统默认的临时文件夹

upload _ max _ filesize=8m看文本业务,也就是最大允许文件大小。默认为2M

post _ max _ size=8m指PHP通过表单POST可以接收的最大值,包括表单中的所有值。默认值为8M

一般设置好以上四个参数后,上传一个=8M的文件不是问题,网络正常的时候。

但是如果你想上传8M大的文件,只设置以上四项当然可以。

进一步配置以下参数

max _ execution _ time=600每个PHP页面运行的最大时间值(秒),默认值为30秒

max _ input _ time=600默认情况下,每个PHP页面接收数据所需的最长时间是60秒

memory _ limit=8m默认情况下,每个PHP页面消耗的最大内存是8M。修改以上参数后,在网络允许的正常情况下,可以上传大容量文件

最大执行时间=600

最大输入时间=600

memory_limit=32m

file_uploads=on

upload_tmp_dir=/tmp

upload_max_filesize=32m

]]>
169 0 0 0
<![CDATA[GIMP给图片重新着色和抠图]]> http://mixdiy.com/index.php/2021/12/28/gimp-2/ Mon, 27 Dec 2021 17:23:18 +0000 http://mixdiy.com/?p=193
打开图片
在工具菜单中选择“按颜色选择”
删除选择的颜色区块
删除后的效果
在编辑菜单中选择“以前景色填充”
填充后的效果
修改剩下的区域
按同样方式去掉背景后的最终效果

如果是给文字描边、抠图,方法类似。

如果想达到更好的效果,可以搭配MAC系统的图片预览工具使用高清放大能力。

经过上述处理过的图片锯齿小,图片清晰

]]>
193 0 0 0
<![CDATA[国内访问GitHub的无忧方式]]> http://mixdiy.com/index.php/2021/12/28/github/ Tue, 28 Dec 2021 01:48:30 +0000 http://mixdiy.com/?p=228

国内经常访问不了GitHub,但这个网站对于广大程序员和技术工作者又不可或缺,这里给大家一个链接,可以直接访问无忧,链接如下:

https://hub.fastgit.org/

]]>
228 0 0 0
<![CDATA[最新电视源]]> http://mixdiy.com/index.php/2021/12/29/m3u/ Wed, 29 Dec 2021 04:28:40 +0000 http://mixdiy.com/?p=246

最新网络电视播放链接,对于喜欢自己diy机顶盒的朋友是好资源,其实用树莓派 zero 做一个机顶盒也就几十块钱,频道多到眼花缭乱。当然对于不喜欢捣鼓技术的也可以电脑直接播放。下面放出链接(直接下载TV.txt文件即可):

]]>
246 0 0 0
<![CDATA[真正的智能家庭中心Home Assiatant]]> http://mixdiy.com/index.php/2021/12/30/home-assiatanthhh/ Thu, 30 Dec 2021 00:21:29 +0000 http://mixdiy.com/?p=255

智能家居也是家庭服务器的重头戏。有人问那些智能设备厂家不是都有服务器吗?为什么还要大费周章去自己搭建服务器。原因有两个:一是因为每家智能家居平台都各自封闭,要想打通不同平台的智能家居解决方案就需要自己搭建服务器;二是国内互联网企业窃取用户隐私的行为有目共睹,已经是行业顽疾,自己搭建服务器至少没有那么受制于平台。

文章图片1

homeassistant是一个很完善的开源智能家居解决方案,我目前用的也是homeassistant。

一、docker安装homeassistant

homeassistant可以直接安装,也可以基于docker安装,理论上直接安装更好一点。但是我习惯了用docker搭建服务:

sudo docker run -d--name homeassistant--privileged--restart=unless-stopped-p 8123:8123-e TZ=Asia/Shanghai-v /home/hass/config:/configghcr.io/home-assistant/home-assistant:stable

安装之后通过http://IP:8123访问控制中心

二、安装HACS

homeassistant一大门槛就是通过yaml配置智能家居,但是现在的homeassistant已经有了很多插件让大家可以零基础上手,无需配置yaml也能使用。这就需要用到HACS,这个插件管理工具:

HACS 基于github安装,所以安装之前需要进行如下操作

1.到ipaddress查询github和githubusercontent的ip地址,并设置docker的host文件,接下来就按照我的操作进行安装即可:

sudo docker exec -i homeassistant bash#设置GitHub的IP地址echo 真实IP raw.githubusercontent.com >> /etc/hostsecho 真实IP github.com >> /etc/hosts#下载HACS一键安装脚本wget -O - https://raw.githubusercontent.com/hacs/install/main/install | bash -

安装完毕之后,需要用GitHub账户进行授权登陆:

如果你嫌上面的操作太麻烦,可以用下面的一键安装脚本,地址已经替换成国内源:

#一键安装脚本wget -q -O - https://cdn.jsdelivr.net/gh/al-one/hass-xiaomi-miot/install.sh | bash -

三、安装Xiaomi Miot Auto

接下来我们基于HACS安装一个Xiaomi Miot Auto的插件,这个插件的功能就是通过登陆小米账户,无需配置可以全部控制你的米家智能产品(目前这个插件适配的米家智能家居还是很多的)

文章图片2

安装插件之后,进入集成里搜索Xiaomi Miot Auto,添加集成并登陆你的米家账户,就可以调用其中的智能家居了。

四、配置仪表盘

当你的homeassistant接入智能家居之后,你就可以通过编辑仪表盘对智能家居进行操作和展示。

仪表盘的编辑并不复杂,第一步是选择仪表盘的类型:

文章图片3

然后就是将卡片添加实体(智能家居)这样就可以通过卡片展示或者操作智能家居了。

文章图片4

四、配置configuration.yaml

上面的方法是走了使用插件的捷径,实际上homeassistant可以通过编辑config的yaml文件实现更强大的功能。我举两个例子,第一个例子是通过编辑config添加onvif摄像头,关于摄像头的内容我之前在搭建家用安防的文章中已经有了介绍。大家可以看到我的homeassistant界面里有摄像头画面,不过homeassistant直接接入摄像头会有卡顿,建议通过zoneminder再接入homeassistant会流畅不少。

#摄像头camera:- platform: ffmpegname: mycaminput: -rtsp_transport tcp -i rtsp://admin:@192.168.1.107:10554/tcp/av0_0extra_arguments: -pred 1 -q:v 2 -s 1280x720ffmpeg:ffmpeg_bin: /usr/bin/ffmpeg

我们还可以通过homeassistant的传感器检测功能,把服务器的CPU、内存、硬盘等情况展示出来:

#传感器sensor:- platform: systemmonitorresources:- type: disk_use_percent- type: memory_use_percent- type: processor_use- type: last_boot

效果就是:

文章图片5

五、安装安卓客户端

手机端还可以通过F-Drorid安装HomeAssistant手机APP,通过智能手机对家里的智能家居进行操控。

另外homeassistant还可以接入prometheus,已经进行各种智能家居联动(甚至是不同平台的智能家居联动)和进行警报。

]]>
255 0 0 0
<![CDATA[WordPress人气主题Astra去掉底部版权信息的方法]]> http://mixdiy.com/index.php/2022/01/01/astra/ Sat, 01 Jan 2022 11:15:15 +0000 http://mixdiy.com/?p=258

Astra是WordPress非常有名的主题,深受广大网管喜爱,但在安装好Astra主题后,默认它会在网站底部添加一个指向Astra官网的链接,大多数情况下大家并不希望出现这些信息,此时可用Astra自带的功能将其去掉,具体如下:

打开主题自定义,选择页脚选项

然后选择 页脚栏,找到区域1的自定义文本框,这里就是我们在页脚看到的所有文字,其中[theme_author]短代码表示的就是Astra主题链接

这里我们选择将其修改成需要的信息,当然你也可以使用html代码添加链接等其他格式的内容

最后点击 发布 即可

]]>
258 0 0 0
<![CDATA[树莓派安装cURL和XMLReader的简单方式]]> http://mixdiy.com/index.php/2022/01/01/xml/ Sat, 01 Jan 2022 12:32:13 +0000 http://mixdiy.com/?p=268

在树莓派上安装WordPress,如果需要SSL安全链接或Astra这样的经典主题,这时就需要cURL和XMLReader环境支持,这两个环境如何建立,网上有大量资料可供参考,但都比较复杂而且容易安装失败,本站之前也提供了类似方法。为方便大家越过这些非关键但必要的步骤,这里总结一下简单易用的环境搭建方法(我用的是php7.4,各位根据自己的版本修改即可):

1、安装cURL

sudo apt-get install php7.4-curl

2、安装XMLReader

sudo apt install php7.4-xml

安装完成后需要重启apache

sudo service apache2 restart

]]>
268 0 0 0
<![CDATA[树莓派bullseye的国内镜像源]]> http://mixdiy.com/index.php/2022/01/03/bullseye/ Mon, 03 Jan 2022 01:08:31 +0000 http://mixdiy.com/?p=276

使用说明Debian 11 (bullseye)

  • 官方源
    /etc/apt/sources.list
    deb http://raspbian.raspberrypi.org/raspbian/ bullseye main contrib non-free rpi
  • #Uncomment line below then 'apt-get update' to enable 'apt-get source'
  • #deb-src http://raspbian.raspberrypi.org/raspbian/ bullseye main contrib non-free rpi
  • /etc/apt/sources.list.d/raspi.list
    deb http://archive.raspberrypi.org/debian/ bullseye main
  • #Uncomment line below then 'apt-get update' to enable 'apt-get source'
  • #deb-src http://archive.raspberrypi.org/debian/ bullseye main

1、 清华源

# 编辑 `/etc/apt/sources.list` 文件,删除原文件所有内容,用以下内容取代:
deb [arch=armhf] http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ bullseye main non-free contrib rpi
deb-src http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ bullseye main non-free contrib rpi

# 如果需要 arm64 软件源,在 `/etc/apt/sources.list` 中加上
deb [arch=arm64] http://mirrors.tuna.tsinghua.edu.cn/raspbian/multiarch/ bullseye main

# 编辑 `/etc/apt/sources.list.d/raspi.list` 文件,删除原文件所有内容,用以下内容取代:
deb http://mirrors.tuna.tsinghua.edu.cn/raspberrypi/ bullseye main

注意:网址末尾的raspbian重复两次是必须的。因为 Raspbian 的仓库中除了APT软件源还包含其他代码。APT软件源不在仓库的根目录,而在raspbian/子目录下。

编辑镜像站后,请使用sudo apt-get update命令,更新软件源列表,同时检查您的编辑是否正确。

2、阿里源

sudo nano /etc/apt/sources.list

编辑 /etc/apt/sources.list 文件,删除原文件所有内容,用以下内容取代:

deb http://mirrors.aliyun.com/raspbian/raspbian/ bullseye main non-free contrib
deb-src http://mirrors.aliyun.com/raspbian/raspbian/ bullseye main non-free contrib

sudo nano /etc/apt/sources.list.d/raspi.list

编辑 /etc/apt/sources.list.d/raspi.list 文件,删除原文件所有内容,用以下内容取代:

deb http://mirrors.aliyun.com/raspberrypi/ bullseye main

]]>
276 0 0 0
<![CDATA[树莓派系统关机执行了哪些操作?]]> http://mixdiy.com/index.php/2022/01/03/rip-shutdown/ Mon, 03 Jan 2022 13:21:40 +0000 http://mixdiy.com/?p=283

用特殊方法捕捉到的树莓派关机过程,这些信息对于系统级调试和开发有很好的帮助。关机信息如下:

]]>
283 0 0 0
<![CDATA[用树莓派建造一个家庭恒温器]]> http://mixdiy.com/index.php/2022/01/04/top-10-rpi-project/ Tue, 04 Jan 2022 11:02:45 +0000 http://mixdiy.com/?p=289

ThermOS项目是现成智能恒温器诸多缺点的答案。

我和妻子于2020年10月搬进了新家。天一冷,我们就意识到家里旧的供暖系统(包括一个一直开着的供暖区)的一些缺点。我们之前家里有Nest恒温器,目前的设置远没有那么方便。我们家有多个恒温器,有些有计划供暖时间表,有些有不同的时间表,有些根本没有。

旧恒温器

是时候该换一换了,但房子有一些限制:

  • 它建于 20 世纪 60 年代末,在 90 年代进行了翻修。
  • 热量是水暖的(热水)。
  • 它有六个加热区的六个恒温器。
  • 每个恒温器(红色和白色)只有两根电线加热。
炉子阀门

购买还是建造?

我想要对所有热区(时间表、自动化、家/客场等)进行“智能”恒温器控制。如果我想买现货,我有几种选择,但它们都有缺点:

选项1:Nest 或 Ecobee

  • 很贵:没有智能恒温器可以处理多个区域,所以我每个区域都需要一个(~200美元*6 = 1200美元)。
  • 这很难:我必须重新运行恒温器线才能获得臭名昭著的C wire,这使恒温器能够连续供电。电线每根在墙上长20到100英尺,可能会钉在螺柱上。

选项2:电池供电的恒温器,如Sensi WiFi恒温器

  • 电池只能用一两个月。
  • 在仅限电池的模式下,它不兼容HomeKit。

选项3:商用现成的恒温器,但只有一家提供(有点过分了):霍尼韦尔的TrueZONE 

  • 它很旧,支持不力(于2008年发布)。
  • 它很贵——仅控制器就超过300美元,您需要一个RedLINK网关才能让劣质应用程序工作。

最后我的选择是...

选项4:我自己来做!
我决定建造自己的多区智能恒温器,我把它命名为ThermOS

  • 它集中在一块电路板里(你需要一个设备,而不是六个设备)。
  • 它使用现有的墙内恒温器线。
  • 它兼容HomeKit,具有自动化、调度、家/场景等。
  • 等等等等,这...有趣吗?是的,有趣......我想。

ThermOS硬件

我知道我想用树莓派。由于它们变得如此便宜,我决定使用树莓派4型号B 2GB。我相信我可以用树莓派Zero W,但那是未来的修订。

以下是我使用的部件的完整列表:

姓名数量价格
树莓派4型号B 2GB129.99 美元
树莓派4官方15W电源16.99 美元
内陆400系点面包板12.99 美元
Arduino的内陆8通道5V继电器模块18.99 美元
内陆杜邦跳线20厘米(3包)14.99 美元
Mouser.com的DS18B20温度传感器(正品)66.00 美元
3 针螺丝接线板(40 件装)17.99 美元
用于树莓派的RPi GPIO终端块突破板模块117.99 美元
鳄鱼夹测试线索(10包)15.89美元
Southwire 18/2恒温器线(50英尺)110.89美元
收缩包装14.99 美元
可焊面包板(5包)111.99 美元
PCB安装支架(50包)17.99 美元
塑料外壳/外壳127.92美元

我开始在draw.io上绘制硬件图表,并意识到我缺乏一些关于电路板的关键知识。我打开侧板,找到了拆卸变压器,该变压器采用120V电线,使其成为加热系统的24V。如果您的加热系统与我的系统相似,您将在塔可区阀门之间看到许多跳线。Taco上的3号端口跳过了我所有的区域阀门。这是因为无论打开/打开多少阀门,它只是控制循环泵。如果一到五个阀门的任何组合是打开的,它应该打开;如果没有阀门打开,它应该关闭......很简单!

炉子布线建筑

在本质上,恒温器只是一种开关。一旦恒温器内的热敏电阻(温度传感器)检测到较低的温度,开关就会关闭并完成24V电路。这个项目不是在每个房间都有一个恒温器,而是把它们都放在炉子旁边,这样所有六区阀门都可以由一个继电器模块控制,使用八个继电器中的六个。树莓派充当恒温器的大脑,独立控制每个继电器。

使用树莓派和Python手动设置继电器

下一个问题是如何从每个房间获得温度读数。我可以在每个房间有一个无线温度传感器,在Arduino或树莓派上运行,但这可能会变得昂贵和复杂。相反,我想重复使用墙上现有的恒温器线,但纯粹是为了温度传感器。

“1-wire”DS18B20温度传感器似乎符合要求:

  • 它的准确度为+/- 0.5°C或0.9°F。
  • 它使用“1线”协议进行数据处理。
  • 最重要的是,DS18B20可以使用“parasitic power”模式,只需两根电线即可获得电力和数据。提醒一下......几乎所有的DS18B20都是假冒的。我买了一些(希望它们是真的),但当我尝试使用电源时,它们不会起作用。然后我从Mouser.com上买了真品,它们就像一个符咒一样!
温度传感器

从面包板和本地所有组件开始,我开始编写代码来与所有组件交互。一旦我证明了这个概念,我就在混合物中添加了现有的壁内恒温器线。我得到了与那个设置一致的读数,所以我开始让他们更精致一点。在我父亲的帮助下,我们焊接了自称“足够好”的焊料,我们焊接了三针螺丝端子(以避免传感器过热),然后将传感器连接到终端上。现在传感器可以用线螺母连接到现有的壁内布线。

附加温度传感器

我仍在“美化”我的温度传感器壁挂装置,但我经历了一些3D打印修订,我想我快到了。

壁挂

ThermOS软件

像往常一样,写逻辑不是难的部分。然而,决定应用程序架构和框架是一个令人困惑的多天过程。我开始评估像PiHome这样的开源项目,但它依赖于特定的硬件,并且是用PHP编写的。我是Python的粉丝,决定从头开始,编写自己的REST API。

由于HomeKit集成如此重要,我想我最终会写一个HomeBridge插件来集成它。我没有意识到有一个名为HAP-Python的Python HomeKit框架来实现配件协议。它帮助我在30分钟内通过iPhone的“家庭”应用程序运行和控制概念验证。

ThermOS HomeKit集成
ThermOS软件架构

其余的“临时”逻辑相对简单,但我确实想强调一下我最初错过的一块。我的代码运行了几天,我正在处理硬件,这时我注意到我的继电器每隔几秒钟就会打开和关闭一次。这种“短循环”不一定有害,但它肯定没有效率。为了避免这种情况,我添加了一些阈值,以确保热开关仅在+/- 0.5°时切换。

以下是阈值逻辑(你可以参考rubber-duck debugging):

# check that we want heat
if self.target_state.value == 1:
    # if heat relay is already on, check if above threshold
    # if above, turn off .. if still below keep on
    if GPIO.input(self.relay_pin):
        if self.current_temp.value - self.target_temp.value >= 0.5:
            status = 'HEAT ON - TEMP IS ABOVE TOP THRESHOLD, TURNING OFF'
            GPIO.output(self.relay_pin, GPIO.LOW)
        else:
            status = 'HEAT ON - TEMP IS BELOW TOP THRESHOLD, KEEPING ON'
            GPIO.output(self.relay_pin, GPIO.HIGH)
    # if heat relay is not already on, check if below threshold
    elif not GPIO.input(self.relay_pin):
        if self.current_temp.value - self.target_temp.value <= -0.5:
            status = 'HEAT OFF - TEMP IS BELOW BOTTOM THRESHOLD, TURNING ON'
            GPIO.output(self.relay_pin, GPIO.HIGH)
        else:
          status = 'HEAT OFF - KEEPING OFF'
门槛

我实现了我的最终目标——能够从手机上控制所有这一切。

ThermOS作为HomeKit Hub

把我的ThermOS放在午餐盒里

我的概念证明相当混乱。

初始ThermOS设置

随着软件和一般硬件设计的到位,我开始想出如何以更持久和更精致的形式打包所有组件。我对永久安装的主要担忧之一是使用带有杜邦跳线的面包板。我订购了一些可焊接面包板和一个螺丝终端突破板(感谢@arduima的树莓派GPIO销)。

以下是带有支架和外壳的可焊接面包板正在进行中的样子。

正在打包的ThermOS硬件

在这里,安装在锅炉房里。

安装的热操作系统

现在我只需要整理电线并贴上标签,然后我就可以开始将恒温器的剩余部分交换到ThermOS上。我将开始我的下一个项目:用于中央空调的ThermOS。

]]>
289 0 0 0
<![CDATA[用 Cockpit 管理你的树莓派]]> http://mixdiy.com/index.php/2022/01/04/cockpit/ Tue, 04 Jan 2022 11:16:25 +0000 http://mixdiy.com/?p=298

用 Cockpit 建立你的树莓派的控制中心。

去年,我写了关于使用 Cockpit 管理我的 Linux 服务器的文章。它是一个基于 Web 的工具,为管理多个服务器及其相关的服务和应用提供了一个简洁、强大的界面。它还简化了日常的管理任务。

在这篇文章中,我将会介绍如何在树莓派基金会提供的标准操作系统树莓派 OS 上安装用于 Linux 服务器的 Cockpit Web 控制台。我还会简要介绍它的特性。

在树莓派 OS 上安装 Cockpit

在 sudo 权限下使用一个账户通过 SSH 登录你的树莓派系统。如果你还没有建立一个账户:

$ ssh pibox
alan@pibox's password:
Linux pibox.someplace.org 5.10.17-v7+ #1403 SMP Mon Feb 22 11:29:51 GMT 2021 armv7l
 
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
 
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue May  4 09:55:57 2021 from 172.1.4.5
alan@pibox:~ $

在树莓派 OS 上安装 Cockpit Web 控制台和在 Linux 服务器上一样简单:

$ sudo apt install cockpit

Cockpit 只需要 60.4 KB 的磁盘空间。加上它的几个包依赖项,总使用量是 115MB。

安装过程将负责设置和启动服务。你可以使用 systemctl 命令来验证状态:

$ systemctl status cockpit.socket
● cockpit.socket - Cockpit Web Service Socket
   Loaded: loaded (/lib/systemd/system/cockpit.socket; enabled; vendor preset: enabled)
   Active: active (listening) since Tue 2021-05-04 10:24:43 EDT; 35s ago
     Docs: man:cockpit-ws(8)
   Listen: 0.0.0.0:9090 (Stream)
  Process: 6563 ExecStartPost=/usr/share/cockpit/motd/update-motd  localhost (code=exited, status=0/SUCCESS)
  Process: 6570 ExecStartPost=/bin/ln -snf active.motd /run/cockpit/motd (code=exited, status=0/SUCCESS)
    Tasks: 0 (limit: 2181)
   CGroup: /system.slice/cockpit.socket

使用 Cockpit

连接

默认的监听端口号是 9090。打开你最喜欢的 Web 浏览器并输入地址,如果是本机就输入: http://127.0.0.1:9090。如果远程登录就输入:http://192.168.0.100:9090,(这里假设IP地址为192.168.0.100)。


你现在可以使用你的普通账户登录。同样,这个账户上需要有使用 sudo 的权限 —— 很可能就是你用来 SSH 和运行 Apt 的那个账户。一定要勾选“为特权任务重用我的密码”。

管理你的树莓派

Cockpit 的初始屏幕以 “System” 页开始,提供当前 CPU 和内存使用的详细信息和图表。你可以从这个屏幕看到硬件细节。

通过点击每一项来展开左边的列(例如,日志、存储、服务等)。这些是标准的 Cockpit 部分,不言自明。让我快速描述一下每个部分。

日志

这部分展示了日志。它们可以根据日期和严重程度来过滤。

存储

存储部分展示了已经安装的物理驱动器和 RAID 设备。例如大小、序列号等细节都被展示了出来。还展示了读/写活动和实际空间使用的图表。存储的具体日志显示在底部。

网络

这部分展示了发送和接收活动、IP 地址以及网络特定的日志。你还可以使用相应的按钮添加更多的网络设备,如绑定、网桥和 VLAN。

账户

这里展示了已有的账户。点击每个账户来管理,或使用创建新账户按钮来添加用户。账户也可以被删除。

服务

这部分可以让管理员查看系统所有服务的状态。点击任何服务都会转到一个包含启动、重启和禁用的标准任务的屏幕。

应用程序

通常,这个屏幕提供了各种用于管理功能的应用程序,例如 389 目录服务器或创建 Podman 容器。但在我的树莓派 OS 上,这个屏幕只显示“没有安装或可用的应用程序”。在写这篇文章的时候,这个或许还没有实现。虽然,你可能会怀疑这类型的程序对于树莓派硬件来说是否太过沉重。

软件更新

对任何系统管理员来说,保持软件最新是最重要的任务之一。Cockpit 的软件更新部分可以检查并进行更新。

终端

Cockpit 最方便的特点之一是终端。你可以使用它,而不是打开一个单独的终端模拟器并使用 SSH。我使用终端来安装 ScreenFetch:

$ sudo apt install screenfetch

使用 ScreenFetch 生成了这张截图:

使用 Cockpit 的中心控制

Cockpit 在树莓派上的表现就像它在其他 Linux 系统上一样。你可以将它添加到仪表盘上进行集中控制。它允许企业在 Cockpit 作为管理仪表盘解决方案的任何地方,将基于树莓派的服务和系统整合到他们的整体 Linux 基础设施中。因为树莓派经常在高密度机架数据中心以无外接控制headless方式运行,而这些数据中心通常会缺乏 KVM 访问方式,这是非常方便的。

]]>
298 0 0 0
<![CDATA[使用树莓派投射安卓设备]]> http://mixdiy.com/index.php/2022/01/04/air-mirror/ Tue, 04 Jan 2022 11:24:22 +0000 http://mixdiy.com/?p=302

使用Scrpy将您的手机屏幕转换为与树莓派或任何其他基于Linux的设备上的应用程序一起运行的应用程序。

很难远离我们每天使用的小玩意儿。在现代生活的喧嚣中,我想确保我不会错过手机屏幕上弹出的朋友和家人的重要通知。我也很忙,不想迷失在分心中,拿起电话回复消息往往会分散注意力。

使事情进一步复杂化的是,有很多设备。幸运的是,他们中的大多数人,从强大的工作站到笔记本电脑,甚至卑微的树莓派,都可以运行Linux。因为他们运行Linux,我为一台设备找到的几乎每个解决方案都非常适合其他设备。

一刀切

我想在我凝视的任何屏幕上都能统一我生活中的不同数据来源。

我决定通过将手机屏幕复制到电脑上来解决这个问题。本质上,我把我的手机变成了一个与所有其他应用程序一起运行的应用程序。这有助于我将注意力集中在桌面上,防止我在精神上徘徊,并让我更容易回复紧急通知。

听起来很吸引人吗?这也是你做这件事的方法。

设置Scrpy

Scrcpy,通常称为屏幕复制,是一个开源的屏幕镜像工具,用于从Linux、Windows或macOS显示和控制Android设备。Android设备和计算机之间的通信主要通过USB连接和Android调试桥(ADB)进行。它使用TCP/IP,不需要任何根访问。

Scrcpy的设置和配置非常简单。如果您正在运行Fedora,您可以从Copr存储库安装它:

$ sudo dnf copr enable zeno/scrcpy
$ sudo dnf install scrcpy -y

在Debian或Ubuntu上:

sudo apt install scrcpy

您还可以自己编译scrcpy。使用scrcpyGitHub页面上的说明,即使在树莓派上也可以构建很长时间。

在树莓派上:

先安装snap

$ sudo apt install snaps

scrcpy目前树莓派上没有正式版,安装beta版

$ sudo snap install --beta scrcpy

设置手机

安装scrcpy后,您必须启用USB调试,并将每台设备(树莓派、笔记本电脑或工作站)授权为受信任的控制器。

打开安卓设备上的“设置”应用程序,然后向下滚动到开发人员选项。如果开发人员选项未激活,请按照Android的说明解锁它

接下来,启用USB调试

启用USB调试选项

然后通过USB将手机连接到树莓派或笔记本电脑(或您正在使用的任何设备),并将模式设置为PTP(如果可以的话)。如果您的手机不使用PTP,请设置手机用于传输文件的模式(例如,而不是用作连接或MIDI设备)。

您的手机可能会提示您授权您的计算机,并通过其RSA指纹进行识别。您只需要在第一次连接时这样做;之后,您的手机将识别并信任您的计算机。

使用lsusb命令确认设置:

$ lsusb
Bus 007 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 011 Device 004: ID 046d:c21d Logitech, Inc. F310 Gamepad 
Bus 005 Device 005: ID 0951:1666 Kingston Technology DataTraveler G4
Bus 005 Device 004: ID 05e3:0608 Genesys Logic, Inc. Hub
Bus 004 Device 001: ID 18d1:4ee6 Google Inc. Nexus/Pixel Device (PTP + debug)
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

然后执行$ scrcpy,使用默认设置启动它。

Scrcpy在树莓派上运行

性能和响应能力因您使用什么设备来控制手机而异。在Pi上,一些动画可能很慢,甚至响应有时也会滞后。Scrcpy为此提供了一个简单的修复方法:降低图像scrcpy显示器的比特率和分辨率使您的计算机更容易跟上。使用以下操作:

scrcpy --bit-rate 1M --max-size 800

尝试不同的价值观来找到你喜欢的。为了更容易键入,一旦您确定了命令,请考虑创建自己的Bash别名

切断电源线

scrcpy运行后,您甚至可以通过WiFi连接手机和计算机。scrcpy安装过程还安装adb,这是一个与Android设备通信的命令。Scrcpy还使用此命令与您的设备通信,adb可以通过TCP/IP进行连接。

在计算机上运行的Scrpy

要尝试,请确保您的手机通过WiFi连接到计算机正在使用的同一无线网络上。暂时不要断开手机与USB的连接!

接下来,通过导航到“设置”并选择“关于手机”来获取手机的IP地址。查看“状态”选项以获取您的地址。它通常以192.168或10开头。

或者,您可以使用adb获取手机的IP地址:

$ adb shell ip route | awk '{print $9}'

To connect to your device over WiFi, you must enable TCP/IP connections. This, you must do through the adb command:
$ adb tcpip 5555
Now you can disconnect your mobile from USB.
Whenever you want to connect over WiFi, first connect to the mobile with the command adb connect. For instance, assuming my mobile's IP address is 10.1.1.22, the command is: 
$ adb connect 10.1.1.22:5555

一旦连接好,您可以像往常一样运行scrcpy。

遥控器

Scrcpy易于使用。您可以在终端或GUI应用程序中尝试。

您是否使用其他屏幕镜像应用程序?如果是这样,请在评论中告诉我们。

]]>
302 0 0 0
<![CDATA[小度小度、小爱同学、天猫精灵的安全私有开源替代品]]> http://mixdiy.com/index.php/2022/01/04/replace-xiaomi/ Tue, 04 Jan 2022 12:29:46 +0000 http://mixdiy.com/?p=310

Mycroft为您的语音助理应用程序带来了更多的隐私、安全性和自由。

我在农村长大,我们不是很富裕,尽管作为一个偏僻地方的孩子,但我并不真正意识到我们的社会经济地位。我知道这个米色盒子叫一台电脑,放在我爸爸的办公室里。爸爸在银行工作,对于他的工作来说,拥有一台电脑对他来说至关重要。因此,虽然我们从未拥有最新的游戏机,但我们总是有一台电脑。

也许正因为如此,我与所有计算机都建立了相当密切的关系。今天,我很幸运能为重要的计算机公司工作,但那完全是另一回事。我在80年代长大,在90年代中期度过了青少年时代,我对计算机是如何变化的有生动的认识。我记得我爸爸第一次在网上登录我们的9600波特率调制解调器。我记得我第一次能够在我最喜欢的游戏《模拟人生2000》中下载建筑物的皮肤。

我提到这一切是有原因的:我一生都在看着计算机不断发展,从将神秘命令混合到DOS(具有讽刺意味的是,有人说,几年后,我在计算机公司中的角色中仍然执行相同的任务),到“胖”应用程序的GUI,再到网络应用程序和移动应用程序。一个突出的共同点是最终用户对不断简化的界面的需求,以便与技术交互。

从触摸到声音

几年来,很酷的事情是能够触动设备的屏幕并做出反应。触摸屏风靡一时。如今,消费者空间正呈数字助理的趋势。一些统计数据显示,到2022年,55%的美国家庭将拥有语音助理。其他消息人士表示,亚马逊每季度销售约1400万台此类设备,而谷歌落后于约1100万台。如果您想要一个具体的市场价值,MarketWatch估计,到2023年,该行业的价值将为78亿美元。这甚至没有考虑到每个人口袋里的手机!无论您使用什么指标,有一点是明确的:语音助理变得越来越重要。

那么,您唯一的选择是谷歌、亚马逊还是苹果?不!如果他们是的话,我不会写这篇文章,我会在那里把你介绍给Mycroft

Mycroft:面向开发人员的开源语音助理

对我来说,Mycroft最大的销售特点是它是开源的。这意味着我可以进入,修补,并为该项目做出贡献。它主要基于Python,这对我来说还不错。

在我进一步之前,我想提请您注意副标题:“适用于开发人员”。这些是我的话,虽然我为这个项目做出了贡献,但我不代表它。然而,我想强调的是,该项目尚未处于准备获得大规模采用的状态。Mycroft的官方文件通常非常好,但有些粗糙的地方一直在被改进。因此,如果您愿意付出一些努力,也许会归档一些错误,并从长远来看,这个项目绝对值得您花时间。如果您正在寻找一个现成的、准备去的项目,您可能需要一年左右回来。

以下是参与Mycroft项目的一些主要原因。

隐私

正如我提到的,语音助理无处不在,而且只会增加。虽然他们经常被遗忘,但他们正在倾听一切。苹果谷歌亚马逊都在收听他们的语音助手录制的录音。在某些情况下,这些录音甚至在法院使用。

如果你在家庭办公室工作(甚至在世界被新冠病毒颠覆之前)怎么办?您如何保护用户和客户的隐私,特别是当大多数人甚至没有意识到旨在让他们的生活更轻松的技术正在捕捉他们的私人对话时?

即使您不担心暴露您的对话,您也同意公司收集这些数据进行有针对性的广告吗?Mycroft将隐私放在首位。其默认语音转文本(STT)后端是谷歌的STT服务,但您可以从各种各样的提供商中进行选择,包括谷歌、IBM的Watson、Mozilla、Kaldi、微软的Azure等。此外,Mycroft代理通过其服务器为特定提供商(谷歌就是其中之一)的所有STT请求。

安全

还记得我说Mycroft是给开发人员的吗?由于您是Mycroft的主机,这意味着它的安全性是您可以做到的。您可以(也应该)实现VLAN、路由规则、防火墙规则以及与您的环境相关的任何其他安全性。

即使您的安全性不是一流的,您因拥有如此微小的足迹而获得的混淆意味着,除了随机机会或定向攻击外,您的Mycroft实例对外部世界相对安全。如果它在您的网络上,您可以控制什么以及谁可以访问您的语音助理。这是件好事。

我提到Python了吗?

我喜欢Python。这是我能够如此迅速地参与进来的主要原因之一。我在日常工作中广泛使用Python,既是为了乐趣,也是为了盈利。我把我的妻子介绍给Mycroft,她立即开始在我的积压中添加任务,以处理她希望她的语音助理能够做的所有事情。

当我努力处理积压时,我注意到Mycroft的意图解析器之一(我将在我的下一篇文章中讨论)没有正确处理撇号。因此,以真正的开源方式,我分叉了代码,修复了代码,并根据项目创建了拉取请求(PR)。经过一些小的来回,我的公关达到了项目标准,瞧!Mycroft现在处理撇号更好。那有多酷?

A)我能够叉开代码,修复它,并在本地运行修复程序,然后

B)项目接受这些更改以惠及每个人

这太令人惊叹了。

不仅如此,包括KDE Plasma on TV在内的其他项目也集成了Mycroft,使其项目更具吸引力。我很想听到更多利用Mycroft的项目,所以如果您知道有任何项目,请联系Twitter或在下面留下评论。

科学非小说

小时候,我一直喜欢科幻小说;事实上,我仍然喜欢。我想像Jean-Luc Picard一样说:“Computer, Earl Grey. Hot,”让事情神奇地发生。虽然食物复制商还有很长的路要走,但有了Mycroft和一点诀窍,但我可以说:“嘿,Mycroft,启动茶壶”或“嘿,Mycroft,把面包添加到我的购物清单中”,Mycroft会这样做的。

我对Mycroft开发人员社区相对较新,但我们是一小群关系密切的人,他们希望生活在一个Alexas、Siris和Googles开源竞争对手的世界里。顺便在Mattermost上和我们聊天。

]]>
310 0 0 0
<![CDATA[如何让树莓派屏幕保持常亮]]> http://mixdiy.com/index.php/2022/01/05/keeplight/ Tue, 04 Jan 2022 16:36:53 +0000 http://mixdiy.com/?p=314

sudo nano /etc/lightdm/lightdm.conf
找到[SeatDefaults]段下的’xserver-command’,取消注释,修改为如下:

#xserver-command=X 将这一行修改为
xserver-command=X -s 0 -dpms

-s # –设置屏幕保护不启用
dpms #关闭电源节能管理

重启即可

sudo reboot

]]>
314 0 0 0
<![CDATA[如何强制树莓派不接显示器启动显示?]]> http://mixdiy.com/index.php/2022/01/05/force-display/ Wed, 05 Jan 2022 01:41:46 +0000 http://mixdiy.com/?p=319

树莓派在不接显示器的情况下启动后,如此后再接显示器是没有输出的,这给很多使用者带来不便,如何不接显示器启动又能在需要的时候接上显示器正常显示?由于树莓派并没有传统意义上的BIOS, 所以包括显示等各种系统配置参数通常被存在”config.txt”这个文本文件中,树莓派的config.txt文件会在ARM内核初始化之前被GPU读取。这个文件存在引导分区上,对于Linux,路径通常是/boot/config.txt。如果是Windows (或者OS X) 它会被识别为SD卡中可访问部分的一个普通文件。将树莓派SD卡通过读卡器接到电脑上,就可以修改这个文件实现强制显示器输出,按下述方式修改即可。

sudo nano /boot/config.txt

将 #hdmi_force_hotplug=1前的注释去掉

hdmi_force_hotplug=1 # 伪装成HDMI热插拔信号被检测到, 出现HDMI显示器被接入

hdmi_group=1

hdmi_mode=16

这样就能正常显示了。

]]>
319 0 0 0
<![CDATA[给树莓派增加物理按钮一键开、关机]]> http://mixdiy.com/index.php/2022/01/07/power-off-and-on/ Fri, 07 Jan 2022 02:44:43 +0000 http://mixdiy.com/?p=336

一直以来,树莓派关机要么直接拔掉电源,要么登陆后执行shutdown命令关机。这两种方式,第一种简单粗暴,但是断电对树莓派的SD伤害非常大。第二种方式比较安全,但是很麻烦。树莓派其实支持物理按键开关机,介绍如下。

一、实现方式

/boot/config.txt 文件末尾添加一行代码:

dtoverlay=gpio-shutdown

保存,然后重启树莓派,在树莓派的GPIO3(BCM3)和GND引脚之间接一个按键。这时按下按键树莓派就关机了,再按一下按键,树莓派开机。

树莓派引脚

二、实现的原理

在树莓派的/boot/overlays/文件下,包含了大量的设备树,使得树莓派的内核支持大量的硬件配置,前提是要开启才行。 开启的方式也比较简单,只需要在/boot/config.txt文件中添加或者删除相应的命令即可。树莓派的/boot/config.txt文件相当于电脑的BIOS。 这里只看/boot/overlays/README中和本文相关的内容。

树莓派系统/boot/overlays/README文件中关于关机是这么描述的:

Name:   gpio-shutdown
Info:   Initiates a shutdown when GPIO pin changes. The given GPIO pin
        is configured as an input key that generates KEY_POWER events.
        This event is handled by systemd-logind by initiating a
        shutdown. Systemd versions older than 225 need an udev rule
        enable listening to the input device:

                ACTION!="REMOVE", SUBSYSTEM=="input", KERNEL=="event*", \
                        SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", \
                        ATTRS{keys}=="116", TAG+="power-switch"

        This overlay only handles shutdown. After shutdown, the system
        can be powered up again by driving GPIO3 low. The default
        configuration uses GPIO3 with a pullup, so if you connect a
        button between GPIO3 and GND (pin 5 and 6 on the 40-pin header),
        you get a shutdown and power-up button.
Load:   dtoverlay=gpio-shutdown,<param>=<val>
Params: gpio_pin                GPIO pin to trigger on (default 3)

        active_low              When this is 1 (active low), a falling
                                edge generates a key down event and a
                                rising edge generates a key up event.
                                When this is 0 (active high), this is
                                reversed. The default is 1 (active low).

        gpio_pull               Desired pull-up/down state (off, down, up)
                                Default is "up".

                                Note that the default pin (GPIO3) has an
                                external pullup.

大概翻译以下:

在/boot/config.txt文件中添加“dtoverlay = gpio-shutdown”时,当GPIO引脚的电平状态发生变化时关闭树莓派,此时给定的GPIO引脚被配置为KEY_POWER事件的输入键,即不断的监控连接到GPIO引脚的按键是否被按下。

树莓派关机后,树莓派系统可以通过将GPIO3驱动为低电平再次开机。 因此如果连接了GPIO3和GND之间的按钮(40引脚接头连接器上的引脚5和6),你就获得了关机和开机按钮。

即添加代码后,树莓派的GPIO3会不断的检测电平状态,如果检测到GPIO3变为低电平(GPIO3连接到GND时),树莓派就会关机,关机后再次将GPIO3变为低电平,则树莓派会开机。 这样在树莓派的GPIO3和GPND之间连接一个按键,就能够实现树莓派的开关机功能。

dtoverlay=gpio-shutdown

注:该功能(开关机)的实现与I2C有部分冲突,要使用GPIO3实现开关机,则必须关闭I2C功能,如不关闭,则只能实现开机功能,而关机功能失效。如需在启用I2C的情况下一键实现开关机,则可以使用gpio_pin=17,即GPIO_0(物理引脚PIN11)做关机键,GPIO3做开机键,简单说就是GPIO3和GPIO17在开关侧并线,两键共地即可。修改如下:sudo nano /boot/config.txt

dtoverlay=gpio-shutdown

dtoverlay=gpio-shutdown,gpio_pin=17,active_low=1,gpio_pull=up

GPIO3与GPIO17并线,实现I2C开启情况下的一键开关机

即添加代码后,树莓派的GPIO3会不断的检测电平状态,如果检测到GPIO3变为低电平(GPIO3连接到GND时),树莓派就会关机,关机后再次将GPIO3变为低电平,则树莓派会开机。 这样在树莓派的GPIO3和GPND之间连接一个按键,就能够实现树莓派的开关机功能。 还可以按照以下格式进行自定义配置:

dtoverlay = gpio-shutdown,<param> = <val>

其中的 parm 和 val 的值可以选择的配置有:

        gpio_pin                打开触发功能的GPIO引脚(默认3)

        active_low              当它为1(低电平有效)时,下降边缘生成按下事 
                                件,并且上升沿会产生按键上升事件。
                                当它是0(高电平有效)时,
                                这时和1的状态相反。默认值为1(低电平有效)。

        gpio_pull               所需的上拉/下拉状态(关闭,下拉,上拉)
                                默认为“上拉”。

                                请注意,默认引脚(GPIO3)具有一个
                                外部上拉。

Python实现

t = open("/boot/config.txt","a")
line_on = "dtoverlay=gpio-shutdown"               # 调用文件的 readline()方法 
t.writelines(line_on+'\n')
line_off = "dtoverlay=gpio-shutdown,gpio_pin=17,active_low=1,gpio_pull=up"
t.writelines(line_off+'\n')
t.close()  

]]>
336 0 0 0
<![CDATA[树莓派系统语言设置成中文]]> http://mixdiy.com/index.php/2022/01/07/chinesefont/ Fri, 07 Jan 2022 14:07:21 +0000 http://mixdiy.com/?p=368

步骤:

1.安装中文字体

sudo apt-get -y install ttf-wqy-zenhei

2.设置显示中文

sudo raspi-config

选择:4 Localisation Options --> I1 Change Locale
操作提示:按空格键在前面打勾或去掉勾(星号=勾),
PageUp 和 PageDown快速翻页,
Tab键跳到OK按钮上

去掉en_GB.UTF-8 UTF-8,
勾上:“en_US.UTF-8 UTF-8”、“zh_CN.UTF-8 UTF-8”、“zh_CN.GBK GBK”(只勾选这三个) >>>>>然后ok
下一屏幕默认语言选 zh_CN.UTF-8>>>>>然后ok

树莓派 系统语言设置成中文 失败,没反应?
也有人是例外:

重启之后没反应?依然是英文?

在根目录下找到 /etc/profile
修改profile文件,添加以下两行.以便重启之后也能生效:

export LANG=zh_CN.utf8
export LC_ALL=zh_CN.utf8

注意: /etc/profile的修改需要root权限才能修改!

]]>
368 0 0 0
<![CDATA[树莓派安装opencv的简易方法]]> http://mixdiy.com/index.php/2022/01/08/installopencv/ Fri, 07 Jan 2022 16:29:48 +0000 http://mixdiy.com/?p=371

树莓派安装opencv常常会出现很多问题,网上大部分的教程是自行编译,编译有两个问题,1是时间特别长,2是容易出错,特别是头文件目录需要修改,否则很难编译下去。这些对于没有C语言基础的人来说非常困难。这里给出很简单的安装方法:

1、sudo pip3 install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple

2、 sudo pip3 install -U numpy -i https://pypi.tuna.tsinghua.edu.cn/simple

3、sudo pip3 install opencv-contrib-python -i https://pypi.tuna.tsinghua.edu.cn/simple

第二步主要是避免版本冲突导致import cv2失败。如安装过程中总是出现“已杀死”而无法安装,则在命令后加上:—no-cache-dir

注:

另一种简单安装方式是

sudo apt-get install libopencv-dev

sudo apt-get install python3-opencv

]]>
371 0 0 0
<![CDATA[树莓派开机启动python程序或gui程序]]> http://mixdiy.com/index.php/2022/01/10/autostart/ Mon, 10 Jan 2022 12:55:16 +0000 http://mixdiy.com/?p=379

如果希望在树莓派开机进入界面后,自动启动python的gui程序,以gui.py文件为例:

1、给文件设置运行权限

chmod +x gui.py

2、在这个gui.py文件最开始,加上python编译器的地址

比如 #!/usr/bin/env python

如果不清楚python编译器的地址是什么,可以用 which python命令查看。如果是用python3,则用 which python3

3、打开自启动的文件(以用户为pi为例)

sudo nano /home/pi/.config/lxsession/LXDE-pi/autostart

最下面增加一行

@/home/pi/Downloads/gui.py

]]>
379 0 0 0
<![CDATA[ 苹果开发者大会]]> http://mixdiy.com/index.php/2022/01/10/videotest/ Mon, 10 Jan 2022 14:53:57 +0000 http://mixdiy.com/?p=383
]]>
383 0 0 0
<![CDATA[树莓派检测恶意软件攻击准确率高达 99.82%]]> http://mixdiy.com/index.php/2022/01/12/rd/ Wed, 12 Jan 2022 11:27:06 +0000 http://mixdiy.com/?p=402

近日,IRISA(the Research Institute of Computer Science and Random Systems)的一个研究小组开发了一套独特的解决方案来检测恶意软件。该解决方案使用树莓派为主体,借助与之配套的设备来扫描电脑中发出的电磁波,通过分析电磁波即可得知此时是否有恶意活动正在发生。在测试期间,该检测系统的准确率高达 99.82%。
由树莓派所组成的恶意软件检测系统不拦截任何软件,也不调查流入和流出计算机的任何数据包。正如上段所说,树莓派通过分析特定的电磁(EM)波,以极高的精确度探测正在进行的恶意软件活动。为了开发这个恶意软件检测系统,研究人员将 Raspberry Pi 与示波器(Picoscope 6407)和一个 H-field 探头连接起来,以检测电磁波的变化。上述操作只能让树莓派检测电磁,为了让它还能够识别出计算机在受到恶意软件攻击时发出的不同电磁波所传达出来的信息,研究人员对树莓派进行了安全和恶意数据集的训练,从而获得潜在威胁的参数。经过训练的树莓派能够成功地检测到恶意软件正在进行的攻击,准确率更是高达 99.82%。这一整套恶意软件检测系统的最大优势是它是一个外部系统,与一般内置在系统中的检测系统或检测软件完全不同。用户不需要在待扫描的设备上安装任何软件。也因为是外部系统,这个系统对许多恶意软件所具备的反监测手段完全免疫,也不会遭到恶意软件的篡改。虽然这个系统已经具备极高的可用性,但研究人员表示还需要更多的测试来微调该系统。此外,研究人员还需要定期向该系统提供新的恶意软件进行训练,以确保它能够与时俱进,准确地检测到各种类型的攻击,该研究报告完整版在此:https://dl.acm.org/doi/abs/10.1145/3485832.3485894。

]]>
402 0 0 0
<![CDATA[国务院关于印发]]> http://mixdiy.com/index.php/2022/01/12/govinfo/ Wed, 12 Jan 2022 11:50:38 +0000 http://mixdiy.com/?p=405

“十四五”数字经济发展规划的通知

各省、自治区、直辖市人民政府,国务院各部委、各直属机构:

现将《“十四五”数字经济发展规划》印发给你们,请认真贯彻执行。

国务院

2021年12月12日

(此件公开发布)

“十四五”数字经济发展规划

数字经济是继农业经济、工业经济之后的主要经济形态,是以数据资源为关键要素,以现代信息网络为主要载体,以信息通信技术融合应用、全要素数字化转型为重要推动力,促进公平与效率更加统一的新经济形态。数字经济发展速度之快、辐射范围之广、影响程度之深前所未有,正推动生产方式、生活方式和治理方式深刻变革,成为重组全球要素资源、重塑全球经济结构、改变全球竞争格局的关键力量。“十四五”时期,我国数字经济转向深化应用、规范发展、普惠共享的新阶段。为应对新形势新挑战,把握数字化发展新机遇,拓展经济发展新空间,推动我国数字经济健康发展,依据《中华人民共和国国民经济和社会发展第十四个五年规划和2035年远景目标纲要》,制定本规划。

一、发展现状和形势

(一)发展现状。

“十三五”时期,我国深入实施数字经济发展战略,不断完善数字基础设施,加快培育新业态新模式,推进数字产业化和产业数字化取得积极成效。2020年,我国数字经济核心产业增加值占国内生产总值(GDP)比重达到7.8%,数字经济为经济社会持续健康发展提供了强大动力。

信息基础设施全球领先。建成全球规模最大的光纤和第四代移动通信(4G)网络,第五代移动通信(5G)网络建设和应用加速推进。宽带用户普及率明显提高,光纤用户占比超过94%,移动宽带用户普及率达到108%,互联网协议第六版(IPv6)活跃用户数达到4.6亿。

产业数字化转型稳步推进。农业数字化全面推进。服务业数字化水平显著提高。工业数字化转型加速,工业企业生产设备数字化水平持续提升,更多企业迈上“云端”。

新业态新模式竞相发展。数字技术与各行业加速融合,电子商务蓬勃发展,移动支付广泛普及,在线学习、远程会议、网络购物、视频直播等生产生活新方式加速推广,互联网平台日益壮大。

数字政府建设成效显著。一体化政务服务和监管效能大幅度提升,“一网通办”、“最多跑一次”、“一网统管”、“一网协同”等服务管理新模式广泛普及,数字营商环境持续优化,在线政务服务水平跃居全球领先行列。

数字经济国际合作不断深化。《二十国集团数字经济发展与合作倡议》等在全球赢得广泛共识,信息基础设施互联互通取得明显成效,“丝路电商”合作成果丰硕,我国数字经济领域平台企业加速出海,影响力和竞争力不断提升。

与此同时,我国数字经济发展也面临一些问题和挑战:关键领域创新能力不足,产业链供应链受制于人的局面尚未根本改变;不同行业、不同区域、不同群体间数字鸿沟未有效弥合,甚至有进一步扩大趋势;数据资源规模庞大,但价值潜力还没有充分释放;数字经济治理体系需进一步完善。

(二)面临形势。

当前,新一轮科技革命和产业变革深入发展,数字化转型已经成为大势所趋,受内外部多重因素影响,我国数字经济发展面临的形势正在发生深刻变化。

发展数字经济是把握新一轮科技革命和产业变革新机遇的战略选择。数字经济是数字时代国家综合实力的重要体现,是构建现代化经济体系的重要引擎。世界主要国家均高度重视发展数字经济,纷纷出台战略规划,采取各种举措打造竞争新优势,重塑数字时代的国际新格局。

数据要素是数字经济深化发展的核心引擎。数据对提高生产效率的乘数作用不断凸显,成为最具时代特征的生产要素。数据的爆发增长、海量集聚蕴藏了巨大的价值,为智能化发展带来了新的机遇。协同推进技术、模式、业态和制度创新,切实用好数据要素,将为经济社会数字化发展带来强劲动力。

数字化服务是满足人民美好生活需要的重要途径。数字化方式正有效打破时空阻隔,提高有限资源的普惠化水平,极大地方便群众生活,满足多样化个性化需要。数字经济发展正在让广大群众享受到看得见、摸得着的实惠。

规范健康可持续是数字经济高质量发展的迫切要求。我国数字经济规模快速扩张,但发展不平衡、不充分、不规范的问题较为突出,迫切需要转变传统发展方式,加快补齐短板弱项,提高我国数字经济治理水平,走出一条高质量发展道路。

二、总体要求

(一)指导思想。

以习近平新时代中国特色社会主义思想为指导,全面贯彻党的十九大和十九届历次全会精神,立足新发展阶段,完整、准确、全面贯彻新发展理念,构建新发展格局,推动高质量发展,统筹发展和安全、统筹国内和国际,以数据为关键要素,以数字技术与实体经济深度融合为主线,加强数字基础设施建设,完善数字经济治理体系,协同推进数字产业化和产业数字化,赋能传统产业转型升级,培育新产业新业态新模式,不断做强做优做大我国数字经济,为构建数字中国提供有力支撑。

(二)基本原则。

坚持创新引领、融合发展。坚持把创新作为引领发展的第一动力,突出科技自立自强的战略支撑作用,促进数字技术向经济社会和产业发展各领域广泛深入渗透,推进数字技术、应用场景和商业模式融合创新,形成以技术发展促进全要素生产率提升、以领域应用带动技术进步的发展格局。

坚持应用牵引、数据赋能。坚持以数字化发展为导向,充分发挥我国海量数据、广阔市场空间和丰富应用场景优势,充分释放数据要素价值,激活数据要素潜能,以数据流促进生产、分配、流通、消费各个环节高效贯通,推动数据技术产品、应用范式、商业模式和体制机制协同创新。

坚持公平竞争、安全有序。突出竞争政策基础地位,坚持促进发展和监管规范并重,健全完善协同监管规则制度,强化反垄断和防止资本无序扩张,推动平台经济规范健康持续发展,建立健全适应数字经济发展的市场监管、宏观调控、政策法规体系,牢牢守住安全底线。

坚持系统推进、协同高效。充分发挥市场在资源配置中的决定性作用,构建经济社会各主体多元参与、协同联动的数字经济发展新机制。结合我国产业结构和资源禀赋,发挥比较优势,系统谋划、务实推进,更好发挥政府在数字经济发展中的作用。

(三)发展目标。

到2025年,数字经济迈向全面扩展期,数字经济核心产业增加值占GDP比重达到10%,数字化创新引领发展能力大幅提升,智能化水平明显增强,数字技术与实体经济融合取得显著成效,数字经济治理体系更加完善,我国数字经济竞争力和影响力稳步提升。

——数据要素市场体系初步建立。数据资源体系基本建成,利用数据资源推动研发、生产、流通、服务、消费全价值链协同。数据要素市场化建设成效显现,数据确权、定价、交易有序开展,探索建立与数据要素价值和贡献相适应的收入分配机制,激发市场主体创新活力。

——产业数字化转型迈上新台阶。农业数字化转型快速推进,制造业数字化、网络化、智能化更加深入,生产性服务业融合发展加速普及,生活性服务业多元化拓展显著加快,产业数字化转型的支撑服务体系基本完备,在数字化转型过程中推进绿色发展。

——数字产业化水平显著提升。数字技术自主创新能力显著提升,数字化产品和服务供给质量大幅提高,产业核心竞争力明显增强,在部分领域形成全球领先优势。新产业新业态新模式持续涌现、广泛普及,对实体经济提质增效的带动作用显著增强。

——数字化公共服务更加普惠均等。数字基础设施广泛融入生产生活,对政务服务、公共服务、民生保障、社会治理的支撑作用进一步凸显。数字营商环境更加优化,电子政务服务水平进一步提升,网络化、数字化、智慧化的利企便民服务体系不断完善,数字鸿沟加速弥合。

——数字经济治理体系更加完善。协调统一的数字经济治理框架和规则体系基本建立,跨部门、跨地区的协同监管机制基本健全。政府数字化监管能力显著增强,行业和市场监管水平大幅提升。政府主导、多元参与、法治保障的数字经济治理格局基本形成,治理水平明显提升。与数字经济发展相适应的法律法规制度体系更加完善,数字经济安全体系进一步增强。

展望2035年,数字经济将迈向繁荣成熟期,力争形成统一公平、竞争有序、成熟完备的数字经济现代市场体系,数字经济发展基础、产业体系发展水平位居世界前列。

三、优化升级数字基础设施

(一)加快建设信息网络基础设施。建设高速泛在、天地一体、云网融合、智能敏捷、绿色低碳、安全可控的智能化综合性数字信息基础设施。有序推进骨干网扩容,协同推进千兆光纤网络和5G网络基础设施建设,推动5G商用部署和规模应用,前瞻布局第六代移动通信(6G)网络技术储备,加大6G技术研发支持力度,积极参与推动6G国际标准化工作。积极稳妥推进空间信息基础设施演进升级,加快布局卫星通信网络等,推动卫星互联网建设。提高物联网在工业制造、农业生产、公共服务、应急管理等领域的覆盖水平,增强固移融合、宽窄结合的物联接入能力。

(二)推进云网协同和算网融合发展。加快构建算力、算法、数据、应用资源协同的全国一体化大数据中心体系。在京津冀、长三角、粤港澳大湾区、成渝地区双城经济圈、贵州、内蒙古、甘肃、宁夏等地区布局全国一体化算力网络国家枢纽节点,建设数据中心集群,结合应用、产业等发展需求优化数据中心建设布局。加快实施“东数西算”工程,推进云网协同发展,提升数据中心跨网络、跨地域数据交互能力,加强面向特定场景的边缘计算能力,强化算力统筹和智能调度。按照绿色、低碳、集约、高效的原则,持续推进绿色数字中心建设,加快推进数据中心节能改造,持续提升数据中心可再生能源利用水平。推动智能计算中心有序发展,打造智能算力、通用算法和开发平台一体化的新型智能基础设施,面向政务服务、智慧城市、智能制造、自动驾驶、语言智能等重点新兴领域,提供体系化的人工智能服务。

(三)有序推进基础设施智能升级。稳步构建智能高效的融合基础设施,提升基础设施网络化、智能化、服务化、协同化水平。高效布局人工智能基础设施,提升支撑“智能+”发展的行业赋能能力。推动农林牧渔业基础设施和生产装备智能化改造,推进机器视觉、机器学习等技术应用。建设可靠、灵活、安全的工业互联网基础设施,支撑制造资源的泛在连接、弹性供给和高效配置。加快推进能源、交通运输、水利、物流、环保等领域基础设施数字化改造。推动新型城市基础设施建设,提升市政公用设施和建筑智能化水平。构建先进普惠、智能协作的生活服务数字化融合设施。在基础设施智能升级过程中,充分满足老年人等群体的特殊需求,打造智慧共享、和睦共治的新型数字生活。

四、充分发挥数据要素作用

(一)强化高质量数据要素供给。支持市场主体依法合规开展数据采集,聚焦数据的标注、清洗、脱敏、脱密、聚合、分析等环节,提升数据资源处理能力,培育壮大数据服务产业。推动数据资源标准体系建设,提升数据管理水平和数据质量,探索面向业务应用的共享、交换、协作和开放。加快推动各领域通信协议兼容统一,打破技术和协议壁垒,努力实现互通互操作,形成完整贯通的数据链。推动数据分类分级管理,强化数据安全风险评估、监测预警和应急处置。深化政务数据跨层级、跨地域、跨部门有序共享。建立健全国家公共数据资源体系,统筹公共数据资源开发利用,推动基础公共数据安全有序开放,构建统一的国家公共数据开放平台和开发利用端口,提升公共数据开放水平,释放数据红利。

(二)加快数据要素市场化流通。加快构建数据要素市场规则,培育市场主体、完善治理体系,促进数据要素市场流通。鼓励市场主体探索数据资产定价机制,推动形成数据资产目录,逐步完善数据定价体系。规范数据交易管理,培育规范的数据交易平台和市场主体,建立健全数据资产评估、登记结算、交易撮合、争议仲裁等市场运营体系,提升数据交易效率。严厉打击数据黑市交易,营造安全有序的市场环境。

(三)创新数据要素开发利用机制。适应不同类型数据特点,以实际应用需求为导向,探索建立多样化的数据开发利用机制。鼓励市场力量挖掘商业数据价值,推动数据价值产品化、服务化,大力发展专业化、个性化数据服务,促进数据、技术、场景深度融合,满足各领域数据需求。鼓励重点行业创新数据开发利用模式,在确保数据安全、保障用户隐私的前提下,调动行业协会、科研院所、企业等多方参与数据价值开发。对具有经济和社会价值、允许加工利用的政务数据和公共数据,通过数据开放、特许开发、授权应用等方式,鼓励更多社会力量进行增值开发利用。结合新型智慧城市建设,加快城市数据融合及产业生态培育,提升城市数据运营和开发利用水平。

五、大力推进产业数字化转型

(一)加快企业数字化转型升级。引导企业强化数字化思维,提升员工数字技能和数据管理能力,全面系统推动企业研发设计、生产加工、经营管理、销售服务等业务数字化转型。支持有条件的大型企业打造一体化数字平台,全面整合企业内部信息系统,强化全流程数据贯通,加快全价值链业务协同,形成数据驱动的智能决策能力,提升企业整体运行效率和产业链上下游协同效率。实施中小企业数字化赋能专项行动,支持中小企业从数字化转型需求迫切的环节入手,加快推进线上营销、远程协作、数字化办公、智能生产线等应用,由点及面向全业务全流程数字化转型延伸拓展。鼓励和支持互联网平台、行业龙头企业等立足自身优势,开放数字化资源和能力,帮助传统企业和中小企业实现数字化转型。推行普惠性“上云用数赋智”服务,推动企业上云、上平台,降低技术和资金壁垒,加快企业数字化转型。

(二)全面深化重点产业数字化转型。立足不同产业特点和差异化需求,推动传统产业全方位、全链条数字化转型,提高全要素生产率。大力提升农业数字化水平,推进“三农”综合信息服务,创新发展智慧农业,提升农业生产、加工、销售、物流等各环节数字化水平。纵深推进工业数字化转型,加快推动研发设计、生产制造、经营管理、市场服务等全生命周期数字化转型,加快培育一批“专精特新”中小企业和制造业单项冠军企业。深入实施智能制造工程,大力推动装备数字化,开展智能制造试点示范专项行动,完善国家智能制造标准体系。培育推广个性化定制、网络化协同等新模式。大力发展数字商务,全面加快商贸、物流、金融等服务业数字化转型,优化管理体系和服务模式,提高服务业的品质与效益。促进数字技术在全过程工程咨询领域的深度应用,引领咨询服务和工程建设模式转型升级。加快推动智慧能源建设应用,促进能源生产、运输、消费等各环节智能化升级,推动能源行业低碳转型。加快推进国土空间基础信息平台建设应用。推动产业互联网融通应用,培育供应链金融、服务型制造等融通发展模式,以数字技术促进产业融合发展。

(三)推动产业园区和产业集群数字化转型。引导产业园区加快数字基础设施建设,利用数字技术提升园区管理和服务能力。积极探索平台企业与产业园区联合运营模式,丰富技术、数据、平台、供应链等服务供给,提升线上线下相结合的资源共享水平,引导各类要素加快向园区集聚。围绕共性转型需求,推动共享制造平台在产业集群落地和规模化发展。探索发展跨越物理边界的“虚拟”产业园区和产业集群,加快产业资源虚拟化集聚、平台化运营和网络化协同,构建虚实结合的产业数字化新生态。依托京津冀、长三角、粤港澳大湾区、成渝地区双城经济圈等重点区域,统筹推进数字基础设施建设,探索建立各类产业集群跨区域、跨平台协同新机制,促进创新要素整合共享,构建创新协同、错位互补、供需联动的区域数字化发展生态,提升产业链供应链协同配套能力。

(四)培育转型支撑服务生态。建立市场化服务与公共服务双轮驱动,技术、资本、人才、数据等多要素支撑的数字化转型服务生态,解决企业“不会转”、“不能转”、“不敢转”的难题。面向重点行业和企业转型需求,培育推广一批数字化解决方案。聚焦转型咨询、标准制定、测试评估等方向,培育一批第三方专业化服务机构,提升数字化转型服务市场规模和活力。支持高校、龙头企业、行业协会等加强协同,建设综合测试验证环境,加强产业共性解决方案供给。建设数字化转型促进中心,衔接集聚各类资源条件,提供数字化转型公共服务,打造区域产业数字化创新综合体,带动传统产业数字化转型。

六、加快推动数字产业化

(一)增强关键技术创新能力。瞄准传感器、量子信息、网络通信、集成电路、关键软件、大数据、人工智能、区块链、新材料等战略性前瞻性领域,发挥我国社会主义制度优势、新型举国体制优势、超大规模市场优势,提高数字技术基础研发能力。以数字技术与各领域融合应用为导向,推动行业企业、平台企业和数字技术服务企业跨界创新,优化创新成果快速转化机制,加快创新技术的工程化、产业化。鼓励发展新型研发机构、企业创新联合体等新型创新主体,打造多元化参与、网络化协同、市场化运作的创新生态体系。支持具有自主核心技术的开源社区、开源平台、开源项目发展,推动创新资源共建共享,促进创新模式开放化演进。

(二)提升核心产业竞争力。着力提升基础软硬件、核心电子元器件、关键基础材料和生产装备的供给水平,强化关键产品自给保障能力。实施产业链强链补链行动,加强面向多元化应用场景的技术融合和产品创新,提升产业链关键环节竞争力,完善5G、集成电路、新能源汽车、人工智能、工业互联网等重点产业供应链体系。深化新一代信息技术集成创新和融合应用,加快平台化、定制化、轻量化服务模式创新,打造新兴数字产业新优势。协同推进信息技术软硬件产品产业化、规模化应用,加快集成适配和迭代优化,推动软件产业做大做强,提升关键软硬件技术创新和供给能力。

(三)加快培育新业态新模式。推动平台经济健康发展,引导支持平台企业加强数据、产品、内容等资源整合共享,扩大协同办公、互联网医疗等在线服务覆盖面。深化共享经济在生活服务领域的应用,拓展创新、生产、供应链等资源共享新空间。发展基于数字技术的智能经济,加快优化智能化产品和服务运营,培育智慧销售、无人配送、智能制造、反向定制等新增长点。完善多元价值传递和贡献分配体系,有序引导多样化社交、短视频、知识分享等新型就业创业平台发展。

(四)营造繁荣有序的产业创新生态。发挥数字经济领军企业的引领带动作用,加强资源共享和数据开放,推动线上线下相结合的创新协同、产能共享、供应链互通。鼓励开源社区、开发者平台等新型协作平台发展,培育大中小企业和社会开发者开放协作的数字产业创新生态,带动创新型企业快速壮大。以园区、行业、区域为整体推进产业创新服务平台建设,强化技术研发、标准制修订、测试评估、应用培训、创业孵化等优势资源汇聚,提升产业创新服务支撑水平。

七、持续提升公共服务数字化水平

(一)提高“互联网+政务服务”效能。全面提升全国一体化政务服务平台功能,加快推进政务服务标准化、规范化、便利化,持续提升政务服务数字化、智能化水平,实现利企便民高频服务事项“一网通办”。建立健全政务数据共享协调机制,加快数字身份统一认证和电子证照、电子签章、电子公文等互信互认,推进发票电子化改革,促进政务数据共享、流程优化和业务协同。推动政务服务线上线下整体联动、全流程在线、向基层深度拓展,提升服务便利化、共享化水平。开展政务数据与业务、服务深度融合创新,增强基于大数据的事项办理需求预测能力,打造主动式、多层次创新服务场景。聚焦公共卫生、社会安全、应急管理等领域,深化数字技术应用,实现重大突发公共事件的快速响应和联动处置。

(二)提升社会服务数字化普惠水平。加快推动文化教育、医疗健康、会展旅游、体育健身等领域公共服务资源数字化供给和网络化服务,促进优质资源共享复用。充分运用新型数字技术,强化就业、养老、儿童福利、托育、家政等民生领域供需对接,进一步优化资源配置。发展智慧广电网络,加快推进全国有线电视网络整合和升级改造。深入开展电信普遍服务试点,提升农村及偏远地区网络覆盖水平。加强面向革命老区、民族地区、边疆地区、脱贫地区的远程服务,拓展教育、医疗、社保、对口帮扶等服务内容,助力基本公共服务均等化。加强信息无障碍建设,提升面向特殊群体的数字化社会服务能力。促进社会服务和数字平台深度融合,探索多领域跨界合作,推动医养结合、文教结合、体医结合、文旅融合。

(三)推动数字城乡融合发展。统筹推动新型智慧城市和数字乡村建设,协同优化城乡公共服务。深化新型智慧城市建设,推动城市数据整合共享和业务协同,提升城市综合管理服务能力,完善城市信息模型平台和运行管理服务平台,因地制宜构建数字孪生城市。加快城市智能设施向乡村延伸覆盖,完善农村地区信息化服务供给,推进城乡要素双向自由流动,合理配置公共资源,形成以城带乡、共建共享的数字城乡融合发展格局。构建城乡常住人口动态统计发布机制,利用数字化手段助力提升城乡基本公共服务水平。

(四)打造智慧共享的新型数字生活。加快既有住宅和社区设施数字化改造,鼓励新建小区同步规划建设智能系统,打造智能楼宇、智能停车场、智能充电桩、智能垃圾箱等公共设施。引导智能家居产品互联互通,促进家居产品与家居环境智能互动,丰富“一键控制”、“一声响应”的数字家庭生活应用。加强超高清电视普及应用,发展互动视频、沉浸式视频、云游戏等新业态。创新发展“云生活”服务,深化人工智能、虚拟现实、8K高清视频等技术的融合,拓展社交、购物、娱乐、展览等领域的应用,促进生活消费品质升级。鼓励建设智慧社区和智慧服务生活圈,推动公共服务资源整合,提升专业化、市场化服务水平。支持实体消费场所建设数字化消费新场景,推广智慧导览、智能导流、虚实交互体验、非接触式服务等应用,提升场景消费体验。培育一批新型消费示范城市和领先企业,打造数字产品服务展示交流和技能培训中心,培养全民数字消费意识和习惯。

八、健全完善数字经济治理体系

(一)强化协同治理和监管机制。规范数字经济发展,坚持发展和监管两手抓。探索建立与数字经济持续健康发展相适应的治理方式,制定更加灵活有效的政策措施,创新协同治理模式。明晰主管部门、监管机构职责,强化跨部门、跨层级、跨区域协同监管,明确监管范围和统一规则,加强分工合作与协调配合。深化“放管服”改革,优化营商环境,分类清理规范不适应数字经济发展需要的行政许可、资质资格等事项,进一步释放市场主体创新活力和内生动力。鼓励和督促企业诚信经营,强化以信用为基础的数字经济市场监管,建立完善信用档案,推进政企联动、行业联动的信用共享共治。加强征信建设,提升征信服务供给能力。加快建立全方位、多层次、立体化监管体系,实现事前事中事后全链条全领域监管,完善协同会商机制,有效打击数字经济领域违法犯罪行为。加强跨部门、跨区域分工协作,推动监管数据采集和共享利用,提升监管的开放、透明、法治水平。探索开展跨场景跨业务跨部门联合监管试点,创新基于新技术手段的监管模式,建立健全触发式监管机制。加强税收监管和税务稽查。

(二)增强政府数字化治理能力。加大政务信息化建设统筹力度,强化政府数字化治理和服务能力建设,有效发挥对规范市场、鼓励创新、保护消费者权益的支撑作用。建立完善基于大数据、人工智能、区块链等新技术的统计监测和决策分析体系,提升数字经济治理的精准性、协调性和有效性。推进完善风险应急响应处置流程和机制,强化重大问题研判和风险预警,提升系统性风险防范水平。探索建立适应平台经济特点的监管机制,推动线上线下监管有效衔接,强化对平台经营者及其行为的监管。

(三)完善多元共治新格局。建立完善政府、平台、企业、行业组织和社会公众多元参与、有效协同的数字经济治理新格局,形成治理合力,鼓励良性竞争,维护公平有效市场。加快健全市场准入制度、公平竞争审查机制,完善数字经济公平竞争监管制度,预防和制止滥用行政权力排除限制竞争。进一步明确平台企业主体责任和义务,推进行业服务标准建设和行业自律,保护平台从业人员和消费者合法权益。开展社会监督、媒体监督、公众监督,培育多元治理、协调发展新生态。鼓励建立争议在线解决机制和渠道,制定并公示争议解决规则。引导社会各界积极参与推动数字经济治理,加强和改进反垄断执法,畅通多元主体诉求表达、权益保障渠道,及时化解矛盾纠纷,维护公众利益和社会稳定。

九、着力强化数字经济安全体系

(一)增强网络安全防护能力。强化落实网络安全技术措施同步规划、同步建设、同步使用的要求,确保重要系统和设施安全有序运行。加强网络安全基础设施建设,强化跨领域网络安全信息共享和工作协同,健全完善网络安全应急事件预警通报机制,提升网络安全态势感知、威胁发现、应急指挥、协同处置和攻击溯源能力。提升网络安全应急处置能力,加强电信、金融、能源、交通运输、水利等重要行业领域关键信息基础设施网络安全防护能力,支持开展常态化安全风险评估,加强网络安全等级保护和密码应用安全性评估。支持网络安全保护技术和产品研发应用,推广使用安全可靠的信息产品、服务和解决方案。强化针对新技术、新应用的安全研究管理,为新产业新业态新模式健康发展提供保障。加快发展网络安全产业体系,促进拟态防御、数据加密等网络安全技术应用。加强网络安全宣传教育和人才培养,支持发展社会化网络安全服务。

(二)提升数据安全保障水平。建立健全数据安全治理体系,研究完善行业数据安全管理政策。建立数据分类分级保护制度,研究推进数据安全标准体系建设,规范数据采集、传输、存储、处理、共享、销毁全生命周期管理,推动数据使用者落实数据安全保护责任。依法依规加强政务数据安全保护,做好政务数据开放和社会化利用的安全管理。依法依规做好网络安全审查、云计算服务安全评估等,有效防范国家安全风险。健全完善数据跨境流动安全管理相关制度规范。推动提升重要设施设备的安全可靠水平,增强重点行业数据安全保障能力。进一步强化个人信息保护,规范身份信息、隐私信息、生物特征信息的采集、传输和使用,加强对收集使用个人信息的安全监管能力。

(三)切实有效防范各类风险。强化数字经济安全风险综合研判,防范各类风险叠加可能引发的经济风险、技术风险和社会稳定问题。引导社会资本投向原创性、引领性创新领域,避免低水平重复、同质化竞争、盲目跟风炒作等,支持可持续发展的业态和模式创新。坚持金融活动全部纳入金融监管,加强动态监测,规范数字金融有序创新,严防衍生业务风险。推动关键产品多元化供给,着力提高产业链供应链韧性,增强产业体系抗冲击能力。引导企业在法律合规、数据管理、新技术应用等领域完善自律机制,防范数字技术应用风险。健全失业保险、社会救助制度,完善灵活就业的工伤保险制度。健全灵活就业人员参加社会保险制度和劳动者权益保障制度,推进灵活就业人员参加住房公积金制度试点。探索建立新业态企业劳动保障信用评价、守信激励和失信惩戒等制度。着力推动数字经济普惠共享发展,健全完善针对未成年人、老年人等各类特殊群体的网络保护机制。

十、有效拓展数字经济国际合作

(一)加快贸易数字化发展。以数字化驱动贸易主体转型和贸易方式变革,营造贸易数字化良好环境。完善数字贸易促进政策,加强制度供给和法律保障。加大服务业开放力度,探索放宽数字经济新业态准入,引进全球服务业跨国公司在华设立运营总部、研发设计中心、采购物流中心、结算中心,积极引进优质外资企业和创业团队,加强国际创新资源“引进来”。依托自由贸易试验区、数字服务出口基地和海南自由贸易港,针对跨境寄递物流、跨境支付和供应链管理等典型场景,构建安全便利的国际互联网数据专用通道和国际化数据信息专用通道。大力发展跨境电商,扎实推进跨境电商综合试验区建设,积极鼓励各业务环节探索创新,培育壮大一批跨境电商龙头企业、海外仓领军企业和优秀产业园区,打造跨境电商产业链和生态圈。

(二)推动“数字丝绸之路”深入发展。加强统筹谋划,高质量推动中国—东盟智慧城市合作、中国—中东欧数字经济合作。围绕多双边经贸合作协定,构建贸易投资开放新格局,拓展与东盟、欧盟的数字经济合作伙伴关系,与非盟和非洲国家研究开展数字经济领域合作。统筹开展境外数字基础设施合作,结合当地需求和条件,与共建“一带一路”国家开展跨境光缆建设合作,保障网络基础设施互联互通。构建基于区块链的可信服务网络和应用支撑平台,为广泛开展数字经济合作提供基础保障。推动数据存储、智能计算等新兴服务能力全球化发展。加大金融、物流、电子商务等领域的合作模式创新,支持我国数字经济企业“走出去”,积极参与国际合作。

(三)积极构建良好国际合作环境。倡导构建和平、安全、开放、合作、有序的网络空间命运共同体,积极维护网络空间主权,加强网络空间国际合作。加快研究制定符合我国国情的数字经济相关标准和治理规则。依托双边和多边合作机制,开展数字经济标准国际协调和数字经济治理合作。积极借鉴国际规则和经验,围绕数据跨境流动、市场准入、反垄断、数字人民币、数据隐私保护等重大问题探索建立治理规则。深化政府间数字经济政策交流对话,建立多边数字经济合作伙伴关系,主动参与国际组织数字经济议题谈判,拓展前沿领域合作。构建商事协调、法律顾问、知识产权等专业化中介服务机制和公共服务平台,防范各类涉外经贸法律风险,为出海企业保驾护航。

十一、保障措施

(一)加强统筹协调和组织实施。建立数字经济发展部际协调机制,加强形势研判,协调解决重大问题,务实推进规划的贯彻实施。各地方要立足本地区实际,健全工作推进协调机制,增强发展数字经济本领,推动数字经济更好服务和融入新发展格局。进一步加强对数字经济发展政策的解读与宣传,深化数字经济理论和实践研究,完善统计测度和评价体系。各部门要充分整合现有资源,加强跨部门协调沟通,有效调动各方面的积极性。

(二)加大资金支持力度。加大对数字经济薄弱环节的投入,突破制约数字经济发展的短板与瓶颈,建立推动数字经济发展的长效机制。拓展多元投融资渠道,鼓励企业开展技术创新。鼓励引导社会资本设立市场化运作的数字经济细分领域基金,支持符合条件的数字经济企业进入多层次资本市场进行融资,鼓励银行业金融机构创新产品和服务,加大对数字经济核心产业的支持力度。加强对各类资金的统筹引导,提升投资质量和效益。

(三)提升全民数字素养和技能。实施全民数字素养与技能提升计划,扩大优质数字资源供给,鼓励公共数字资源更大范围向社会开放。推进中小学信息技术课程建设,加强职业院校(含技工院校)数字技术技能类人才培养,深化数字经济领域新工科、新文科建设,支持企业与院校共建一批现代产业学院、联合实验室、实习基地等,发展订单制、现代学徒制等多元化人才培养模式。制定实施数字技能提升专项培训计划,提高老年人、残障人士等运用数字技术的能力,切实解决老年人、残障人士面临的困难。提高公民网络文明素养,强化数字社会道德规范。鼓励将数字经济领域人才纳入各类人才计划支持范围,积极探索高效灵活的人才引进、培养、评价及激励政策。

(四)实施试点示范。统筹推动数字经济试点示范,完善创新资源高效配置机制,构建引领性数字经济产业集聚高地。鼓励各地区、各部门积极探索适应数字经济发展趋势的改革举措,采取有效方式和管用措施,形成一批可复制推广的经验做法和制度性成果。支持各地区结合本地区实际情况,综合采取产业、财政、科研、人才等政策手段,不断完善与数字经济发展相适应的政策法规体系、公共服务体系、产业生态体系和技术创新体系。鼓励跨区域交流合作,适时总结推广各类示范区经验,加强标杆示范引领,形成以点带面的良好局面。

(五)强化监测评估。各地区、各部门要结合本地区、本行业实际,抓紧制定出台相关配套政策并推动落地。要加强对规划落实情况的跟踪监测和成效分析,抓好重大任务推进实施,及时总结工作进展。国家发展改革委、中央网信办、工业和信息化部要会同有关部门加强调查研究和督促指导,适时组织开展评估,推动各项任务落实到位,重大事项及时向国务院报告。

]]>
405 0 0 0
<![CDATA[树莓派究竟有什么用?]]> http://mixdiy.com/index.php/2022/01/16/test/ Sat, 15 Jan 2022 16:53:23 +0000 http://mixdiy.com/?p=416

关于树莓派,有太多争议,有人说性价比不高,有人说速度太慢,有人说就是个吸尘器等等,但在种种不一样的声音下,树莓派得到了更快的发展,甚至有人认为它成就了英国(树莓派诞生于英国),目前树莓派在全球的用户达到了7000万。至今,

树莓派为代表的单板计算机得到大力发展,为linux提供了非常广泛的用户和丰富的生态,树莓派的出现有力推动了linux的普及。

毋庸置疑,树莓派是个好的产品和系统

树莓已成一派

从此海阔天空

]]>
416 0 0 0
<![CDATA[fritzing(版本0.9.9)]]> http://mixdiy.com/index.php/2022/01/16/filetype/ Sat, 15 Jan 2022 16:39:41 +0000 http://mixdiy.com/?p=417

Mac OS版本

Linux版本(64位)

Windows 版本(64位/32位)

]]>
417 0 0 0
<![CDATA[解除WordPress上传文件类型限制]]> http://mixdiy.com/index.php/2022/01/16/filepermission/ Sat, 15 Jan 2022 17:09:59 +0000 http://mixdiy.com/?p=441

安装如下插件之一即可解除文件类型限制

]]>
441 0 0 0
<![CDATA[手机充电器工作3D演示]]> http://mixdiy.com/index.php/2022/01/16/phone-charger/ Sun, 16 Jan 2022 03:18:38 +0000 http://mixdiy.com/?p=448
]]>
448 0 0 0
<![CDATA[树莓派出手打击对手,自研MCU公开售价低至0.7美金]]> http://mixdiy.com/index.php/2022/01/19/picocostdown/ Tue, 18 Jan 2022 16:54:02 +0000 http://mixdiy.com/?p=459

Raspberry Pi 宣布将开始向客户销售其自研的 RP2040 微控制器——其价格将从每片 1 美元降至 0.70 美元。他们声称,公司有足够的材料来制造2000 万个单元,以作为解决因竞争对手零件短缺而停滞不前的项目。

Raspberry Pi大约一年前在 Raspberry Pi Pico 开发板上推出了 RP2040 ,作为其内部芯片团队的第一款产品。RP2040 采用双核 Arm Cortex-M0+ 处理器设计、具有264kB 静态 RAM (SRAM)、一系列通用输入/输出 (GPIO) 引脚,包括 16 个脉冲宽度调制 (PWM) ) 支持、四个模拟输入、两个 UART、两个 I2C、两个 SPI 总线、一个内部温度探头、USB 支持和八个可编程输入/输出 (PIO) 状态机。

所有这些功能都在一个通孔和表面贴装开发板兼模块中提供,仅需 4 美元。当 Raspberry Pi 宣布将开始以 1 美元的价格自行销售这些芯片时,这让人们大吃一惊。从那时起,近百种由 RP2040 驱动的设计已经投放市场——现在 Raspberry Pi 的目标是让其他人更容易参与其中。

“我们正在推出 Raspberry Pi Direct,这项服务将允许人们一次直接从我们这里购买 RP2040 微控制器(即 500 个及以上),”Eben Upton 通过电子邮件告诉我们。“该芯片价格为 0.70 美元(3,400 off)和 0.80 美元(500 off),这甚至低于通过我们的认可经销商网络提供的 1 美元单件定价。”

“我们一直在帮助缺乏其他微控制器芯片的制造商企业临时迁移到 RP2040,随着芯片短缺进入第二年,我们觉得我们应该更广泛地推广它。我们手上大约有 2000 万台,还有更多在路上,所以希望我们能够度过剩下的短缺。”

Raspberry Pi Direct现在已在公司网站上上线,并且可以直接面向企业销售;想要购买这些零件的个人仍然被指向经销商网络,其中每个芯片的价格为 1 美元。

]]>
459 0 0 0
<![CDATA[跨平台文件传输利器,树莓派爱好者的福音]]> http://mixdiy.com/index.php/2022/01/22/https-cowtransfer-com/ Sat, 22 Jan 2022 00:58:02 +0000 http://mixdiy.com/?p=467

大家都工作在手机、微信等平台上,文件传输要么微信、要么百度网盘,但这些对于树莓派爱好者非常不友好,各种文档文件在树莓派和手机或电脑之间传输成为一个烧脑的活,这里推荐大家使用奶牛快传,它非常方便地解决了跨平台的文件传输共享。

]]>
467 0 0 0
<![CDATA[debian参考手册]]> http://mixdiy.com/index.php/2022/01/22/debian/ Sat, 22 Jan 2022 08:58:33 +0000 http://mixdiy.com/?p=471 ]]> 471 0 0 0 <![CDATA[树莓派安装docker]]> http://mixdiy.com/index.php/2022/01/22/install-docker-in-rpi/ Sat, 22 Jan 2022 12:18:57 +0000 http://mixdiy.com/?p=476

安装DOCKER 及 DOCKER-COMPOSE步骤

  1. apt更新:
apt-get update
  1. 安装python和pip(如已安装可省略):
apt-get install -y python python-pip
  1. 安装libffi-dev:
apt-get install libffi-dev
  1. 用pip安装docker 及 docker-compose:
sudo curl -sSL https://get.docker.com | sh
pip install docker
pip install docker-compose
]]>
476 0 0 0
<![CDATA[判断专注力的程序]]> http://mixdiy.com/index.php/2022/01/22/judge/ Sat, 22 Jan 2022 15:32:09 +0000 http://mixdiy.com/?p=479

今天应儿子要求比赛专注程度,也就是将1-25个数字打乱后分配在5*5的格子里,然后比谁先能按数字顺序数完,失败多次后,我觉得他写的字不容易辨识,随后他提出我写个程序打印出来公平竞赛,写了python程序如下:

import random
x=[]
for i in range(1,26):
    x.append(i)
random.shuffle(x)

for j in range(0,25):
    if j>0 and j%5==0:
        print('\n')
    if x[j]<10:
        print(str(x[j])+'    ',end='')
    else:
        print(str(x[j])+'   ',end='')
运行结果

如果希望打印出来在纸上比试,可以

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import random

if __name__ == '__main__':
    x=[]
    j=0
    for i in range(1,26):
        x.append(i)
    random.shuffle(x)

    t = np.arange(25).reshape(5,5)

    for a in range(0,5):
        for b in range(0,5):
            j=j+1
            t[a,b]=x[j-1]

    df = pd.DataFrame(t)

    fig, ax = plt.subplots(figsize=(5, 5))

    ax.axis('off')
    ax.axis('tight')

    ax.table(cellText=df.values,
#             colLabels=df.columns,
             bbox=[0, 0, 1, 1],
             )
    
    plt.show()
    如想去掉底部的工具栏并修改标题,代码如下:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import random

if __name__ == '__main__':
    x=[]
    j=0
    for i in range(1,26):
        x.append(i)
    random.shuffle(x)

    t = np.arange(25).reshape(5,5)

    for a in range(0,5):
        for b in range(0,5):
            j=j+1
            t[a,b]=x[j-1]

    df = pd.DataFrame(t)
    
    plt.rcParams['toolbar'] = 'None'
    
    fig,ax=plt.subplots(num="注意力测试",
           figsize=(5, 5),
           dpi=100,
           facecolor="white",
           edgecolor='green')
       
    ax.axis('off')

    ax.table(cellText=df.values,
             bbox=[0, 0, 1, 1],
             )
    
    plt.show()

某些人对图像边框特别敏感,下面的代码去掉了图像白边:

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import random

if __name__ == '__main__':
    x=[]
    j=0
    for i in range(1,26):
        x.append(i)
    random.shuffle(x)

    t = np.arange(25).reshape(5,5)

    for a in range(0,5):
        for b in range(0,5):
            j=j+1
            t[a,b]=x[j-1]

    df = pd.DataFrame(t)
    
    plt.rcParams['toolbar'] = 'None'
    
    fig,ax=plt.subplots(num="注意力测试",
           figsize=(5, 5),
           dpi=100,
           facecolor="white",
           edgecolor='black')

    ax.axis('off')
    # 去除图像周围的白边
    plt.subplots_adjust(top=1, bottom=0, left=0, right=1, hspace=0, wspace=0)

    ax.axis('tight')
    ax.table(cellText=df.values,
             bbox=[0, 0, 1, 1],
             )    
    plt.show()
]]>
479 0 0 0
<![CDATA[树莓派上实现截屏]]> http://mixdiy.com/index.php/2022/01/23/scrot/ Sun, 23 Jan 2022 00:34:33 +0000 http://mixdiy.com/?p=485

在树莓派上截屏的方法很简单。首先在终端中用下面的命令安装名叫“scrot”的截屏工具。

sudo apt-get install scrot

截取全屏幕执行:

sudo scrot

用鼠标选区屏幕区域截取执行:

sudo scrot -s

20秒后截取,参数可以自定义:

sudo scrot -d20

执行下面的指令可以查看更多用法:

sudo scrot -h
]]>
485 0 0 0
<![CDATA[PyCharm中搭建PyQt5开发环境]]> http://mixdiy.com/index.php/2022/01/24/pyqt5-in-pycharm/ Mon, 24 Jan 2022 15:21:43 +0000 http://mixdiy.com/?p=514

一、配置PyQt5

1. 安装PyQt5

pip install pyqt5

2. PyQt5 不再提供 QtDesigner 等工具,所以需要再安装 pyqt5-tools工具

pip install pyqt5-tools

3. 打开 Pycharm,依次选择 /File/Settings/Tools/External Tools/ 进入外部工具添加界面

4. 点击绿色加号来创建一个外部工具 QtDesigner,配置如下:

-- 工具名称,用于菜单显示
Name = QtDesigner
-- 工具作用描述
Description = Qt tool for designing and building GUIs with Qt Widgets
-- 可通过浏览模块安装路径下的designer.exe程序
Program = D:\devsoft\Python\Python36\Lib\site-packages\pyqt5-tools\designer.exe    
-- 默认为空即可
Arguments =     
-- 通过点击右侧 Insert Macro 来选择要插入的宏,$FileDir$ 表示文件当前所在目录
Working directory = $FileDir$
其他配置保持默认即可。

5. 创建一个外部工具 PyUiCompiler,用于将 Qt 的UI界面设计文件 ui文件 转换成 py代码,配置如下:

-- 工具名称,用于菜单显示
Name = PyUiCompiler
-- 工具作用描述
Description = Python User Interface Compiler for Qt
-- 可通过浏览Python安装路径下的python.exe程序
Program = D:\devsoft\Python\Python36\python.exe
-- 调用 PyQt5.uic.pyuic 来执行转换操作
Arguments = -m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py    
-- 通过点击右侧 Insert Macro 来选择要插入的宏,$FileDir$ 表示文件当前所在目录
Working directory = $FileDir$
其他配置保持默认即可。

6. 外部工具使用
选中要进行操作的文件后,依次选择主菜单 /Tools/External Tools/ 下的自定义工具即可。

或者右键依次选择 /External Tools/ 下的自定义工具即可。

编写ui界面方式

1. 通过ui文件实现
首先点击 QtDesigner 外部工具菜单,设计生成 hello.ui 文件,然后点击 PyUiCompiler 通过 hello.ui 文件生成对应的 hello.py 文件。

然后编写测试程序 main.py 来调用 hello.py,从而显示出界面。(main.py)

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import hello     # 导入 hello.py 模块

from PyQt5.QtWidgets import QApplication, QMainWindow

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainWindow = QMainWindow()
    ui = hello.Ui_MainWindow()
    ui.setupUi(mainWindow)
    mainWindow.show()
    sys.exit(app.exec_())

或者不通过 PyUiCompiler 工具来生成 hello.ui 文件对应的 hello.py 文件,而是直接在代码中加载 hello.ui 文件。(main2.py)

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5 import uic

# 加载 ui 文件
Ui_MainWindow, QtBaseClass = uic.loadUiType('hello.ui')


class MainUi(QMainWindow, Ui_MainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        Ui_MainWindow.__init__(self)
        self.setupUi(self)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MainUi()
    win.show()
    sys.exit(app.exec_())

2. 通过代码直接实现
直接通过代码方式实现ui界面的设计和生成。(main3.py)

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication, QWidget

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = QWidget()
    win.resize(250, 150)
    win.move(300, 300)
    win.setWindowTitle('Hello World')
    win.show()

    sys.exit(app.exec_())

二、添加Pycharm到桌面

找到/usr/share/applications这个目录,其中存放的全部是dash中的启动器,将Pycharm添加其中即可,具体操作步骤为:

命令行操作内容:

cd /usr/share/applications
sudo nano Pycharm.desktop

打开需要编辑的文本内容为:

[Desktop Entry]
Encoding=UTF-8
Name=Pycharm
Comment=Pycharm
Exec=/home/AppImage/pycharm-community-2021.3.1/bin/pycharm.sh
Icon=/home/AppImage/pycharm-community-2021.3.1/bin/pycharm.png
Terminal=false
StartupNotify=true
Type=Application
Categories=Application;
Development;

这样就可以在dash中看到Pycharm的启动器图标了,也可以直接将其添加锁定到launcher

]]>
514 0 0 0
<![CDATA[Pycharm等编程工具反缩进方法]]> http://mixdiy.com/index.php/2022/01/26/pycharm/ Wed, 26 Jan 2022 08:22:44 +0000 http://mixdiy.com/?p=536

编成时常用到缩进和反缩进,缩进直接Tab键,但反缩进很多人不了解,其实就是Shift+Tab组合键而以。

]]>
536 0 0 0
<![CDATA[在树莓派上安装pyqt5]]> http://mixdiy.com/index.php/2022/01/26/install-pyqt5-on-rasp/ Wed, 26 Jan 2022 09:27:19 +0000 http://mixdiy.com/?p=545

安装很简单:

sudo apt-get install python3-pyqt5

]]>
545 0 0 0
<![CDATA[rpipin]]> http://mixdiy.com/index.php/2022/01/27/rpipin/ Wed, 26 Jan 2022 16:03:39 +0000 http://mixdiy.com/?p=549 ]]> 549 0 0 0 <![CDATA[树莓派最小图形系统运行GUI程序出错解决]]> http://mixdiy.com/index.php/2022/01/27/rpiminios-gui-program/ Thu, 27 Jan 2022 15:01:42 +0000 http://mixdiy.com/?p=552

树莓派最小图形系统(如何搭建最小图形系统见前述文章)在运行pyqt5图形程序时出错,错误提示:

ImportError: libGLESv2.so.2: cannot open shared object file: No such file or directory [919] Failed to execute script 'xxx' due to unhandled exception!

网上几乎没有解决方案,研究了一下,可以用以下方法解决:

sudo apt install libgles2-mesa-dev
]]>
552 0 0 0
<![CDATA[树莓派快速进入终端快捷键]]> http://mixdiy.com/index.php/2022/01/27/cmd/ Thu, 27 Jan 2022 15:11:21 +0000 http://mixdiy.com/?p=558

树莓派在图形界面中想随时进入终端调试,可以使用快捷键Ctrl+Alt+F1进入终端

如果想进入图形界面的终端,则需要使用快捷键Ctrl+Alt+T

]]>
558 0 0 0
<![CDATA[GUIGPIO]]> http://mixdiy.com/index.php/2022/01/28/guigpio/ Thu, 27 Jan 2022 16:28:28 +0000 http://mixdiy.com/?p=561
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'startgui.ui'
#
# Created by: PyQt5 UI code generator 5.14.1
#
# WARNING! All changes made in this file will be lost!


from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *  # 导入线程相关模块

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(480, 640)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.dial = QtWidgets.QDial(self.centralwidget)
        self.dial.setGeometry(QtCore.QRect(30, 50, 171, 171))
        palette = QtGui.QPalette()
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(127, 131, 124))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(106, 109, 103))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(56, 58, 55))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(127, 131, 124))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(106, 109, 103))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(56, 58, 55))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(127, 131, 124))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(106, 109, 103))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(56, 58, 55))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.PlaceholderText, brush)
        self.dial.setPalette(palette)
        self.dial.setAutoFillBackground(False)
        self.dial.setObjectName("dial")
        self.dial_2 = QtWidgets.QDial(self.centralwidget)
        self.dial_2.setGeometry(QtCore.QRect(270, 40, 171, 191))
        palette = QtGui.QPalette()
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(48, 111, 203))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(40, 92, 169))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(16, 37, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(21, 49, 90))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(143, 164, 195))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(48, 111, 203))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(40, 92, 169))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(16, 37, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(21, 49, 90))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(143, 164, 195))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(16, 37, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(48, 111, 203))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(40, 92, 169))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(16, 37, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(21, 49, 90))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(16, 37, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(16, 37, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.PlaceholderText, brush)
        self.dial_2.setPalette(palette)
        self.dial_2.setMaximum(999)
        self.dial_2.setProperty("value", 500)
        self.dial_2.setOrientation(QtCore.Qt.Horizontal)
        self.dial_2.setObjectName("dial_2")
        self.dial_3 = QtWidgets.QDial(self.centralwidget)
        self.dial_3.setEnabled(False)
        self.dial_3.setGeometry(QtCore.QRect(160, 130, 151, 151))
        palette = QtGui.QPalette()
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 239, 100))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(246, 225, 50))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(118, 106, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(158, 141, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(246, 233, 127))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 239, 100))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(246, 225, 50))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(118, 106, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(158, 141, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(246, 233, 127))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(118, 106, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 239, 100))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(246, 225, 50))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(118, 106, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(158, 141, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(118, 106, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(118, 106, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.PlaceholderText, brush)
        self.dial_3.setPalette(palette)
        self.dial_3.setMouseTracking(False)
        self.dial_3.setTabletTracking(False)
        self.dial_3.setFocusPolicy(QtCore.Qt.WheelFocus)
        self.dial_3.setLayoutDirection(QtCore.Qt.RightToLeft)
        self.dial_3.setAutoFillBackground(False)
        self.dial_3.setStyleSheet("color: rgb(52, 101, 164);")
        self.dial_3.setInputMethodHints(QtCore.Qt.ImhNone)
        self.dial_3.setSliderPosition(30)
        self.dial_3.setTracking(True)
        self.dial_3.setInvertedAppearance(False)
        self.dial_3.setInvertedControls(False)
        self.dial_3.setWrapping(False)
        self.dial_3.setNotchesVisible(False)
        self.dial_3.setObjectName("dial_3")
        self.lcdNumber = QtWidgets.QLCDNumber(self.centralwidget)
        self.lcdNumber.setGeometry(QtCore.QRect(140, 350, 211, 71))
        palette = QtGui.QPalette()
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(204, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 51, 51))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(229, 25, 25))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(102, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(136, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(211, 215, 207))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(204, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(229, 127, 127))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(211, 215, 207, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(204, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 51, 51))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(229, 25, 25))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(102, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(136, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(211, 215, 207))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(204, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(229, 127, 127))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(211, 215, 207, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(102, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(204, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 51, 51))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(229, 25, 25))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(102, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(136, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(102, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(102, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(204, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(204, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(204, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.PlaceholderText, brush)
        self.lcdNumber.setPalette(palette)
        self.lcdNumber.setFrameShape(QtWidgets.QFrame.VLine)
        self.lcdNumber.setFrameShadow(QtWidgets.QFrame.Plain)
        self.lcdNumber.setLineWidth(0)
        self.lcdNumber.setMidLineWidth(0)
        self.lcdNumber.setSmallDecimalPoint(False)
        self.lcdNumber.setDigitCount(5)
        self.lcdNumber.setSegmentStyle(QtWidgets.QLCDNumber.Filled)
        self.lcdNumber.setProperty("value", 14.12)
        self.lcdNumber.setObjectName("lcdNumber")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(70, 480, 89, 25))
        self.pushButton.setObjectName("pushButton")
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setGeometry(QtCore.QRect(310, 480, 89, 25))
        self.pushButton_2.setObjectName("pushButton_2")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 480, 28))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)

        self.pushButton_2.clicked.connect(MainWindow.close)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        self.r = SpeedSensor1()  # 创建兔子线程对象
        self.t = SpeedSensor2()
        self.r.sinOut.connect(self.SpeedSensor1)  # 将线程信号连接到槽函数
        self.t.sinOut.connect(self.speedsensor2)
        self.e = gpioctl()
#        self.e.sinOut.connect(self.gpioctl)

        self.pushButton.clicked.connect(self.start)  # 开始两个线程

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "启动"))
        self.pushButton_2.setText(_translate("MainWindow", "关闭"))

    def start(self):
        self.r.start()  # 启动兔子线程
        self.t.start()
        self.e.start()

        # 显示兔子的跑步距离

    def SpeedSensor1(self, str):
        self.dial.setValue(int(str))


    def speedsensor2(self, str):
        self.dial_2.setValue(int(str))
'''
    def gpioctl(self):
        print('ok')
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
'''

class SpeedSensor1(QThread):  # 创建兔子线程类
    sinOut = pyqtSignal(str)  # 自定义信号,用来发射兔子比赛动态

    def __init__(self):
        super(SpeedSensor1, self).__init__()

    # 重写run()方法
    def run(self):
        for i in range(1, 100):
            # 循环10次模拟赛跑的过程
            QThread.msleep(100)  # 线程休眠0.1秒,模拟兔子在跑步
            self.sinOut.emit(str(i))  # 显示兔子的跑步距离

class SpeedSensor2(QThread):  # 创建兔子线程类
    sinOut = pyqtSignal(str)  # 自定义信号,用来发射兔子比赛动态

    def __init__(self):
        super(SpeedSensor2, self).__init__()

    # 重写run()方法
    def run(self):
        for i in range(1, 1001):
            # 循环10次模拟赛跑的过程
            QThread.msleep(8)  # 线程休眠0.1秒,模拟兔子在跑步
            self.sinOut.emit(str(i))  # 显示兔子的跑步距离

class gpioctl(QThread):
    sinOut = pyqtSignal(str)
    def __init__(self):
        super(gpioctl,self).__init__()
    def run(self):
        import RPi.GPIO as GPIO                     #引入RPi.GPIO库函数命名为GPIO
        import time                                 #引入计时time函数
        # BOARD编号方式,基于插座引脚编号
        GPIO.setmode(GPIO.BOARD)                    #将GPIO编程方式设置为BOARD模式
        # 输出模式
        GPIO.setup(40, GPIO.OUT)                    #将GPIO引脚11设置为输出引脚

        while True:                                 # 条件为真,下面程序一直循环执行     
                GPIO.output(40, GPIO.HIGH)          #将11引脚电压置高,点亮LED灯
                time.sleep(1)                       #延时1秒
                GPIO.output(40, GPIO.LOW)           #将11引脚电压置低,熄灭LED灯
                time.sleep(1)                       #延时1秒


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()  # 创建窗体对象
    ui = Ui_MainWindow()  # 创建PyQt5设计的窗体对象
    ui.setupUi(MainWindow)  # 调用PyQt5窗体的方法对窗体对象进行初始化设置
    MainWindow.show()  # 显示窗体
    sys.exit(app.exec_())  # 程序关闭时退出进程
]]>
561 0 0 0
<![CDATA[多线程控制GUI-GPIO]]> http://mixdiy.com/index.php/2022/01/28/thread/ Fri, 28 Jan 2022 03:11:51 +0000 http://mixdiy.com/?p=566 ]]> 566 0 0 0 <![CDATA[The.com让个人建站更简单]]> http://mixdiy.com/index.php/2022/01/28/the-com/ Fri, 28 Jan 2022 08:17:13 +0000 http://mixdiy.com/?p=571

在确保网络创作者的利益同时,初创公司 The.com 正努力让建站变得更简单、更现代化。该公司近日完成了 440 万美元的种子轮融资,创建了所谓“少代码建站平台”,旨在抛弃已经成为行业标准的基于模板的方法。

相反,The.com 的网站建设者使用社区创建的组件,你可以将其放入你的网站并与他人分享。网站创建者甚至可以在建站过程中相互协作,直接聊天。该公司目前已经完成了 440 万美元的种子资金,由 NFX 领投,Sound Ventures、VSC Ventures、Village Global 和 Harry Stebbings 参投。

The.com 的想法来自于联合创始人杰夫(Jeff)和克拉克·麦金农(Clarke McKinnon)兄弟。创始人有网站开发和设计的背景,在创立 The.com 之前,他们从 2012 年到 2019 年在博尔德(Boulder)经营自己的网站开发机构。Clarke 说,在这段时间里,他们亲身经历了传统网站开发所带来的挫折。

Clarke 表示:“我们一直在等待别人拿出完美的平台,解决我们所有的需求。但这并没有发生。所以我们开始自己建立它”。

Clarke 解释说,在传统的网络开发和传统平台上,存在着速度和安全问题。但也有一个持续的斗争,即客户要求更深入的定制,然后需要硬编码和持续的工作来保持更新。此外,现在的许多网络建设平台都是面向已经是网站设计师的人。但是,联合创始人从他们自己的经验中知道,往往有一大群为网站做出贡献的人并不是设计师。

Clarke 说:“我们只是需要一个网站建设平台,让每个人都能在同一起跑线上竞争”。

在 The.com 上开始一个新的网站,就像按下“创建网站”按钮一样简单。用户可以邀请其他人作为合作者与他们一起建设,然后点击一个按钮开始编辑网站。创建者不需要从头开始或挑选模板,而是挑选他们想要使用的单个部件和组件。

这意味着他们可以从一个The.com创作者那里获得导航元素,从其他人那里获得页脚,以及从不同的社区成员那里获得其他元素,如英雄图像、标题、横幅、主体元素等等。当你挑选元素时,屏幕上会出现彩纸,将功劳归于原始组件的创造者。未来,The.com 也将推出一种对创造者进行经济奖励的方式。

它是一个浮动窗口,位于正在创建的网站前面。但它比一些所见即所得的设计器更先进一些,有管理网站的页面、块和表(本质上是自由形式的数据库)的部分。在你添加单个网站元素后,你可以进一步定制它们--更新字体、颜色、图像、文本和更多,就像你在编写或编辑网站代码时一样。你还可以通过添加原始的JavaScript或从头开始创建新的元素来进行修改。

]]>
571 0 0 0
<![CDATA[gui 1920 for rpi]]> http://mixdiy.com/index.php/2022/01/31/gui1920-for-rpi/ Sun, 30 Jan 2022 16:21:02 +0000 http://mixdiy.com/?p=575

树莓派上成功运行随机加速并可控制gpio口

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'startgui_1280_1.ui'
#
# Created by: PyQt5 UI code generator 5.14.1
#
# WARNING! All changes made in this file will be lost!


from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *  # 导入线程相关模块
import random

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1280, 400)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.dial = QtWidgets.QDial(self.centralwidget)
        self.dial.setGeometry(QtCore.QRect(30, 40, 171, 171))
        palette = QtGui.QPalette()
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(127, 131, 124))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(106, 109, 103))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(56, 58, 55))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(127, 131, 124))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(106, 109, 103))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(56, 58, 55))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(127, 131, 124))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(106, 109, 103))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(56, 58, 55))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(42, 43, 41))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(85, 87, 83))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.PlaceholderText, brush)
        self.dial.setPalette(palette)
        self.dial.setAutoFillBackground(False)
        self.dial.setObjectName("dial")
        self.dial_2 = QtWidgets.QDial(self.centralwidget)
        self.dial_2.setGeometry(QtCore.QRect(480, 30, 171, 191))
        palette = QtGui.QPalette()
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(48, 111, 203))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(40, 92, 169))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(16, 37, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(21, 49, 90))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(143, 164, 195))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(48, 111, 203))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(40, 92, 169))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(16, 37, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(21, 49, 90))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(143, 164, 195))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(16, 37, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(48, 111, 203))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(40, 92, 169))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(16, 37, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(21, 49, 90))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(16, 37, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(16, 37, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(32, 74, 135))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.PlaceholderText, brush)
        self.dial_2.setPalette(palette)
        self.dial_2.setMaximum(999)
        self.dial_2.setProperty("value", 500)
        self.dial_2.setOrientation(QtCore.Qt.Horizontal)
        self.dial_2.setObjectName("dial_2")
        self.dial_3 = QtWidgets.QDial(self.centralwidget)
        self.dial_3.setEnabled(True)
        self.dial_3.setGeometry(QtCore.QRect(230, 170, 231, 211))
        palette = QtGui.QPalette()
        brush = QtGui.QBrush(QtGui.QColor(52, 101, 164))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 239, 100))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(246, 225, 50))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(118, 106, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(158, 141, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(52, 101, 164))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(52, 101, 164))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(246, 233, 127))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(52, 101, 164))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 239, 100))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(246, 225, 50))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(118, 106, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(158, 141, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(52, 101, 164))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(52, 101, 164))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(246, 233, 127))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(52, 101, 164))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 239, 100))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(246, 225, 50))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(118, 106, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(158, 141, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(52, 101, 164))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(52, 101, 164))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(237, 212, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.PlaceholderText, brush)
        self.dial_3.setPalette(palette)
        font = QtGui.QFont()
        font.setPointSize(20)
        self.dial_3.setFont(font)
        self.dial_3.setMouseTracking(False)
        self.dial_3.setTabletTracking(False)
        self.dial_3.setFocusPolicy(QtCore.Qt.WheelFocus)
        self.dial_3.setAcceptDrops(False)
        self.dial_3.setLayoutDirection(QtCore.Qt.RightToLeft)
        self.dial_3.setAutoFillBackground(False)
        self.dial_3.setStyleSheet("color: rgb(52, 101, 164);")
        self.dial_3.setInputMethodHints(QtCore.Qt.ImhNone)
        self.dial_3.setMaximum(5000)
        self.dial_3.setProperty("value", 1000)
        self.dial_3.setSliderPosition(1000)
        self.dial_3.setTracking(True)
        self.dial_3.setInvertedAppearance(False)
        self.dial_3.setInvertedControls(False)
        self.dial_3.setWrapping(False)
        self.dial_3.setNotchTarget(100.0)
        self.dial_3.setNotchesVisible(True)
        self.dial_3.setObjectName("dial_3")
        self.lcdNumber = QtWidgets.QLCDNumber(self.centralwidget)
        self.lcdNumber.setGeometry(QtCore.QRect(770, 120, 251, 101))
        palette = QtGui.QPalette()
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(69, 78, 81))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(57, 65, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(23, 26, 27))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(30, 34, 36))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(23, 26, 27))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Active, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(69, 78, 81))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(57, 65, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(23, 26, 27))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(30, 34, 36))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(23, 26, 27))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Inactive, QtGui.QPalette.PlaceholderText, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(69, 78, 81))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(57, 65, 67))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(23, 26, 27))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(30, 34, 36))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(46, 52, 54))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.SolidPattern)
        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255, 128))
        brush.setStyle(QtCore.Qt.SolidPattern)
#        palette.setBrush(QtGui.QPalette.Disabled, QtGui.QPalette.PlaceholderText, brush)
        self.lcdNumber.setPalette(palette)
        self.lcdNumber.setAutoFillBackground(False)
        self.lcdNumber.setStyleSheet("color: rgb(46, 52, 54);")
        self.lcdNumber.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.lcdNumber.setFrameShadow(QtWidgets.QFrame.Plain)
        self.lcdNumber.setLineWidth(0)
        self.lcdNumber.setMidLineWidth(0)
        self.lcdNumber.setSmallDecimalPoint(True)
        self.lcdNumber.setDigitCount(5)
        self.lcdNumber.setSegmentStyle(QtWidgets.QLCDNumber.Outline)
        self.lcdNumber.setProperty("value", 16000.0)
        self.lcdNumber.setProperty("intValue", 16000)
        self.lcdNumber.setObjectName("lcdNumber")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(1120, 130, 89, 25))
        self.pushButton.setObjectName("pushButton")
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setGeometry(QtCore.QRect(1120, 180, 89, 25))
        self.pushButton_2.setObjectName("pushButton_2")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1280, 28))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.dial.setWrapping(True)
        self.dial_2.setWrapping(True)
        self.dial_3.setWrapping(True)
        self.retranslateUi(MainWindow)
        self.pushButton_2.clicked.connect(MainWindow.close)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        self.r = SpeedSensor1()  # 创建兔子线程对象
        self.t = SpeedSensor2()


        self.r.sinOut.connect(self.SpeedSensor1)  # 将线程信号连接到槽函数
        self.t.sinOut.connect(self.speedsensor2)
#        self.e.sinOut.connect(self.gpioctl)

        self.e = gpioctl()

        self.lcdNumber.setProperty("intValue", 0)


        self.pushButton.clicked.connect(self.start)  # 开始两个线程

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        MainWindow.setWindowFlags(Qt.FramelessWindowHint)  #y
        self.pushButton.setText(_translate("MainWindow", "启动"))
        self.pushButton_2.setText(_translate("MainWindow", "关闭"))

    def start(self):
        self.r.start()  # 启动兔子线程
        self.t.start()
        self.e.start()
        # 显示兔子的跑步距离

    def SpeedSensor1(self, str):
        self.dial.setValue(int(str))


    def speedsensor2(self, str):
        self.dial_2.setValue(int(str))
        self.lcdNumber.setProperty("intValue", int(str))
        self.dial_3.setValue(int(str)*10)

class SpeedSensor1(QThread):  # 创建兔子线程类
    sinOut = pyqtSignal(str)  # 自定义信号,用来发射兔子比赛动态

    def __init__(self):
        super(SpeedSensor1, self).__init__()

    # 重写run()方法
    def run(self):
        while True:
            j = random.randint(0,100)
            for i in range(1, j):
                # 循环10次模拟赛跑的过程
                QThread.msleep(i)  # 线程休眠0.1秒,模拟兔子在跑步
                self.sinOut.emit(str(i))  # 显示兔子的跑步距离
            for i in range(-j,1):
                QThread.msleep(100-j)  # 线程休眠0.1秒,模拟兔子在跑步
                self.sinOut.emit(str(abs(i)))  # 显示兔子的跑步距离                




class SpeedSensor2(QThread):  # 创建兔子线程类
    sinOut = pyqtSignal(str)  # 自定义信号,用来发射兔子比赛动态

    def __init__(self):
        super(SpeedSensor2, self).__init__()

    # 重写run()方法
    def run(self):
        while True:
            j = random.randint(0,1000)
            for i in range(1, j):
                # 循环10次模拟赛跑的过程
                QThread.msleep(i/100)  # 线程休眠0.1秒,模拟兔子在跑步
                self.sinOut.emit(str(i))  # 显示兔子的跑步距离
            for i in range(-j,1):
                QThread.msleep((1000-j)/100)  # 线程休眠0.1秒,模拟兔子在跑步
                self.sinOut.emit(str(abs(i)))  # 显示兔子的跑步距离         



class gpioctl(QThread):
    sinOut = pyqtSignal(str)
    def __init__(self):
        super(gpioctl,self).__init__()
    def run(self):
        import RPi.GPIO as GPIO                     #引入RPi.GPIO库函数命名为GPIO
        import time                                 #引入计时time函数
        # BOARD编号方式,基于插座引脚编号
        GPIO.setmode(GPIO.BOARD)                    #将GPIO编程方式设置为BOARD模式
        # 输出模式
        GPIO.setup(40, GPIO.OUT)                    #将GPIO引脚11设置为输出引脚

        while True:                                 # 条件为真,下面程序一直循环执行     
                GPIO.output(40, GPIO.HIGH)          #将11引脚电压置高,点亮LED灯
                time.sleep(1)                       #延时1秒
                GPIO.output(40, GPIO.LOW)           #将11引脚电压置低,熄灭LED灯
                time.sleep(1)                       #延时1秒



if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()  # 创建窗体对象
    ui = Ui_MainWindow()  # 创建PyQt5设计的窗体对象
    ui.setupUi(MainWindow)  # 调用PyQt5窗体的方法对窗体对象进行初始化设置
    MainWindow.show()  # 显示窗体
    sys.exit(app.exec_())  # 程序关闭时退出进程
]]>
575 0 0 0
<![CDATA[Linux Lite新版发布,交互友好性提升]]> http://mixdiy.com/index.php/2022/02/01/linux-lite/ Tue, 01 Feb 2022 05:12:01 +0000 http://mixdiy.com/?p=582


近日,Linux Lite 5.8正式发布。Linux Lite是一个对新手友好的Linux发行版,它基于 Ubuntu LTS,并以Xfce桌面为特色。Linux Lite主要针对Windows用户而设计,其目标是提供一整套应用以帮助用户完成他们的日常电脑活动,包括完整的办公套件、媒体播放器和其它必要的日常软件。

据悉,该操作系统基于Ubuntu 20.04.3开发,该发行版使用Linux内核5.4.0-96,但也可以使用其他内核,范围从3.13到5.16都可以自由安排。Linux Lite 5.8还附带一些优秀的软件包,如Mozilla Firefox 96、Thunderbird 91.5.0、LibreOffice6.4.7.2、VLC 3.0.9.2和GIMP 2.10.18,新版还带来了九张新壁纸。

]]>
582 0 0 0
<![CDATA[中俄科学家在吉尔吉斯坦发现可抗癌植物 ]]> http://mixdiy.com/index.php/2022/02/02/medica/ Wed, 02 Feb 2022 14:39:58 +0000 http://mixdiy.com/?p=591

据俄罗斯媒体报道,中国和俄罗斯生物学家在吉尔吉斯斯坦发现一种可以消灭癌细胞的植物该植物是一种长径藤类植物,科学家研究其分子组成显示,它具有强大的抗氧化和抗癌特性。

报道称:“科学家们在长梗菟葵(Eranthis longistipitata)叶子中发现几种有用分子。长梗菟葵是毛茛科中的一个小属,为多年生早春类短命植物,有块状根状茎。”

]]>
591 0 0 0
<![CDATA[树莓派官方推出 64 位版本,更好支持 4GB 内存以上硬件 ]]> http://mixdiy.com/index.php/2022/02/05/64bit-rpi/ Sat, 05 Feb 2022 14:54:38 +0000 http://mixdiy.com/?p=594

树莓派 Raspberry Pi 的官方操作系统 Raspberry Pi OS 此前一直是 32 位版本,近日,树莓派基金会宣布,其专为迷你 DIY 计算机设计的 Raspberry Pi OS 现已推出 64 位版本。

64 位的主要好处是内存超过 4GB 的硬件可以得到充分利用。在所有 Raspberry Pi 计算机中,Raspberry Pi 1、Pi 2 和 Zero 支持 32 位操作系统,而 Zero 2、Pi 3 和 Pi 4 都能够运行 64 位操作系统。

官方表示,由于一些闭源软件仅适用于 arm64 硬件,新推出的 64 位版本系统能够更好地运行这些软件。

IT之家了解到,Raspberry Pi OS 此前还宣布创建一个“新的”Debian 分支系统,并提供长期支持。该版本有以下特性:

  • 基于 Buster 的 Raspberry Pi OS 的先前版本
  • 硬件加速的 Chromium 被移除并替换为上游软件浏览器
  • Linux 内核分支于 5.10.y 并且只从 Linux内核中获取安全补丁
  • 树莓派固件分支,只为现有产品提供安全和硬件支持补丁
]]>
594 0 0 0
<![CDATA[申请免费 SSL 泛域名证书 ]]> http://mixdiy.com/index.php/2022/02/08/gethttpsca/ Mon, 07 Feb 2022 23:59:31 +0000 http://mixdiy.com/?p=611

为了在您的网站上启用 HTTPS,您需要从证书颁发机构(CA)获取证书(一种文件)。Let’s Encrypt 是一个证书颁发机构(CA)。要从 Let’s Encrypt 获取您网站域名的证书,您必须证明您对域名的实际控制权。您可以在您的 Web 主机上运行使用 ACME 协议的软件来获取 Let’s Encrypt 证书。

这里介绍由国人利用 shell 脚本开发的 acme.sh 脚本,这脚本使用方便,兼容各种 Linux 系统。他本身就是一个 shell 脚本,仅仅依赖 curl,acme.sh 实现了 acme 协议, 可以从 letsencrypt 生成免费的证书。

作为家庭部署 WEB SSL 业务来说,面临最大的问题就是没有 80 端口,这令申请证书尤为困难。而 acme.sh 最后一种申请证书的模式 --dns 模式,它支持 41 个域名商的 API,可以实现全自动利用 API 来生成 dns txt 解析,从而实现域名鉴权工作。

acme.sh 使用方法

acme.sh 安装

安装很简单, 一个命令:

curl https://get.acme.sh | sh

更高级的安装选项请参考: 
https://github.com/Neilpang/acme.sh/wiki/How-to-install

acme.sh 生成证书

这里我只列举 --dns 的使用方法。使用阿里云域名解析。

前往阿里云申请 Access keys

将 Access Key ID 和 Access Key Secret 记录下来备用

使用 acme.sh--dns模块获取 Letsencrypt 证书

1.首先录入阿里云的 Access Key ID 和 Access Key Secret 到环境变量。

$ export Ali_Key="xxxxxxxxxxxx" 
$ export Ali_Secret="xxxxxxxxxxxxxxxx"

2.执行 acme.sh 命令申请证书

acme.sh --issue --dns dns_ali -d example.com

申请完毕后可以到 ~/.acme.sh/example.com 查看你的证书

Docker 使用 acme.sh

acme.sh 作者做了个 13M的容器 neilpang/acme.sh

拉取镜像

$ docker pull neilpang/acme.sh

创建 acme.sh 证书保存目录

$ mkdir ~/acme.sh

运行 acme.sh 容器

$ docker run --rm \
-v ~/acme.sh:/acme.sh \
-e Ali_Key="xxxxxxxxxxxx" \
-e Ali_Secret="xxxxxxxxxxxx" \
neilpang/acme.sh --issue --dns dns_ali -d example.com

这一步有时候是因为 
https://acme-v01.api.letsencrypt.org 无法访问导致报错,这时候可以多尝试几次,控制台输出 Sleep 120 seconds for the txt records to take effect 的时候,就证明正在设置 txt 解析,等120秒解析生效就可以申请证书下来了。

2. 以守护进程的方式运行 acme.sh 容器

$ docker run --name acme.sh \
-d --restart unless-stopped \
-v ~/acme.sh:/acme.sh \
neilpang/acme.sh daemon

至此 acme.sh 证书到60天就会自动更新。

]]>
611 0 0 0
<![CDATA[rpipico pcb]]> http://mixdiy.com/index.php/2022/02/08/rpipico-pcb/ Tue, 08 Feb 2022 14:14:48 +0000 http://mixdiy.com/?p=623 ]]> 623 0 0 0 <![CDATA[树莓派添加环境变量]]> http://mixdiy.com/index.php/2022/02/08/rpipath/ Tue, 08 Feb 2022 15:59:18 +0000 http://mixdiy.com/?p=626

环境变量
一、查看环境变量
env
二、查看PATH
export
或者单独查看PATH环境变量
echo $PATH
三、修改PATH
1、直接用export命令:
export PATH=$PATH:你的路径
查看是否已经设好,命令如上
2、修改profile文件:
vim /etc/profile
在里面加入:
export PATH="$PATH:你的路径"
3. 修改.bashrc文件:
vim /root/.bashrc
在里面加入:
export PATH="$PATH:你的路径"
后两种方法一般需要重新注销系统才能生效,然后查看PATH环境变量:
echo $PATH
一个“一劳永逸”的办法是把这个路径加入环境变量。命令 “PATH=$PATH:路径”可以把这个路径加入环境变量,但是退出这个命令行就失效了。要想永久生效,需要把这行添加到环境变量文件里。有两个文件可选:“/etc/profile”和用户主目录下的“.bash_profile”,“/etc/profile”对系统里所有用户都有效,用户主目录下的“.bash_profile”只对这个用户有效。
sudo vim /etc/profile
在文件最后加上
PATH=$PATH:你的路径
比如我的路径是/home/pi/.local/bin,则:
PATH=$PATH:/home/pi/.local/bin

查看路径
查看软件路径方法(以python为例):
方法1:whereis python
查看所有python的路径,不止一个
方法2:which python
查看当前使用的python路径

]]>
626 0 0 0
<![CDATA[开机启动多线程图形GPIO]]> http://mixdiy.com/index.php/2022/02/09/power-on-start-gui-gpio/ Tue, 08 Feb 2022 17:21:21 +0000 http://mixdiy.com/?p=630 ]]> 630 0 0 0 <![CDATA[自定义树莓派开机画面]]> http://mixdiy.com/index.php/2022/02/09/startup-rpi/ Wed, 09 Feb 2022 03:44:00 +0000 http://mixdiy.com/?p=635

https://blog.csdn.net/aphero/article/details/43124689

https://www.freesion.com/article/3137879416/

https://www.pianshen.com/article/22171676330/

https://www.cnblogs.com/gzggyy/archive/2012/08/07/2626574.html

https://shumeipai.nxez.com/2018/12/16/customize-the-boot-startup-screen-of-the-raspberry-pi.html

https://www.jianshu.com/p/dcddafcaeab2

启动时去掉树莓派logo、打印信息等,只需要修改boot下的cmdline.txt,具体如下:

sudo nano /boot/cmdline.txt

-将“console=tty1”替换为“console=tty3”,将引导消息重定向到第三控制台-添加“loglevel=3”以禁用非关键内核日志消息,如果文本光标也需要隐藏,那么在/boot/cmdline.txt文件中添加“vt.global_cursor_default=0”。

logo.nologo 作用: 去掉raspberry Logo

更改后,要进入控制台,只需在引导过程中或引导后按Alt+F3。

]]>
635 0 0 0 修改树莓派的开机启动画面]]>
<![CDATA[将树莓派CM4的WiFi天线配置为外置天线]]> http://mixdiy.com/index.php/2022/02/09/cm4-wifi-switch/ Wed, 09 Feb 2022 07:34:28 +0000 http://mixdiy.com/?p=648

树莓派 CM4 带 WiFi 的型号除了在 PCB 上内置了天线之外,还预留了一个天线插座,用于安插外置天线。有的用户在安装了外置天线之后,发现 WiFi 信号并没有改善,这可能是因为还没有手动将 CM4 的天线从内置的 PCB 天线切换到外置天线上。

CM4 在设计时考虑到兼容性,出厂默认系统是选用的内置 PCB 天线。下面来介绍如何进行设置,切换到外置 WiFi 天线。

硬件准备

CM4 + CM4IO 开发板 X 1
树莓派 CM4 天线(Antenna Kit) X 1

编辑 /boot/config.txt 文件。

sudo nano /boot/config.txt

在文件末尾加入一行配置:

dtparam=ant2

然后重启 CM4 让配置生效即可。

]]>
648 0 0 0
<![CDATA[宝马开机程序]]> http://mixdiy.com/index.php/2022/02/09/1920400program/ Wed, 09 Feb 2022 07:57:03 +0000 http://mixdiy.com/?p=654 ]]> 654 0 0 0 <![CDATA[图标资源 ico]]> http://mixdiy.com/index.php/2022/02/09/ico/ Wed, 09 Feb 2022 10:29:32 +0000 http://mixdiy.com/?p=659 ]]> 659 0 0 0 <![CDATA[添加AppImage程序到Ubuntu桌面]]> http://mixdiy.com/index.php/2022/02/09/install-appimage-to-desktop/ Wed, 09 Feb 2022 14:14:19 +0000 http://mixdiy.com/?p=663

ubuntu下将appimage程序添加到桌面,下面以fritzing0.9.9版本为例,该版本没有提供deb或其它安装方式,如需添加到桌面,可按下列方式进行:

新建一个文件fritz.desktop
sudo nano ~/.local/share/applications/fritz.desktop

输入以下内容
[Desktop Entry]
Encoding=UTF-8
Name=fritz
Exec=/home/peter/下载/fritzing-0.9.9-l348-f0af53a9.appimage
Icon=/home/peter/下载/Empty.ico
Terminal=false
Type=Application
Categories=Internet;
原firtzing0.9.6的设置
~/.local/share/applications$ cat org.fritzing.Fritzing.desktop
[Desktop Entry]
Name=Fritzing
GenericName=Electronic Design Automation software
GenericName[fi]=Elektroniikan suunnitteluohjelma
GenericName[ru]=Программа проектирования электроники
Comment=Create circuits and lay out printed circuit boards
Comment[fi]=Suunnittele elektronisia kytkentöjä ja piirilevyjä
Comment[ru]=Создавайте схемы и выкладывайте печатные платы
Exec=Fritzing %F
Icon=/home/peter/.local/share/icons/hicolor/128x128/apps/fritzing.png
Terminal=false
Type=Application
Categories=Development;IDE;Electronics;
Keywords=EDA;PCB;prototype;circuit;schematic;
StartupNotify=true
MimeType=application/x-fritzing-fz;application/x-fritzing-fzz;application/x-fritzing-fzp;application/x-fritzing-fzpz;application/x-fritzing-fzb;application/x-fritzing-fzbz;application/x-fritzing-fzm;
]]>
663 0 0 0
<![CDATA[性能最高提升 1400%,树莓派 32 位/64 位系统对比测试]]> http://mixdiy.com/index.php/2022/02/10/32pk64/ Thu, 10 Feb 2022 14:11:22 +0000 http://mixdiy.com/?p=668

上周,树莓派发布了用户期待已久的 64 位 Raspberry Pi OS,从理论上来说 64 位的操作系统能够更好地利用 64 位处理器所带来的优势(较新版本的树莓派已采用 64 位处理器)、可以支持 4GB 以上的内存(树莓派 4B 最高配备 8GB 内存),以及在处理多媒体内容时能够有更佳的表现。

虽然理论上是这么说,那么 32 位和 64 位在实际测试中表现如何呢?近日外媒 Phoronix 对两者进行了详细测试,让我们一起看看从 Raspberry Pi OS 32 位转换到 64 位后的测试数据和性能表现。

为了测试公平起见,消除因处理器或内存不同引起的结果差异,本次测试使用的是 Raspberry Pi 400,该设备的硬件配置如下:

  • CPU:博通 BCM2711 四核 Cortex-A72 (ARM v8) 64-bit SoC @ 1.8GHz
  • 内存:4GB LPDDR4-3200
  • 存储:SanDisk 16GB
  • 解码:H.265 (4K@60)、H.264 (1080P@60, 1080P@30)、OpenGL ES 3.0

本次测试使用的 Linux 内核版本为 5.10,测试中使用的软件包版本均相同,测试唯一的区别只是从最新的 Raspberry Pi OS 32 位切换到 64 位。

在首先进行的 WebP 图像编码测试中(分数越低越好,这是对 Google 的 libwebp 的测试,使用 cwebp 图像编码实用程序),无论是在默认编码设置下,还是在质量为 100 的编码设置下,64 位系统的编码速度比 32 位快 4.5% — 7% 左右,两者仅有较小的差距。

在接下来的 GraphicsMagick 测试中(分数越高越高,对 GraphicsMagick 及其 OpenMP 实现的测试),32 位和 64 位的测试成绩就有了比较明显的差别,其中在进行 HWB 色彩空间测试时两者差距最大,64 位相比 32 位快了约 47%,而在进行 Swirl 操作时也有约 32% 的性能差距。除此之外,在进行旋转、锐化和高斯噪声处理时,也有 17% — 28% 左右的性能差距。

在 FLAC 音频编码测试中(分数越低越好,计算示例 WAV 文件编码为 FLAC 格式所需的时间64 位比 32 位也提升了约 17%LAME MP3 编码测试中(分数越低越好,计算将 WAV 文件编码为 MP3 格式所需的时间),64 位提升了约 43%。

在前面的测试中也能看出,64 位相比 32 位在某些测试场景下有了 40% 以上的性能提升,而在 Stress-NG 测试中(分数越高越好,Stress-NG 是 Linux 压力测试工具,可以对 CPU、Memory、IO、磁盘进行测试),两者的性能差距更是进一步被放大,其中在 Vector Math 中 64 位的性能提升幅度达到了约 232%;在 Glibc C String Functions 测试中,也提升了约 196%。

除了多媒体编码和系统的压力测试以外,也专门对 Python 和 PHP 的性能进行了测试。其中在 PyBench 测试中(分数越低越好,PyBench 报告了不同函数的平均测试时间,提供了对 Python 在系统上的平均性能的估计64 位相比 32 位提升了约 13%;而在 PHPBench 测试中(分数越高越好,PHPBench 执行大量测试,以便对 PHP 解释器的各个方面进行评估),64 位性能则是提升了约 54%。

虽然上述的测试结果已经表明在同等测试环境下,64 位操作系统的性能相比 32 位有了大幅提升,但这还不是所有测试环节中性能差距的最大体现。

在 Sysbench 测试中(分数越高越好,Sysbench 是一个基于 LuaJIT 的多线程基准测试工具,专门测试 CPU 和内存),64 位处理器 + 64 位操作系统的优势被最大化,64 位的性能提升幅度达到了惊人的 1380%

我们在这里仅仅是从几十种不同的负载测试中挑出了一些具有代表性的测试,但纵观全部测试结果,将 Raspberry Pi OS 切换至 64 位版本后,其平均性能提高了约 48%。想要查看所有测试数据的用户可以访问 Phoronix 官网。

相比其他的 Linux 发行版,Raspberry Pi OS 直到现在才推出 64 位系统,确实是姗姗来迟。但从测试结果来看,64 位系统这么大的提升幅度让用户也没有白等。看到这里你

]]>
668 0 0 0
<![CDATA[树莓派已支持直接从网络安装系统]]> http://mixdiy.com/index.php/2022/02/10/installfromnet/ Thu, 10 Feb 2022 14:17:35 +0000 http://mixdiy.com/?p=671

对于刚拿到的树莓派来说,安装操作系统通常需要借助另一台电脑。但是,如果用户一开始没有另一台电脑,该怎么把操作系统安装到 SD 卡上呢?

近日,树莓派官方解决了这一问题,在最新的 Beta 版 Raspberry Pi bootloader 中,树莓派可以直接通过网络安装操作系统了。只需要一根网线,就能从互联网上下载系统并进行安装。

据官方介绍,新的网络安装功能可直接在 Raspberry Pi 4 或 Raspberry Pi 400 上启动 Raspberry Pi Imager 应用程序。Raspberry Pi Imager 应用程序将在 Raspberry Pi 的内存中运行,然后像正常情况一样,将操作系统安装到空白 SD 卡或 USB 磁盘上。

据了解,用户目前需要安装 Beta 版 Raspberry Pi bootloader 才能使用这一功能,未来将为所有树莓派出厂内置该功能。具体安装方法,可以点此查看官方教程。

]]>
671 0 0 0
<![CDATA[工业和信息化领域数据安全管理办法(试行)]]> http://mixdiy.com/index.php/2022/02/11/iig/ Fri, 11 Feb 2022 13:08:41 +0000 http://mixdiy.com/?p=675

(公开征求意见稿)

第一章 总则 

第一条【目的依据】为规范工业和信息化领域数据处理活动,加强数据安全管理,保障数据安全,促进数据开发利用,保护个人、组织的合法权益,维护国家安全和发展利益,根据《中华人民共和国数据安全法》《中华人民共和国网络安全法》《中华人民共和国个人信息保护法》《中华人民共和国国家安全法》《中华人民共和国民法典》等法律法规,制定本办法。 

第二条【适用范围】在中华人民共和国境内开展的工业和信息化领域数据处理活动及其安全监管,应当遵守相关法律、行政法规和本办法的要求。 

第三条【数据定义】工业和信息化领域数据包括工业数据、电信数据和无线电数据。工业数据是指工业各行业各领域在研发设计、生产制造、经营管理、运行维护、平台运营等过程中产生和收集的数据。 

电信数据是指在电信业务经营活动中产生和收集的数据。 

无线电数据是指在开展无线电业务活动中产生和收集的无线电频率、台(站)等电波参数数据。 

工业和信息化领域数据处理者是指对工业和信息化领域数据进行收集、存储、使用、加工、传输、提供、公开等数据处理活动的工业企业、软件和信息技术服务企业、取得电信业务经营许可证的电信业务经营者和无线电频率、台(站)使用单位等工业和信息化领域各类主体。 

第四条【监管机构】在国家数据安全工作协调机制统筹协调下,工业和信息化部负责督促指导各省、自治区、直辖市及计划单列市、新疆生产建设兵团工业和信息化主管部门(以下统称地方工业和信息化主管部门),各省、自治区、直辖市通信管理局(以下统称地方通信管理局)和各省、自治区、直辖市无线电管理机构(以下统称地方无线电管理机 

构)开展数据安全监管,对工业和信息化领域数据处理者的数据处理活动和安全保护进行监督管理。 

地方工业和信息化主管部门负责对本地区工业数据处理者的数据处理活动和安全保护进行监督管理。地方通信管理局负责对本地区电信数据处理者的数据处理活动和安全保护进行监督管理。地方无线电管理机构负责对本地区无线电数据处理者的数据处理活动和安全保护进行监督管理。 

工业和信息化部及地方工业和信息化主管部门、通信管理局、无线电管理机构统称为行业(领域)监管部门。 

行业(领域)监管部门依照有关法律、行政法规的规定,依法配合有关部门开展的数据安全监管相关工作。 

第五条【产业发展】行业(领域)监管部门鼓励数据开发利用和数据安全技术研究,支持推广数据安全产品和服务,培育数据安全企业、研究和服务机构,发展数据安全产业,提升数据安全保障能力,促进数据的创新应用。 

工业和信息化领域数据处理者研究、开发、使用数据新技术、新产品、新服务,应当有利于促进经济社会和行业发展,符合社会公德和伦理。 

第六条【标准制定】行业(领域)监管部门推进工业和信息化领域数据开发利用和数据安全标准体系建设,组织开展行业相关标准制修订工作。鼓励支持企业、研究机构、高等院校、行业组织等不同主体,合作开展国际标准、国家标准、行业标准、团体标准、企业标准制定。引导工业和信息化领域数据处理者开展数据管理、数据安全贯标达标工作。 

第二章 数据分类分级管理 

第七条【分类分级工作要求】工业和信息化部组织制定工业和信息化领域数据分类分级、重要数据和核心数据识别认定、数据分级防护等标准规范,指导开展数据分类分级管理工作,制定行业重要数据和核心数据具体目录并实施动态管理。 

地方工业和信息化主管部门、通信管理局、无线电管理机构组织开展本地区工业和信息化领域数据分类分级管理及重要数据和核心数据识别工作,确定本地区行业(领域)重要数据和核心数据具体目录并上报工业和信息化部,目录发生变化的,应当及时上报更新。 

工业和信息化领域数据处理者应当定期梳理数据,按照相关标准规范识别重要数据和核心数据并形成目录。 

第八条【分类分级方法】根据行业要求、特点、业务需求、数据来源和用途等因素,工业和信息化领域数据分类类别包括但不限于研发数据、生产运行数据、管理数据、运维数据、业务服务数据等。 

根据数据遭到篡改、破坏、泄露或者非法获取、非法利用,对国家安全、公共利益或者个人、组织合法权益等造成的危害程度,工业和信息化领域数据分为一般数据、重要数据和核心数据三级。 

工业和信息化领域数据处理者可在此基础上细分数据的类别和级别。 

第九条【一般数据】危害程度符合下列条件之一的数据为一般数据: 

(一)对公共利益或者个人、组织合法权益造成较小影响,社会负面影响小; 

(二)受影响的用户和企业数量较少、生产生活区域范围较小、持续时间较短,对企业经营、行业发展、技术进步和产业生态等影响较小; 

(三)其他未纳入重要数据、核心数据目录的数据。 

第十条【重要数据】危害程度符合下列条件之一的数据为重要数据: 

(一)对政治、国土、军事、经济、文化、社会、科技、电磁、网络、生态、资源、核安全等构成威胁,影响海外利益、生物、太空、极地、深海、人工智能等与国家安全相关的重点领域;

(二)对工业和信息化领域发展、生产、运行和经济利益等造成严重影响; 

(三)造成重大数据安全事件或生产安全事故,对公共利益或者个人、组织合法权益造成严重影响,社会负面影响大; 

(四)引发的级联效应明显,影响范围涉及多个行业、区域或者行业内多个企业,或者影响持续时间长,对行业发展、技术进步和产业生态等造成严重影响; 

(五)经工业和信息化部评估确定的其他重要数据。 

第十一条【核心数据】危害程度符合下列条件之一的数据为核心数据: 

(一)对政治、国土、军事、经济、文化、社会、科技、电磁、网络、生态、资源、核安全等构成严重威胁,严重影响海外利益、生物、太空、极地、深海、人工智能等与国家安全相关的重点领域; 

(二)对工业和信息化领域及其重要骨干企业、关键信息基础设施、重要资源等造成重大影响; 

(三)对工业生产运营、电信网络(含互联网)运行和服务、无线电业务开展等造成重大损害,导致大范围停工停产、大面积无线电业务中断、大规模网络与服务瘫痪、大量业务处理能力丧失等; 

(四)经工业和信息化部评估确定的其他核心数据。 

第十二条【重要数据和核心数据目录备案】工业和信息化领域数据处理者应当将本单位重要数据和核心数据目录向地方工业和信息化主管部门(工业领域)或通信管理局(电信领域)或无线电管理机构(无线电领域)备案。备案内容包括但不限于数据类别、级别、规模、处理目的和方式、使用范围、责任主体、对外共享、跨境传输、安全保护措施等 

基本情况,不包括数据内容本身。 

地方工业和信息化主管部门(工业领域)或通信管理局(电信领域)或无线电管理机构(无线电领域)应当在工业和信息化领域数据处理者提交备案申请的二十个工作日内完成审核工作,备案内容符合要求的,予以备案并发放备案凭证,同时将备案情况报工业和信息化部;不予备案的应当及时反馈备案申请人并说明理由。 

重要数据和核心数据的类别或规模变化 30%以上的,或者其它备案内容发生重大变化的,工业和信息化领域数据处 

理者应当在发生变化的三个月内履行备案变更手续。 

第三章 数据全生命周期安全管理 

第十三条【主体责任】工业和信息化领域数据处理者应当对数据处理活动负安全主体责任,对各类数据实行分级防护,不同级别数据同时被处理且难以分别采取保护措施的,应当按照其中级别最高的要求实施保护,确保数据持续处于有效保护和合法利用的状态。 

(一)建立数据全生命周期安全管理制度,针对不同级别数据,制定数据收集、存储、使用、加工、传输、提供、公开等环节的具体分级防护要求和操作规程; 

(二)根据需要配备数据安全管理人员,统筹负责数据处理活动的安全监督管理,协助行业(领域)监管部门开展工作; 

(三)合理确定数据处理活动的操作权限,严格实施人员权限管理; 

(四)根据应对数据安全事件的需要,制定应急预案,并定期进行演练; 

(五)定期对从业人员开展数据安全教育和培训; 

(六)法律、行政法规等规定的其他措施。 

工业和信息化领域重要数据和核心数据处理者,还应当: 

(一)建立覆盖本单位相关部门的数据安全工作体系,明确数据安全负责人和管理机构,建立常态化沟通与协作机制。本单位法定代表人或者主要负责人是数据安全第一责任人,领导团队中分管数据安全的成员是直接责任人; 

(二)明确数据处理关键岗位和岗位职责,并要求关键岗位人员签署数据安全责任书; 

(三)建立内部登记、审批机制,对重要数据和核心数据的处理活动进行严格管理并留存记录。 

第十四条【数据收集】工业和信息化领域数据处理者收集数据应当遵循合法、正当的原则,不得窃取或者以其他非法方式收集数据。 

数据收集过程中,应当根据数据安全级别采取相应的安全措施,加强重要数据和核心数据收集人员、设备的管理,并对收集时间、类型、数量、频度、流向等进行记录。 

通过间接途径获取重要数据和核心数据的,工业和信息化领域数据处理者应当与数据提供方通过签署相关协议、承诺书等方式,明确双方法律责任。 

第十五条【数据存储】工业和信息化领域数据处理者应当依据法律规定或者与用户约定的方式和期限存储数据。存储重要数据和核心数据的,应当采用校验技术、密码技术等措施进行安全存储,不得直接提供存储系统的公共信息网络访问,并实施数据容灾备份和存储介质安全管理,定期开展数据恢复测试。存储核心数据的,还应当实施异地容灾备份。 

第十六条【数据使用加工】工业和信息化领域数据处理者利用数据进行自动化决策分析的,应当保证决策分析的透明度和结果公平合理。使用、加工重要数据和核心数据的,还应当加强访问控制。工业和信息化领域数据处理者提供数据处理服务,涉及经营电信业务的,应当按照相关法律、行政法规规定取得电信业务经营许可。 

第十七条【数据传输】工业和信息化领域数据处理者应当根据传输的数据类型、级别和应用场景,制定安全策略并采取保护措施。传输重要数据和核心数据的,应当采取校验技术、密码技术、安全传输通道或者安全传输协议等措施。 

第十八条【数据提供】工业和信息化领域数据处理者提供数据,应当明确提供的范围、类别、条件、程序等,并与数据获取方签订数据安全协议。提供重要数据和核心数据的,应当对数据获取方数据安全保护能力进行评估或核实,采取必要的安全保护措施。 

第十九条【数据公开】工业和信息化领域数据处理者应当在数据公开前分析研判可能对公共利益、国家安全产生的影响,存在重大影响的不得公开。 

第二十条【数据销毁】工业和信息化领域数据处理者应当建立数据销毁制度,明确销毁对象、规则、流程和技术等要求,对销毁活动进行记录和留存。个人、组织依据法律规定、合同约定等请求销毁的,工业和信息化领域数据处理者应当销毁相应数据。 

销毁重要数据和核心数据的,应当及时向地方工业和信息化主管部门(工业领域)或通信管理局(电信领域)或无线电管理机构(无线电领域)更新备案,不得以任何理由、任何方式对销毁数据进行恢复。 

第二十一条【数据出境】工业和信息化领域数据处理者在中华人民共和国境内收集和产生的重要数据和核心数据,法律、行政法规有境内存储要求的,应当在境内存储,确需向境外提供的,应当依法依规进行数据出境安全评估。工业和信息化部根据有关法律和中华人民共和国缔结或者参加的国际条约、协定,或者按照平等互惠原则,处理外国工业、电信、无线电执法机构关于提供工业和信息化领域数据的请求。非经工业和信息化部批准,工业和信息化领域数据处理者不得向外国工业、电信、无线电执法机构提供存储于中华人民共和国境内的工业和信息化领域数据。 

第二十二条【数据转移】工业和信息化领域数据处理者因兼并、重组、破产等原因需要转移数据的,应当明确数据转移方案,并通过电话、短信、邮件、公告等方式通知受影响用户。涉及重要数据和核心数据的,应当及时向地方工业和信息化主管部门(工业领域)或通信管理局(电信领域)或无线电管理机构(无线电领域)更新备案。 

第二十三条【委托处理】工业和信息化领域数据处理者委托他人开展数据处理活动的,应当通过签订合同协议等方式,明确委托方与被委托方的数据安全责任和义务。委托处理重要数据和核心数据的,应当对被委托方的数据安全保护能力、资质进行评估或核实。 

除法律、行政法规等另有规定外,未经委托方同意,被委托方不得将数据提供给第三方。 

第二十四条【核心数据跨主体处理】跨主体提供、转移、委托处理核心数据的,应当评估安全风险,采取必要的安全保护措施,并经由地方工业和信息化主管部门(工业领域)或通信管理局(电信领域)或无线电管理机构(无线电领域)报工业和信息化部。工业和信息化部按照有关规定进行审查。 

第二十五条【日志留存】工业和信息化领域数据处理者应当在数据全生命周期处理过程中,记录数据处理、权限管理、人员操作等日志。日志留存时间不少于六个月。 

第四章 数据安全监测预警与应急管理 

第二十六条【监测预警机制】工业和信息化部建立数据安全风险监测机制,组织制定数据安全监测预警接口和标准,统筹建设数据安全监测预警技术手段,形成监测、溯源、预警、处置等能力,与相关部门加强信息共享。 

地方工业和信息化主管部门、通信管理局和无线电管理机构建设本地区数据安全监测预警机制,组织开展本地区工业、电信行业和无线电数据安全风险监测,按照有关规定及时发布预警信息,通知本地区工业和信息化领域数据处理者及时采取应对措施。 

工业和信息化领域数据处理者应当开展数据安全风险监测,及时排查安全隐患,采取必要的措施防范数据安全风险。 

第二十七条【信息上报和共享】工业和信息化部建立数据安全风险信息上报和共享机制,统一汇集、分析、研判、通报数据安全风险信息,鼓励安全服务机构、行业组织、科研机构等开展数据安全风险信息上报和共享。 

地方工业和信息化主管部门、通信管理局和无线电管理机构汇总分析本地区工业、电信行业和无线电数据安全风险,及时将可能造成重大及以上安全事件的风险上报工业和信息化部。 

工业和信息化领域数据处理者应当及时将可能造成较大及以上安全事件的风险向地方工业和信息化主管部门(工业领域)或通信管理局(电信领域)或无线电管理机构(无线电领域)报告。 

第二十八条【应急处置】工业和信息化部制定工业和信息化领域数据安全事件应急预案,组织协调重要数据和核心数据安全事件应急处置工作。 

地方工业和信息化主管部门、通信管理局和无线电管理机构组织开展本地区工业、电信行业和无线电数据安全事件应急处置工作。涉及重要数据和核心数据的安全事件,应当立即上报工业和信息化部,并及时报告事件发展和处置情况。 

工业和信息化领域数据处理者在数据安全事件发生后,应当按照应急预案,及时开展应急处置,涉及重要数据和核心数据的安全事件,应当第一时间向地方工业和信息化主管部门(工业领域)或通信管理局(电信领域)或无线电管理机构(无线电领域)报告。事件处置完成后应当在规定期限内形成总结报告,每年向地方工业和信息化主管部门(工业领域)或通信管理局(电信领域)或无线电管理机构(无线电领域)报告数据安全事件处置情况。 

工业和信息化领域数据处理者对可能损害用户合法权益的数据安全事件,应当及时告知用户,并提供减轻危害措施。 

第二十九条【举报投诉处理】工业和信息化部委托相关行业组织建立工业和信息化领域数据安全违法行为投诉举报渠道,地方工业和信息化主管部门、通信管理局、无线电管理机构建立本地区工业、电信行业和无线电数据安全违法行为投诉举报机制或渠道,依法接收、处理投诉举报,根据工作需要开展执法调查。鼓励工业和信息化领域数据处理者 

建立用户投诉处理机制。 

第五章 数据安全检测、认证、评估管理 

第三十条【安全检测与认证】工业和信息化部鼓励、引导具备相应资质的机构,依据相关标准开展行业数据安全检测、认证工作。 

第三十一条【安全评估】工业和信息化部制定行业数据安全评估机构管理制度,开展评估机构管理工作。制定行业数据安全评估规范,指导评估机构开展数据安全风险评估、合规评估、能力评估、出境评估等工作。 

地方工业和信息化主管部门、通信管理局和无线电管理机构负责组织开展本地区工业、电信行业和无线电数据安全评估工作。 

工业和信息化领域重要数据和核心数据处理者应当自行或委托第三方评估机构,每年至少开展一次安全评估,及时整改风险问题,并向地方工业和信息化主管部门(工业领域)或通信管理局(电信领域)或无线电管理机构(无线电领域)报送评估报告。 

第六章 监督检查 

第三十二条【监督检查和协助义务】行业(领域)监管部门对工业和信息化领域数据处理者落实本办法要求的情况进行监督检查。 

工业和信息化领域数据处理者应当对行业(领域)监管部门监督检查予以配合。 

第三十三条【数据安全审查】工业和信息化部在国家数据安全工作协调机制指导下,开展数据安全审查相关工作。 

第三十四条【保密要求】行业(领域)监管部门及其委托的数据安全评估机构工作人员对在履行职责中知悉的个人信息和商业秘密等,应当严格保密,不得泄露或者非法向他人提供。 

第七章 法律责任 

第三十五条【约谈整改】行业(领域)监管部门在履行数据安全监督管理职责中,发现数据处理活动存在较大安全风险的,可以按照规定权限和程序对工业和信息化领域数据处理者进行约谈,并要求采取措施进行整改,消除隐患。 

第三十六条【法律责任】有违反本办法规定行为的,由行业(领域)监管部门依照相关法律法规,根据情节严重程度给予没收违法所得、罚款、暂停业务、停业整顿、吊销业务许可证等行政处罚;构成犯罪的,依法追究刑事责任。 

第八章 附则 

第三十七条【个人信息保护】开展涉及个人信息的数据处理活动,还应当遵守有关法律、行政法规的规定。 

第三十八条【其他规定参照】涉及军事、国家秘密信息、密码使用等数据处理活动,按照国家有关规定执行。 

第三十九条【政务数据排除】工业和信息化领域政务数据处理活动的具体办法,由工业和信息化部另行规定。 

第四十条【国防科工、烟草领域】国防科技工业、烟草领域数据安全管理由国防科工局、国家烟草专卖局负责,具体制度参照本办法另行制定。 

第四十一条【施行日期】本办法自 2022 年 月 日起施行。

]]>
675 0 0 0
<![CDATA[无需任何电脑,从网络安装树莓派操作系统]]> http://mixdiy.com/index.php/2022/02/11/boot-from-internet/ Fri, 11 Feb 2022 13:45:50 +0000 http://mixdiy.com/?p=680

大部分时候,我们都是用另一台计算机(比如运行 Raspberry Pi Imager写入镜像)来给树莓派装系统的。

但是,如果你一开始没有PC,如何将操作系统安装到 SD 卡上呢?

这是经典的先有鸡还是先有蛋的问题,我们刚刚解决了它。

新的网络安装程序正在运行

现在有一个实现网络安装的 Raspberry Pi 引导加载程序(beta版),希望你能帮助我们进行测试。

下载地址:

https://www.raspberrypi.com/software/

新的网络安装功能可直接在Raspberry Pi 4或Raspberry Pi 400上启动Raspberry Pi Imager应用程序。

注:要用以太网电缆从 Internet 下载。


Raspberry Pi Imager 应用程序将在你的 Raspberry Pi 内存中运行,然后把操作系统写入空白 SD 卡或 USB 磁盘,就像平常一样。


如何使用网络安装程序

如果你想试用这个网络安装程序,首先必须安装bootloader的测试版。

对于当前已经上市的电路板,需要更新bootloader。

不过一旦测试期结束,我们最终将在树莓派出厂时直接安装新的网络引导加载程序,不再需要此步骤。

安装新的 beta 引导加载程序

在 Raspberry Pi 4 或 400 上更新bootloader最简单方法是在 Raspberry Pi 或另一台计算机上运行Raspberry Pi Imager,将所需的软件复制到 SD 卡上。


你需要一张备用的空白 SD 卡,如果你使用的是 Raspberry Pi 或另一台没有 SD 卡插槽的计算机,则需要一个 USB to SD转接头。

你应该知道,用于更新 Raspberry Pi 引导加载程序的 SD 卡将被擦除所有现有数据,因此不要将有重要数据的 SD 卡装在当前Raspberry Pi上 。

在Imager应用程序中,单击“选择操作系统”按钮并在弹出窗口中向下滚动“操作系统”列表。选择“Misc utility images”,然后选择“Beta Test Bootloader”。

选择新的 Beta 测试引导加载程序

接下来,你需要选择引导顺序。

所有选项看上去差不多,但启动顺序不同。除非有其它理由,否则应该选择“SD Card Boot”。

选择引导顺序

然后按照正常说明将更新的引导加载程序写入SD卡。

当SD卡完成写入后,你应该关闭 Raspberry Pi 并移除现有的SD卡并将其放在安全的地方。

将 Imager 应用程序写过的卡插入 Raspberry Pi 并重新打开电源。板子 LED 会闪烁,屏幕会变绿,新的引导加载程序成功启动。

如果你想将引导加载程序恢复到“Release”版本,可以再次执行这些步骤,选择顶部的“引导加载程序”选项而不是“Beta 测试引导加载程序”选项。


现在移除你的引导加载程序更新 SD 卡并重启树莓派。

尝试网络启动

现在你已经更新了引导加载程序,一切应该和以前一样工作。

当 Raspberry Pi 启动时,引导加载程序会搜索要加载的软件。它首先查找 SD 卡,然后查找 USB 记忆棒,依此类推。

它会一直循环下去,直到找到要使用的软件。几秒钟后,你会在屏幕上看到一个诊断信息,告知它正在做什么。

如果你想插入原来的 SD 卡(你在这一切开始之前使用的那个),你的 Raspberry Pi 应该会像往常一样无缝地引导回操作系统。

如果你在插槽中没有 SD 卡或使用空白 SD 卡的情况下启动 Raspberry Pi,并且连接了键盘,那么你现在会看到一些不同的东西。

如果你连接了键盘(Raspberry Pi 400 总是如此),但 Raspberry Pi 无法找到操作系统,它将显示新的网络安装界面。

新的网络安装界面

在后台,你的 Raspberry Pi 仍在寻找可运行的操作系统。但是此时,你可以通过按住该Shift键三秒钟来启动网络安装过程。在出现提示时按确认你要继续按Space,它应该会要求你插入以太网电缆。

你需要通过以太网电缆将 Raspberry Pi 物理连接到路由器,而不是将其连接到无线网络。你需要一根两端都有公头 RJ45 连接器的以太网电缆。

大多数家用路由器的背面都有网口,可让你插入以太网电缆,因此将电缆的一端插入路由器,另一端插入树莓派。

记得插网线

当它检测到已插入电缆时,它会自动下载 Raspberry Pi Imager。如果下载失败,你可以重复该过程。

安装 Raspberry Pi Imager

最终,Raspberry Pi Imager 应用程序在树莓派上启动,允许你将完整的操作系统安装到新的空白 SD 卡或 USB 记忆棒上。

如果你还没有这样做,此时应该将另一张空白 SD 卡插入 Raspberry Pi 卡槽。Raspberry Pi Imager 允许你直接从 Internet 烧录系统。

系统装好后不会在启动时看到网络安装界面。

如果还想运行它,只需要删除所有可启动磁盘,等 Raspberry Pi Imager 运行再重新插入它们。但注意不要覆盖任何有重要工作内容的磁盘!

]]>
680 0 0 0
<![CDATA[树莓派GPIO使用指南]]> http://mixdiy.com/index.php/2022/02/14/rpi-gpio/ Mon, 14 Feb 2022 13:45:30 +0000 http://mixdiy.com/?p=693

 RPi.GPIO是 Python的一个module( 模块 ), 树莓派官方系统默认已经安装, 仍在不断更新中, 截至20180521, 最新版0.6.3, 适配了树莓派3B+, 可以访问  python主页下载源码 .      本文根据树莓派RPI.GPIO模块的官方文档翻译,当时的模块版本为0.6.3。官方的帮助文档的链接: https://sourceforge.net/p/raspberry-gpio-python/wiki/BasicUsage/

1、导入模块

要导入RPi.GPIO模块,请执行以下操作:

import RPi.GPIO  as GPIO 

通过这样做,您可以通过脚本的其余部分将其称为GPIO。

导入模块并检查它是否成功:

tryimport RPi.GPIO  as GPIO 
except RuntimeError print
"Error importing RPi.GPIO!  This is probably because you need superuser privileges.  You can achieve this by using 'sudo' to run your script"

2、引脚编号

     在RPi.GPIO中,有两种方法可以对Raspberry Pi上的IO引脚进行编号。第一种是使用BOARD编号系统。这是指Raspberry Pi板上P1接头上的引脚号。使用这种编号系统的优点是,无论树莓派的电路板版本如何,您的硬件都能正常工作。你不需要重新连接你的连接器或更改你的代码。

     第二个编号系统是BCM号码。这是一种较低级别的工作方式 - 它指的是Broadcom SOC上的通道号码。您必须始终使用那个通道编号所对应的树莓派板上哪个引脚的图表。您的脚本程序可能会在Raspberry Pi板的硬件修订后而不能使用。

        树莓派引脚有BOARD和BCM两种编号方式( 使用python时? 似乎使用C还有一种wringPi编号方式 ), BOARD具有很好的适用性( 不用看接口图,数引脚1~40就可以接线 ), 不论树莓派1 2 3, 都不用修改代码, 吼啊! BCM编号方式换个版本再接线时数引脚是不行的, 需要看下下面的接口图…不难看出推荐用BOARD编号方式. 但很多程序中使用BCM方式. 
下面给出一张树莓派2B的硬件接口图( 来源找不到了,侵删 ): 

图中的GPIOxx的方框即是BCM编码方式, 直接写数字的深灰框是BOARD编码方式, 如BCM编码方式的 GPIO02 对应BOARD编码方式的 3.

只需要使用BCM编号方式时, 用下面这两张: 


要指定您使用引脚编号方式:

GPIO.setmode(GPIO.BOARD)
  # or
GPIO.setmode(GPIO.BCM)

要检测哪个引脚编号系统已被设置模式(例如,由另一个Python模块配置过模式):

mode = GPIO.getmode()

模式将是GPIO.BOARD,GPIO.BCM或None

3、警告

您可能在Raspberry Pi的GPIO上有多个脚本/电路。因此,如果RPi.GPIO检测到引脚已被配置为默认(输入)以外的其他引脚,则在尝试配置脚本时会收到警告。要禁用这些警告:

GPIO.setwarnings(False)

4、设置一个通道

您需要设置您用作输入或输出的每个通道。将通道配置为输入:

GPIO.setup(channel, GPIO.IN)

(其中通道是基于您指定的编号系统(BOARD或BCM)的通道编号)。

有关设置输入通道的更多高级信息可以在这里找到。

要将通道设置为输出:

GPIO.setup(channel, GPIO.OUT)

(其中通道是基于您指定的编号系统(BOARD或BCM)的通道编号)。

您还可以为您的输出通道指定一个初始值:

GPIO.setup(channel, GPIO.OUT, initial=GPIO.HIGH)

4、设置多个频道

您可以一次设置多个通道(从0.5.8开始)。例如:

chan_list  =  [ 11 ,12 ]     #加你想尽可能多的渠道!
                       #你可以用元组代替,即:
                       #chan_list =(11,12)
GPIO.setup(chan_list, GPIO.OUT)

5、输入

读取GPIO引脚的值:

GPIO.input(channel)

     ( 其中通道是基于您指定的编号系统(BOARD或BCM)的通道编号)。这将返回0 / GPIO.LOW / False或1 / GPIO.HIGH / True。

     有几种方法可以将GPIO输入到您的程序中。第一种也是最简单的方法是在某个时间点检查输入值。这就是所谓的“轮询”,如果你的程序在错误的时间读取了值,可能会错过输入。轮询在循环中执行,并可能是处理器密集型的。响应GPIO输入的另一种方式是使用'中断'(边沿检测)。边沿是从高电平到低电平(下降沿)或从低电平到高电平(上升沿)的意思。

5.1 上拉/下拉电阻

     如果你没有连接到任何输入引脚,它将'浮空'。换句话说,读入的值是未定义的,因为它只有在按下按钮或开关时才会连接到任何东西。由于引脚会接收到干扰,可能读取到变化的值。

      为了解决这个问题,我们使用上拉或下拉电阻。这样,可以设置输入的默认值。可以在硬件上使用上拉/下拉电阻并使用软件。在硬件中,通常使用输入通道和3.3V(上拉)或0V(下拉)之间的10K电阻。RPi.GPIO模块允许您配置Broadcom SOC以在软件中执行此操作:

GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)
  # or
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

(其中通道是基于您指定的编号系统的通道编号 - BOARD或BCM)。

5.2 测试输入(轮询)

您可以立即读取IO引脚的输入值:

if GPIO.input(channel):
    print('Input was HIGH')
else:
    print('Input was LOW')

要通过轮询轮询等待按钮按下:

while GPIO.input(channel) == GPIO.LOW:
    time.sleep(0.01)  # wait 10 ms to give CPU chance to do other things

(这里假设按下按钮将输入从LOW改变为HIGH)

5.3 中断和边缘检测

    边沿是电信号从低电平变为高电平(上升沿)或从高电平变为低电平(下降沿)的状态变化。很多时候,我们更关心输入状态的变化而非价值。这种状态变化是一个事件。

     为了避免在程序忙于做其他事情时按下按钮,有两种方法可以解决这个问题:

  • wait_for_edge()函数
  • event_detected()函数
  • 在检测到边缘时运行线程的回调函数

wait_for_edge()函数

wait_for_edge()函数设计用于阻止程序的执行,直到检测到边缘。换句话说,上面等待按钮按下的示例可以被重写为:

GPIO.wait_for_edge(channel, GPIO.RISING)

    请注意,您可以检测GPIO.RISING,GPIO.FALLING或GPIO.BOTH类型的边沿。这样做的好处是它使用的CPU时间可以忽略不计,因此CPU还有很多工作要做。

如果您只想等待一段时间,则可以使用timeout参数:

#上升沿等待最多5秒(超时以毫秒为单位)
channel = GPIO.wait_for_edge(channel, GPIO_RISING, timeout=5000)
if channel is None:
    print('Timeout occurred')
else:
    print('Edge detected on channel', channel)

event_detected()函数

event_detected()函数设计用于与其他工作一起循环使用,但与轮询不同,在CPU忙于处理其他事情时,不会错过输入状态的变化。当使用类似Pygame或PyQt的东西时,这可能很有用,因为主循环会及时监听和响应GUI事件。

GPIO.add_event_detect(channel, GPIO.RISING)  # add rising edge detection on a channel
do_something()
if GPIO.event_detected(channel):
    print('Button pressed')

请注意,您可以检测GPIO.RISING,GPIO.FALLING或GPIO.BOTH的事件。

Threaded回调

RPi.GPIO为回调函数运行第二个线程。这意味着回调函数可以与主程序同时运行,并立即响应边缘事件。例如:

def my_callback(channel):
    print('This is a edge event callback function!')
    print('Edge detected on channel %s'%channel)
    print('This is run in a different thread to your main program')

GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback)  # add rising edge detection on a channel
...the rest of your program...

如果你想要多个回调函数:

ef my_callback_one(channel):
    print('Callback one')

def my_callback_two(channel):
    print('Callback two')

GPIO.add_event_detect(channel, GPIO.RISING)
GPIO.add_event_callback(channel, my_callback_one)
GPIO.add_event_callback(channel, my_callback_two)

请注意,在这种情况下,回调函数按顺序运行,而不是同时运行。这是因为只有一个线程用于回调,每个回调都按照定义的顺序运行。

5.4开关抖动

您可能会注意到,每次按下按钮都会多次调用回调。这是所谓的“开关抖动”的结果。处理抖动有两种方法:

  • 在开关输入脚上添加一个0.1uF的电容。
  • 软件去除抖动
  • 以上两种方法的结合

要使用软件去抖动,请将bouncetime =参数添加到指定回调函数的函数中。抖动时间应以毫秒为单位指定。例如:

#在通道上添加上升沿检测,忽略处理
   GPIO的开关抖动操作的进一步边缘200ms 
GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback, bouncetime=200)

要么

GPIO.add_event_callback(channel, my_callback, bouncetime=200)

5.5 删除事件检测

如果由于某种原因,您的程序不再希望检测边缘事件,则可以删除它们:

GPIO.remove_event_detect(channel)

6、输出

要设置GPIO引脚的输出状态,请执行以下操作:

GPIO.output(channel, state)

(其中通道是基于您指定的编号系统(BOARD或BCM)的通道编号)。

状态可以是0 / GPIO.LOW / False或1 / GPIO.HIGH / True。

A.设置输出高电平:

GPIO.output(12, GPIO.HIGH)
 # or
GPIO.output(12, 1)
 # or
GPIO.output(12, True)

B.设置输出低电平:

GPIO.output(12, GPIO.LOW)
 # or
GPIO.output(12, 0)
 # or
GPIO.output(12, False)

7、输出到几个通道

您可以一次设置输出多个频道(从0.5.8开始)。例如:

chan_list = [11,12]                             # also works with tuples
GPIO.output(chan_list, GPIO.LOW)                # sets all to GPIO.LOW
GPIO.output(chan_list, (GPIO.HIGH, GPIO.LOW))   # sets first HIGH and second LOW

8.在RPi.GPIO中使用PWM

要创建一个PWM实例:

p = GPIO.PWM(channel, frequency)

要启动PWM:

p.start(dc)   # where dc is the duty cycle (0.0 <= dc <= 100.0)

要更改频率:

p 。ChangeFrequency (freq )   #其中freq是以Hz为单位的新频率

要改变占空比:

p.ChangeDutyCycle(dc)  # where 0.0 <= dc <= 100.0

要停止PWM:

p.stop()

请注意,如果实例变量'p'超出范围,PWM也会停止。

每两秒闪烁一次LED的示例:

import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)

p = GPIO.PWM(12, 0.5)
p.start(1)
input('Press return to stop:')   # use raw_input for Python 2
p.stop()
GPIO.cleanup()

增亮/调暗LED的示例:

import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)

p = GPIO.PWM(12, 50)  # channel=12 frequency=50Hz
p.start(0)
try:
    while 1:
        for dc in range(0, 101, 5):
            p.ChangeDutyCycle(dc)
            time.sleep(0.1)
        for dc in range(100, -1, -5):
            p.ChangeDutyCycle(dc)
            time.sleep(0.1)
except KeyboardInterrupt:
    pass
p.stop()
GPIO.cleanup()

9、GPIO恢复默认

    在程序的末尾,清理您可能使用的任何资源是一种很好的做法。这与RPi.GPIO没有什么不同。通过将您用过的所使用的通道返回到到无上拉/下拉输入的状态,这样可以避免短接GPIO引脚来导致意外损坏您的树莓派。请注意,这样只会清除你写的脚本中使用的GPIO通道。请注意,GPIO.cleanup()也会清除正在使用的引脚编号系统。

       在你的脚本程序的末尾写上:

GPIO.cleanup()

当您的程序退出时,可能不希望清理每个通道,而留下一些设置。您可以清理个别通道,使用通道的元组或列表做为参数输入:

GPIO.cleanup(channel)
GPIO.cleanup( (channel1, channel2) )
GPIO.cleanup( [channel1, channel2] )

10、RPi板信息和RPi.GPIO版本

发现有关您的RPi的信息:

GPIO.RPI_INFO

发现Raspberry Pi电路板版本:

GPIO.RPI_INFO [ 'P1_REVISION']
GPIO.RPI_REVISION(不建议使用)

要发现RPi.GPIO的版本:

GPIO.VERSION

11、编写一个测试程序blinkled.py

      这个测试程序控制树莓派上一GPIO 25每2秒变化一个电平,如果接一个LED灯到这个IO上面,就会看到这个灯亮2秒灭2秒。

#!/usr/bin/python#*coding:utf-8*#GPIO控制LED灯程序import RPi.GPIO as GPIOimport timepin = 25GPIO.setmode(GPIO.BCM)GPIO.setup(pin, GPIO.OUT) while True:    GPIO.output(pin, GPIO.HIGH)    time.sleep(2)    GPIO.output(pin, GPIO.LOW)    time.sleep(2) 

     在这个blinkled.py的目录中,在命令行中执行sudo python blinkled.py就可以运行此程序,LED就会一闪一闪的了

]]>
693 0 0 0
<![CDATA[用Edge Impulse实现MCU机器学习]]> http://mixdiy.com/index.php/2022/02/18/impluse1/ Fri, 18 Feb 2022 15:16:34 +0000 http://mixdiy.com/?p=714

将AI应用带到边缘终端设备(智能手机、可穿戴、汽车和物联网设备等)可以降低功耗,并减少数据中心资源的负载,其中在MCU上实现机器学习是这种愿景中最为活跃的领域之一,tinyML是一个致力于该领域的新的研究方向。本文讲述了使用Edge Impulse训练验证模型、模型转换、生成代码并部署的过程。Edge Impulse是一个在边缘设备上进行机器学习的在线开发平台,正在获得嵌入式系统产业界的广泛支持。

业界对机器学习 的兴趣近来大增,已经有许多云应用帮助我们进行机器学习的开发。但是,如果应用缺乏互联网连接,需要处理敏感数据,或者要求低延迟,在小型微控制器上运行机器学习会更为合理。本文通过一个样例项目演示使用Edge Impulse在小型、低功耗、低成本微控制器上进行机器学习的开发。

在应用端运行机器学习(ML)算法被称为边缘计算, 比如在工业园区的一个厂房内。在工业4.0和物联网中通过边缘计算使用ML意味着数据不会离开现场,基于ML的决策也可以被快速采用。模型的训练需要大量的计算,但是我们可以在小型、高性价比的微控制器上运行这些计算。基于非对称的ML的大量研究是tinyML基金会的成立的前奏。在嵌入式设备上运行机器学习模型的主要原因有三个:

•本地处理:避免传输传感器收集的数据。不需要互联网连接的话,设备部署的限制就会更少。

•低能耗:微控制器的能耗非常低。一个电池驱动的微控制器可以持续运行图像辨识算法一年。

•成本和部署:微控制器普适而且低成本,方便ML模型的部署。通过简单的软件升级,它们可以很快部署新的或者升级后的模型,而不需要替换硬件。

tinyML依然是一个比较新的研究方向,但是其应用非常广泛,包括农业、医疗和预测性维护等。

Edge Impluse

创建账户并登录之后,你就可以创建你的第一个tinyML项目了,如图1所示,SDK的界面简洁、直观。

图1 Edge Impulse SDK仪表盘

登录之后,仪表盘(Dashboard)显示项目的概况,以及如何启动你的第一个项目,或者继续一个现有项目的指南。界面左侧,在仪表盘下方是其他的主菜单选项,它们的顺序反映了开发的不同阶段,比如设备(Devices)页显示已连接的设备列表,数据获取(Data acquisition)则显示已经收集好的测试和训练数据。Impulse设计(Impulse design)创建一个Impulse,Impulse是一个接收原始数据、运用信号处理提取特性和通过学习块对新数据进行分类的过程。

连接设备时,需要参照仪表盘画面中间的指示,也可以前往设备栏点击连接新设备(Connect a new device)。开发环境支持下列设备:

•ST IoT Discovery Kit:即B-L745E-IOT01A,开发板上搭载使用Cortex-M4处理器的微控制器、MEMS动作传感器、麦克风和WiFi。

•Arduino Nano 33 BLE Sense:一个小尺寸的开发板,上面搭载基于Cortex-M4的微控制器、动作传感器、麦克风和BLE。

•AI ECM3532 Eta Compute Sensor:一个小尺寸的开发板,上面的TENSAI ECM3532 SoC搭载基于Cortex-M3的微控制器和一个单独的CoolFlux DSP用于机器学习的加速。开发板上有两个麦克风,一个6轴加速度计/陀螺仪和一个气压/温度传感器。ECM3532 SoC支持连续电压频率缩放,这允许系统在运行时通过缩放时钟频率和电压调整到最佳的电源效率,从而支持超低功耗机器学习。

•智能手机:Edge Impulse支持能够运行现代浏览器的任何智能手机,过程和方法和其他设备一样。最终,任何数据/模型都可以部署到嵌入式设备上。

图2 设备页显示已连接的设备

收集数据

首先,需要收集用于训练机器学习模型的音频数据。为了检测到水从水龙头流出的声音,我们需要收集一些流水的声音,以及典型的背景噪声(没有流水)的声音样本,这样模型就能够学习两者间的区别。这两类样本代表着我们模型中的两个类型:背景噪声和水龙头开启。在SDK的数据获取(Data acquisition)页可以收集设备传感器数据,这里是存储原始数据的地方。如果设备已经连接到远程管理API,就可以从数据获取页开始新的数据采样。

现在让我们先收录一段水龙头没有打开时的背景噪声,在收录新数据(Record new data)部分选择数据,设置标签(Label)为噪声,定义样本长度(Sample length)为1000,传感器(Sensor)选择内建麦克风。这意味着我们将收录一秒的声音,并将其标记为噪声,标签之后还可以再修改。点击开始采样(Start sampling),设备会录音一秒,并将数据传输到Edge Impulse。数据载入之后,已收集的数据(Collected data)下会出现新的一行记录,可以在界面上分析信号波形或者重播音频(见图3)。

图3 数据收集页收录和回放音频数据的部分

构建数据集

接下来,开始创建一个数据集。对于一个比较简单的音频分类模型,我们应该收集大约10分钟的数据。两个类型的样本应该基本均衡,所以我们需要收集:五分钟的背景噪声,标记为“噪声”;五分钟水龙头开启时的声音,标记为“水龙头”。

在真实世界中,我们感兴趣的声音往往会和其他声音混杂在一起,比如水龙头的流水声往往会伴随着洗碗的声音或者厨房的交谈声,背景噪声还可能有电视、小孩玩耍或者车辆从附近经过的声音。训练用的数据必须包含这些实际环境的声音,如果模型没有接触到它们,准确度会降低。在本文中将收录下面的样本:

•背景噪声:

o没有其他声音:两分钟

o同时有电视/音乐的声音:一分钟

o同时有偶尔的讲话/谈话:一分钟

o同时有做家务的声音:一分钟

•水龙头开启的声音:

o水龙头开启流水的声音:一分钟

o另一个不同的水龙头开启流水的声音:一分钟

o同时有电视/音乐的声音:一分钟

o同时有偶尔的聊天/对话:一分钟

o同时有做家务的声音:一分钟

即使无法取得全部的样本,也不必担心。我们的目标是每个分类都有五分钟的真实世界数据,在具有代表性的数据集上训练出的模型会更加准确有效。无法保证模型能准确分辨数据集以外的声音,所以我们必须尽可能地贴近真实世界,在数据集中包括多种声音。另一个选项是从Edge Impulse下载现成的十分钟样本 ,解压到本地后,在数据获取页点击上传数据(Upload data)图标(见图4),然后上传下载的文件,并添加标签。

图4 数据获取页,用于上传预先准备好的数据集

一次性可以收录的音频长度取决于特定硬件的存储大小,ST B-L745E-IOT01A板的存储容量允许一次性录音60秒,Arduino Nano 33 BLE Sense则只能录音16秒。录音60秒意味着需要将样本长度设置为60 000。从开发板上传输数据往往很慢,在Edge Impulse中采集60秒的样本大约需要7分钟。取得需要的10分钟数据后,就可以开始设计Impulse了。

设计Impulse

Impulse读取原始数据,将其分割为多个更小的窗口,然后通过信号处理块提取特征,通过学习块分类新的数据。信号处理块的作用是简化原始数据的处理,针对同样的输入同一个信号处理块总会返回一致的数值,因此学习块可从之前的经验中学习。在本文使用MFCC信号处理块,MFCC指梅尔频率倒谱系数[ ],这听上去很复杂,实际上就是从声音数据中移除大量的冗余信息。接下来,我们会将简化后的音频数据送到一个神经网络块(学习块)中,学习块将会学习如何区分两个音频类别(水龙头开启和噪声)。现在,打开创建Impulse(Create impulse)页面,你会看到原始数据(Raw data)部分。

同之前提过的一样,Edge Impulse在训练时将样本分割为小的窗口,然后将数据发送到机器学习模型中。窗口大小(Window size)参数控制每个数据段的长度(单位毫秒),一秒长的音频样本足够确定水龙头是否开启,所以设置为1000毫秒。每一个原始样本被分为多个窗口,窗口间距(Window increase)的大小决定下一个窗口和上一个窗口的间距,1000毫秒的窗口间距意味着每个窗口在上一个窗口开始的一秒后开始。

如果窗口间距的数值小于窗口大小,则可创建互相重叠的窗口。虽然窗口间会包括一部分相似的数据,每一个样本依然是独特的。互相重叠的窗口帮助我们高效地利用训练数据,比如1000毫秒的窗口大小和100毫秒的窗口间距意味着可以从两秒的数据中提取十个独特的窗口。本文中设置窗口大小为1000毫秒,窗口间距为300毫秒。点击添加处理块(Add a processing block),选择MFCC块,然后点击添加学习块(Add a learning block),选择Keras神经网络块(Neural Network - Keras),最后点击保存。创建Impulse的页面如图5所示。

图5 创建Impulse

MFCC块配置

目前已经凑齐了Impulse所需要的基本组成模块,现在可以单独配置每个部分了。在左侧的导航菜单中点击MFCC标签进入块配置页,界面上可以预览数据将如何被转换。在右侧可以看到一个频谱图上显示了MFCC处理音频输入后的输出结果。MFCC块将一个音频样本转换为一个数据表格,每一行是一个频率范围,每一列是本时间区段内频率范围内声音的强度。频谱图用颜色填充每个单元格,颜色的深浅表示振幅的高低。图6中对比了噪声和水龙头声音的频谱图。

图6 噪声(上)和水龙头声音(下)的频谱图

我们的双眼很难分辨出两张图的区别,但对于一个神经网络而言,它们间的差别足够用来学习两个类别的不同。在参数(Parameter)框中可以配置MFCC块,Edge Impulse的默认数值适用于多数情形,这里不做修改。MFCC块产生的频谱图会被发送到神经网络中,神经网络的特性意味着它特别适合学习此类列表数据背后的模式。

在训练神经网络之前,需要从采集的所有音频窗口中产生MFCC特征。点击页面上方的产生特征(Generate feature),然后点击绿色的同名按钮,分析十分钟的样本需要几分钟的时间,这一过程结束后,特征浏览器(Features explorer)会以可视化的形式显示数据集,你可以检查不同类型间的区别是否明显,并查找错误的数据标签。为了能在三维空间内显示所有的特性,这里用到了降维手段。

神经网络配置

神经网络借鉴人脑的工作方式,能够学习辨识训练数据中的模式。我们训练的神经网络从MFCC中取得数据,然后决定输入数据属于噪声类别还是水龙头类别。点击左侧菜单的神经网络分类器(NN Classifier)进入块配置窗口。一个神经网络由多层虚拟的神经元组成,在页面的左侧可以看到当前的状态。输入数据(来自MFCC的频谱图)首先进入第一层神经元,这些神经元有着各自独特的内部状态,会对输入进行过滤和转换。第一层神经元的输出会被送到第二层神经元,以此类推,神经网络会一点点转变原始输入,最终变成完全不同的输出。在本例中,频谱图经过4个中间层后变成两个数字,分别是输入数据代表噪声和水龙头开启的可能性。

在训练中,为了让神经网络能够正确地转换输入并输出我们想要的结果,每个神经元的内部状态都会逐渐转变。当输入一个样本时,根据网络输出结果和正确的响应(标签)间的距离,神经元的内部状态会被调整,这样下次网络给出正确响应的可能性就会更大。重复这一过程几千次,就会得到经过训练的网络。

对于神经网络而言,神经元层的安排被称为“结构”,不同的结构适用于不同的任务。虽然Edge Impulse中默认的神经网络结构很适合当前的项目,但也可以定义你自己的结构。在开始训练模型前,需要更改配置中的一些数值。首先将训练周期数(Number of training cycles)设为300,这意味着整个数据集在训练中会被处理300次。如果周期数太少,网络无法从训练数据中充分学习,但是如果周期数太多,网络可能会过度切合训练数据,无法正确处理新数据,这一问题被称为“过拟合”。

接下来,将最低置信度(Minimum confidence rating)设为0.7,这意味着当神经网络作出预测时,除非声音是水龙头流水声的概率为0.7以上(比如0.8),Edge Impulse将会忽略这一预测。现在我们可以点击开始训练(Start training),训练将会花上几分钟时间,结束后会在页面下方看到上次训练表现面板(见图7)。

图7 训练后网络的表现

虽然完成了Edge Impulse中神经网络的训练,但是结果中这些数字都意味着什么?在训练开始前,20%的数据被预设为验证数据,它们被用来验证模型的性能,而不是用做训练数据。上次训练表现(Last training performance)面板上显示验证的结果,从而提供关于模型性能的信息,你看到的数字可能会和这里的不同。准确度(Accuracy)指被正确分类的音频样本的百分比,数字越高越好,不过一般不太可能接近100%,极高的准确度往往也意味着过拟合。就许多应用而言,高于80%的准确度已经很好了。面板中央的混淆矩阵 显示正确和错误分类的数目。

分类新的数据

虽然上一步中的性能数据说明模型在训练数据上的表现很好,但在部署前用新数据测试它依然是非常重要的,这能帮助我们确认模型没有过拟合。Edge Impulse内建了一些测试工具,帮助从设备上实时抓取数据并立即进行分类。点击左侧菜单上的实时分类(Live classification)按钮,你的设备应该出现在分类新数据(Classify new data)面板上,点击开始采样(Start sampling)将会收集5秒的背景噪声数据。

测试模型

通过实时分类(Live classification)标签页可以快速测试你的模型,了解模型的行为。不过,如果想要确保你的模型的正确性,需要更加严格地测试你的模型。模型测试(Model testing)标签页正是为此而存在的。理想状况下,你的测试数据集大小应该至少是训练数据集的25%,假设训练数据是10分钟,测试数据至少需要2分30秒。此外,还需要确保测试数据能够反映大量的真实情形,这样能够以不同的输入测试模型的性能。

比如说,收集若干不同水龙头的声音是一个很好的主意,可以从数据获取(Data acquisition)标签页管理你的测试数据。打开标签页,点击上方的测试数据(Test data),使用上传(Upload)功能导入数据,过程同之前上传训练数据一样。确保数据的标签是正确的,结束后返回模型测试(Model testing)标签页,选择全部样本,然后点击分类选中的数据(Classify selected)。

图8是分类的结果,面板显示模型的准确率是73.42%。针对每个样本,面板上会显示其准确率,比如其中一个样本的分类准确度为67%。有很多误分类的样本非常宝贵,这样的样本中有许多模型目前不拟合的音频类型,通常需要将此类样本加入到训练数据中。

图8 测试数据分类的结果

在硬件设备上部署模型

设计、训练并验证了我们的impulse之后,就可以在硬件设备上部署了,这意味着模型可以在没有互联网的情况下以最小的延迟和最低的功耗运行。Edge Impulse可以将整个impulse包装为一个C++代码库以供编程使用,包括MFCC算法、神经网络和分类代码。点击菜单中的部署(Deployment)开始模型的导出过程,接下来在构建固件(Build firmware)下选择正确的开发选项,然后点击构建(Build),就会针对特定的开发板将impulse输出为二进制可执行文件。构建过程完成后程序会提供二进制文件下载,将文件保存到本地。点击构建(Build)按钮时,程序会弹出窗口、提供关于部署的说明,指导你在构建后进行测试。可以在命令行上运行如下命令以打开连接硬件固件的串行接口:$ edge-impulse-run-impulse

这一命令会打开麦克风收录声音,在数据上运行MFCC代码,并对产生的频谱图进行分类。

结 语

在微控制器上运行机器学习是一个比较新的领域,对于不熟悉人工智能的开发者而言很难上手。Edge Impulse简化了数据的收集和分析、神经网络的训练和构建,以及在微控制器上部署的过程。这类模型有着诸多应用,从监控工业机械到辨识语音命令都可以适用。Edge Impulse易于使用,界面直观,而且免费,这意味着可以立即开始探索在嵌入式设备上运行tinyML。(本文首先在Elettronica Open Source 上以意大利文发表。)

相关参考链接:

https://www.expert.ai/blog/machine-learning-definition/.

https://www.tinyml.org/.

https://www.tinyml.org/.

https://cdn.edgeimpulse.com/datasets/faucet.zip.

https://www.tinyml.org/.

https://www.dataschool.io/simple-guide-to-confusion-matrix-terminology/.

https://it.emcelettronica.com/.

]]>
714 0 0 0
<![CDATA[pico+tensorflow]]> http://mixdiy.com/index.php/2022/02/18/picotensorflow/ Fri, 18 Feb 2022 15:35:20 +0000 http://mixdiy.com/?p=717

Raspberry Pi Pico,上市不足3个月,售价仅有4美元,擅长低时延的 I/O 通信和模拟信号输入,功耗低,可以弥补树莓派在与物理世界互动方面的不足。今天我们项目分享就用Pi Pico+Edge Impulse实现入侵者监测系统。

该项目将热像仪数据给Raspberry Pi Pico,再使用Tensorflow Lite模型分析,达到在黑暗中检测入侵者的目的。这个项目有两大优点:

1)虽然很多家庭会安装摄像头,但有一些是无法在黑暗中工作的。项目中使用的热像仪监测就没有这个问题了。

2)如果一个动物入侵,我们不希望电子设备误报,摄像头肯定是做不到的。这个项目借助Edge Impulse可以准确无误判断当前入侵的是不是人。

注:Tensorflow Lite是针对移动、嵌入式以及IoT设备的工具,能够在终端本地运行机器学习模型的能力,而不必将数据上传到云端服务器进行分析。这样不仅节省网络流量,并且可以用户自己的隐私。

Edge Impulse在线网站自行训练进行分类识别。使用EdgeImpulse在线训练主要分为以下四个步骤:数据集采集、上传、训练以及部署。

01 项目使用到的软硬件

硬件部分:

  • 树莓派PICO开发板
  • MLX90640热成像夜视摄像头
  • Seeed Wio 终端
  • LED灯

开发软件:

  • Raspberry Pi Pico C / C ++ SDK

02 训练数据集

在机器学习项目中,第一步也是最重要的一步是收集训练数据,使训练集能够涵盖给定任务的大多数代表性的案例。

这里使用Seeed Wio终端进行数据采集。Wio终端上的3个按钮用于标记3个类(人,目标和背景)。把收集到的数据保存到Wio终端内置的micro SD卡中。每个热图像数据被捕获为一个单独的文件。该文件不包含标题行,只包含以逗号分隔的768(24x32)个温度读数。示例文件内容如下所示:

采集到的数据的直观表现如下:

03 将数据上传至Edge Impulse

当前,Edge Impulse不支持非时间序列数据类型(图像除外)。为了使用Edge Impulse,数据被假定为时间序列的实例。下面的代码是将原始数据转换为Edge Impluse的数据采集JSON格式。(全部代码可以在“达尔闻说”回复:入侵检测。)

  • import json
  • import time
  • import hmac
  • import hashlib
  • import os
  • HMAC_KEY = "<insert your edge impulse hmac key>"
  • labels = { 
  • '1': 'Person',
  • '2': 'Object',
  • '3': 'Background'
  • }
  • dir = 'raw_data'
  • for filename in os.listdir(dir):
  • if filename.endswith('.csv'):
  • prefix, ext = os.path.splitext(filename)
  • label = labels[prefix[-1]] 
  • outfilename = os.path.join('formatted_data', '{}.{}.json'.format(label, prefix)) 
  • with open(os.path.join(dir, filename)) as fp: 
  • values = [[float(i)] for i in fp.read().split(',')] 
  • emptySignature = ''.join(['0'] * 64)
  • data = { "
  • protected": {
  • "ver": "v1", 
  • "alg": "HS256",
  • "iat": time.time()
  • }, 
  • "signature": emptySignature, 
  • "payload": { 
  • "device_name": "A0:C0:D3:00:43:11",
  • "device_type": "Raspberry_Pi_Pico", 
  • "interval_ms": 1, 
  • "sensors": [
  • { "name": "temperature", "units": "Cel" },
  • ],
  • "values": values
  • }
  • # encode in JSON
  • encoded = json.dumps(data)
  • # sign message signature = hmac.new(bytes(HMAC_KEY, 'utf-8'), msg = encoded.encode('utf-8'), digestmod = hashlib.sha256).hexdigest() # 
  • set the signature again in the message, and encode again data['signature'] = signature encoded = json.dumps(data, indent=4)
  • with open(outfilename, 'w') as fout: 
  • fout.write(encoded)

数据使用Edge Impulse CLI上传,需要先在Edge Impulse注册一个账户,并创建一个新项目来上传数据。下面的命令用于上传所有JSON文件,这些文件会自动分成训练和测试数据集。$ edge-impulse-uploader --category split *.json

上传的数据可以在Edge Impulse Studio查看。

训练数据是以1 ms为间隔的时间序列,但通过设置窗口大小768 ms(等于32x24=768热读数),它被用作单个数据实例。由于这不是一个时间序列数据,我们将使用原始数据块(无需预处理),直接反馈到神经网络块。下面是训练输出。因为没有很多的实例样本数据作为支撑,所以准确率只能达到80%,但是之后通过大量的实例训练数据集可以进一步提高。

04 硬件设置

Pi Pico通过I2C协议与MLX90640热像仪连接,通过SPI协议连接TFT显示器。TFT显示屏用于演示。红色LED灯连接到树莓派Pico的GPIO引脚3上。

Raspberry Pi Pico ----- MLX90640热感相机

GP8 ------------------ SDA

GP9 ------------------ SCL

3V3(OUT) ----------------- 5V/3V3

GND. ------------------ GND

Raspberry Pi Pico ------ TFT Display

GP14 ------------------ MISO

GP13 ------------------ CS

GP6 ------------------ SCK

GP7 ----------------- MOSI

3V3(OUT) ----------------- 5V

GND. ------------------ GND

GP15 ----------------- DC

GP14 ----------------- RST

05 Edge Impluse移植

只要根据其他平台可用的移植代码,创建一个移植代码文件,就可以在Edge Impulse SDK中加入对Raspberry Pico硬件的支持。以下是代码片段。(在写这篇文章的时候,Edge Impulse还没有正式支持Raspberry Pi Pico,但是相信在不久的将来会支持它的。)#include "ei_classifier_porting.h"
#include "pico/stdlib.h"
#define EI_WEAK_FN __attribute__((weak))
EI_WEAK_FN EI_IMPULSE_ERROR ei_run_impulse_check_canceled() {   
return EI_IMPULSE_OK;
}
EI_WEAK_FN EI_IMPULSE_ERROR ei_sleep(int32_t time_ms) {   
sleep_ms(time_ms);   
return EI_IMPULSE_OK;
}
uint64_t ei_read_timer_ms() {   
return to_ms_since_boot(get_absolute_time());}
uint64_t ei_read_timer_us() {   
return to_us_since_boot(get_absolute_time());} 

06 在Pi Pico上进行模型分类

复制下面代码:$ git clone  https://github.com/metanav/pico_person_detection_thermal.git$ cd pico_person_detection_thermal$ mkdir build$ cd build$ cmake ..$ make -j4

可以通过下面的步骤将生成的pico_person_detection_thermal.uf2二进制文件烧录到Raspberry Pi Pico上。1. 按住BOOTSEL按钮,将Raspberry Pi Pico插入电脑的USB接口,就会显示一个名为RPI-RP2的大容量存储设备。2. 将pico_person_detection_thermal.uf2二进制文件拖放到RPI-RP2上。烧录二进制文件后,Raspberry Pi Pico将重新启动,开始执行检测。

最终如上面演示视频所示,就可以看到,当有一个人出现时,摄像头检测到数据,随后树莓派Pico分析出是人,LED灯亮起。

]]>
717 0 0 0
<![CDATA[树莓派通过usb摄像头定时拍照]]> http://mixdiy.com/index.php/2022/02/23/capture-by-rpi/ Wed, 23 Feb 2022 15:31:25 +0000 http://mixdiy.com/?p=719

树莓派使用usb摄像头需要安装fswebcam,具体如下:

1、sudo apt-get install fswebcam

2、sudo nano capture.sh

#!/bin/bash
#DATE=$(TZ=UTC-8 date +%Y-%m-%d" "%H:%M:%S)
DATE=$(date +%Y-%m-%H-%M)
fswebcam -d /dev/video0 --no-banner -r 1024X768 -S 60 -D 2 -F 2 /home/pi/$DATE.jpg

3、chmod +x capture.sh

4、crontab -e

* * * * * /home/pi/capture.sh >/dev/null 2>&1

5、记得重启crontab

#重启crontab程序
sudo /etc/init.d/cron restart

fswebcam参数详解
-? --help 显示此帮助页面并退出
-c --config <文件名> 从文件加载配置
-q --quiet 隐藏所有消息(错误除外)
-v --verbose 捕获时显示额外的消息
--version 显示版本并退出
-l --loop <秒> 循环运行
-b --background 在后台运行
-o --output <文件名> 将日志输出到文件
-d --device <摄像头> 设置要使用的摄像头
-i --input 选择要使用的输入
-t --tuner 选择要使用的调谐器
-f --frequency 选择频率使用
-p --palette 选择要使用的调色板格式
-D --delay 设置预捕获延迟时间(秒)
-r --resolution <宽x高> 设置拍摄分辨率
--fps <帧率> 设置捕获帧率
-F --frames 设置要拍摄的帧数
-S --skip 设置要跳过的帧数
--dumpframe 将原始帧转储到文件
-s --set = 设定参数值
--revert 恢复原始捕获的图像
--flip 翻转图像
--crop [,] 裁剪图像的一部分
--scale 缩放图像
--rotate 垂直旋转图像
--deinterlace 减少隔行失真
--invert 反转图像颜色
--greyscale 去除图像的颜色
--swapchannels 交换c1和c2的通道
--no-banner 隐藏横幅
--top-banner 将横幅放在顶部
--bottom-banner 将横幅放在底部(默认)
--banner-colour 设置横幅颜色(#AARRGGBB)
--line-colour 设置横幅线条颜色
--text-colour 设置文字颜色
--font <[name][:size]> 设置字体和和大小
--no-shadow 禁用文字阴影
--shadow 启用文字阴影
--title 设置主标题(左上方)
--no-title 清除主标题
--subtitle 设置字幕 (左下方)
--no-subtitle 清除字幕
--timestamp 设置时间戳格式(右上)
--no-timestamp 清除时间戳记
--gmt 使用GMT代替本地时区
--info 设置信息文本(右下)
--no-info 清除信息文本
--underlay 设置参考图像
--no-underlay 清除底衬
--overlay 设置覆盖图像
--no-overlay 清除覆盖
--jpeg 输出JPEG图像
--png 输出PNG图像(-1, 0 - 10)
--save <文件名> 将图像保存到文件
--exec <命令> 执行命令并等待其完成

]]>
719 0 0 0
<![CDATA[autodial]]> http://mixdiy.com/index.php/2022/02/24/autodial/ Thu, 24 Feb 2022 10:45:47 +0000 http://mixdiy.com/?p=727 ]]> 727 0 0 0 <![CDATA[树莓派通过Esp32定时开、关机]]> http://mixdiy.com/index.php/2022/02/25/esp32-rpi-poweronoff/ Fri, 25 Feb 2022 07:29:52 +0000 http://mixdiy.com/?p=733 ]]> 733 0 0 0 <![CDATA[烧录esp8266注意事项]]> http://mixdiy.com/index.php/2022/02/26/burn-esp8266/ Sat, 26 Feb 2022 03:13:31 +0000 http://mixdiy.com/?p=747

很多人第一次用USB转串口模块烧录esp8266失败,这里有几个注意事项:

USB转串口模块与esp8266的接线方式如下:

1、RX->TX,2、TX->RX,3、3V3->3V3,4、GND->GND,以上接线需要确认好。

还有就是要特别注意的一点:烧录时IO0需要接地,烧录成功后需要断开IO0与GND的连接。另外固件选择要对,我的模块很便宜,选择是1M flash的,具体可以到micropython.org上下载对应固件,thonny这个软件就可以直接烧录,而不需要下载esptool工具。

]]>
747 0 0 0
<![CDATA[树莓派自动开关机执行延时录像]]> http://mixdiy.com/index.php/2022/02/27/jj/ Sun, 27 Feb 2022 01:07:11 +0000 http://mixdiy.com/?p=755

esp32侧程序,功能有3个,1、是连接Wi-Fi同步网络时间,2、是通过UART口跟树莓派进行通信,检查树莓派是否在预定时间内工作或关机,3、根据时间段控制树莓派开机或关机。该程序在esp32上的文件名为main.py(记得要改名),这样esp32一通电即可工作。程序如下:

上位机(树莓派)与esp32的通信程序check.py,程序如下:

import serial
Port = "/dev/ttyAMA0"
ser = serial.Serial(Port, baudRate, timeout=10)

while True:
   send = '2'
   ser.write(send.encode())
   str = ser.readline().decode()
   if(str !=""):
      print(str)
ser.close()

该程序需要开机自动运行,具体可以将其加入rc.local,具体:

sudo nano /etc/rc.local

在最后一行前加入:python check.py,保存退出重启即可

注意:

树莓派使用串口需要进行以下工作:

1、sudo raspi-config

根据以下步骤进行设置:

选择Interfacing Options

选择serial

再选择 no,禁用串口登录功能,将串口用于通信。
再选择 yes,启动串口硬件。
禁用蓝牙(硬件串口与mini串口默认映射对换

接线:(树莓派(左)------esp32(右))

RXD(GPIO15) <——> TXD(IO12)、TXD(GPIO14) <——> RXD(IO13)、GND <——> GND

sudo nano /boot/config.txt

在打开的文件最后面添加:
(注意:树莓派4b也一样是pi3)

dtoverlay=pi3-disable-bt

修改保存后重启树莓派:

reboot

安装serial,具体如下:

sudo apt-get install python3-serial

]]>
755 0 0 0
<![CDATA[车牌识别]]> http://mixdiy.com/index.php/2022/03/01/detec/ Tue, 01 Mar 2022 00:57:21 +0000 http://mixdiy.com/?p=777

https://m.toutiaocdn.com/i7069721183423300103/?app=news_article&timestamp=1646095855&use_new_style=1&req_id=202203010850550101310570981A270CB5&group_id=7069721183423300103&share_token=A23A4B9A-9350-48B1-9010-48F041EB9EAF&tt_from=weixin&utm_source=weixin&utm_medium=toutiao_ios&utm_campaign=client_share&wxshare_count=1

]]>
777 0 0 0
<![CDATA[树莓派延时摄影(街角的一天)]]> http://mixdiy.com/index.php/2022/03/02/one-day-in-the-street-conner/ Wed, 02 Mar 2022 15:39:20 +0000 http://mixdiy.com/?p=788

具体代码及实现方法请见上期内容

https://wordpress.tv/2020/05/09/matias-ventura-matt-mullenweg-matt-y-wordpress/
]]>
788 0 0 0 ]]>
<![CDATA[ESP32-C3串口通信定时开关机延时摄影]]> http://mixdiy.com/index.php/2022/03/03/esp32-c3-debug/ Thu, 03 Mar 2022 07:52:31 +0000 http://mixdiy.com/?p=795

esp32-c3系列因其低廉的价格颇具竞争力,某宝买了个9.9的esp32-c3开发板,用micropython开发uart通信,一开始接上电路板丝印的RX、TX引脚,但一直未能通信甚至干扰了调试,咨询厂家,因C3系列较新,厂家FAE也不熟悉micropython,翻看micropython官网等也找不到相关资料,给调试工作带来很大麻烦。这里提供一个简单的方法找出uart引脚,也就一行代码:

esp32-c3开发板正面
esp32-c3开发板背面
注意点一:
uart = machine.UART(1, baudrate=115200,timeout=10)
print(uart)
#此时可以看到rx=10,tx=9,但如果将树莓派的tx、rx分别接esp32C3的引脚10和9,则不会有任何信息,这是最大的坑,此时需将上述代码更新为
uart = machine.UART(1, baudrate=115200,rx=10,tx=9,timeout=10)
这样就没有任何问题了

注意点二:
上位机程序的timeout数值一定要大于等于下位机time.sleep的时间

全部代码如下:

import ntptime
import network,time
import machine
from machine import Pin,RTC

rtc = RTC()
#print("同步前本地时间:%s" %str(time.localtime()))
led=Pin(2,Pin.OUT)

# 联WIFI
def WIFI_Connect():

    wlan = network.WLAN(network.STA_IF) #STA模式
    wlan.active(True)                   #激活接口
    start_time=time.time()              #记录时间做超时判断
    if not wlan.isconnected():
        print('connecting to network...')
        wlan.connect('HUAWEI', 'china') #输入WIFI账号密码
        while not wlan.isconnected():
            if time.time()-start_time > 15 :
                print('WIFI Connected Timeout!')
                break
    if wlan.isconnected():
        print('connected!')
        print('network information:', wlan.ifconfig())

# 同步时间
def sync_ntp():
     ntptime.NTP_DELTA = 3155644800   # 可选 UTC+8偏移时间(秒),不设置就是UTC0
     ntptime.host = 'ntp1.aliyun.com'  # 可选,ntp服务器,默认是"pool.ntp.org"
     try:
         ntptime.settime()   # 修改设备时间,到这就已经设置好了
     except:
         for i in range(6):
            led.value(1)              #turn off 0是亮
            time.sleep(0.1)
            led.value(0)             
            time.sleep(0.1)
         print('同步失败')

uart = machine.UART(1, baudrate=115200,rx=10,tx=9,timeout=10)
#uart = machine.UART(1, baudrate=115200, timeout=10)
p22 = Pin(6, Pin.OUT)

readmsg = ''
sendmsg = '1'

WIFI_Connect()
while True:
    sync_ntp()
    dt=time.localtime()
    print(uart)
    time.sleep(20)     
    if uart.any() > 0:
        readmsg = uart.read()
        print(readmsg)
        if '2' in readmsg:
            uart.write(sendmsg)
            if dt[3]>=20:
                print('power off')
                p22.on()
                time.sleep(0.5)
                p22.off()
        else:
            print(readmsg)
            
            print('reset now')
            p22.on()
            time.sleep(0.5)
            p22.off()

    elif uart.any()==0:                             #多次读取,防止有信息丢失导致误判
        print(dt[3])
        if dt[3]>=0 and dt[3]<20:      #如白天读不到信息则开机
            print('power on')
            p22.on()
            time.sleep(0.5)
            p22.off()

改进版本(自动根据上位机发送的串口信息生成包含wifi及开关机时间等信息的配置文件,避免了在烧录程序时写死wifi密码等信息)

import ntptime
import network,time
import machine
from machine import Pin,RTC


uart = machine.UART(1, baudrate=115200,rx=10,tx=9,timeout=10)
readmsg = ''
sendmsg = 'huanghe'
    
rtc = RTC()
#print("同步前本地时间:%s" %str(time.localtime()))
led=Pin(2,Pin.OUT)

# 联WIFI
def WIFI_Connect():
    wlan = network.WLAN(network.STA_IF) #STA模式
    wlan.active(True)                   #激活接口
    start_time=time.time()              #记录时间做超时判断
    if not wlan.isconnected():
        print('connecting to network...'+ssid[1])
        wlan.connect(ssid[1], pwd[1]) #输入WIFI账号密码
        while not wlan.isconnected():
            if time.time()-start_time > 15 :
                print('WIFI Connected Timeout!')
                break
    if wlan.isconnected():
        print('connected!')
        print('network information:', wlan.ifconfig())

# 同步时间
def sync_ntp():
     ntptime.NTP_DELTA = 3155644800   # 可选 UTC+8偏移时间(秒),不设置就是UTC0
     ntptime.host = 'ntp1.aliyun.com'  # 可选,ntp服务器,默认是"pool.ntp.org"
     try:
         ntptime.settime()   # 修改设备时间,到这就已经设置好了
     except:
         for i in range(6):
            led.value(1)              #turn off 0是亮
            time.sleep(0.1)
            led.value(0)             
            time.sleep(0.1)
         print('同步失败')


#uart = machine.UART(1, baudrate=115200, timeout=10)
p22 = Pin(6, Pin.OUT)

try:
    with open('config.txt',mode='r',encoding='utf-8') as f:
        s=f.read().split(',')
        ssid=s[0].split(':')
        pwd=s[1].split(':')
        startt=s[2].split(':')
        endt=s[3].split(':')
        print(startt[1])
        print(endt[1])
except:
    print('config file error')
    time.sleep(5)   #必不可少,并且时间不能小于上位机发送定时
    if uart.any() > 0:
        readmsg = uart.read()
        print(readmsg)
        if 'SSID' in readmsg:
            content = readmsg
            with open('config.txt',mode='w',encoding='utf-8') as f:
                f.write(content)
            machine.reset()


try:
    WIFI_Connect()
except:
    print('wifi connect error')
while True:
    sync_ntp()
    dt=time.localtime()
    print(uart)
    time.sleep(20)     
    if uart.any() > 0:
        readmsg = uart.read()
        print(readmsg)
        if 'changjiang' in readmsg:
            uart.write(sendmsg)
            print('dt[3]='+str(dt[3]))
            print('endt[1]='+endt[1])
            if dt[3]>=int(endt[1]):
                print('power off')
                p22.on()
                time.sleep(0.5)
                p22.off()
        else:
            print(readmsg)
            print('reset now')
            p22.on()
            time.sleep(0.5)
            p22.off()

    elif uart.any()==0:                             #多次读取,防止有信息丢失导致误判
        print(dt[3])
        if dt[3]>=int(startt[1]) and dt[3]<int(endt[1]):      #如白天读不到信息则开机
            print('power on')
            p22.on()
            time.sleep(0.5)
            p22.off()

去错版本

#coding:utf-8
import ntptime
import network,time
import machine
import os
from machine import Pin,RTC


uart = machine.UART(1, baudrate=115200,rx=10,tx=9,timeout=10)
readmsg = ''
sendmsg = 'huanghe'
startt=6
endt=20

rtc = RTC()
#print("同步前本地时间:%s" %str(time.localtime()))
led=Pin(2,Pin.OUT)

# 联WIFI
def WIFI_Connect():
    wlan = network.WLAN(network.STA_IF) #STA模式
    wlan.active(True)                   #激活接口
    start_time=time.time()              #记录时间做超时判断
    if not wlan.isconnected():
        print('connecting to network...'+ssid[1])
        wlan.connect(ssid[1], pwd[1]) #输入WIFI账号密码
        while not wlan.isconnected():
            if time.time()-start_time > 15 :
                print('WIFI Connected Timeout!')
                break
    if wlan.isconnected():
        print('connected!')
        print('network information:', wlan.ifconfig())

# 同步时间
def sync_ntp():
     ntptime.NTP_DELTA = 3155644800   # 可选 UTC+8偏移时间(秒),不设置就是UTC0
     ntptime.host = 'ntp1.aliyun.com'  # 可选,ntp服务器,默认是"pool.ntp.org"
     try:
         ntptime.settime()   # 修改设备时间,到这就已经设置好了
     except:
         for i in range(6):
            led.value(1)              #turn off 0是亮
            time.sleep(0.1)
            led.value(0)             
            time.sleep(0.1)
         print('同步失败')


#uart = machine.UART(1, baudrate=115200, timeout=10)
p22 = Pin(6, Pin.OUT)

try:
    with open('config.txt',mode='r',encoding='utf-8') as f:
        s=f.read().split(',')
        if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in s:
            ssid=s[0].split(':')
            pwd=s[1].split(':')
            startt=s[2].split(':')
            endt=s[3].split(':')
            startta=startt[1]
            endta=endt[1]
            print(startta)
            print(endta)
        else:
            print('file format error,will drop it')
            os.remove('config.txt')
except:
    print('config file not find')
    startta=6
    endta=20
    time.sleep(5)   #必不可少,并且时间不能小于上位机发送定时
    if uart.any() > 26:  #接受字符串个数
        readmsg = uart.read()
        print(readmsg)
        if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in str(readmsg):
            content = readmsg
            with open('config.txt',mode='w',encoding='utf-8') as f:
#                f.seek(0)
                f.write(content)
            machine.reset()
        else:
            try:
                os.remove('config.txt')
            except:
                print('未找到待删除文件')
try:
    WIFI_Connect()
except:
    print('wifi connect error')
while True:
    sync_ntp()
    dt=time.localtime()
    readmsg = uart.read()
    print(uart)
    time.sleep(20)     
    if uart.any() > 0:
        readmsg = uart.read()
        print(readmsg)
        if 'changjiang' in readmsg:
            uart.write(sendmsg)
            print('dt[3]='+str(dt[3]))
            print('endt[1]='+str(endta))
            if dt[3]>=int(endta):
                print('power off')
                p22.on()
                time.sleep(0.5)
                p22.off()
        else:
            print(readmsg)
            print('reset now')
            p22.on()
            time.sleep(0.5)
            p22.off()

    elif uart.any()==0:                             #多次读取,防止有信息丢失导致误判
        print(dt[3])
        if dt[3]>=int(startta) and dt[3]<int(endta):      #如白天读不到信息则开机
            print('power on')
            p22.on()
            time.sleep(0.5)
            p22.off()

对应上位机程序

import serial #导入模块

with open('/home/pi/config.txt',mode='r',encoding='utf-8') as f:
    s=f.read()

#端口,GNU/Linux上的/dev/ttyUSB0 等或Windows上的 COM3 等
portx="/dev/ttyAMA0"
#波特率,标准值之一:50,75,110,134,150,200,300,600,1200,1800,2400,4800,9600,19200,38400,57600,115200
bps=115200
#超时设置,None:永远等待操作,0为立即返回请求结果,其他值为等待超时时间(单位为秒)
timex=5
# 打开串口,并得到串口对象
ser=serial.Serial(portx,bps,timeout=timex)

while True:
    # 写数据
    send=s
    result=ser.write(send.encode())
    print("写总字节数:",result)
    str1 =ser.readline().decode()
    if('huanghe' in str1):
        print(str1)

ser.close()#关闭串口

更新版本

ESP32-C3侧

#coding:utf-8
from machine import UART,Pin,RTC
import machine
import time,network
import ntptime

led=Pin(2,Pin.OUT)
rtc = RTC()

def linedetect():
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=5)
    idsend='huanghe'
    idreceive='changjiang'
    uart.write(idsend)
    time.sleep(15)      #树莓派启动需要的时间,否则会反复重启
    receive_data=uart.readline()
    if (idreceive in str(receive_data)):
        return 1
    else:
        return 0

def onoff(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.on()
    time.sleep(0.5)
    p.off()

def createconfigfile(filename):
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=5)
    time.sleep(5)
    readmsg = uart.readline()
    if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in str(readmsg):
        content = readmsg
        with open(filename,mode='w',encoding='utf-8') as f:
            f.write(content)

def readconfigfile(filename):
    with open(filename,mode='r',encoding='utf-8') as f:
        s=f.read().split(',')
        if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in s:
            ssid=s[0].split(':')[1]
            pwd=s[1].split(':')[1]
            startt=s[2].split(':')[1]
            endt=s[3].split(':')[1]
            return ssid,pwd,startt,endt
        else:
            print('file format error,will drop it')
            os.remove(filename)

# 同步时间
def sync_ntp():
     ntptime.NTP_DELTA = 3155644800   # 可选 UTC+8偏移时间(秒),不设置就是UTC0
     ntptime.host = 'ntp1.aliyun.com'  # 可选,ntp服务器,默认是"pool.ntp.org"
     try:
         ntptime.settime()   # 修改设备时间,到这就已经设置好了
     except:
         for i in range(6):
            led.value(1)              #turn off 0是亮
            time.sleep(0.1)
            led.value(0)             
            time.sleep(0.1)
         print('同步失败')

# 联WIFI
def WIFI_Connect(ssid,pwd):
    wlan = network.WLAN(network.STA_IF) #STA模式
    wlan.active(True)                   #激活接口
    start_time=time.time()              #记录时间做超时判断
    if not wlan.isconnected():
        print('connecting to network...'+ssid)
        wlan.connect(ssid, pwd) #输入WIFI账号密码
        while not wlan.isconnected():
            if time.time()-start_time > 15 :
                print('WIFI Connected Timeout!')
                break
    if wlan.isconnected():
        print('connected!')

try:
    a,b,c,d=readconfigfile('config.txt')
except:
    print('error')
    createconfigfile('config.txt')
    machine.reset()
    
WIFI_Connect(a,b)
sync_ntp()
print(a,b,c,d)

while True:
    dt=time.localtime()
    print(dt)
    linestate=linedetect()
    if linestate!=0:
        print('on line')
        if dt[3]>int(d):
            onoff(4)
    else:
        print('off line')
        if (dt[3]<=int(d) and dt[3]>int(c)):
            onoff(4)


'''

while True:
    a=linedetect()
    if a==0:
        print('off line')
    else:
        print('on line')
'''

树莓派侧

import serial
i=0
with open('/home/pi/config.txt',mode='r',encoding='utf-8') as f:
   s=f.read()
Port = '/dev/ttyAMA0'
ser = serial.Serial(Port,115200,timeout=15)
while True:
   send=s
   ser.write(send.encode('utf-8'))
   str1=ser.read(7).decode('utf-8')
   if ('huanghe' in str(str1)):
      print(str1+' '+str(i))
   else:
      print('get info is '+str(str1))
ser.close

注:uart连接一定要接地,否则会报错

开关机时间调整

#coding:utf-8
from machine import UART,Pin,RTC
import machine
import time,network
import ntptime
import os

led1=Pin(12,Pin.OUT)
led2=Pin(13,Pin.OUT)
rtc = RTC()
k=0
def linedetect():
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)  #设置Pin6=tx,Pin7=rx
    idsend='huanghe'
    idreceive='changjiang'
    uart.write(idsend)
    time.sleep(5)      #树莓派启动需要的时间,否则会反复重启
    receive_data=uart.readline()
    if (idreceive in str(receive_data)):
        return 1
    else:
        return 0

def onoff(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.on()
    time.sleep(0.5)
    p.off()

def createconfigfile(filename):
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)
    time.sleep(5)
    readmsg = uart.readline()
    if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in str(readmsg):
        content = readmsg
        with open(filename,mode='w',encoding='utf-8') as f:
            f.write(content)

def readconfigfile(filename):
    with open(filename,mode='r',encoding='utf-8') as f:
        s=f.read().strip('\n').split(',')
        if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in s:
            ssid=s[0].split(':')[1]
            pwd=s[1].split(':')[1]
            startt=s[2].split(':')[1]
            endt=s[3].split(':')[1]
            return ssid,pwd,startt,endt
        else:
            print('config file format error,will drop it')
            os.remove(filename)

# 同步时间
def sync_ntp():
     ntptime.NTP_DELTA = 3155644800   # 可选 UTC+8偏移时间(秒),不设置就是UTC0
     ntptime.host = 'ntp1.aliyun.com'  # 可选,ntp服务器,默认是"pool.ntp.org"
     try:
         ntptime.settime()   # 修改设备时间,到这就已经设置好了
     except:
         for i in range(6):
            led1.value(1)              #turn off 0是亮
            time.sleep(0.1)
            led1.value(0)             
            time.sleep(0.1)
         print('同步失败')

# 联WIFI
def WIFI_Connect(ssid,pwd):
    wlan = network.WLAN(network.STA_IF) #STA模式
    wlan.active(True)                   #激活接口
    start_time=time.time()              #记录时间做超时判断
    if not wlan.isconnected():
        print('connecting to network...'+ssid)
        wlan.connect(ssid, pwd) #输入WIFI账号密码
        time.sleep(1)
        while not wlan.isconnected():
            if time.time()-start_time > 15 :
                print('wifi Connected Timeout!')
                os.remove('config.txt')
                machine.reset()
    if wlan.isconnected():
        print('wifi was connected!')        

try:
    a,b,c,d=readconfigfile('config.txt')
except:
    print('can not find config.txt,will create it!')
    createconfigfile('config.txt')
    time.sleep(2)
    machine.reset()   
WIFI_Connect(a,b)
print(a,b,c,d)
while True:
    led2.value(1)
    time.sleep(0.1)
    led2.value(0)
    time.sleep(0.1)
    sync_ntp()
    dt=time.localtime()
    print(dt)
    linestate=linedetect()
    if linestate!=0:
        print('on line')
        if dt[3]>=int(d):
            onoff(2)
    else:
        print('off line')
        if (dt[3]<int(d) and dt[3]>=int(c)):
            onoff(2)

自动同步上位机配置文件更新

ESP-32-C3程序

#coding:utf-8
from machine import UART,Pin,RTC
import machine
import time,network
import ntptime
import os

led1=Pin(12,Pin.OUT)
led2=Pin(13,Pin.OUT)
rtc = RTC()
k=0
def linedetect():
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)  #设置Pin6=tx,Pin7=rx
    idsend='huanghe'
    idreceive='changjiang'
    uart.write(idsend)
    time.sleep(5)      #树莓派启动需要的时间,否则会反复重启
    receive_data=uart.readline()
    if (idreceive in str(receive_data)):
        return 1
    else:
        return 0

def onoff(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.on()
    time.sleep(0.5)
    p.off()

def createconfigfile(filename):
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)
    time.sleep(5)
    readmsg = uart.readline()
    if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in str(readmsg):
        content = readmsg
        with open(filename,mode='w',encoding='utf-8') as f:
            f.write(content)

def readconfigfile(filename):
    with open(filename,mode='r',encoding='utf-8') as f:
        s=f.read().strip('\n').split(',')
        if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in s:
            ssid=s[0].split(':')[1]
            pwd=s[1].split(':')[1]
            startt=s[2].split(':')[1]
            endt=s[3].split(':')[1]
            return ssid,pwd,startt,endt
        else:
            print('config file format error,will drop it')
            os.remove(filename)

# 同步时间
def sync_ntp():
     ntptime.NTP_DELTA = 3155644800   # 可选 UTC+8偏移时间(秒),不设置就是UTC0
     ntptime.host = 'ntp1.aliyun.com'  # 可选,ntp服务器,默认是"pool.ntp.org"
     try:
         ntptime.settime()   # 修改设备时间,到这就已经设置好了
     except:
         for i in range(6):
            led1.value(1)              #turn off 0是亮
            time.sleep(0.1)
            led1.value(0)             
            time.sleep(0.1)
         print('同步失败')

# 联WIFI
def WIFI_Connect(ssid,pwd):
    wlan = network.WLAN(network.STA_IF) #STA模式
    wlan.active(True)                   #激活接口
    start_time=time.time()              #记录时间做超时判断
    if not wlan.isconnected():
        print('connecting to network...'+ssid)
        wlan.connect(ssid, pwd) #输入WIFI账号密码
        time.sleep(1)
        while not wlan.isconnected():
            if time.time()-start_time > 15 :
                print('wifi Connected Timeout!')
                os.remove('config.txt')
                machine.reset()
    if wlan.isconnected():
        print('wifi was connected!')        

try:
    a,b,c,d=readconfigfile('config.txt')
except:
    print('can not find config.txt,will create it!')
    createconfigfile('config.txt')
    time.sleep(2)
    machine.reset()   
WIFI_Connect(a,b)
print(a,b,c,d)
while True:
    led2.value(1)
    time.sleep(0.1)
    led2.value(0)
    time.sleep(0.1)
    sync_ntp()
    dt=time.localtime()
    print(dt)
    linestate=linedetect()
    if linestate!=0:
        print('on line')
        if dt[3]>=int(d):
            onoff(2)
    else:
        print('off line')
        if (dt[3]<int(d) and dt[3]>=int(c)):
            onoff(2)

树莓派侧程序

#coding:utf-8
import serial
import time

with open('/home/pi/config.txt',mode='r',encoding='utf-8') as f:
   s=f.read()

i=0
Port ="/dev/ttyAMA0"
ser = serial.Serial(Port,115200,timeout=1)
while True:
#   send = 'SSID:HUAWEI-WULIAN,PWD:onlychina,START:6,END:20,changjiang'
   send=s
   i=i+1
   ser.write(send.encode('utf-8'))
#   str = ser.readline().decode()
   str1 = ser.readline().decode('utf-8')
#   time.sleep(5)
   if('huanghe' in str(str1)):
      print(str1+' '+str(i))
   else:
      print('can not get right info ,now info is '+str(str1))
      with open('/home/pi/config.txt',mode='r',encoding='utf-8') as f:
         s=f.read()
ser.close()

改进Ctrl+c的中断事件

#coding:utf-8
import serial
import time

try:
    s=''
    try:
        with open('/home/pi/config.json',mode='r',encoding='utf-8') as f:
           s=f.read()
    except:
        print("File 'config.json' can not find in dir /home/pi/,please create it first and run again.未能在/home/pi/目录下发现文件config.json,请按要求创建后再执行此程序")
    i=0
    Port ="/dev/ttyAMA0"
    ser = serial.Serial(Port,115200,timeout=1)
    while True:
    #   send = 'SSID:HUAWEI-WULIAN,PWD:onlychina,START:6,END:20,changjiang'
       send=s
       i=i+1
       ser.write(send.encode('utf-8'))
    #   str = ser.readline().decode()
       str1 = ser.readline().decode('utf-8')
    #   time.sleep(5)
       if('hello' in str(str1)):
          print(str1+' '+str(i))
       else:
          print('can not get right info ,now info is '+str(str1))
    ser.close()
except KeyboardInterrupt:
    print("Application exit!")

#coding:utf-8
from machine import UART,Pin,RTC
import machine
import time,network
import ntptime
import os

led1=Pin(12,Pin.OUT)
led2=Pin(13,Pin.OUT)
rtc = RTC()
k=0
def linedetect():
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)  #设置Pin6=tx,Pin7=rx
    idsend='hello'
    idreceive='world'
    uart.write(idsend)
    time.sleep(5)      #树莓派启动需要的时间,否则会反复重启
    receive_data=uart.readline()
    if (idreceive in str(receive_data)):
        return 1
    else:
        return 0

def onoff(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.on()
    time.sleep(0.5)
    p.off()

def createconfigfile(filename):
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)
    time.sleep(5)
    readmsg = uart.readline()
    if 'SSID' and 'PWD' and 'START' and 'END' and 'world' in str(readmsg):
        content = readmsg
        with open(filename,mode='w',encoding='utf-8') as f:
            f.write(content)

def readconfigfile(filename):
    with open(filename,mode='r',encoding='utf-8') as f:
        s=f.read().strip('\n').split(',')
        if 'SSID' and 'PWD' and 'START' and 'END' and 'world' in s:
            ssid=s[0].split(':')[1]
            pwd=s[1].split(':')[1]
            startt=s[2].split(':')[1]
            endt=s[3].split(':')[1]
            return ssid,pwd,startt,endt
        else:
            print('config file format error,will drop it')
            os.remove(filename)

# 同步时间
def sync_ntp():
     ntptime.NTP_DELTA = 3155644800   # 可选 UTC+8偏移时间(秒),不设置就是UTC0
     ntptime.host = 'ntp1.aliyun.com'  # 可选,ntp服务器,默认是"pool.ntp.org"
     try:
         ntptime.settime()   # 修改设备时间,到这就已经设置好了
     except:
         for i in range(6):
            led1.value(1)              #turn off 0是亮
            time.sleep(0.1)
            led1.value(0)             
            time.sleep(0.1)
         print('同步失败')

# 联WIFI
def WIFI_Connect(ssid,pwd):
    wlan = network.WLAN(network.STA_IF) #STA模式
    wlan.active(True)                   #激活接口
    start_time=time.time()              #记录时间做超时判断
    if not wlan.isconnected():
        print('connecting to network...'+ssid)
        wlan.connect(ssid, pwd) #输入WIFI账号密码
        time.sleep(1)
        while not wlan.isconnected():
            if time.time()-start_time > 15 :
                print('wifi Connected Timeout!')
                os.remove('config.json')
                machine.reset()
    if wlan.isconnected():
        print('wifi was connected!')        

try:
    a,b,c,d=readconfigfile('config.json')
except:
    print('can not find config.json,will create it!')
    createconfigfile('config.json')
    time.sleep(2)
    machine.reset()   
WIFI_Connect(a,b)
print(a,b,c,d)
while True:
    led2.value(1)
    time.sleep(0.1)
    led2.value(0)
    time.sleep(0.1)
    sync_ntp()
    dt=time.localtime()
    print(dt)
    linestate=linedetect()
    if linestate!=0:
        print('on line')
        if dt[3]>=int(d):
            onoff(2)
    else:
        print('off line')
        if (dt[3]<int(d) and dt[3]>=int(c)):
            onoff(2)
]]>
795 0 0 0
<![CDATA[自动生成域名]]> http://mixdiy.com/index.php/2022/03/04/domain-reg/ Thu, 03 Mar 2022 23:32:09 +0000 http://mixdiy.com/?p=807

最近想注册个好一点的域名,而且长度不能超过5个字母的短域名,否则不容易记,另外因.com的权威性,当然选择后缀com,但注册时发现好域名都已经被注册了,自己一个一个试挺麻烦的,写了一个python程序,很容易就随机生成了短域名,一般域名查询网站都不允许一次查询超过50个域名,那每次生成50个即可,上代码:

list1=[]
for i in range(97,123):               
    list1.append(chr(i)) 

import random
for i in range(50):
	str1=''.join(random.choices(list1,k=5))
	print(f'{str1}')

改进版本

list1=[]
s=''
j=0
t=2

for i in range(97,123):               
    list1.append(chr(i)) 

import random
for i in range(26**t):
    str1='tip'+''.join(random.choices(list1,k=t))
    s=str1+','+s
a = s.split(',')
b = list(set(a))

for str2 in b:
    j=j+1
    if j % 50 ==0:
        print('\n\n')
    if str2!='':
        print(str2)

优化版本(非随机抽取,这样可以防止部分域名丢失)

baseword=[];domain='';spliti=0
t=26;k=0
keystart='zero'
keyend=''

for i in range(97,123):               
    baseword.append(chr(i)) 

for i in range(t):
    if k>0:
        for j in range(k):
            domain=domain+keystart+baseword[i]+baseword[j]+keyend+','
    elif k==0:
        domain=domain+keystart+baseword[i]+keyend+','

domainlist = domain.split(',')
print(len(domainlist))
for regdomain in domainlist:
    spliti=spliti+1
    if spliti % 50 ==0:
        print('')
    if regdomain!='':
        print(regdomain)

递进版本

import itertools
spliti=0
baseword=[]
domainlist=''
abc=0
keystart='pid'
for i in range(97,123):               
    baseword.append(chr(i))

for item in itertools.product(keystart,baseword,baseword,baseword):
    spliti=spliti+1
    if spliti % 1000 ==0:
        abc=abc+1
    if item!='':
        with open(str(abc)+'.txt',mode='a',encoding='utf-8') as f:
            f.write(domainlist.join(item)+'\n')

更迭版本

import itertools
spliti=0
baseword=[]
domainlist=''
abc=0
keystart='way'

for i in range(97,123):               
    baseword.append(chr(i))

for item in itertools.product(keystart[0],keystart[1],keystart[2],baseword,baseword):
    spliti=spliti+1
    if spliti % 1000 ==0:
        print('')
        abc=abc+1
    if item!='':
#        print(domainlist.join(item))
        with open(str(abc)+'.txt',mode='a',encoding='utf-8') as f:
            f.write(domainlist.join(item)+'\n')

跃升版本

from itertools import product
spliti=0
domainlist=''
filename=0
li=[]

'''
s=list(keystart)
print(len(keystart))
s.extend(keystart)
s0=(','.join(keystart))
'''

def getdomainword(key,nums,flag):
    baseword=[]
    a=list(key)
    for i in range(97,123):               
        baseword.append(chr(i))  
    if flag==0:
        for i in range(nums):
            a.append(baseword)
    elif flag==1:
        for i in range(nums):
            a.insert(i,baseword)
    return a

print('请输入域名关键词: ',end='')
domain=input()
print('请输入位数(穷举英文位数): ',end='')
numbs=int(input())
print('请输入关键词 "'+domain+'" 在域名中的位置("0"在头部,"1"在尾部): ',end='')
flag=int(input())

li=getdomainword(domain,numbs,flag)

for item in product(*li):
    spliti=spliti+1
    if spliti % 1000 ==0:
        print('')
        filename=filename+1
    if item!='':
        print(domainlist.join(item))
        
#        with open(str(filename)+'.txt',mode='a',encoding='utf-8') as f:
#            f.write(domainlist.join(item)+'\n')

下面是各个系统的可执行文件,直接下载解压执行即可

Mac OS

Windows(64Bit)

Linux(32Bit)

]]>
807 0 0 0
<![CDATA[ESP32-C3/S2 快速参考手册]]> http://mixdiy.com/index.php/2022/03/13/esp32-refrance/ Sun, 13 Mar 2022 14:43:44 +0000 http://mixdiy.com/?p=842

通用控制

MicroPython 的串口交互调试(REPL)在 UART0 (GPIO21=TX, GPIO20=RX),波特率为:115200。 Tab按键补全功能对于找到每个对象的使用方法非常有用。 粘贴模式 (ctrl-E) 对需要复制比较多 的python代码到REPL是非常有用。

The machine module:

import machine

machine.freq()          # 获取CPU当前工作频率

machine.freq(240000000) # 设置CPU的工作频率为 240 MHz

The esp module:

import esp

esp.osdebug(None)       # 关闭原厂 O/S 调试信息

esp.osdebug(0)          # 将原厂 O/S 调试信息重定向到 UART(0) 输出

# flash交互的低级方法

esp.flash_size()

esp.flash_user_start()

esp.flash_erase(sector_no)

esp.flash_write(byte_offset, buffer)

esp.flash_read(byte_offset, buffer)

The esp32 module:

import esp32

esp32.hall_sensor()     # 读取内部霍尔传感器

esp32.raw_temperature() # 读取内部温度传感器,在MCU, 单位:华氏度F

esp32.ULP()             # 使用超低功耗协处理器(ULP

请注意ESP32内部温度读取数值会比实际要高,因为芯片工作时候回发热。 从睡眠状态唤醒后立即读取温度传感器可以最大限度地减少这种影响。

Networking

The network module:

import network

wlan = network.WLAN(network.STA_IF) # 创建 station 接口

wlan.active(True)       # 激活接口

wlan.scan()             # 扫描允许访问的SSID

wlan.isconnected()      # 检查创建的station是否连已经接到AP

wlan.connect('essid', 'password') # 连接到指定ESSID网络

wlan.config('mac')      # 获取接口的MAC地址

wlan.ifconfig()         # 获取接口的 IP/netmask(子网掩码)/gw(网关)/DNS 地址

ap = network.WLAN(network.AP_IF) # 创捷一个AP热点接口

ap.config(essid='ESP-AP') # 激活接口

ap.config(max_clients=10) # 设置热点允许连接数量

ap.active(True)         # 设置APESSID名称

连接到本地WIFI网络的函数参考:

def do_connect():

    import network

    wlan = network.WLAN(network.STA_IF)

    wlan.active(True)

    if not wlan.isconnected():

        print('connecting to network...')

        wlan.connect('essid', 'password')

        while not wlan.isconnected():

            pass

    print('network config:', wlan.ifconfig())

一旦网络建立成功,你就可以通过 socket 模块创建和使用 TCP/UDP sockets 通讯, 以及通过 urequests 模块非常方便地发送 HTTP 请求。

延时和时间

Use the time module:

import time

time.sleep(1)           # 睡眠1

time.sleep_ms(500)      # 睡眠500毫秒

time.sleep_us(10)       # 睡眠10微妙

start = time.ticks_ms() # 获取毫秒计时器开始值

delta = time.ticks_diff(time.ticks_ms(), start) # 计算从开始到当前时间的差值

定时器

ESP32-C3拥有2个定时器。使用 machine.Timer 类通过设置timer ID号为 0-1

from machine import Timer

tim0 = Timer(0)

tim0.init(period=5000, mode=Timer.ONE_SHOT, callback=lambda t:print(0))

tim1 = Timer(1)

tim1.init(period=2000, mode=Timer.PERIODIC, callback=lambda t:print(1))

该周期的单位为毫秒(ms)

Virtual timers are not currently supported on this port.

引脚和GPIO

使用 machine.Pin 模块:

from machine import Pin

p0 = Pin(0, Pin.OUT)    # 创建对象p0,对应GPIO0口输出

p0.on()                 # 设置引脚为 "on" (1)高电平

p0.off()                # 设置引脚为 "off" (0)低电平

p0.value(1)             # 设置引脚为 "on" (1)高电平

p2 = Pin(2, Pin.IN)     # 创建对象p2,对应GPIO2口输入

print(p2.value())       # 获取引脚输入值, 0(低电平)或者 1(高电平)

p4 = Pin(4, Pin.IN, Pin.PULL_UP) # 打开内部上拉电阻

p5 = Pin(5, Pin.OUT, value=1) # 初始化时候设置引脚的值为 1(高电平)

可以使用引脚排列如下 (包括首尾): 0-10, 18-21.分别对应ESP32-C3芯片的实际引脚编号。 请注意,用户使用自己其它的开发板有特定的引脚命名方式(例如:DO, D1, …)。 由于MicroPython致力于支持不同的开发板和模块,因此我们采用最原始简单具且有共同特征的引 脚命名方式。如果你使用自己的开发板,请参考其原理图。

注意:

  • 引脚21和20分别是串口交互(REPL)的TX和RX。
  • 引脚19和18分是USB的D+和D-。
  • 部分引脚的pull值可以设置为 Pin.PULL_HOLD 以降低深度睡眠时候的功耗。

UART (serial bus)

See machine.UART.

from machine import UART

uart1 = UART(1, baudrate=115200, tx=6, rx=7)

uart1.write('hello')  # write 5 bytes

uart1.read(5)         # read up to 5 bytes

The ESP32-C3 has 2 hardware UARTs: UART0, UART1. They each have default GPIO assigned to them, however depending on your ESP32-C3 variant and board, these pins may conflict with embedded flash, onboard PSRAM or peripherals.

Any GPIO can be used for hardware UARTs using the GPIO matrix, so to avoid conflicts simply provide tx and rx pins when constructing. The default pins listed below.


UART0UART1
tx21任意IO
rx20任意IO

PWM (脉宽调制)

There’s a higher-level abstraction machine.Signal which can be used to invert a pin. Useful for illuminating active-low LEDs using on() or value(1).

PWM 能在所有可输出引脚上实现。基频的范围可以从 40Hz 到 40MHz 但需要权衡: 随着基频的 增加 占空分辨率 下降. 详情请参阅: LED Control . 现在占空比范围为 0-1023

Use the machine.PWM class:

from machine import Pin, PWM

pwm0 = PWM(Pin(0))      # 1个引脚中创建PWM对象

pwm0.freq()             # 获取当前频率

pwm0.freq(1000)         # 设置频率

pwm0.duty()             # 获取当前占空比

pwm0.duty(200)          # 设置占空比

pwm0.deinit()           # 关闭引脚的 PWM

pwm2 = PWM(Pin(2), freq=20000, duty=512) # 在同一语句下创建和配置 PWM

ADC (模数转换)

ADC功能在ESP32-C3引脚0-4上可用(ADC1的5个输入通道)。请注意,使用默认配置时,ADC引脚 上的输入电压必须介于0.0v和1.0v之间(任何高于1.0v的值都将读为4095)。如果需要增加测 量范围,需要配置衰减器。

Use the machine.ADC class:

from machine import ADC

adc = ADC(Pin(0))          # ADC引脚上创建ADC对象

adc.read()                  # 读取测量值, 0-4095 表示电压从 0.0v - 1.0v

adc.atten(ADC.ATTN_11DB)    # 设置 11dB 衰减输入 (测量电压大致从 0.0v - 3.6v)

adc.width(ADC.WIDTH_9BIT)   # 设置 9位精度输出 (返回值 0-511)

adc.read()                  # 获取重新配置后的测量值

ESP32-C3 特定的 ADC 类使用方法说明:

ADC.atten(attenuation)

该方法允许设置ADC输入的衰减量,以获取更大的电压测量范围,但是以精度为代价的。 (配置后相同的位数表示更宽的范围)。衰减选项如下:

  • ADC.ATTN_0DB: 0dB 衰减, 最大输入电压为 1.00v - 这是默认配置
  • ADC.ATTN_2_5DB: 2.5dB 衰减, 最大输入电压约为 1.34v
  • ADC.ATTN_6DB: 6dB 衰减, 最大输入电压约为 2.00v
  • ADC.ATTN_11DB: 11dB 衰减, 最大输入电压约为3v

Warning

尽管通过配置11dB衰减可以让测量电压到达3.6v,但由于ESP32-C3芯片的最大允许输入电压是3.6V, 因此输入接近3.6V的电压可能会导致IC烧坏!

ADC.width(width)

该方法允许设置ADC输入的位数精度。 选项如下:

  • ADC.WIDTH_9BIT: 9 bit data
  • ADC.WIDTH_10BIT: 10 bit data
  • ADC.WIDTH_11BIT: 11 bit data
  • ADC.WIDTH_12BIT: 12 bit data - 这是默认配置

软件SPI总线

EPS32内部有两个SPI驱动。其中1个是通过软件实现 (bit-banging),并允许配置到所有引脚, 通过 machine.SoftSPI 类模块配置:

from machine import Pin, SoftSPI

# 在给定的引脚上创建SoftSPI总线

# (极性)polarity是指 SCK 空闲时候的状态

# (相位)phase=0 表示SCK在第1个边沿开始取样,phase=1 表示在第2个边沿开始。

spi = SoftSPI(baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4))

spi.init(baudrate=200000) # 设置频率

spi.read(10)            # MISO引脚读取10字节数据

spi.read(10, 0xff)      # MISO引脚读取10字节数据同时在MOSI输出0xff

buf = bytearray(50)     # 建立缓冲区

spi.readinto(buf)       # 读取数据并存放在缓冲区 (这里读取50个字节)

spi.readinto(buf, 0xff) # 读取数据并存放在缓冲区,同时在MOSI输出0xff

spi.write(b'12345')     # MOSI引脚上写5字节数据

buf = bytearray(4)      # 建立缓冲区

spi.write_readinto(b'1234', buf) # MOSI引脚上写数据并将MISO读取数据存放到缓冲区

spi.write_readinto(buf, buf) # MOSI引脚上写缓冲区的数据并将MISO读取数据存放到缓冲区

Warning

目前在创建软件SPI对象时,sck, mosi 和 miso 所有 的引脚 必须 定义。

硬件SPI总线

有两个硬件SPI通道允许更高速率传输(到达80MHz)。 也可以配置成任意引脚,但相关引脚要 符合输入输出的方向性,这可以参阅(see 引脚和GPIO口)内容。通过自定义引脚而非 默认引脚,会降低传输速度,上限为40MHz。以下是硬件SPI总线默认引脚:


HSPI (id=1)VSPI (id=2)
sck1418
mosi1323
miso1219

Hardware SPI is accessed via the machine.SPI class and has the same methods as software SPI above:

from machine import Pin, SPI

hspi = SPI(1, 10000000)

hspi = SPI(1, 10000000, sck=Pin(14), mosi=Pin(13), miso=Pin(12))

vspi = SPI(2, baudrate=80000000, polarity=0, phase=0, bits=8, firstbit=0, sck=Pin(18), mosi=Pin(23), miso=Pin(19))

SoftI2C总线

I2C总线分软件和硬件对象,硬件可以定义0和1,通过配置可以在任意引脚上实现改功能, 详情请看 machine.SoftI2C 类模块:

from machine import Pin, SoftI2C

# 构建1I2C对象

i2c = SoftI2C(scl=Pin(5), sda=Pin(4), freq=100000)

# 构建一个硬件 I2C 总线

i2c = I2C(0)

i2c = I2C(1, scl=Pin(5), sda=Pin(4), freq=400000)

i2c.scan()              # 扫描从设备

i2c.readfrom(0x3a, 4)   # 从地址为0x3a的从机设备读取4字节数据

i2c.writeto(0x3a, '12') # 向地址为0x3a的从机设备写入数据"12"

buf = bytearray(10)     # 创建110字节缓冲区

i2c.writeto(0x3a, buf)  # 写入缓冲区数据到从机

Hardware I2C bus

There are two hardware I2C peripherals with identifiers 0 and 1. Any available output-capable pins can be used for SCL and SDA but the defaults are given below.


I2C(0)I2C(1)
scl1825
sda1926

The driver is accessed via the machine.I2C class and has the same methods as software I2C above:

from machine import Pin, I2C

i2c = I2C(0)

i2c = I2C(1, scl=Pin(5), sda=Pin(4), freq=400000)

实时时钟(RTC)

See machine.RTC

from machine import RTC

rtc = RTC()

rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # 设置时间(年,月,日,星期,时,分,秒,微秒)

                                             # 其中星期使用0-6表示星期一至星期日。

rtc.datetime() # 获取当前日期和时间

WDT (Watchdog timer)

See machine.WDT.

from machine import WDT

# enable the WDT with a timeout of 5s (1s is the minimum)

wdt = WDT(timeout=5000)

wdt.feed()

深度睡眠模式

下面代码可以用来睡眠、唤醒和检测复位唤醒:

import machine

# 检测设备是否从深度睡眠中唤醒

if machine.reset_cause() == machine.DEEPSLEEP_RESET:

    print('woke from a deep sleep')

# 使设备进入深度睡眠,时间10秒。

machine.deepsleep(10000)

注意事项:

  • 调用深度睡眠函数 deepsleep() 如果不提供参数(时间)的话可能会让设备无限期休眠。
  • 软件复位不能触发复位事件(reset cause)。
  • 可能会出现一些泄漏电流流经内部上下拉电阻,为了进一步降低功耗,可以关闭GPIO的上下拉电阻:
    p1 = Pin(4, Pin.IN, Pin.PULL_HOLD)



  • 退出深度睡眠后,有必要恢复GPIO原来的状态 (例如:原来是输出引脚)
    p1 = Pin(4, Pin.OUT, None)


RMT

The RMT is ESP32-specific and allows generation of accurate digital pulses with 12.5ns resolution. See esp32.RMT for details. Usage is:

import esp32

from machine import Pin

r = esp32.RMT(0, pin=Pin(18), clock_div=8)

r   # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8)

# The channel resolution is 100ns (1/(source_freq/clock_div)).

r.write_pulses((1, 20, 2, 40), start=0) # Send 0 for 100ns, 1 for 2000ns, 0 for 200ns, 1 for 4000ns

单总线驱动(Onewire

单总线驱动允许通过软件在各个引脚上实现:

from machine import Pin

import onewire

ow = onewire.OneWire(Pin(12)) # 在引脚 GPIO12 创建单总线对象ow

ow.scan()               # 扫描设备,返回设备编号列表

ow.reset()              # 复位总线

ow.readbyte()           # 读取1字节

ow.writebyte(0x12)      # 写入1个字节(0x12

ow.write('123')         # 写入多个字节('123')

ow.select_rom(b'12345678') # 根据ROM编号选择总线上的指定设备

下面是一个DS18B20设备的驱动函数:

import time, ds18x20

ds = ds18x20.DS18X20(ow)

roms = ds.scan()

ds.convert_temp()

time.sleep_ms(750)

for rom in roms:

    print(ds.read_temp(rom))

确保数据引脚连接了 4.7k 的上拉电阻。另外请注意每次采集温度都需要用到 convert_temp() 模块。

NeoPixel and APA106 driver

Use the neopixel and apa106 modules:

from machine import Pin

from neopixel import NeoPixel

pin = Pin(0, Pin.OUT)   # 设置引脚GPIO0来驱动 NeoPixels

np = NeoPixel(pin, 8)   # GPIO0上创建一个 NeoPixel对象,包含8个灯珠

np[0] = (255, 255, 255) # 设置第一个灯珠显示数据为白色

np.write()              # 写入数据

r, g, b = np[0]         # 获取第一个灯珠的颜色

The APA106 driver extends NeoPixel, but internally uses a different colour order:

from apa106 import APA106

ap = APA106(pin, 8)

r, g, b = ap[0]

APA102 (DotStar) uses a different driver as it has an additional clock pin.

低级别的 NeoPixel 驱动:

import esp

esp.neopixel_write(pin, grb_buf, is800khz)

Warning

默认情况下 NeoPixel 被配置成控制更常用的 800kHz 单元设备。用户可以通过使用替代的定时器 来说控制其他频率的设备 (通常是 400kHz)。 可以通过使用定时器 timing=0 当构建 NeoPixel 对象的时候。

DHT 驱动

DHT 温湿度驱动允许通过软件在各个引脚上实现:

import dht

import machine

d = dht.DHT11(machine.Pin(4))

d.measure()

d.temperature() # eg. 23 (°C)

d.humidity()    # eg. 41 (% RH)

d = dht.DHT22(machine.Pin(4))

d.measure()

d.temperature() # eg. 23.6 (°C)

d.humidity()    # eg. 41.3 (% RH)

WebREPL (Web浏览器交互提示)

WebREPL (通过WebSockets的REPL, 可以通过浏览器使用) 是ESP8266端口实验的功能。 可以从 https://github.com/micropython/webrepl 下载并打开html文件运行。 (在线托管版可以通过访问 http://micropython.org/webrepl)直接使用, 通过执行 以下命令进行配置:

import webrepl_setup

按照屏幕的提示操作。重启后,允许使用WebREPL。如果你禁用了开机自动启动WebREPL, 可以通过以下命令使用:

import webrepl

webrepl.start()

# 也可在在开始时候设置一个密码

webrepl.start(password='mypass')

这个 WebREPL 通过连接到ESP32-C3的AP使用,如果你的路由器配网络配置正确,这个功能 也可以通过STA方式使用,那意味着你可以同时上网和调试ESP32-C3。(如果遇到不可行的 特殊情况, 请先使用ESP32-C3 AP方式)。

除了终端/命令符的访问方式, WebREPL同时允许传输文件 (包含上传和下载)。Web客户端有相应的 功能按钮,也可以通过 webrepl_cli.py 模块上存储的命令行进行操作。

有关将文件传输到ESP32-C3其他支持的替代方法,请参阅MicroPython论坛

]]>
842 0 0 0
<![CDATA[python获取英文单词库]]> http://mixdiy.com/index.php/2022/03/14/get-english-words/ Mon, 14 Mar 2022 14:38:57 +0000 http://mixdiy.com/?p=855

清洗文章获得单词库,生成域名

f = open("/home/peter/danci.txt")               # 返回一个文件对象
t = open("/home/peter/dan.txt","a")
line = f.readline()               # 调用文件的 readline()方法 
while line: 
    print(line)                   # 后面跟 ',' 将忽略换行符 
#    print(line, end = " ")     # 在 Python 3 中使用 
    line = f.readline() [0:-5]
    t.writelines(line+'\n')
f.close()  
]]>
855 0 0 0
<![CDATA[Fritzing使用简介]]> http://mixdiy.com/index.php/2022/03/20/fritzing-user-manal/ Sun, 20 Mar 2022 14:58:12 +0000 http://mixdiy.com/?p=872

Fritzing是一款图形化Arduino电路开发软件,还是个电子设计自动化软件,可以用于学习和制作电路原理图和PCB,它支持设计师,艺术家,研究人员和爱好者参加从物理原型到进一步实际的产品。还支持用户记录其阿尔杜伊诺和其他电子为基础的原型,与他人分享,在教室里教电子,并建立一家生产印刷电路板的布局。

应用介绍

Frizing简化了过去PCB布局工程师在干的事情,全部使用“拖拖拉拉”的方式完成复杂的电路设计,不过目前这套软件的元件库还有点算很少(但可以自己画),也没看到CRC功能,未来应该慢慢元件库多了以后会比较实用一点。

但对艺术家或是非电子信息背景的人来说,Frizing算是很好上手的工具,你可以很简单方式拖拉元件以及连接线路,最后按下File-》Export,就可以输出成标准Eagle使用的格式,连线路都会帮忙AutoRouter喔!。最棒的是,电路的样式还帮你设计成可以直接挂载在Arduino上面,方便你送洗电路板回来之后,焊接好元件就马上可以用啰。

光看界面很像是个很棒的软件,它不是模拟Arduino运作,也不能替代Arduino程序语言开发,但是…它可以快速帮你设计电路。

或许可以构架在此上来设计类似Proteus仿真工具,那就真的完全不需要买ardunio。

使用方法

1、如果在元件库中找不到元件,通用IC是个好帮手。把他拖到你的草稿上,然后使用指示器中的小工具:从25种不同的通孔和SMD包中选择;修改针脚标签;对DIP和SDP修改针脚数。你也可以通过针标签编辑器修改针脚名称。

2、在面包板视图中,拖动元件保持它的脚连接在面包板上,在开始拖动时按住Alt(Linux:meta)键。

3、在面包板视图中,增加曲线,绑定腿,按住Control(mac:command)键拖动。你可以在配置中设置是否弯曲的线是默认值。

4、在面包板视图中,从针脚拖出一条线,按住Alt(Linux:Meta)键。

5、要拖动画布,按住空格键使用鼠标拖动它。

6、自由旋转面包板或PCB视图中的元件,选择它,然后使鼠标接近它的一角直到看到旋转的光标。按下鼠标,那个角将会跟随你的鼠标拖动旋转。

7、在PCB视图中自由转动logo文本或图像,按住Alt(linux:meta)键,然后自由旋转它。

8、在视图中开启某一层的可视度,进入视图菜单,选择一个可视的层的项目。或者从窗口菜单打开层面板。

9、元件库中的图标可能和多个元件相关联。所以从元件库中拖动一个图标到sketch中时,查看指示栏。指示栏将显示可以选择修改的元件,或者将它转换为相关的元件。元件库的图标也是一个小的“堆栈”,而不只是一个图标。

10、从Fritzing中导出图像时,可以选择导出哪一些层。在选择‘导出。..’命令前,进入视图菜单,隐藏不想被看到的层。

11、编辑元件标签,双击它,或者使用指示窗口中的文本输入控件(在指示栏Inspector下面的“芯片标签”里输入芯片名字)。

12、在元件标签上显示不同的属性,例如旋转或改变字体,右键点标签选择。

13、在导线上增加曲点,在想要增加的地方双击。

14、删除一个导线上的曲点,只要双击它。

15、更精确的移动选定的元件,使用方向键。shift-方向键移动10个单位。

16、拖动一段线段(在两个曲点之间的),可以按住Alt键(Linux:Meta)拖动。如果同时按住Shift,线段将会垂直或者水平移动。

17、使用shift拖动一段导线终端或者弯曲点,可以强制线段为45度(或者45度的倍数)。如果导线连接到了其他的导线,拖动的线段将和他临近的线段以90度角连接。。

18、在原理图和PCB视图中,如果按住Alt键(Linux:Meta)拖动一个曲点,将会从那个曲点处拖出一条新导线。

19、强制一个元件水平或者垂直的运动,在拖动时按下shift键。

20、如果在选择元件或者导线(段)时遇到问题,尝试选择挡住的元件,把它发送到下面:使用在元件菜单上的上升和下降功能,或者环境菜单(右键菜单)。

]]>
872 0 0 0
<![CDATA[在Fritzing中创建自定义元件]]> http://mixdiy.com/index.php/2022/03/20/diy-parts-in-fritzing/ Sun, 20 Mar 2022 15:10:50 +0000 http://mixdiy.com/?p=880

自定义元件

自定义元件

  1. http://fritzing.org/learning/tutorials/creating-custom-parts/
  2. https://www.cnblogs.com/tony-ning/p/5167732.html
    在Fritzing中创建自定义元件(转自:http://www.eeworld.com.cn/mp/ROHM/a22224.jspx)
    这篇文章来源于DevicePlus.com英语网站的翻译稿。
    请添加图片描述
    Fritzing是一个开源的原型制作平台,用户可通过此软件轻松创建电路。与其他免费电路设计工具(比如Eagle或KiCAD EDA)相比,Fritzing的主要优势是能够在“面包板”视图中创建电路——就像连接真实元件。Fritzing拥有许多内置电子元件和第三方库,但是有时候,您的电路可能会用到一些没有包含在其中的新元件或稀有元件。本教程将逐步指导您如何将这些元件转换为Fritzing元件。

软件

  • Fritzing – 下载地址: https://fritzing.org/home/
  • Inkscape – 下载地址: https://inkscape.org/

第1步:收集所有信息

绘制矢量图形和设计元件之前,我们需要获得元件的所有信息,主要是指创建面包板视图所需的元件物理尺寸。面包板视图中的元件尺寸应与实际元件相同。元件的数据表通常包含元件尺寸,所以最好从这里开始。制造商倾向于将尺寸放在数据表的最后几页上。
请添加图片描述
图1. 数据表中的元件尺寸

不幸的是,有时,您正在设计的元件根本没有数据表,尤其是该元件是一个分线板的时候。您可能能够找到分线版上模块的数据表,但是没有分线板自身的尺寸。这时您就不得不临时制作了。请先尝试在互联网上搜索一下。尽管还没有人制作这个Fritzing元件,但是有人可能会为您测量尺寸。如果网上没有,那么请拿出卡尺并开始测量吧。

作为例子,我将制作Fritzing元件:UV传感器(ML8511)。所有传感器的创建步骤几乎都是相同的。该传感器位于一个小型分线板上,由于没有分线板尺寸,我们必须测量大多数项目。
在这里插入图片描述
图2. ML8511分线板

要为面包板视图创建图形,我们至少需要外板尺寸、安装孔位置和连接器位置。所有这些都会影响元件在Fritzing中的位置以及与其他元件的连接,因此必须非常精确!
在这里插入图片描述
图3. 所需尺寸

**提示:**分线板上的许多元件都具有标准化的尺寸。比如,排针的间距为2.54毫米(0.1英寸)。像IC这样的元件尺寸我们可以查询其数据表。测量这些通常不是一个好主意,因为您的测量结果还不如数据表中的尺寸准确。

由于面包板视图应与真实元件相同,因此我们还应该测量元件(比如电容、电阻和IC)在面包板上的焊接位置。测量这些元件的位置时不必像连接器那样精确。但是,它们在面包板视图中的位置应与实际元件的位置大致相同。

我们有了需要的所有尺寸,是时候为真实元件创建精确的矢量图形了!

第2步:面包板视图

Fritzing中的所有图形都基于矢量,并以SVG格式存储 – 可扩展矢量图形(Scalable Vector Graphics)。 通常,计算机中的图像通过像素阵列表示。这种存储图形信息的方法称为光栅图形(raster),该方法存在一个主要问题:您永远无法获得小于一个像素的细节。

另一方面,矢量图形处理的是对象,而不是像素。如果您在光栅图形编辑器(比如经典的Microsoft Paint)中创建一个矩形时,您可以更改某些像素的颜色。而在矢量图形编辑器中,您可以创建一个矩形对象。该对象具有颜色等属性;您可以移动、拉伸、转动或以其他方式编辑该对象。放大矢量矩形对象时,您永远不会丧失精度。然后,完成矢量图形后,您可以将它们导出到位图–一个光栅图像。此时,最大的优势就是:您可以定义该位图的分辨率。

我使用Inkscape实现所有矢量图形。该软件是免费的,而且简单易用。另外,它还内置了使用教程!下图为Inkscape的主窗口。左侧为工具栏,右侧为捕捉选项,顶部为当前工具设置,底部提供一些基本颜色选择。
在这里插入图片描述
图4. 主窗口

首先,我们需要启用网格,这会让对象的放置更容易一些。点击“File(文件)”菜单并单击“Document Properties(文档属性)”(或使用键盘快捷键Ctrl + Shift + D)。系统会弹出一个新的窗口。转到第三个选项卡(“Grids(网格)”)。选择“Rectangular grid(矩形网格)”并点击“New(新建)”按钮。
在这里插入图片描述
图5. 文件属性窗口

主窗口中会出现一个蓝色网格,我们可以通过更改参数“Spacing X(X间距)”,“Spacing Y(Y间距)”和“Units(单位)”来更改网格大小。由于我们将以毫米或英寸为单位处理尺寸,因此我建议将网格尺寸设置为0.1毫米或0.01英寸。当然,我们可以随时更改网格的尺寸,具体取决于我们处理的是哪种部件。

首先创建一个矩形来定义元件的外部尺寸。在主窗口中,选择矩形工具,并通过单击和拖动创建一个新的矩形。
在这里插入图片描述
图6. 新矩形

点击菜单“Object(对象)”,然后单击选项“Fill and Stroke(填充和描边)”(键盘快捷键为Ctrl + Shift + F)。首先,我们需要禁用描边,所以选择“Stroke paint(描边颜料)”选项卡并选择“No paint(无颜料)”(大X)。接下来,我们要更改矩形的颜色以匹配分线板的颜色。该板的颜色为深灰色,我们可以在“Fill(填充)”选项卡中进行设置,我选择颜色#141414

现在我们需要确定矩形的尺寸。选择矩形,并在顶部栏中更改其宽度和高度以匹配元件的外部尺寸。ML8511分线板为20.0 mm x 20.0 mm,因此我输入这些尺寸。接下来,删除正方形周围的所有空白画布是一个不错的主意,这样我们就不必担心多余的画布。再次打开“Document Properties(文档属性)”窗口,并在第一个选项卡“Page(页面)”中按下“Resize page to drawing or selection(根据图纸或选择调整页面大小)”按钮。现在这个页面跟这个正方形一样大!
在这里插入图片描述
图7. 调整页面大小以匹配正方形电路板

该正方形将成为所有部件的基板,就像真实的PCB一样。首先,我们必须添加两个安装孔。创建两个直径与实际安装孔相同的白色圆圈。要创建一个圆,请选择Circle/Ellipse(圆/椭圆工具)并创建一个椭圆。然后,就像我们处理矩形一样,将其颜色改为白色(以便我们可以在电路板的深灰色背景下看到它),并设置其宽度和高度(在本例中为3.3毫米 x 3.3毫米)。将两个圆圈放置在要创建安装孔的位置。
在这里插入图片描述
图8. 准备安装孔

现在我们来处理非常酷的矢量内容——选择页面上的所有对象(灰色方块和两个白色圆圈),然后进入菜单“Path(路径)”。点击“Object to Path(对象转路径)”选项。现在,不再有不同类型的对象(矩形和圆形)了,所有对象都具有相同的类型:路径。路径也是对象,能够使用原始对象无法实现的方法进行操作。比如,您可以将一个矩形转换一个路径并在边上添加两个点,将其变成一个六边形!您还可以对路径执行数学运算;您可以将两条路径合并成一条路径。我们将使用其中一种方法在灰色方块中“钻孔”。选择所有对象,返回“Path(路径)”菜单,单击选项“Exclusion(排除)”。眨眼间,白色圆圈现在变成了两个孔!
在这里插入图片描述
图9. 创建安装孔

最关键的元件是连接器。如您在真正的分线板上看到的那样,排针指向电路板下方,我将在设计中反映这一点。我将绘制针脚的顶部焊盘,而不是绘制针脚。首先,我将为VDD针脚创建一个浅灰色(#C1C1C1)方块,为其他三个针脚各创建一个浅灰色圆圈。正方形为2.0毫米×2.0毫米,圆圈的直径为2.0毫米。我还在两个针脚的中心添加一个较暗的圆圈,以便Fritzing中的电线具有明确的连接区域。
在这里插入图片描述
图10. 连接器针脚基础

要将一个对象移至另外一个对象的中心,我们应使用另一个功能:“Object(对象)”菜单中的“Align and Distribute(对齐和分配)(Ctrl + Shift + A)。选择第一个对象(比如浅灰色方块),然后选择第二个对象(小深灰色圆圈)。现在,在“Align and Distribute(对齐和分配)”窗口中,将“Relative to(相对于)”设置为“First selected(首先选中)”,然后单击“Center on vertical axis(垂直轴居中对齐)”和“Center on horizontal axis(水平轴居中对齐)”。这会将小圆圈恰好移至浅灰色方块的中心。
在这里插入图片描述
图11. 连接器针脚的顶视图

为了更容易操作完成的针脚,您可以按住Shift单击选择两者,然后按下Ctrl + G,将方块和中心圆组合在一起。接下来,复制并粘贴圆形针脚两次。垂直对齐所有针脚,并将间距改为标准0.1英寸。最简单的方法是将网格大小更改为0.1英寸,然后使用网格对齐。做完这些之后,将四个排针组合在一起,我们的连接器就完成了。剩下的就是将连接器放置到深灰色的分线板上。
在这里插入图片描述
图12. 连接器已经完成的电路板

至此,所有关键部件都已到位,现在要做的是添加细节,使Fritzing元件看起来像真实元件。添加的细节完全取决于您自己。我添加了两个电容、一个电阻和实际的传感器芯片。电阻和电容的尺寸是标准化的,传感器芯片的尺寸可以在其数据表中找到。
在这里插入图片描述
图13. 带有所有部件的分线板

下一步是添加文字和标记。撰写本文时,Fritzing仅支持OCRADroid字体,并会将其他字体转换为上述字体的其中一种,因此所有文本我都使用Droid字体,大小为4.5。最后,按Ctrl + A选择所有内容并将它们组合在一起(Ctrl + G)。将图形保存为新的SVG文件。结果如下图所示。我觉得这非常接近真实元件!
在这里插入图片描述
图14. 左侧为实际ML8511传感器,右侧为完成的面包板视图

好消息 – 最困难的部分已经完成!接下来的事情 – 创建原理图视图 – 要容易的多。

第3步:原理图视图

为原理图创建矢量图比面包板要容易得多。我们不必测量任何东西,因为在原理图中,分线板只是一个带有四个输入端的块。为了让事情变得更容易,我建议您导出现有的Fritzing原理图,然后进行编辑,以满足需求。要从Fritzing中导出零件SVG图形,打开面包板视图,添加一些部件(比如核心元件的三轴加速度计),然后右键单击并选择“Edit (new parts editor)(编辑(新元件编辑器))”,在元件编辑器中将其打开。系统会弹出另一个窗口——元件编辑器。稍后当我们将所有图形组合成一个Fritzing元件时,我们将使用该编辑器。现在切换到原理图,点击“文件(File)”菜单,选择“导出 “Export(导出)” → “as Image(导出为图像)” → “SVG”。在Inkscape打开这个SVG文件。
在这里插入图片描述
图17. 导出的原理图

现在我们需要添加一个间距为0.1的新矩形网格。这是因为原理图视图应以0.1英寸的倍数完成。比如,引线的长度为0.2英寸,其间距为0.1英寸。
在这里插入图片描述
图18. 准备原理图

从Fritzing导出的图形通常会被组合到一起,因此只需选择所有内容并点击“Ungroup(取消分组)”(Ctrl + Shift + G),直到没有分组为止。然后,我们只需编辑文本,删除两个引线,然后重命名其他引线以匹配分线板。将所有内容重新组合在一起,并将文件保存为SVG文件,原理图视图就完成了!
在这里插入图片描述
图19. 完成的原理图

第4步:PCB视图

说实话,涉及到绘制PCB布局时,我并不是Fritzing的忠实粉丝。也许这仅仅是因为习惯了Eagle,但是,我承认Fritzing的简单易用非常有魅力,特别是对于初学者。我在Fritzing中设计了一块小型单面PCB,而不是更复杂的双层(或更多层)板。这就是说,PCB视图是环境的一个组成部分,如果我们要确保我们的新元件能正常工作,我们也必须为这个视图创建一个图形。

就像原理图一样,最简单的方法就是从Fritzing中导出现有PCB图形并进行编辑。我从元件编辑器的PCB视图中再次导出三轴加速度计元件。这里有一个小问题:用Inkscape打开导出的文件时,您只会看到铜焊盘。这是因为在SVG文件中,所有的丝印形状和文本都是白色的,尽管在Fritzing中显示为黑色。Inkscape允许您更改文档背景颜色,所以再次打开“Document Properties(文档属性)”窗口,在“Page(页面)”选项卡的顶部,将背景颜色更改为黑色。
在这里插入图片描述
图22. 导出的PCB图形

就像原理图一样,所有东西都组合在一起。因此,请执行几次“Ungroup(取消组合)”命令,直到您可以单独选择所有白色丝印形状。将轮廓更改为与分线板一样大:20.0毫米x 20.0毫米,并且调整页面大小,以进行匹配。然后,删除我们不需要的丝印形状:中间的箭头和芯片轮廓。我们的分线板只有4个针脚,因此我们更改连接器的丝印轮廓,以与之匹配。最后,重新组合连接器轮廓,以便我们可以轻松移动它,更改其位置使其与真实的电路板相匹配。
在这里插入图片描述
图23. 修改丝印之后的PCB图形

现在我们需要更新铜焊盘的位置。从Fritzing输出PCB图形后,实际上两组六个铜焊盘是堆叠在一起的,即一组是顶部焊盘,另一组是底部焊盘。将其中一个铜焊盘组移开,然后取消组合,删除我们不需要的两个焊盘,并重新组合其他四个。然后,将它们对齐到连接器轮廓的中心。对第二组铜焊盘重复这个过程。
在这里插入图片描述
图24. 完成的PCB视图

最后一步是更改SVG文件的结构,使其达到Fritzing标准。SVG文件基本上是一个XML(可扩展标记语言)文件,所以您可以直接在任何文本编辑器中进行编辑。然而,使用Inkscape的内置编辑器会更方便。该编辑器的打开方法如下:在“Edit(编辑)”菜单中单击“XML Editor(XML编辑器)”(Ctrl + Shift + X)。
请添加图片描述
图25. Inkscape XML编辑器

选择其中一个XML节点时,系统会在Inkscape主窗口中选择适当的对象,这样您可以轻松识别给定的SVG对象与哪个XML节点相对应。我们需要添加三个新的XML节点,因此选择根节点(顶层节点,名为svg:svg id="svg2"“)并按下顶部栏最左边的按钮(“New element node(新元素节点)”)。系统会提示您输入节点名称,因此请输入不带引号的“svg:g”。这会创建一个新的组节点;我们需要将其ID从默认值改为“silkscreen”。
请添加图片描述
图26. 新“silkscreen(丝印)”节点

现在,再创建两个ID为“copper0”和“copper1”的组节点。将包含丝印对象和轮廓的所有节点移至“silkscreen”节点中。然后,将其中一个铜焊盘组移至节点“copper0”中,将整个“copper0”节点移至“copper1”节点中。最后,将第二组铜焊盘移至节点“copper1”中。最终的XML结构如下图所示。
请添加图片描述
图27. 最终的XML结构
这部分工作完成!我们已经为所有三种Fritzing视图准备好了图形,那么现在剩下的唯一任务就是将它们全部整合到一个全新的Fritzing元件中!

第5步:创建Fritzing元件

之前的所有工作都是为这一步做准备!Fritzing不允许直接创建新元件,所以唯一的方法就是编辑现有元件。比如,在面包板视图中,添加一些元件:我们之前使用的加速度计。右键单击该元件并选择选“Edit (new parts editor)(编辑(新元件编辑器))”。系统会在新窗口中打开元件编辑器。
请添加图片描述
图30. Fritzing元件编辑器

元件编辑器与Fritzing主窗口非常相似。该窗口有三个基本视图(面包板、原理图和PCB),但是,还有三个额外的选项卡:Icon(图标)、Metadata(元数据)和Connectors(连接器)。Icon(图标)选项卡允许您为元件选择一个图标。Metadata(元数据)允许您编辑元件参数,比如名称以及元件的附加信息等。Connectors(连接器)允许您添加或删除连接针脚并更改其名称和类型。

首先,我们需要设置这三个基本视图。进入面包板视图,在“File(文件)”菜单中单击“Load image for view(加载图像进行查看)”。然后,选择我们在步骤2中创建的SVG面包板文件。之后,请为原理图和PCB视图执行相同的操作。
请添加图片描述
图31. ML8511A面包板视图

接下来,我们要编辑连接器。打开“Connectors(连接器)”选项卡并将连接器数量设置为4。然后,更改连接器名称,使其与实际电路板上的连接器名称相同。
请添加图片描述
图32. ML8511A连接器视图

在“Connectors(连接器)”选项卡中设置完所有内容后,我们需要将连接器分配给SVG文件中的对象。分配完成之后,Fritzing就会知道将导线连至我们的元件。打开面包板视图,在右侧栏中,单击第一个连接器(VDD)的“Select graphic(选择图形)”按钮。现在,当您将鼠标悬停在SVG图形上时,系统会用紫色突出显示不同的SVG元素。单击要用作连接器的元素。此时,该元素上会出现一个虚线叉,并且右侧栏的VDD针脚旁边出现一个刻度标记。对面包板视图中的所有其余连接器重复此操作,原理图和PCB视图也执行相同操作。
请添加图片描述
图33. 面包板视图中指定的VDD连接器

现在我们需要添加与元件相关的所有附加信息。打开元数据选项卡,并更改标题、作者和说明。或者,您可以更改属性和标签,以便更好地对新元件进行分类。
请添加图片描述
图34 ML8511A元数据视图

我们仍然缺少的最终细节就是即将显示在元件箱中的图标。您可以为图标创建一个新的SVG图形,但是您也可以重新使用面包板、原理图或PCB视图中的图形。我建议使用面包板中的图形,因为它看起来就像真正的元件。转到Icon(图标)选项卡,然后在“File(文件)”菜单中选择“Reuse breadboard image(重新使用面包板图像)”选项。
请添加图片描述
图35. ML8511A图标视图

点击“File(文件)”菜单中的“Save as new part(另存为新元件)”,以保存元件。系统会提示您输入文件名前缀,但这不是必需的,因此只需保留默认值即可。任务完成!您现在可以关闭元件编辑器,并打开Fritzing主窗口。您的新元件将位于“My Parts(我的元件)”箱中,从现在开始,您可以像使用其他Fritzing元件一样使用它!

结果

下图显示了Fritzing面包板视图中已连至面包板的新元件!
请添加图片描述
图36. Fritzing中制作完成的ML8511A、BH1745NUC和BD7411G元件

这里还有一个额外步骤!现在,您可以对Fritzing社区做出原创性贡献,让其他人使用您制作的元件。要做到这一点,请右键单击“My parts(我的元件)”箱中的元件并选择“Export Part(导出元件)”。该元件将导出为.fzpz文件,您可以在线共享该文件!

好了,创建自定义Fritzing元件的简短指南到此结束。

另一个参考

fritzing中的器件编辑并不得到想要的效果,所以我们要借助于Inkscape去绘制,然后导入fritzing中。
首先打开fritzing,找到一个合适的器件,例如我要绘制MPU6050。
所以我先找到MPU6050的资料,也就是实物图。
 
接下来在fritzing中找到一个TB6612FNG,借助于它的面包板视图,原理图,pcb板视图。
 
选中TB6612FNG,右键单击选择    编辑(新元器件编辑器)。
出现
 
对面包板视图,原理图,pcb板视图进行导出。
依次点击   文件,导出,作为图像,SVG。这就完成一部分工作了。
接下来我们需要上面说到的工具,Inkscape
Inkscape – 下载地址:
https://inkscape.org/en/
打开inkscape,打开导出的面包板视图,
 
点击“File(文件)”菜单并单击“Document Properties(文档属性)”(或使用键盘快捷键Ctrl + Shift + D)。系统会弹出一个新的窗口。转到第三个选项卡(“Grids(网格)”)。选择“Rectangular grid(矩形网格)”并点击“New(新建)”按钮。
 
依照我们打开的对象去新建属于自己的视图,选中导入的视图,从Fritzing导出的图形通常会被组合到一起,因此只需选择所有内容并点击“Ungroup(取消分组)”(Ctrl + Shift + 
G),直到没有分组为止。效果如图。
 
因为有时候下载下来的inkscape中没有fritzing所需的字体,所以我选择借用其它器件进行编辑。注:Fritzing仅支持OCR A或Droid Sans字体。
首先我们新建一个矩形,绘制一个矩形可能一开始看不到,是透明的,但是确实存在,接下来改颜色
点击菜单“Object(对象)”,然后单击选项“Fill and Stroke(填充和笔刷)”(键盘快捷键为Ctrl + Shift + F)。首先,我们需要禁用描边,所以选择“Stroke paint(笔刷绘制)”选项卡并选择“No paint(无颜料)”(大X)。接下来,我们要更改矩形的颜色以匹配分线板的颜色。我们可以在“Fill(填充)”选项卡中进行设置。
因为一直设置不成功,所以我先选择了从对象拾取颜色,然后更改成想要的颜色。

接下来设置矩形尺寸,设置的时候要选中矩形,移动的时候选中箭头。

接下来就是挖空,我是根据参考挖的。可能由于图层或者其他原因,直接移动会被覆盖,所以我选择粘贴复制。如图。

小圆是乱的,我们需要均匀分布,选中所有小圆,点击对象,对齐和分散。根据需要进行排布,图中是我选用的。

接下来就是挖空,和绘制矩形一样,绘制小圆,选成白色,通过点击对象,对其和分散,进行调整。然后选择路径,互斥,挖出小圆。分前、中、后。

 
然后都选中,按下(ctrl+G)进行组合,按照上面再挖两个大孔,我是最后挖的。
接下来就是电容,芯片啥的制作,从我们导出的面包板视图上选取需要的部分,复制粘贴,组合到一起就可以了。
 
对于文字部分,我建议复制粘贴,修改内容。
这是我的成果图。
 
原理图部分基本一致。
附赠详细版链接一条,就是字太多
https://rohm.eefocus.com/article/id-407

_I07~LCVL[S3FOZO}V8Z)U2.png (160.87 KB, 下载次数: 44)

_I07~LCVL[S3FOZO}V8Z)U2.png

新的补充,包括修改字体等

什么是Fritzing?

Fritzing Logo

Fritzing是一个强大的开源工具让任何一个人用于教学,分享和模仿他们的电子项目! 它允许你设计原理图,因此设计一个部件,然后可以添加到非常专业的接线图。 你甚至可以设计自己的PCB,并从你设计的文件制造。 在SparkFun,我们在教室中使用Fritzing,我们的连接指南,以及任何其他地方,我们需要展示如何将我们的电路板连接到其他硬件。

Fritzing Hookup Example

一个利用Frizting将INA169连接到Arduino的例子

Fritzing的令人敬畏的事情是,你可以为你的项目制作自己的Fritzing零件,并在社区中分享! 本教程将从头开始一步步介绍如何在Fritzing(新)零件编辑器中创建自定义Fritzing零件,

你是否需要做一个自定义的Fritzing零件?

Fritzing在软件安装的时候就带有大量的电子元件。 SparkFun还有一个Fritzing Github库,用于我们在Fritzing中尚未创建的零件。 在创建自己的零件之前,请仔细检查我们的零件是否在自带的元件库或者Github上已经有,或者如果另一个Fritzing用户已经在Fritzing论坛上创建了你所需要的零件。 如果零件已经创建,它将为你节省大量时间! 但是,如果你确定你需要的零件在Frizting里面还没有,请继续阅读!

建议阅读

本教程假设你已经熟悉Adobe Illustrator,Inscape或两者。 使用这些程序超出了本教程的范围。 如果你需要更多的信息,如何使用这些程序的eithwer,他们各自的网站有很多教程和指南,如何开始矢量图形。 如果失败,总是有Google。

这里是其他相关的教程,你阅读这个教程前可能想查看:

下载和安装

你需要下载和安装Frizting软件,并跟着教程来制作你自己的Frizting零件

请注意:如果你只需要做一个基本的IC,Frizting(新)部件编辑器让你轻松制作自定义IC,你不需要下载矢量图形编辑器。但你仍然可以往下阅读,因为本教程将在Frizting(新)零件编辑器中构建一个自定义的IC。

Fritzing

Frizting官网(可能需要科学上网)的下载页面,为你的操作系统下载最新的Frizting版本,找到你要将Fritzing安装在硬盘驱动器上的位置,然后在该位置解压缩Fritzing文件夹。

矢量图形编辑器

有很多不同类型的矢量图形编辑器。我们在SparkFun使用的矢量图形编辑器是Adobe Illustrator和Inkscape。选择你最熟悉和用起来最舒服的。如果你没有一个矢量图形编辑器,Inkscape是一个强大的开源软件选择,并且它是免费的。

Inkscape

Inkscape Logo

Inkscape下载页面,并为你的计算机下载相应的正式发行包。

Windows用户:双击可执行文件。 按照Inkscape设置向导。

Mac OS X用户:按照Inkscape网站上的最新说明进行操作。

Adobe Illustrator

Adobe Illustrator Logo

Adobe Illustrator不是免费的,但如果你已经有Adobe Creative Cloud,你可以下载它。 你还可以购买Illustrator每月会员资格。

请注意:我们与Adobe没有任何关系,只是宣传Illustrator,因为它是一个强大的软件,对在本教程中很有帮助。

Other Downloads其它需要下载的东西

Fritzing字体和模板

Fritzing使用OCR-A字体作为IC使用。 对于所有其他零件,你可以使用OCR-A和Droid Sans fonts字体。 Fritzing具有可在其网站上下载的字体和模板。 你将需要下载Fritzing的图形标准来遵循本教程。 转到他们的模板下载页面,并下载Fritzing的图形标准文件夹。 下载其zip文件后,你需要解压zip文件夹,并放置在计算机上你将要在计算机上安装字体的位置。

SparkFun Fritzing示例模板

本教程将引用很多SparkFun Fritzing示例模板。 如果你正在为SparkFun板制作Fritzing零件或想要一个起点,请从SparkFun Fritzing零件Github库中下载这组示例模板。 SparkFun Fritzing模板将具有本教程的示例,SparkFun T5403气压计SVG,要比较和处理的文件。

面包板视图

当Fritzing启动时,你将会进入欢迎界面,并且你将要转到面包板视图。

breadboard view

在面包板视图中你需要做两个重要的步骤,首先,创建你的面包板SVG,然后上传。 Fritzing更倾向于使用SVG格式,所以你的图像看起来很棒,当你放大和缩小! 其次,你需要改变连接器针脚。

请注意:如果你只制作一个基本的IC,你可以跳到本教程的编辑面包板视图部分。

Fritzing图形标准

在Fritzing网站上,有很多图形标准要遵循。 这是一个很好规范,因为遵循图形标准,你的零件可以匹配其他Fritzing零件。

模板

当你制作零件时,建议从模板开始。 有一个要引用的零件的图像,因此,当制作你的SVG文件时,该过程会更快。

提示:如果你在为EAGLE中设计的板制作自定义Fritzing零件,则可以下载ULP将板转换为SVG格式。 这样,你可以有一个准确的SVG的EAGLE板作为参考。 你可以在Cadsoft网站上找到EAGLE ULP。

现在是时候为你的面包板视图制作你的图形!

创建新元件

在本教程中,我们将会为SparkFun T5403气压计创建一个Frizting新零件。

T5403 Breakout Image

SparkFun T5403的EAGLE图

打开Fritzing应用程序。 你应该在程序顶部看到欢迎,面包板,示意图和PCB的选项卡。 单击面包板按钮以确保你目前在面包板视图中。

Breadboard Button

检查库中自带元件

如果你只是在Fritzing中更新一个板,首先检查是否有一个与你想要创建的Fritzing部分相接近或相关的零件。你可以在搜索栏中键入零件的名称。

Search for Part

搜索栏可以在零件窗口的顶部找到

你也可以在Fritzing的零件窗口的不同部分查看类似的零件。

Parts Window

寻找SparkFun火焰并点击可以看到一个巨大的SparkFun Fritzing零件库

从绘制IC开始

如果没有一个零件像你想要的,使用IC作为基础是一个好的开始。 单击零件窗口中的CORE选项卡。 向下滚动,直到看到IC。 在ICs部分下,单击并拖动IC图标到Breadboard窗口。

Core Tab

自定义IC很简单,因为Fritzing可以改变引脚和IC封装的数量

Dragging IC on breadboard window

更改IC的名称

查看右侧的“查看栏”中IC的属性。 将IC的名称更改为元件名。 然后,更改引脚部分中模块或元件的引脚数。 对于SparkFun T5403气压计,我们需要8个引脚。 你将在“面包板”视图中看到模块中IC更改后的名称。

Changing name

Fritzing(新)元件编辑器

右键单击面包板窗口中的IC,然后选择编辑(新元件编辑器)。Fritzing(新)元件编辑器弹出。

Go to Parts Editor

Fritzing(新)零件编辑器有6个主要部分,你需要在其中进行更改。那些是:

  • 面包板
  • 示意图
  • PCB
  • 图标
  • 元数据
  • 连接器

真的没有你需要遵循的命令。在做了几个不同的自定义部件后,你可能会最终在一个视图开始之前的其他工作。在本教程中,我们只是去一个个讲述教程。

作者注释:我发现,对于具有大量引脚的板,从连接器视图开始的板可以节省更多的时间,因为你可以在列表中更快地命名连接器引脚。

在继续之前,最好先保存为新零件。如果你需要在制作自定义部件时随时停止,你可以在将来再次使用。转到文件。然后,选择另存为新零件

Save as new Part

如果需要,你可以选择命名前缀


让我们继续面包板视图!

自定义面包板SVG

创建文件

打开矢量图形编辑器并创建一个新文件。文件的图像大小应与你的开发板的大小相同。SparkFun T5403气压计爆破尺寸为1“x 0.650”。你将要使用一个好的命名约定保存文件,因为在创建Fritzing部分时最终需要3个不同的svg文件。

Illustrator用户: 你可以通过转到文件 - >另存为,保存为SVG,然后点击保存保存。

对于此示例,面包板SVG命名为:SFE_T5403_Barometer_Breakout_breadboard.svg

使用模板作为参考

要比较不同的图层和组,你可以打开Fritzing BreadboardViewGraphic_Template.svg文件,该文件位于先前下载的Fritzing Fonts and Template文件夹中。你还可以从SparkFun Fritzing Parts Github仓库打开示例SparkFun T5403晴雨表分线板SVG模板文件。

你可以看到示例模板如何保持图层的组织。对于SparkFun T5403气压计,有一个“面包板”组。在面包板组内,它将具有一组部件,铜层,丝印组和板路径。

制作你的自定义面包板图形的提示

你现在可以创建自定义零件的面包板图形。这里有一些有用的提示!

遵循Fritzing图形标准

Here are some main color standards for Breadboard images:

这里是面包板图像的一些主要颜色标准:

为了符合Fritzing图形标准,你将要使铜触点成为铜/镀锡颜色。

Copper Green

HEX: 9A916C, RGB: 154 145 108

如果你的板上有任何部件的引脚,使用的颜色是灰色的。

Leg Grey

HEX: 8C8C8C, RGB: 140 140 140

SparkFun红色是:HEX:E62C2E,RGB:230 44 46

把事情简单化

Fritzing的伟大之处在于,你可以使你的板子变得简单或你想要的结果。由于SparkFun总是试图使我们的产品更好的修订和有很多的板,更容易和更快的我们不包括某些细节,如痕迹或每个组件,在我们的板子上。如果电路板有新的变化,如电阻值的变化,我们不必在Fritzing部分进入和更改该电阻。更多地关注重要的组件,如IC,可能这是个更好的方式来投入你的时间。它仍然会看起来不错,但工作量少!

使用已经存在的组件

如果你需要在板上的使用已经在Fritzing有的SMD LED,请继续使用它!这将节省你的时间,并保持所有的Fritzing部分具有相同的标准。如果你创建一个自定义的板子,其他人可以使用的组件,你可以在Fritzing网站上分享,让其他人也可以使用!确保在你正在使用的矢量图形编辑器中组织好组件图形,因此在以后的的板子上使用时很容易找到这些部件。

铜组中的名称连接器引脚

Naming your connectors will be a huge time saver. For the SparkFun T5403 Barometer Breakout example, under the copper group, each connector is named connector#pad.

命名你的连接器将是一个巨大的节省时间。对于SparkFun T5403气压计示例,在铜组下,每个连接器命名为连接器#垫。

Copper Layers

示例在Illustrator中。如果你使用Inkscape,你仍然需要确保连接器已正确命名。

使用ORC-A或Droid Sans字体。

坚持Fritzing字体保持所有Fritzing部分看起来一样。建议标准字体大小为5pt。然而,有时候,你不会有空间更小的板。你最好不要低于3pt,因为它开始变得更难看到,而不放大。在Fritzing的网站,他们提到使用黑色作为字体颜色。无论你的丝印颜色往往看起来更好。对于这个例子,我们使用白色,因为这是分线板的丝印颜色,它更容易阅读红色背景。

创建复合路径以制作板开口

Illustrator用户:在PCB的大小中创建路径。对于SparkFun T5403气压计,你可以使用矩形工具制作1“x 0.650”的矩形。然后,在你的板子有开口的路径。例如,你可以使用椭圆形工具,在矩形工具下,制作完整的圆形,其中有支座和连接器针脚的开口。选择所有的开孔层和底部PCB层。

Select All

确保选择底部PCB层

接下来转到Object-> Compound Path-> Make。你现在应该有一个复合的路径,你将能够看到通过Fritzing的开口。

Final Breadboard Image

最终面包板图形

保存

确保在创建自定义板后再次另存为SVG!现在,你可以继续编辑面包板视图。

面包板视图 - 零件编辑器

加载图像

创建自定义面包板图像后,你将需要在Fritzing(新)零件编辑器中加载面包板SVG。首先,返回到Fritzing(新)零件编辑器,然后单击面包板按钮进入面包板视图。转到文件 - >加载图像的视图。

Load graphic

接下来,你将选择刚刚创建的面包板SVG,然后单击打开。图形现在应该在Fritzing(新)零件编辑器中。

连接器

在Fritzing应用程序中,你可以使用彩色线连接不同的Fritzing零件,以显示零件如何连接。为了使Fritzing知道板或部件上的连接器引脚,你需要告诉Fritzing这些连接器在哪里。

连接器引脚的名称和说明

对于面包板视图,连接器窗口将位于Fritzing(新)零件编辑器的右侧。选择一个引脚以更改引脚的名称并添加描述。

Select Pin

选择任何连接器针脚进行编辑

Change Connector Pin Name

选择连接器针脚的图形

单击连接器针脚名称右侧的选择图形按钮。然后,单击连接器针脚的图形。这将设置锚点。锚点是电线连接到该连接器的位置。默认情况下,终点将显示在所选图形的中间。如果要移动终点,你可以单击终点并按住以移动。你也可以通过在连接器窗口中单击“中心”,“W”,“N”,“S”或“E”来更改终端点。

Example Terminal Placement

你可以看到更改终端点时导线位置的差异

更改连接器类型

在“连接器”窗口中更改连接器的类型。你可以选择公头,母头或焊盘。对于SparkFun T5403气压计,所有连接器针脚都是母头。

Set Connector Type

在下图中,你可以看到将连接器类型设置为公头和母头之间的差异。

Different Connector Type

顶板的连接器类型设置为公头。底板的连接器类型正确设置为母头。

对所有连接器针脚重复此操作

名称,选择适当的图形,并更改所有连接器引脚的连接器类型。你还可以在“连接器”窗口中设置“内部连接”。

原理图

自定义原理图SVG

回到Illustrator,Inkscape或你正在使用的矢量图形编辑器。在下载的字体和模板文件夹中打开Fritzing的SchematicViewGraphic_Template.svg。你还可以从SparkFun Fritzing零件Github仓库打开示例SparkFun T5403气压计原理图SVG模板文件。

当编辑原理图以匹配电路板时,你需要确保显示每个连接器引脚。你将需要更改引脚标签以匹配连接器引脚名称。根据你的部件,你可能需要调整模板原理图的大小。确保主零件符号正方形和外销的边缘之间有0.1“的空间。

Schematic Example

确保删除0.1“维度帮助程序框,因此它不会显示在最终的Fritzing原理图图形中

保存SVG

你需要确保保存为一个新的SVG。记住要有一个命名约定,很容易说明为Fritzing部分创建的其他SVG文件之间的区别。

在零件编辑器中编辑原理图视图

加载SVG

返回零件编辑器,点击原理图按钮进入原理图视图。转到文件 - >加载图像的视图。接下来,你将选择刚刚创建的原理图SVG,然后单击打开。零件现在应该在Fritzing(新)零件编辑器中。

设置连接器引脚

如果你看看右侧的连接器窗口,你会注意到你的引脚名称已经存在。当你更改连接器引脚的名称和描述时,在面包板,原理图,PCB或连接器视图中,零件编辑器将自动更改其他视图的连接器引脚名称和描述。此外,连接器类型(公头,母头或焊盘)将仍然相同。

就像在面包板视图中所做的那样,你仍然需要为每个引脚选择一个图形。点击“选择图形”按钮,并为该引脚选择适当的图形。对于示意图视图,你将要更改终端点,因此连接线在最远点连接。

最简单的方法是确保连接器引脚的图形仍然被选中,并更改连接器窗口中的终端点。对于GND图形,通过单击“S”将终点移动到南端。

Terminal Point

对所有连接器重复此操作

在更新所有连接器针脚后,你可以继续在PCB视图中编辑。

PCB视图

制作自定义PCB SVG

回到Illustrator,Inkscape或你正在使用的矢量图形编辑器。当制作自定义PCB SVG时,你需要的主要图像组是铜(其将具有所有连接器焊盘)和丝印。

创建PCB图形

你可以在创建PCB SVG,修改自定义面包板SVG或在下载的字体和模板文件夹中编辑Fritzing的PCBViewGraphic_Template.svg时重新启动。对于此示例,修改了自定义面包板SVG,并将该文件另存为一个名为SFE_T5403_Barometer_Breakout_PCB.svg的新SVG。

确保有两个铜组

设置图层时,请确保有两个铜组。所有连接器层都应在铜组中。当你这样做时,Fritzing将知道该组件在PCB的两侧都有铜连接器。

Example of PCB Layers

有两个铜组的Illustrator示例

确保连接器引脚的间距准确

重要的是让PCB连接器引脚与你的电路板精确匹配,并在引脚之间留出适当的间距。Fritzing提供PCB Fab服务。如果你或其他Fritzing用户想要使用你的自定义部件使用该服务,你将需要确保你的PCB视图是准确的。

图形标准

代替连接器引脚是铜/镀锡绿色,PCB视图连接器引脚是“铜”颜色:

Copper Color

Hex: F7BD13 RGB: 247 189 19

自定义面包板SVG的主要变化是主要的组是铜和丝网印刷。丝网仍然是白色的。

Final PCB Graphic

最终PCB图形

在零件编辑器中编辑PCB视图

返回零件编辑器,点击PCB按钮进入PCB视图。转到文件 - >加载图像的视图。接下来,你将选择刚创建的PCB SVG,然后单击打开。零件现在应该在Fritzing(新)零件编辑器中。

更新连接器引脚

为每个连接器针脚选择适当的图形,就像在面包板和示意图视图中所做的那样。

图标视图

重复使用过去的图形

转到Fritzing(新)零件编辑器,然后单击图标按钮进入图标视图。关于Icon视图的一个伟大的事情是,你可以重用你的面包板,原理图或PCB SVG的图标图像,所以没有必要做一个新的图像!所有你需要做的是去文件,并选择你想要重用的图像。对于SparkFun T5403气压计,Icon视图重新使用面包板图像。面包板图像应显示。

Reuse Past Graphic

伟大的斯科特!你现在完成了图标视图!

元数据

转到元数据视图

转到零件编辑器,然后单击元数据按钮进入元数据视图。元数据是你将添加关于你的部件的所有重要信息的地方!

元数据视图中的不同部分

标题:很自然的。这将是你的部分的名称。

日期:在Fritzing中锁定日期条目。日期应显示你创建零件的日期。如果你稍后在道路上更新部件,日期将更改为上次更新的当前日期。

作者:你会想把你的名字在这里,所以,如果你与Fritzing社区分享你的一部分,他们知道谁是谁的部分。

说明:说明应包括对电路板重要的任何事项,例如工作电压。

标签:标签显示在示意图视图中,可以更容易地识别你选择的部件。对于SparkFun T5403气压计突破,标签将更改为零件。原因是,因为Part相当小,SparkFun T5403气压计名称已经在原理图图形本身。它取决于你想要标记你的部分!

URL:考虑张贴零件的网址,这样任何人都可以获得有关零件的更多信息。

家庭:如果你有一个部分有不同的颜色,芯片包等,你会希望他们在同一个家庭。例如,如果你有一个通孔LED有不同的颜色,同一个LED的所有不同的颜色将在同一个家庭。

变体:创建全新零件时,你要确保变体是1.当你以后进行修订时,如果变体2在同一系列中,则会将下一个修订版本更改为变体2。

属性:一个放置重要细节(如零件号,针脚间距等)的地方。

标签:使用可以找到更容易和最好描述你的部分尽可能少的单词的标签。

Metadata

觉得信息有点缺乏?你可以稍后再更新此内容,当你掌握了更多信息

连接器视图

转到连接器视图

转到零件编辑器,然后单击连接器按钮进入连接器视图。在“连接器”视图中,你可以执行以下操作:

  • 更改连接器数量
  • 设置连接器类型
  • 将连接器针脚设置为通孔或SMD
  • 名称连接器引脚
  • 添加连接器引脚描述
Connectors view

你不需要更改“连接器”视图中的任何内容,因为你已经填写了其他视图中的所有信息。如果你需要做任何最后一分钟的变化,现在你可以。请记住,如果你更改此处的连接器数量,则需要返回并更新面包板,原理图和PCB视图。

保存

现在你可以保存你的部分!转到文件>保存

继续导出部件!

导出新零件

Fritzing应用程序中的质量检查

现在是时候在主Fritzing应用程序中检出你的新Fritzing部分。当你在Fritzing(新)零件编辑器中保存为新零件时,零件将自动显示在Fritzing主应用程序中的MINE选项卡的My Parts标签下。

在导出新的自定义零件之前,你需要检查每个视图是否看起来不错。确保你在主Fritzing应用程序,而不是Fritzing(新)零件编辑器。通过单击顶部的面包板按钮,转到面包板视图。在零件窗口中,在右侧,确保你在MINE选项卡。你应该看到你的新部分。在面包板视图上单击并拖动板。

MINE Tab

仔细检查引脚是否命名正确,并且工作正常。在原理图和PCB视图中执行相同操作。一旦进行了质量检查,就可以导出零件。

导出零件

右键单击我的零件窗口中新零件的图标,然后选择导出零件。保存你的Fritzing部分。

Export Part

恭喜你,你做了自己的Fritzing零件!

更多信息和资源

贡献Fritzing

现在你已经完成了你的任务,你可以连接其他Fritzing零件。你可以在Fritzing网站上分享你的部分或项目教程。还有更多的方法来帮助Fritzing社区!查看Fritzing Support Us页面,了解更多支持Fritzing的方法。

大批量的Fritzing零件?

如果你是使用EAGLE的开发人员或投入大量时间来制作Fritzing零件的开发人员,Fritzing团队已经开放了一个工具包,从EAGLE .brd文件制作SVG文件。强烈建议你检查是否正在创建批处理的SVG板文件准备Fritzing。他们在Fritzing Google代码页上有源代码


cc

原始文章采用CC BY-SA 4.0,您可以自由地:

  • 分享 — 在任何媒介以任何形式复制、发行本作品
  • 演绎 — 修改、转换或以本作品为基础进行创作
  • 在任何用途下,甚至商业目的。
  • 只要你遵守许可协议条款,许可人就无法收回你的这些权利。

本文由翻译美国开源硬件厂商Sparkfun(火花快乐)的相关教程翻译,原始教程采用同样的CC BY-SA 4.0协议,为便于理解和方便读者学习使用,部分内容为适应国内使用场景稍有删改或整合,这些行为都是协议允许并鼓励的。

原始文章及相关素材链接:

https://learn.sparkfun.com/tutorials/make-your-own-fritzing-parts

简介: Fritzing - 创建自己的零件

本教程将引导您完成以 Digilent 零件为例创建自己的Fritzing零件的步骤。

在为 Digilent 创建Fritzing零件的过程中,我一直与Fritzing零件专家保持密切联系。他给我的大部分反馈都是我在他们的网站上或我能找到的任何其他部分教程上找不到的信息。正因为如此,我觉得我需要制作自己的教程,结合其他教程中所有缺失的信息。

在开始制作自己的零件之前,请确保您查看该零件是在google零件存储库中还是在Digilent Parts github存储库中。

我希望这有助于让其他人获得比我更流畅的零件创建体验。

有关使用Fritzing的更多常规信息,请参阅本教程。有关Fritzing的一些有用提示,请查看此博客文章

我在此介绍中附加了一个zip文件,其中包含用于引脚和常用部件的插图模板。

附件

  • Fritzing插画家模板.zip下载

第 1 步:所需下载

Fritzing

您需要的第一个软件是Fritzing,它是免费的。

图像编辑器

为了制作零件,您需要为所有三个视图制作图像:面包板,原理图和PCB。这些图像需要导出为 SVG 格式。

  1. 我个人使用Adobe Illustrator(Digilent与Adobe没有隶属关系)。但是,Illustrator并不便宜。如果您想免费试用,可以随时下载30天免费试用版。
  2. 另一种选择是使用Inkscape,这是免费的。

根据我的经验,两者之间存在一些兼容性问题,但除此之外,人们根据个人喜好发现一个比另一个更好。我建议您购买Inkscape和Illustrator的30天免费试用版,并在您决定购买Illustrator之前对其进行测试。

字体

您需要下载的最后一件事是Fritzing标准字体。Fritzing使用OCR A和Droid Sans,两者都可以在这里 或 本地 找到。

模板

您可能还想下载这三个视图的 Fritzing 模板 或本地下载模板,尽管我将包括我在本教程中创建的示例部分的所有三个视图的文件。

添加提示提问评论下载

步骤 2:面包板视图 SVG

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我通常从面包板视图的SVG图像开始制作零件。

在本教程中,我将使用 PmodENC、PmodBB 和 chipKIT Pi 作为示例。我已将 SVG 图像和 png 图像包含在此步骤中。png文件用于预览,如果您想下载SVG文件,可以在AI或Inkscape中打开它们以查看图像中的各个部分。

首先,您需要打开 Illustrator 或 Inkscape 并创建一个新文件。为文件命名,以便准确了解该图像是什么。例如;PmodBB-面包板。

接下来要做的是显示标尺,然后选择要使用的单位。

接下来,选择矩形工具并绘制电路板的基本形状。如果电路板不完全是矩形,则最容易从基本矩形中切出而不是添加形状。您可以创建要剪切的形状,然后使用路径工具中的"删除"工具。在示例中,所有看似白色的圆圈实际上都是孔。

接下来添加要添加的任何其他组件。

注意 :如果您计划制作多个零件,我建议您创建一个经常使用的零件文件。这样,您只需要测量它并创建一次,它就有助于保持一致性。例如,在我常用的零件文件中,我有开关(如 PmodENC 所示)、螺钉(如 PmodBB 所示)、按钮(如 chipKIT Pi 所示)、端口(如 chipKIT Pi 所示)、母引脚和公头引脚(如所有示例所示)以及用于常用大小孔的圆圈。

在这个文件中,我还有一个文本框。如果您经常使用一种尺寸和字体,则必须这样做,它可以节省在此模板文本框中复制和粘贴并更改文本的时间,而不是每次都从头开始。

您可能还想向零件添加徽标。如果您的徽标在上传到Fritzing时未显示,则需要其他文件格式。我使用 eps 文件作为 Digilent 板上的徽标。

附件

步骤 3:原理图视图 SVG

在这里插入图片描述
在这里插入图片描述
为原理图视图创建 SVG 时,首先创建与电路板具有相同尺寸的基本矩形。它应该用白色填充并具有黑色轮廓。
然后将名称放在中心。
原理图中的引脚具有非常具体的标准。针脚必须相距 0.1 英寸,长 0.1 英寸。引脚也必须相应地着色:
红色:电压输入引脚。(3V、5V、VIN 等)
蓝色:输入引脚/模拟引脚,复位,参考引脚等
绿色:输出引脚/数字引脚
黑色:接地引脚

第 4 步:

步骤 5:PCB 查看 SVG

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

PCB视图SVG由多层组成,因此Fritzing图像的PCB视图可以具有更多层。但是,Fritzing层与AI层并不直接相关 - 它们实际上是子层,如上图所示。处理图层的最简单方法是在两个图层(silkscreen and copper1)中创建图像,然后在完成后添加图层并在图层窗口中移动图像。

我给出的例子包括两个Fritzing层,copper层和silkscreen层。丝网印刷层是图像的所有黑色部分,包括板形状的轮廓。铜层具有代表引脚的金色圆圈,以及没有填充或轮廓的电路板的基部形状。在 AI 中,层次结构将包括:

第 1 层

-> copper0

-> copper1 (包含铜部分)

-> silkscreen(丝网印刷)

图层中图像的顺序非常重要。引脚位于 copper1 层中的轮廓上方,否则您将无法选择除轮廓之外的任何内容。换句话说,铜层中最接近顶部的图像将隐藏更靠近底部的图像。

Fritzing建议你在AI中命名每个引脚,就像它们在Fritzing中命名一样。您可以在层菜单中执行此操作,这样Fritzing将为您分配PCB引脚。然而,我发现命名引脚需要更长的时间,而不仅仅是在我进入PCB视图后分配它们。如果您想仔细查看图层,我已经为Digilent PmodBB和PmodENC附加了我的svg文件。

就像在面包板视图中一样,我建议为PCB创建一个常用的零件文件。这将包括常见的引脚配置。

我发现创建PCB视图的最简单,最快捷的方法是从丝网印刷层开始。

  1. 将基本形状和所有文本从面包板视图复制并粘贴到该图层中。这样,文本都与面包板视图位于同一位置
  2. 接下来,将文本设置为黑色,基本形状的填充设置为无填充和轮廓设置为0.3pt黑心边框。
  3. 然后,复制引脚的黑色轮廓
  4. 然后创建铜1层。
  5. 然后将金色圆圈放入铜层中的引脚。确保引脚与面包板视图的引脚位置完全相同。Copper1层只需要引脚没有轮廓或其他任何东西。这层中的任何东西在Fritzing中都将是铜色的。
  6. 然后创建一个名为 copper0 的新层,并将铜层放入该层中。
  7. 然后创建基层,将丝网印刷和铜0拖入其中。

有关Fritzing图形标准的更多信息,包括特定颜色,请单击此处

步骤 6:获取通用部件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

接下来,您需要在Fritzing找到一个通用零件,以用作新零件的基础。我通常使用核心部件中的通用IC。

如果您需要奇数个引脚,您可以将封装更改为SIP(单列直插式),或者在核心部件和连接下使用通用引脚。

接下来,将引脚数更改为所需的数字。如果您计划使用自动生成的面包板图像,则正确获取引脚数非常重要,因为在编辑器中更改引脚数不会更改图形。

步骤 7:新零件编辑器

在这里插入图片描述

确保已选择通用部件,打开部件菜单并选择新部件编辑器。

在本教程中,我将使用 PmodENC 作为示例。我使用具有6个引脚的通用DIP。

步骤 8:元数据

在这里插入图片描述
在这里插入图片描述
将打开新的零件编辑器窗口。

新零件编辑器中有六个部分需要填写;面包板,原理图,PCB,图标,元数据和连接器。我喜欢从元数据开始,否则我往往会忘记它。

在新零件编辑器的顶部,单击顶部显示"元数据"的选项卡。

元数据保存有关您的零件的所有信息,这些信息将显示在检查器中。上图显示了为通用 DIP 和我用作示例的 Pmod 填写的信息。

步骤 9:连接器

在这里插入图片描述

尽管您可以在任何视图中命名和选择连接器的类型,但我发现在连接器视图中执行此操作最容易。在面包板或其他视图中执行此操作需要大量的双击和重新加载,这会减慢过程。

单击名为"连接器"的新零件编辑器顶部的选项卡。

在"连接器"选项卡中,您可以更改引脚数(这不会更改图形上的引脚数),将所有引脚设置为相同类型,在通孔和 SMD 之间进行选择,以及更改各个引脚的名称、描述和类型。

步骤 10:面包板视图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

单击新零件编辑器顶部的选项卡,其中显示"面包板"。

打开文件菜单,然后选择加载图像进行查看。加载您之前创建的面包板映像。

接下来,您需要为每个引脚选择图形。右侧列表中的复选标记表示已为该引脚选择了图形。双击要分配的引脚上的选择图形。单击要分配给该引脚的图形,它将以粉红色突出显示。

您选择的图形将是您可以在Fritzing中将电线连接到的图形。它将自动选择中心作为连接点,但您可以在显示终端点的位置进行更改。

完成分配所有引脚后,即可在"面包板"视图中完成。

步骤11:原理图视图

在这里插入图片描述

单击新零件编辑器顶部的"原理图"选项卡。

Fritzing 将自动为您创建原理图视图。但是,在新版本发布之前,您必须升级到新的0.1英寸标准。打开编辑菜单,然后选择升级到新的 0.1 英寸标准。

引脚已被选中,因此在原理图视图中已完成操作。

注意:如果您上传自定义原理图视图,则必须像在面包板视图中一样选择引脚图形。

步骤12:PCB视图

在这里插入图片描述

单击新零件编辑器顶部的选项卡,其中显示PCB。打开"文件"菜单,然后单击"加载图像"进行查看。选择您创建的 PCB SVG,并像在面包板视图中一样为引脚选择图形。

步骤13:图标

在这里插入图片描述

您最不需要更改的是图标。大多数情况下,Fritzing零件的图标与面包板图像的图像相同。打开文件菜单时,您可以选择重用面包板图像。

步骤 14:另存为新部件

在这里插入图片描述

现在,您已经完成了所有更改以创建新零件。转到文件菜单,然后选择另存为新部件。

添加提示提问评论下载

步骤15:FIlename

在这里插入图片描述

Fritzing会要求一个文件名前缀,我通常使用部分的名称。

新的零件编辑器将关闭,您的草图仍将在那里。

添加提示提问评论下载

步骤 16:导出

在这里插入图片描述

您的新部件将位于"我的部件"箱中。如果要将该部件保留在"我的部件"箱中,请确保在关闭弗里廷时将更改保存到"我的部件"箱中,

为了使零件与Fritzing分开,以便您可以共享零件并展示您的工作,您将需要导出零件。右键单击该部件,然后选择导出部件。

为部件命名并将其保存在所需的任何位置。

]]>
880 0 0 0
<![CDATA[树莓派自动开关机电路板]]> http://mixdiy.com/index.php/2022/03/22/fritzing-onoff-pcb/ Tue, 22 Mar 2022 09:38:20 +0000 http://mixdiy.com/?p=890 ]]> 890 0 0 0 <![CDATA[日出日落]]> http://mixdiy.com/index.php/2022/03/24/video-in-city/ Wed, 23 Mar 2022 17:16:03 +0000 http://mixdiy.com/?p=900
]]>
900 0 0 0
<![CDATA[向esp32、pico等单片机传输二进制文件]]> http://mixdiy.com/index.php/2022/03/28/mpy/ Mon, 28 Mar 2022 00:43:07 +0000 http://mixdiy.com/?p=919

对于一些特殊文件比如图片、二进制程序等等,无法通过thonny等开发环境传输到esp32、树莓派pico等单片机,但micropython官方提供了一种方案,具体如下:

在esp32中运行以下代码建立wifi热点并取esp32的ip地址

import network,time
 
wlan = network.WLAN(network.STA_IF) # 创建一个WLAN实例  create station interface
wlan.active(True)       # 激活实例 activate the interface
wlan.scan()             # 扫描WIFI  scan for access points
wlan.isconnected()      # 判断WIFI连接否, 返回布尔值  check if the station is connected to an AP
wlan.connect('HUAWEI-WULIAN', 'onlychina') # 连接WIFI  connect to an AP
wlan.config('mac')      # 获取实例的MAC地址 get the interface's MAC address
wlan.ifconfig()         # 获取实例的网络信息  get the interface's IP/netmask/gw/DNS addresses
 
ap = network.WLAN(network.AP_IF) # 创建一个AP实例 create access-point interface
ap.active(True)         # 激活实例 activate the interface
ap.config(max_clients=10) # 设定多少个客户端可以连接它 set how many clients can connect to the network
ap.config(essid='ESP-AP') #  配置实例的essid参数 set the ESSID of the access point

time.sleep(3)
import webrepl
webrepl.start()

输入

import webrepl_setup

配置好相关参数后,打开链接:http://micropython.org/webrepl/

(此时主机需要连接到热点:ESP-AP,或在同一无线局域网内使用webrepl分配的局域网地址),此时就可以将相关文件传输给esp32了

]]>
919 0 0 0
<![CDATA[python 源代码保护工具 mpy-cross]]> http://mixdiy.com/index.php/2022/03/28/python-protect-tools-mpy-cross/ Mon, 28 Mar 2022 01:11:39 +0000 http://mixdiy.com/?p=929

pip3 install mpy-cross

mpy-cross yourpythonfile.py

]]>
929 0 0 0
<![CDATA[学习 linux shell脚本]]> http://mixdiy.com/index.php/2022/03/29/study-linux-shell/ Tue, 29 Mar 2022 12:38:27 +0000 http://mixdiy.com/?p=934

长期以来一直是windows的忠实用户,但发现做嵌入式很难脱离linux,只能苦命学linux,最近被日期形式的文件名困扰,一直想实现通过日期形成抓拍图片文件名,磁盘满了自动按时间顺序清楚,也就是先进先出方式,终于通过shell的bash实现了这点。

#!/bin/bash
var1=$(date +'%Y-%m-%d')
var2=$(date +%Y%m%d)
var3=$[$var2-2]
echo $var1
echo $var2
echo $var3

参考https://www.cnblogs.com/wanng/p/14105935.html

#!/bin/bash

h="hello"
hw=${h}" world"
echo ${hw}

var1=$(date +'%Y-%m-%d')
var2=$(date +%Y%m%d)
var3=$[$var2-2]
var4=$[$var2+5]
var5="*"

echo $var1
echo $var2
echo $var3
echo $var4

var5=$(date +%d $date)


h1="*"
hw1=$var4${h1}

ls $hw1.jpg -l
]]>
934 0 0 0
<![CDATA[用Python实现关键数据远程备份]]> http://mixdiy.com/index.php/2022/03/30/python-backup/ Wed, 30 Mar 2022 03:18:17 +0000 http://mixdiy.com/?p=940

用树莓派实现了永久网站,但经常侦测到恶意攻击,为防止关键数据丢失,经常需要备份网站、远程录像文件等,用python实现如下:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Windows或者Linux系统远程备份数据、Python远程备份文件及目录
"""

import paramiko
import os
from stat import S_ISDIR as isdir
from datetime import datetime

def down_from_remote(sftp_obj, remote_dir_name, local_dir_name):
    """远程下载文件"""
    remote_file = sftp_obj.stat(remote_dir_name)
    if isdir(remote_file.st_mode):
        # 文件夹,不能直接下载,需要继续循环
        check_local_dir(local_dir_name)
        #print('开始下载数据目录:' + remote_dir_name)
        for remote_file_name in sftp.listdir(remote_dir_name):
            local_file_name = os.path.join(local_dir, remote_file_name)
            #转换/及\\
            sub_remote = os.path.join(remote_dir_name, remote_file_name)
            sub_remote = sub_remote.replace('\\', '/')
            sub_local = os.path.join(local_dir_name, remote_file_name)
            sub_local = sub_local.replace('\\', '/')
            #如果本地没有远程数据文件夹
            if not os.path.exists(local_file_name):
                down_from_remote(sftp_obj, sub_remote, sub_local)
    else:
        # 文件,直接下载
        print('开始下载数据文件:' + remote_dir_name)
        # 如果本地没有远程数据文件
        local_file_name = os.path.join(local_dir, remote_dir_name)
        if not os.path.exists(local_file_name):
            sftp.get(remote_dir_name, local_dir_name)

def check_local_dir(local_dir_name):
    """本地文件夹是否存在,不存在则创建"""
    if not os.path.exists(local_dir_name):
        os.makedirs(local_dir_name)

if __name__ == "__main__":
    # 服务器连接信息
    host_name = '1.tcp***'
    user_name = '*****'
    password = '******'
    port = 2***
    #日期
    date = datetime.now().strftime("%Y%m%d")
    # 远程文件路径(需要绝对路径)
    remote_dir = '/home/pi/'
    # 本地文件存放路径(绝对路径或者相对路径都可以)
    local_dir = '/home/pi/dvr1/'

    # 连接远程服务器
    t = paramiko.Transport((host_name, port))
    t.connect(username=user_name, password=password)
    sftp = paramiko.SFTPClient.from_transport(t)

    # 远程文件开始下载
    down_from_remote(sftp, remote_dir, local_dir)

    # 关闭连接
    t.close()

文件级别

# coding: utf-8

import paramiko
import re
import os
import stat
from time import sleep

# 定义一个类,表示一台远端linux主机
class Linux(object):
    # 通过IP, 用户名,密码,超时时间初始化一个远程Linux主机
    def __init__(self, ip, username, password, timeout=30):
        self.ip = ip
        self.username = username
        self.password = password
        self.timeout = timeout
        # transport和chanel
        self.t = ''
        self.chan = ''
        # 链接失败的重试次数
        self.try_times = 3

    # 调用该方法连接远程主机
    def connect(self):
        while True:
            # 连接过程中可能会抛出异常,比如网络不通、链接超时
            try:
                self.t = paramiko.Transport(sock=(self.ip, 20131))
                self.t.connect(username=self.username, password=self.password)
                self.chan = self.t.open_session()
                self.chan.settimeout(self.timeout)
                self.chan.get_pty()
                self.chan.invoke_shell()
                # 如果没有抛出异常说明连接成功,直接返回
                print(u'连接%s成功' % self.ip)
                # 接收到的网络数据解码为str
                print(self.chan.recv(65535).decode('utf-8'))
                return
            # 这里不对可能的异常如socket.error, socket.timeout细化,直接一网打尽
            except Exception as e1:
                if self.try_times != 0:
                    print(u'连接%s失败,进行重试' %self.ip)
                    self.try_times -= 1
                else:
                    print(u'重试3次失败,结束程序')
                    exit(1)

    # 断开连接
    def close(self):
        self.chan.close()
        self.t.close()

    # 发送要执行的命令
    def send(self, cmd):
        cmd += '\r'
        # 通过命令执行提示符来判断命令是否执行完成
        p = re.compile(r':~ #')

        result = ''
        # 发送要执行的命令
        self.chan.send(cmd)
        # 回显很长的命令可能执行较久,通过循环分批次取回回显
        while True:
            sleep(0.5)
            ret = self.chan.recv(65535)
            ret = ret.decode('utf-8')
            result += ret
            if p.search(ret):
                print(result)
                return result

    # get单个文件
    def sftp_get(self, remotefile, localfile):
        t = paramiko.Transport(sock=(self.ip, 20131))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)
        sftp.get(remotefile, localfile)
        t.close()

    # put单个文件
    def sftp_put(self, localfile, remotefile):
        t = paramiko.Transport(sock=(self.ip, 20131))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)
        sftp.put(localfile, remotefile)
        t.close()
        
# ------获取远端linux主机上指定目录及其子目录下的所有文件------
    def __get_all_files_in_remote_dir(self, sftp, remote_dir):
        # 保存所有文件的列表
        all_files = list()

        # 去掉路径字符串最后的字符'/',如果有的话
        if remote_dir[-1] == '/':
            remote_dir = remote_dir[0:-1]

        # 获取当前指定目录下的所有目录及文件,包含属性值
        files = sftp.listdir_attr(remote_dir)
        for x in files:
            # remote_dir目录中每一个文件或目录的完整路径
            filename = remote_dir + '/' + x.filename
            # 如果是目录,则递归处理该目录,这里用到了stat库中的S_ISDIR方法,与linux中的宏的名字完全一致
            if stat.S_ISDIR(x.st_mode):
                all_files.extend(self.__get_all_files_in_remote_dir(sftp, filename))
            else:
                all_files.append(filename)
        return all_files
    
    def sftp_get_dir(self, remote_dir, local_dir):
        t = paramiko.Transport(sock=(self.ip, 20131))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)

        # 获取远端linux主机上指定目录及其子目录下的所有文件
        all_files = self.__get_all_files_in_remote_dir(sftp, remote_dir)
        # 依次get每一个文件
        for x in all_files:
            filename = x.split('/')[-1]
            local_filename = os.path.join(local_dir, filename)
            print(u'Get文件%s传输中...' % filename)
            sftp.get(x, local_filename)


# ------获取本地指定目录及其子目录下的所有文件------
    def __get_all_files_in_local_dir(self, local_dir):
        # 保存所有文件的列表
        all_files = list()

        # 获取当前指定目录下的所有目录及文件,包含属性值
        files = os.listdir(local_dir)
        for x in files:
            # local_dir目录中每一个文件或目录的完整路径
            filename = os.path.join(local_dir, x)
            # 如果是目录,则递归处理该目录
            if os.path.isdir(x):
                all_files.extend(self.__get_all_files_in_local_dir(filename))
            else:
                all_files.append(filename)
        return all_files

    def sftp_put_dir(self, local_dir, remote_dir):
        t = paramiko.Transport(sock=(self.ip, 20131))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)

        # 去掉路径字符穿最后的字符'/',如果有的话
        if remote_dir[-1] == '/':
            remote_dir = remote_dir[0:-1]

        # 获取本地指定目录及其子目录下的所有文件
        all_files = self.__get_all_files_in_local_dir(local_dir)
        # 依次put每一个文件
        for x in all_files:
            filename = os.path.split(x)[-1]
            remote_filename = remote_dir + '/' + filename
            print(u'Put文件%s传输中...' % filename)
            sftp.put(x, remote_filename)
            
if __name__ == '__main__':
    remote_path = r'/home/pi/'
    local_path = r'/home/pi/backup/shot_pic/2222/'

    host = Linux('1.tcp.hk.cpolar.io', 'pi', 'raspberry')

    # 将远端remote_path目录中的所有文件get到本地local_path目录
    host.sftp_get_dir(remote_path, local_path)
    # # 将本地local_path目录中的所有文件put到远端remote_path目录
    # host.sftp_put_dir(remote_path, local_path)

文件去重,不再重复拷贝已经存在的文件

# coding: utf-8

import paramiko
import re
import os
import stat
from time import sleep

# 定义一个类,表示一台远端linux主机
class Linux(object):
    # 通过IP, 用户名,密码,超时时间初始化一个远程Linux主机
    def __init__(self, ip, username, password, timeout=30):
        self.ip = ip
        self.username = username
        self.password = password
        self.timeout = timeout
        # transport和chanel
        self.t = ''
        self.chan = ''
        # 链接失败的重试次数
        self.try_times = 3

    # 调用该方法连接远程主机
    def connect(self):
        while True:
            # 连接过程中可能会抛出异常,比如网络不通、链接超时
            try:
                self.t = paramiko.Transport(sock=(self.ip, 20131))
                self.t.connect(username=self.username, password=self.password)
                self.chan = self.t.open_session()
                self.chan.settimeout(self.timeout)
                self.chan.get_pty()
                self.chan.invoke_shell()
                # 如果没有抛出异常说明连接成功,直接返回
                print(u'连接%s成功' % self.ip)
                # 接收到的网络数据解码为str
                print(self.chan.recv(65535).decode('utf-8'))
                return
            # 这里不对可能的异常如socket.error, socket.timeout细化,直接一网打尽
            except Exception as e1:
                if self.try_times != 0:
                    print(u'连接%s失败,进行重试' %self.ip)
                    self.try_times -= 1
                else:
                    print(u'重试3次失败,结束程序')
                    exit(1)

    # 断开连接
    def close(self):
        self.chan.close()
        self.t.close()

    # 发送要执行的命令
    def send(self, cmd):
        cmd += '\r'
        # 通过命令执行提示符来判断命令是否执行完成
        p = re.compile(r':~ #')

        result = ''
        # 发送要执行的命令
        self.chan.send(cmd)
        # 回显很长的命令可能执行较久,通过循环分批次取回回显
        while True:
            sleep(0.5)
            ret = self.chan.recv(65535)
            ret = ret.decode('utf-8')
            result += ret
            if p.search(ret):
                print(result)
                return result

    # get单个文件
    def sftp_get(self, remotefile, localfile):
        t = paramiko.Transport(sock=(self.ip, 20131))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)
        sftp.get(remotefile, localfile)
        t.close()

    # put单个文件
    def sftp_put(self, localfile, remotefile):
        t = paramiko.Transport(sock=(self.ip, 20131))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)
        sftp.put(localfile, remotefile)
        t.close()
        
# ------获取远端linux主机上指定目录及其子目录下的所有文件------
    def __get_all_files_in_remote_dir(self, sftp, remote_dir):
        # 保存所有文件的列表
        all_files = list()

        # 去掉路径字符串最后的字符'/',如果有的话
        if remote_dir[-1] == '/':
            remote_dir = remote_dir[0:-1]

        # 获取当前指定目录下的所有目录及文件,包含属性值
        files = sftp.listdir_attr(remote_dir)
        for x in files:
            # remote_dir目录中每一个文件或目录的完整路径
            filename = remote_dir + '/' + x.filename
            # 如果是目录,则递归处理该目录,这里用到了stat库中的S_ISDIR方法,与linux中的宏的名字完全一致
            if stat.S_ISDIR(x.st_mode):
                all_files.extend(self.__get_all_files_in_remote_dir(sftp, filename))
            else:
                all_files.append(filename)
        return all_files
    
    def sftp_get_dir(self, remote_dir, local_dir):
        all_files1=list()
        all_files2=list()
        all_files3=list()
        all_files_remote = list()
        all_files_local =list()
        will_get_file = list()
        all_files_local_sorted=list()
        t = paramiko.Transport(sock=(self.ip, 20131))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)
        # 获取远端linux主机上指定目录及其子目录下的所有文件
        all_files = self.__get_all_files_in_remote_dir(sftp, remote_dir)
        all_files_remote = sorted(all_files,key=str.lower)

        all_files_local = self.__get_all_files_in_local_dir(local_dir)
        all_files_local_sorted=sorted(all_files_local,key=str.lower)
        
        for i in all_files_remote:
            filen=i.split('/')[-1]
            all_files1.append(filen)
            
        for j in all_files_local_sorted:
            filen=j.split('/')[-1]
            all_files2.append(filen)
        
        #set(list1).difference(set(list2))
        
        will_get_file = set(all_files1).difference(set(all_files2))
        
        for t in sorted(will_get_file):
            filename=remote_dir+t.split('-')[0]+'/'+t[0:10]+'/' +t
            all_files3.append(filename)
            
            
        # 依次get每一个文件
        for x in (sorted(all_files3)):
            filename = x.split('/')[-1]
            local_filename = os.path.join(local_dir, filename)
            print(u'Get文件%s传输中...' % filename)
            sftp.get(x, local_filename)            


# ------获取本地指定目录及其子目录下的所有文件------
    def __get_all_files_in_local_dir(self, local_dir):
        # 保存所有文件的列表
        all_files = list()

        # 获取当前指定目录下的所有目录及文件,包含属性值
        files = os.listdir(local_dir)
        for x in files:
            # local_dir目录中每一个文件或目录的完整路径
            filename = os.path.join(local_dir, x)
            # 如果是目录,则递归处理该目录
            if os.path.isdir(x):
                all_files.extend(self.__get_all_files_in_local_dir(filename))
            else:
                all_files.append(filename)
        return all_files

    def sftp_put_dir(self, local_dir, remote_dir):
        t = paramiko.Transport(sock=(self.ip, 20131))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)

        # 去掉路径字符穿最后的字符'/',如果有的话
        if remote_dir[-1] == '/':
            remote_dir = remote_dir[0:-1]

        # 获取本地指定目录及其子目录下的所有文件
        all_files = self.__get_all_files_in_local_dir(local_dir)
        # 依次put每一个文件
        for x in all_files:
            filename = os.path.split(x)[-1]
            remote_filename = remote_dir + '/' + filename
            print(u'Put文件%s传输中...' % filename)
            sftp.put(x, remote_filename)
            
if __name__ == '__main__':
    remote_path = r'/home/pi/'
    local_path = r'/Users/jungangzhu/Downloads/jpg_from_remote/'

    host = Linux('1.tcp.hk.cpolar.io', 'pi', 'raspberry')

    # 将远端remote_path目录中的所有文件get到本地local_path目录
    host.sftp_get_dir(remote_path, local_path)
    # # 将本地local_path目录中的所有文件put到远端remote_path目录
    # host.sftp_put_dir(remote_path, local_path)
]]>
940 0 0 0
<![CDATA[shell脚本批量生成目录]]> http://mixdiy.com/index.php/2022/03/30/mkdir/ Wed, 30 Mar 2022 10:05:45 +0000 http://mixdiy.com/?p=945

需要在Linux环境中定期批量生成年月日的日期目录,这样,相关的文件可以按照日期类别放到对应的目录中
脚本如下所示:

#!/bin/bash

images_path="/usr/local/images"

TDATE=$(date -d next-day +%Y-%m-%d)
Next_DATE=$(date -d next-month +%Y-%m-%d)

i=0
until [[ $day == $Next_DATE ]]
do
        day=$(date -d "$TDATE $i days" +%Y-%m-%d)
        mkdir -p ${images_path}/$(date +"%Y")/$day/
        ((i++))
done

如果想指定创建日期目录可以如下所示:
创建2018-01-01到2018-02-15的日期目录:

#!/bin/bash
images_path=/wxdk_images
i=0
until [[ $day == "2018-02-15" ]]
do
        day=$(date -d "2018-01-01 $i days" +%Y-%m-%d)
        mkdir -p ${images_path}/$(date +"%Y")/$day/
        ((i++))
done

https://www.cnblogs.com/blogjun/articles/8251530.html

]]>
945 0 0 0
<![CDATA[利用rsync远程同步文件]]> http://mixdiy.com/index.php/2022/03/31/rsync-command/ Thu, 31 Mar 2022 03:09:54 +0000 http://mixdiy.com/?p=949

解决已存在文件等复杂问题

rsync pi@192.168.2.145:/home/pi/2022/2022-03-31/ /home/peter/下载/2022/2022-03-31

对于非22端口,则需重新指定端口才能同步

rsync -aP "-e ssh -p 20123" peter@1.tcp.hk.cpolar.cn:/home/pi/ /Users/Downloads/

如果需要复制特定文件组,也就是需要增加“*”等通配符,此时需要将“”才能正常同步,如下:

rsync -aP "-e ssh -p 20123" "peter@1.tcp.hk.cpolar.cn:/home/pi/2022-03-31*" /Users/Downloads/

网站备份

rsync -aP pi@192.168.0.101:/var/www/ /Users/Downloads/roown.com/
需求:公司有两台备份服务器,一主一从,每天晚上8点开始从服务器就开始同步主服务上的一些数据,但是rsync总会断开连接,尝试了好多方法都没有用,写了个脚本临时解决这个问题,如下

#!/bin/bash

rsync -avzP --timeout=60 -e "ssh -p X" X.X.X.X:/kejiandata/streams/tarena/streams/* /h
zdata/streams/

while ((1 < 100));do
    ps aux | grep /kejiandata/streams/tarena/ | grep rsync | grep timeout
    if [ `echo $?` -eq 1 ];then
        rsync -avzP --timeout=60 -e "ssh -p X" X.X.X.X:/kejiandata/streams/tarena/stre
ams/* /hzdata/streams/
    fi
    sleep 30
    if [ `date +"%H%M"` -ge 0830 ];then
        exit
    fi
done
]]>
949 0 0 0
<![CDATA[利用python将延时摄像图片自动合成为短视频]]> http://mixdiy.com/index.php/2022/04/02/python-pic2avi/ Sat, 02 Apr 2022 08:44:22 +0000 http://mixdiy.com/?p=958

利用python实现延时摄像图片的视频化,也就是将图片合成为视频:

import cv2
import glob
 
def resize(img_array, align_mode):
    _height = len(img_array[0])
    _width = len(img_array[0][0])
    for i in range(1, len(img_array)):
        img = img_array[i]
        height = len(img)
        width = len(img[0])
        if align_mode == 'smallest':
            if height < _height:
                _height = height
            if width < _width:
                _width = width
        else:
            if height > _height:
                _height = height
            if width > _width:
                _width = width
 
    for i in range(0, len(img_array)):
        img1 = cv2.resize(img_array[i], (_width, _height), interpolation=cv2.INTER_CUBIC)
        img_array[i] = img1
 
    return img_array, (_width, _height)
 
def images_to_video(path):
    img_array = []
 
    for filename in glob.glob(path+'/*.jpg'):
        img = cv2.imread(filename)
        if img is None:
            print(filename + " is error!")
            continue
        img_array.append(img)
 
    # 图片的大小需要一致
    img_array, size = resize(img_array, 'largest')
    fps = 5
    out = cv2.VideoWriter('/Users/Downloads/中转文件夹/2022-02-20-03.jpg/demo.avi', cv2.VideoWriter_fourcc(*'DIVX'), fps, size)
 
    for i in range(len(img_array)):
        out.write(img_array[i])
    out.release()
 
def main():
    path = "/Users/Downloads/中转文件夹/2022-02-20-03.jpg/"
    images_to_video(path)
 
if __name__ == "__main__":
    main()


以上代码可能会重复将jpg文件编入视频,改进如下:

import cv2
import glob
import re

def resize(img_array, align_mode):
    _height = len(img_array[0])
    _width = len(img_array[0][0])
    for i in range(1, len(img_array)):
        img = img_array[i]
        height = len(img)
        width = len(img[0])
        if align_mode == 'smallest':
            if height < _height:
                _height = height
            if width < _width:
                _width = width
        else:
            if height > _height:
                _height = height
            if width > _width:
                _width = width
 
    for i in range(0, len(img_array)):
        img1 = cv2.resize(img_array[i], (_width, _height), interpolation=cv2.INTER_CUBIC)
        img_array[i] = img1
 
    return img_array, (_width, _height)
 
def images_to_video(path):
    img_array = []
    jpgfile=glob.glob(path+'/*.jpg')
    ordjpgfile=sorted(jpgfile,key = str .lower)
 
    for filename in ordjpgfile:
        img = cv2.imread(filename)
        if img is None:
            print(filename + " is error!")
            continue
        img_array.append(img)
 
    # 图片的大小需要一致
    img_array, size = resize(img_array, 'largest')
    fps = 10
    out = cv2.VideoWriter('/Users/Downloads/2022/demo.avi', cv2.VideoWriter_fourcc(*'DIVX'), fps, size)
 
    for i in range(len(img_array)):
        out.write(img_array[i])
    out.release()
 
def main():
    path = "/Users/Downloads/2022/"
    images_to_video(path)
 
if __name__ == "__main__":
    main()

自动根据时间生成文件名

import cv2
import glob
import re

def resize(img_array, align_mode):
    _height = len(img_array[0])
    _width = len(img_array[0][0])
    for i in range(1, len(img_array)):
        img = img_array[i]
        height = len(img)
        width = len(img[0])
        if align_mode == 'smallest':
            if height < _height:
                _height = height
            if width < _width:
                _width = width
        else:
            if height > _height:
                _height = height
            if width > _width:
                _width = width
 
    for i in range(0, len(img_array)):
        img1 = cv2.resize(img_array[i], (_width, _height), interpolation=cv2.INTER_CUBIC)
        img_array[i] = img1
 
    return img_array, (_width, _height)
 
def images_to_video(path):
    img_array = []
    jpgfile=glob.glob(path+'/*.jpg')
    ordjpgfile=sorted(jpgfile,key = str .lower)
 
    for filename in ordjpgfile:
        img = cv2.imread(filename)
        if img is None:
            print(filename + " is error!")
            continue
        img_array.append(img)
    aviname=str(ordjpgfile[0])[-23:-13]+'.avi'
    print(aviname)
    # 图片的大小需要一致
    img_array, size = resize(img_array, 'largest')
    fps = 10
    out = cv2.VideoWriter('/home/peter/2022/'+aviname, cv2.VideoWriter_fourcc(*'DIVX'), fps, size)
 
    for i in range(len(img_array)):
        out.write(img_array[i])
    out.release()
 
def main():
    path = "/home/peter/2022"
    images_to_video(path)
 
if __name__ == "__main__":
    main()
]]>
958 0 0 0
<![CDATA[利用expect和rsync以及python实现免密码输入实现自动远程备份]]> http://mixdiy.com/index.php/2022/04/03/backup-avi-and-mix/ Sun, 03 Apr 2022 00:53:39 +0000 http://mixdiy.com/?p=974
#!/usr/bin/expect

#rsync -aP "-e ssh -p 20131" pi@1.tcp.hk.cpolar.io:/home/pi/ /home/pi/backup/shot_pic/
#rsync -aP pi@192.168.0.101:/var/www/ /home/pi/backup/mixdiy/

set timeout 20 
 
if { [llength $argv] < 2} {
    puts "Usage:"
    puts "$argv0 remote_path local_file"
    exit 1
} 
 
set remote_path [lindex $argv 0]
set local_file [lindex $argv 1]
set passwd raspberry
 
set passwderror 0 
 
spawn rsync -avzP --delete "-e ssh -p 20131" --exclude 'var' $remote_path $local_file
 
expect {
    "*assword:*" {
        if { $passwderror == 1 } {
        puts "passwd is error"
        exit 2
        }
        set timeout 1000
        set passwderror 1
        send "$passwd\r"
        exp_continue
    }
    "*es/no)?*" {
        send "yes\r"
        exp_continue
    }
    timeout {
        puts "connect is timeout"
        exit 3
    }
}

单独运行rsync.ex文件,如下:

expect /home/backup/rsync.ex root@45.234.21.101:/home/backup/* /local/backup/

python实现代码如下(是否短线重传及查重还待验证):

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Windows或者Linux系统远程备份数据、Python远程备份文件及目录
"""

import paramiko
import os
from stat import S_ISDIR as isdir
from datetime import datetime

def down_from_remote(sftp_obj, remote_dir_name, local_dir_name):
    """远程下载文件"""
    remote_file = sftp_obj.stat(remote_dir_name)
    if isdir(remote_file.st_mode):
        # 文件夹,不能直接下载,需要继续循环
        check_local_dir(local_dir_name)
        #print('开始下载数据目录:' + remote_dir_name)
        for remote_file_name in sftp.listdir(remote_dir_name):
            local_file_name = os.path.join(local_dir, remote_file_name)
            #转换/及\\
            sub_remote = os.path.join(remote_dir_name, remote_file_name)
            sub_remote = sub_remote.replace('\\', '/')
            sub_local = os.path.join(local_dir_name, remote_file_name)
            sub_local = sub_local.replace('\\', '/')
            #如果本地没有远程数据文件夹
            if not os.path.exists(local_file_name):
                down_from_remote(sftp_obj, sub_remote, sub_local)
    else:
        # 文件,直接下载
        print('开始下载数据文件:' + remote_dir_name)
        # 如果本地没有远程数据文件
        local_file_name = os.path.join(local_dir, remote_dir_name)
        if not os.path.exists(local_file_name):
            sftp.get(remote_dir_name, local_dir_name)

def check_local_dir(local_dir_name):
    """本地文件夹是否存在,不存在则创建"""
    if not os.path.exists(local_dir_name):
        os.makedirs(local_dir_name)

if __name__ == "__main__":
    # 服务器连接信息
    host_name = '1.tcp.hk.cpolar.io'
    user_name = 'pi'
    password = 'raspberry'
    port = 20131
    #日期
    date = datetime.now().strftime("%Y%m%d")
    # 远程文件路径(需要绝对路径)
    remote_dir = '/home/pi/2022/'
    # 本地文件存放路径(绝对路径或者相对路径都可以)
    local_dir = '/home/pi/dvr5/'

    # 连接远程服务器
    try:
        t = paramiko.Transport((host_name, port))
        t.connect(username=user_name, password=password)
        sftp = paramiko.SFTPClient.from_transport(t)

        # 远程文件开始下载
        down_from_remote(sftp, remote_dir, local_dir)

        # 关闭连接
        t.close()
    except:
        print("无法连接远程服务器,请检查服务器是否已关闭")

文件级别并解决查重不再传输已经存在的文件

# coding: utf-8

import paramiko
import re
import os
import stat
from time import sleep

# 定义一个类,表示一台远端linux主机
class Linux(object):
    # 通过IP, 用户名,密码,超时时间初始化一个远程Linux主机
    def __init__(self, ip, username, password, timeout=30):
        self.ip = ip
        self.username = username
        self.password = password
        self.timeout = timeout
        # transport和chanel
        self.t = ''
        self.chan = ''
        # 链接失败的重试次数
        self.try_times = 3

    # 调用该方法连接远程主机
    def connect(self):
        while True:
            # 连接过程中可能会抛出异常,比如网络不通、链接超时
            try:
                self.t = paramiko.Transport(sock=(self.ip, 20131))
                self.t.connect(username=self.username, password=self.password)
                self.chan = self.t.open_session()
                self.chan.settimeout(self.timeout)
                self.chan.get_pty()
                self.chan.invoke_shell()
                # 如果没有抛出异常说明连接成功,直接返回
                print(u'连接%s成功' % self.ip)
                # 接收到的网络数据解码为str
                print(self.chan.recv(65535).decode('utf-8'))
                return
            # 这里不对可能的异常如socket.error, socket.timeout细化,直接一网打尽
            except Exception as e1:
                if self.try_times != 0:
                    print(u'连接%s失败,进行重试' %self.ip)
                    self.try_times -= 1
                else:
                    print(u'重试3次失败,结束程序')
                    exit(1)

    # 断开连接
    def close(self):
        self.chan.close()
        self.t.close()

    # 发送要执行的命令
    def send(self, cmd):
        cmd += '\r'
        # 通过命令执行提示符来判断命令是否执行完成
        p = re.compile(r':~ #')

        result = ''
        # 发送要执行的命令
        self.chan.send(cmd)
        # 回显很长的命令可能执行较久,通过循环分批次取回回显
        while True:
            sleep(0.5)
            ret = self.chan.recv(65535)
            ret = ret.decode('utf-8')
            result += ret
            if p.search(ret):
                print(result)
                return result

    # get单个文件
    def sftp_get(self, remotefile, localfile):
        t = paramiko.Transport(sock=(self.ip, 20131))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)
        sftp.get(remotefile, localfile)
        t.close()

    # put单个文件
    def sftp_put(self, localfile, remotefile):
        t = paramiko.Transport(sock=(self.ip, 20131))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)
        sftp.put(localfile, remotefile)
        t.close()
        
# ------获取远端linux主机上指定目录及其子目录下的所有文件------
    def __get_all_files_in_remote_dir(self, sftp, remote_dir):
        # 保存所有文件的列表
        all_files = list()

        # 去掉路径字符串最后的字符'/',如果有的话
        if remote_dir[-1] == '/':
            remote_dir = remote_dir[0:-1]

        # 获取当前指定目录下的所有目录及文件,包含属性值
        files = sftp.listdir_attr(remote_dir)
        for x in files:
            # remote_dir目录中每一个文件或目录的完整路径
            filename = remote_dir + '/' + x.filename
            # 如果是目录,则递归处理该目录,这里用到了stat库中的S_ISDIR方法,与linux中的宏的名字完全一致
            if stat.S_ISDIR(x.st_mode):
                all_files.extend(self.__get_all_files_in_remote_dir(sftp, filename))
            else:
                all_files.append(filename)
        return all_files
    
    def sftp_get_dir(self, remote_dir, local_dir):
        all_files1=list()
        all_files2=list()
        all_files3=list()
        all_files_remote = list()
        all_files_local =list()
        will_get_file = list()
        all_files_local_sorted=list()
        t = paramiko.Transport(sock=(self.ip, 20131))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)
        # 获取远端linux主机上指定目录及其子目录下的所有文件
        all_files = self.__get_all_files_in_remote_dir(sftp, remote_dir)
        all_files_remote = sorted(all_files,key=str.lower)

        all_files_local = self.__get_all_files_in_local_dir(local_dir)
        all_files_local_sorted=sorted(all_files_local,key=str.lower)
        
        for i in all_files_remote:
            filen=i.split('/')[-1]
            all_files1.append(filen)
            
        for j in all_files_local_sorted:
            filen=j.split('/')[-1]
            all_files2.append(filen)
        
        #set(list1).difference(set(list2))
        
        will_get_file = set(all_files1).difference(set(all_files2))
        
        for t in sorted(will_get_file):
            filename=remote_dir+t.split('-')[0]+'/'+t[0:10]+'/' +t
            all_files3.append(filename)
            
            
        # 依次get每一个文件
        for x in (sorted(all_files3)):
            filename = x.split('/')[-1]
            local_filename = os.path.join(local_dir, filename)
            print(u'Get文件%s传输中...' % filename)
            sftp.get(x, local_filename)            


# ------获取本地指定目录及其子目录下的所有文件------
    def __get_all_files_in_local_dir(self, local_dir):
        # 保存所有文件的列表
        all_files = list()

        # 获取当前指定目录下的所有目录及文件,包含属性值
        files = os.listdir(local_dir)
        for x in files:
            # local_dir目录中每一个文件或目录的完整路径
            filename = os.path.join(local_dir, x)
            # 如果是目录,则递归处理该目录
            if os.path.isdir(x):
                all_files.extend(self.__get_all_files_in_local_dir(filename))
            else:
                all_files.append(filename)
        return all_files

    def sftp_put_dir(self, local_dir, remote_dir):
        t = paramiko.Transport(sock=(self.ip, 20131))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)

        # 去掉路径字符穿最后的字符'/',如果有的话
        if remote_dir[-1] == '/':
            remote_dir = remote_dir[0:-1]

        # 获取本地指定目录及其子目录下的所有文件
        all_files = self.__get_all_files_in_local_dir(local_dir)
        # 依次put每一个文件
        for x in all_files:
            filename = os.path.split(x)[-1]
            remote_filename = remote_dir + '/' + filename
            print(u'Put文件%s传输中...' % filename)
            sftp.put(x, remote_filename)
            
if __name__ == '__main__':
    remote_path = r'/home/pi/'
    local_path = r'/Users/jungangzhu/Downloads/jpg_from_remote/'

    host = Linux('1.tcp.hk.cpolar.io', 'pi', 'raspberry')

    # 将远端remote_path目录中的所有文件get到本地local_path目录
    host.sftp_get_dir(remote_path, local_path)
    # # 将本地local_path目录中的所有文件put到远端remote_path目录
    # host.sftp_put_dir(remote_path, local_path)
]]>
974 0 0 0
<![CDATA[城市之角]]> http://mixdiy.com/index.php/2022/04/03/coner-of-the-city/ Sun, 03 Apr 2022 11:54:30 +0000 http://mixdiy.com/?p=983
]]>
983 0 0 0
<![CDATA[0603switcher]]> http://mixdiy.com/index.php/2022/04/05/0603switcher/ Tue, 05 Apr 2022 04:59:07 +0000 http://mixdiy.com/?p=992 ]]> 992 0 0 0 <![CDATA[npn switcher]]> http://mixdiy.com/index.php/2022/04/05/npn-switcher/ Tue, 05 Apr 2022 07:52:35 +0000 http://mixdiy.com/?p=996 ]]> 996 0 0 0 <![CDATA[light led]]> http://mixdiy.com/index.php/2022/04/05/light-led/ Tue, 05 Apr 2022 12:26:55 +0000 http://mixdiy.com/?p=1002
import RPi.GPIO as GPIO   #导入树莓派提供的python模块
import time   #导入时间包,用于控制闪烁

GPIO.setmode(GPIO.BCM)   #设置GPIO模式,BCM模式在所有数码派通用
GPIO.setup(26, GPIO.OUT)   #设置GPIO18为电流输出
while True:
    GPIO.output(26, GPIO.HIGH)   #GPIO18 输出3.3V
    time.sleep(1)   #程序控制流程睡眠0.05秒
    GPIO.output(26, GPIO.LOW)    #GPIO18 输出0V
    time.sleep(1)   #程序控制流程睡眠0.05秒
]]>
1002 0 0 0
<![CDATA[python 字符串运算]]> http://mixdiy.com/index.php/2022/04/07/compare-char/ Thu, 07 Apr 2022 04:05:36 +0000 http://mixdiy.com/?p=1011

https://www.cnblogs.com/lsdb/p/10275657.html

]]>
1011 0 0 0
<![CDATA[改编的定时浇水(由esp32-c3独立完成)]]> http://mixdiy.com/index.php/2022/04/11/bung/ Mon, 11 Apr 2022 03:08:16 +0000 http://mixdiy.com/?p=1024
#coding:utf-8
from machine import UART,Pin,RTC
import machine
import time,network
import ntptime
import os

led1=Pin(12,Pin.OUT)
led2=Pin(13,Pin.OUT)
rtc = RTC()
k=0
def linedetect():
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)  #设置Pin6=tx,Pin7=rx
    idsend='huanghe'
    idreceive='changjiang'
    uart.write(idsend)
    time.sleep(5)      #树莓派启动需要的时间,否则会反复重启
    receive_data=uart.readline()
    if (idreceive in str(receive_data)):
        return 1
    else:
        return 0

def onoff1(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.on()
#    p.off()
def onoff0(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.off()


def createconfigfile(filename):
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)
    time.sleep(5)
    readmsg = uart.readline()
    if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in str(readmsg):
        content = readmsg
        with open(filename,mode='w',encoding='utf-8') as f:
            f.write(content)

def readconfigfile(filename):
    with open(filename,mode='r',encoding='utf-8') as f:
        s=f.read().strip('\n').split(',')
        if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in s:
            ssid=s[0].split(':')[1]
            pwd=s[1].split(':')[1]
            startt=s[2].split(':')[1]
            endt=s[3].split(':')[1]
            return ssid,pwd,startt,endt
        else:
            print('config file format error,will drop it')
            os.remove(filename)

# 同步时间
def sync_ntp():
     ntptime.NTP_DELTA = 3155644800   # 可选 UTC+8偏移时间(秒),不设置就是UTC0
     ntptime.host = 'ntp1.aliyun.com'  # 可选,ntp服务器,默认是"pool.ntp.org"
     try:
         ntptime.settime()   # 修改设备时间,到这就已经设置好了
     except:
         for i in range(6):
            led1.value(1)              #turn off 0是亮
            time.sleep(0.1)
            led1.value(0)             
            time.sleep(0.1)
         print('同步失败')

# 联WIFI
def WIFI_Connect(ssid,pwd):
    wlan = network.WLAN(network.STA_IF) #STA模式
    wlan.active(True)                   #激活接口
    start_time=time.time()              #记录时间做超时判断
    if not wlan.isconnected():
        print('connecting to network...'+ssid)
        wlan.connect(ssid, pwd) #输入WIFI账号密码
        time.sleep(1)
        while not wlan.isconnected():
            if time.time()-start_time > 15 :
                print('wifi Connected Timeout!')
                os.remove('config.txt')
                machine.reset()
    if wlan.isconnected():
        print('wifi was connected!')        

try:
    a,b,c,d=readconfigfile('config.txt')
except:
    print('can not find config.txt,will create it!')
    createconfigfile('config.txt')
    time.sleep(2)
    machine.reset()   
WIFI_Connect(a,b)
print(a,b,c,d)
dt=time.localtime()
po=dt[2]
iflag=0
for i in range(5):
    sync_ntp()
    time.sleep(1)

while True:
    led2.value(1)
    time.sleep(1)
    led2.value(0)
    time.sleep(1)
    dt=time.localtime()
    print(dt)
    if (po==dt[2] and iflag==0):
        if (dt[3]>=6):
            onoff1(2)
            time.sleep(120)
            onoff0(2)
            iflag=1
    elif po!=dt[2]:
        machine.reset()

放在循环里,这么每天不需要重新启动

#coding:utf-8
from machine import UART,Pin,RTC
import machine
import time,network
import ntptime
import os

led1=Pin(12,Pin.OUT)
led2=Pin(13,Pin.OUT)
rtc = RTC()
k=0
def linedetect():
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)  #设置Pin6=tx,Pin7=rx
    idsend='huanghe'
    idreceive='changjiang'
    uart.write(idsend)
    time.sleep(5)      #树莓派启动需要的时间,否则会反复重启
    receive_data=uart.readline()
    if (idreceive in str(receive_data)):
        return 1
    else:
        return 0

def onoff1(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.on()
#    p.off()
def onoff0(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.off()


def createconfigfile(filename):
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)
    time.sleep(5)
    readmsg = uart.readline()
    if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in str(readmsg):
        content = readmsg
        with open(filename,mode='w',encoding='utf-8') as f:
            f.write(content)

def readconfigfile(filename):
    with open(filename,mode='r',encoding='utf-8') as f:
        s=f.read().strip('\n').split(',')
        if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in s:
            ssid=s[0].split(':')[1]
            pwd=s[1].split(':')[1]
            startt=s[2].split(':')[1]
            endt=s[3].split(':')[1]
            return ssid,pwd,startt,endt
        else:
            print('config file format error,will drop it')
            os.remove(filename)

# 同步时间
def sync_ntp():
     ntptime.NTP_DELTA = 3155644800   # 可选 UTC+8偏移时间(秒),不设置就是UTC0
     ntptime.host = 'ntp1.aliyun.com'  # 可选,ntp服务器,默认是"pool.ntp.org"
     try:
         ntptime.settime()   # 修改设备时间,到这就已经设置好了
     except:
         for i in range(6):
            led1.value(1)              #turn off 0是亮
            time.sleep(0.1)
            led1.value(0)             
            time.sleep(0.1)
         print('同步失败')

# 联WIFI
def WIFI_Connect(ssid,pwd):
    wlan = network.WLAN(network.STA_IF) #STA模式
    wlan.active(True)                   #激活接口
    start_time=time.time()              #记录时间做超时判断
    if not wlan.isconnected():
        print('connecting to network...'+ssid)
        wlan.connect(ssid, pwd) #输入WIFI账号密码
        time.sleep(1)
        while not wlan.isconnected():
            if time.time()-start_time > 15 :
                print('wifi Connected Timeout!')
                os.remove('config.txt')
                machine.reset()
    if wlan.isconnected():
        print('wifi was connected!')        

try:
    a,b,c,d=readconfigfile('config.txt')
except:
    print('can not find config.txt,will create it!')
    createconfigfile('config.txt')
    time.sleep(2)
    machine.reset()   
WIFI_Connect(a,b)
print(a,b,c,d)

while True:
    dt=time.localtime()
    po=dt[2]
    iflag=0
    for i in range(5):
        sync_ntp()
        time.sleep(1)
    while True:
        led2.value(1)
        time.sleep(1)
        led2.value(0)
        time.sleep(1)
        dt=time.localtime()
        print(dt)
        if (po==dt[2] and iflag==0):
            if (dt[3]>=6):
                onoff1(2)
                led2.value(1)
                time.sleep(120)
                onoff0(2)
                led2.value(0)
                iflag=1
        elif po!=dt[2]:
            break

获取天气

https://zhuanlan.zhihu.com/p/187577492

未来15天逐日天气预报和昨日天气 · 语雀 (yuque.com)

天气预报(城市级) · 语雀 (yuque.com)

]]>
1024 0 0 0
<![CDATA[智能浇灌系统]]> http://mixdiy.com/index.php/2022/04/13/urequests-of-micropython/ Wed, 13 Apr 2022 04:38:46 +0000 http://mixdiy.com/?p=1038

urequests.py

import usocket

class Response:

    def __init__(self, f):
        self.raw = f
        self.encoding = "utf-8"
        self._cached = None

    def close(self):
        if self.raw:
            self.raw.close()
            self.raw = None
        self._cached = None

    @property
    def content(self):
        if self._cached is None:
            try:
                self._cached = self.raw.read()
            finally:
                self.raw.close()
                self.raw = None
        return self._cached

    @property
    def text(self):
        return str(self.content, self.encoding)

    def json(self):
        import ujson
        return ujson.loads(self.content)


def request(method, url, data=None, json=None, headers={}, stream=None):
    try:
        proto, dummy, host, path = url.split("/", 3)
    except ValueError:
        proto, dummy, host = url.split("/", 2)
        path = ""
    if proto == "http:":
        port = 80
    elif proto == "https:":
        import ussl
        port = 443
    else:
        raise ValueError("Unsupported protocol: " + proto)

    if ":" in host:
        host, port = host.split(":", 1)
        port = int(port)

    ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM)
    ai = ai[0]

    s = usocket.socket(ai[0], ai[1], ai[2])
    try:
        s.connect(ai[-1])
        if proto == "https:":
            s = ussl.wrap_socket(s, server_hostname=host)
        s.write(b"%s /%s HTTP/1.0\r\n" % (method, path))
        if not "Host" in headers:
            s.write(b"Host: %s\r\n" % host)
        # Iterate over keys to avoid tuple alloc
        for k in headers:
            s.write(k)
            s.write(b": ")
            s.write(headers[k])
            s.write(b"\r\n")
        if json is not None:
            assert data is None
            import ujson
            data = ujson.dumps(json)
            s.write(b"Content-Type: application/json\r\n")
        if data:
            s.write(b"Content-Length: %d\r\n" % len(data))
        s.write(b"\r\n")
        if data:
            s.write(data)

        l = s.readline()
        #print(l)
        l = l.split(None, 2)
        status = int(l[1])
        reason = ""
        if len(l) > 2:
            reason = l[2].rstrip()
        while True:
            l = s.readline()
            if not l or l == b"\r\n":
                break
            #print(l)
            if l.startswith(b"Transfer-Encoding:"):
                if b"chunked" in l:
                    raise ValueError("Unsupported " + l)
            elif l.startswith(b"Location:") and 300 < status < 400:
                new_url = (l[10:-2]).decode('utf8')
                return request(method, new_url, data, json, headers, stream)
    except OSError:
        s.close()
        raise

    resp = Response(s)
    resp.status_code = status
    resp.reason = reason
    return resp


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

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

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

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

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

def delete(url, **kw):
    return request("DELETE", url, **kw)

给个例子,获取南京天气:

import network
import urequests

sta = network.WLAN(network.STA_IF)
sta.active(True)
try:
    sta.connect('HUAWEI-WULIAN', '********')
except:
    print('already connected')
while not sta.isconnected():
    pass


Url = 'https://api.seniverse.com/v3/weather/now.json?key=SK6E_7MBhPJr0_Cs3&location=nanjing&language=zh-Hans&unit=c'
r = urequests_alt.get(Url)
r.json()
print(r.json()['results'][0]['location']['name'],r.json()['results'][0]['now']['temperature'],r.json()['results'][0]['now']['text'])

if '雨' in r.json()['results'][0]['now']['text']:
    print('today is free')

自动获取归属地天气预报并根据当天及未来2天天气预报决定是否浇水及浇多少水

#coding:utf-8
from machine import UART,Pin,RTC
import machine
import time,network
import ntptime
import os
import urequests

led1=Pin(12,Pin.OUT)
led2=Pin(13,Pin.OUT)
rtc = RTC()
k=0

def getpublicip():
    t=urequests.get("http://txt.go.sohu.com/ip/soip")
    nums=(t.text.find('window.sohu_user_ip='))
    ip=(t.text[(nums+21):437])
    return ip

def fromipgetcity(ip):
    url = "http://ip-api.com/json/"+ip+"?lang=zh-CN"
    r = urequests.get(url )
    r.status_code   #打印200,说明请求成功
    r.json()
    return r.json()['city']

def linedetect():
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)  #设置Pin6=tx,Pin7=rx
    idsend='huanghe'
    idreceive='changjiang'
    uart.write(idsend)
    time.sleep(5)      #树莓派启动需要的时间,否则会反复重启
    receive_data=uart.readline()
    if (idreceive in str(receive_data)):
        return 1
    else:
        return 0

def onoff1(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.on()
#    p.off()
def onoff0(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.off()


def createconfigfile(filename):
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)
    time.sleep(5)
    readmsg = uart.readline()
    if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in str(readmsg):
        content = readmsg
        with open(filename,mode='w',encoding='utf-8') as f:
            f.write(content)

def readconfigfile(filename):
    with open(filename,mode='r',encoding='utf-8') as f:
        s=f.read().strip('\n').split(',')
        if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in s:
            ssid=s[0].split(':')[1]
            pwd=s[1].split(':')[1]
            startt=s[2].split(':')[1]
            endt=s[3].split(':')[1]
            return ssid,pwd,startt,endt
        else:
            print('config file format error,will drop it')
            os.remove(filename)

# 同步时间
def sync_ntp():
     ntptime.NTP_DELTA = 3155644800   # 可选 UTC+8偏移时间(秒),不设置就是UTC0
     ntptime.host = 'ntp1.aliyun.com'  # 可选,ntp服务器,默认是"pool.ntp.org"
     try:
         ntptime.settime()   # 修改设备时间,到这就已经设置好了
     except:
         for i in range(6):
            led1.value(1)              #turn off 0是亮
            time.sleep(0.1)
            led1.value(0)             
            time.sleep(0.1)
         print('同步失败')

# 联WIFI
def WIFI_Connect(ssid,pwd):
    wlan = network.WLAN(network.STA_IF) #STA模式
    wlan.active(True)                   #激活接口
    start_time=time.time()              #记录时间做超时判断
    if not wlan.isconnected():
        print('connecting to network...'+ssid)
        wlan.connect(ssid, pwd) #输入WIFI账号密码
        time.sleep(1)
        while not wlan.isconnected():
            if time.time()-start_time > 15 :
                print('wifi Connected Timeout!')
                os.remove('config.txt')
                machine.reset()
    if wlan.isconnected():
        print('wifi was connected!')        

try:
    a,b,c,d=readconfigfile('config.txt')
except:
    print('can not find config.txt,will create it!')
    createconfigfile('config.txt')
    time.sleep(2)
    machine.reset()   
WIFI_Connect(a,b)
print(a,b,c,d)
city=fromipgetcity(str(getpublicip()))
#Url = 'https://api.seniverse.com/v3/weather/now.json?key=SdzxOm0_G_Oi3waGC&location='+city+'&language=zh-Hans&unit=c'
Url = 'https://api.seniverse.com/v3/weather/daily.json?key=SdzxOm0_G_Oi3waGC&location=nanjing&language=zh-Hans&unit=c&start=0&days=5'


while True:
    dt=time.localtime()
    po=dt[2]
    iflag=0    
    r = urequests.get(Url)
    r.json()
    print(r.json())
#    print(r.json()['results'][0]['location']['name'],r.json()['results'][0]['now']['temperature'],r.json()['results'][0]['now']['text'])
    print(r.json()['results'][0]['daily'][0]['date'])
    weather_day=(r.json()['results'][0]['daily'][0]['text_night']+r.json()['results'][0]['daily'][0]['text_day'])            
    for i in range(5):
        sync_ntp()
        time.sleep(1)
    while True:
        dt1=time.localtime()
        if po!=dt1[2]:
            break
#        elif '雨' in r.json()['results'][0]['now']['text']:
        elif '雨' in weather_day:
            print('下雨,继续循环')
            time.sleep(3)
            led1.value(1)
            time.sleep(0.05)
            led1.value(0)
            continue
        else:
            if iflag==0:
                print('未下雨,等待条件启动水泵')
            elif iflag==1:
                print('已浇灌')
            led2.value(1)
            time.sleep(1)
            led2.value(0)
            time.sleep(1)
            dt1=time.localtime()
            print(dt1)
            if (po==dt1[2] and iflag==0):
                if (dt1[3]>=6):
                    print('启动水泵')
                    onoff1(2)
                    led2.value(1)
                    time.sleep(60)
                    onoff0(2)
                    led2.value(0)
                    iflag=1

免费IP归属地查询接口汇总 - _nul1 - 博客园 (cnblogs.com)

根据日出、日落时间自动实现浇灌(日出、日落接口来自免费限时接口)

#coding:utf-8
from machine import UART,Pin,RTC
import machine
import time,network
import ntptime
import os
import urequests

led1=Pin(12,Pin.OUT)
led2=Pin(13,Pin.OUT)
rtc = RTC()
k=0

def getpublicip():
    t=urequests.get("http://txt.go.sohu.com/ip/soip")
    nums=(t.text.find('window.sohu_user_ip='))
    ip=(t.text[(nums+21):437])
    return ip

def fromipgetcity(ip):
    url = "http://ip-api.com/json/"+ip+"?lang=zh-CN"
    r = urequests.get(url )
    r.status_code   #打印200,说明请求成功
    r.json()
    return r.json()['city']

def linedetect():
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)  #设置Pin6=tx,Pin7=rx
    idsend='huanghe'
    idreceive='changjiang'
    uart.write(idsend)
    time.sleep(5)      #树莓派启动需要的时间,否则会反复重启
    receive_data=uart.readline()
    if (idreceive in str(receive_data)):
        return 1
    else:
        return 0

def onoff1(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.on()
#    p.off()
def onoff0(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.off()


def createconfigfile(filename):
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)
    time.sleep(5)
    readmsg = uart.readline()
    if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in str(readmsg):
        content = readmsg
        with open(filename,mode='w',encoding='utf-8') as f:
            f.write(content)

def readconfigfile(filename):
    with open(filename,mode='r',encoding='utf-8') as f:
        s=f.read().strip('\n').split(',')
        if 'SSID' and 'PWD' and 'START' and 'END' and 'changjiang' in s:
            ssid=s[0].split(':')[1]
            pwd=s[1].split(':')[1]
            startt=s[2].split(':')[1]
            endt=s[3].split(':')[1]
            return ssid,pwd,startt,endt
        else:
            print('config file format error,will drop it')
            os.remove(filename)

# 同步时间
def sync_ntp():
     ntptime.NTP_DELTA = 3155644800   # 可选 UTC+8偏移时间(秒),不设置就是UTC0
     ntptime.host = 'ntp1.aliyun.com'  # 可选,ntp服务器,默认是"pool.ntp.org"
     try:
         ntptime.settime()   # 修改设备时间,到这就已经设置好了
     except:
         for i in range(6):
            led1.value(1)              #turn off 0是亮
            time.sleep(0.1)
            led1.value(0)             
            time.sleep(0.1)
         print('同步失败')

# 联WIFI
def WIFI_Connect(ssid,pwd):
    wlan = network.WLAN(network.STA_IF) #STA模式
    wlan.active(True)                   #激活接口
    start_time=time.time()              #记录时间做超时判断
    if not wlan.isconnected():
        print('connecting to network...'+ssid)
        wlan.connect(ssid, pwd) #输入WIFI账号密码
        time.sleep(1)
        while not wlan.isconnected():
            if time.time()-start_time > 15 :
                print('wifi Connected Timeout!')
                os.remove('config.txt')
                machine.reset()
    if wlan.isconnected():
        print('wifi was connected!')        

try:
    a,b,c,d=readconfigfile('config.txt')
except:
    print('can not find config.txt,will create it!')
    createconfigfile('config.txt')
    time.sleep(2)
    machine.reset()   
WIFI_Connect(a,b)
print(a,b,c,d)
city=fromipgetcity(str(getpublicip()))
#Url = 'https://api.seniverse.com/v3/weather/now.json?key=SdzxOm0_G_Oi3waGC&location='+city+'&language=zh-Hans&unit=c'
Url = 'https://api.seniverse.com/v3/weather/daily.json?key=SdzxOm0_G_Oi3waGC&location='+city+'&language=zh-Hans&unit=c&start=0&days=5'

Url_sun = 'https://api.seniverse.com/v3/geo/sun.json?key=SL4MGOKv4L9OcVyNJ&location='+city+'&language=zh-Hans&days=1'
r_sun = urequests.get(Url_sun)
r_sun.json
print(r_sun.json())
sunrise = r_sun.json()['results'][0]['sun'][0]['sunrise']
sunset = r_sun.json()['results'][0]['sun'][0]['sunset']

startwater=int(sunrise.split(':')[0])
stopwater=int(sunset.split(':')[0])+1
print(startwater)
print(stopwater)


while True:
    dt=time.localtime()
    po=dt[2]
    iflag=0
    iflag1=0
    r = urequests.get(Url)
    r.json()
    print(r.json())
#    print(r.json()['results'][0]['location']['name'],r.json()['results'][0]['now']['temperature'],r.json()['results'][0]['now']['text'])
    print(r.json()['results'][0]['daily'][0]['date'])
    weather_day=(r.json()['results'][0]['daily'][0]['text_night']+r.json()['results'][0]['daily'][0]['text_day'])            
    for i in range(5):
        sync_ntp()
        time.sleep(1)
    while True:
        dt1=time.localtime()
        if po!=dt1[2]:
            break
#        elif '雨' in r.json()['results'][0]['now']['text']:
        elif '雨' in weather_day:
            print('下雨,继续循环')
            time.sleep(3)
            led1.value(1)
            time.sleep(0.05)
            led1.value(0)
            continue
        else:
            if iflag==0 and iflag1==0:
                print('未下雨,等待条件启动水泵')
            elif iflag==1 and iflag1==0:
                print('上午已浇灌,等待下午浇灌')
            elif iflag==1 and iflag1==1:
                print('已浇灌')
            led2.value(1)
            time.sleep(1)
            led2.value(0)
            time.sleep(1)
            dt1=time.localtime()
            print(dt1)
            if (po==dt1[2] and iflag==0):
                if (dt1[3]>=startwater):
                    print('启动水泵')
                    onoff1(2)
                    led2.value(1)
                    time.sleep(60)
                    onoff0(2)
                    led2.value(0)
                    iflag=1
            if (po==dt1[2] and iflag1==0):
                if (dt1[3]>=stopwater):
                    print('启动水泵')
                    onoff1(2)
                    led2.value(1)
                    time.sleep(60)
                    onoff0(2)
                    led2.value(0)
                    iflag1=1

改进ip地址获取算法

#coding:utf-8
from machine import UART,Pin,RTC
import machine
import time,network
import ntptime
import os
import urequests

led1=Pin(12,Pin.OUT)
led2=Pin(13,Pin.OUT)
rtc = RTC()
k=0

def getpublicip():
    t=urequests.get("http://txt.go.sohu.com/ip/soip")
    nums=(t.text.find('window.sohu_user_ip='))
    pre_ip=(t.text[(nums+21):(nums+21+15)])
    ip = pre_ip.split('"')[0]
    return ip

def fromipgetcity(ip):
#    url = "http://ip-api.com/json/"+ip+"?lang=zh-CN"
    url = "http://ip-api.com/json/"+ip+"?fields=status,message,country,countryCode,region,regionName,city,zip,lat,lon,timezone,isp,org,as,query&lang=zh-CN"
    r = urequests.get(url )
    r.status_code   #打印200,说明请求成功
    r.json()
    return r.json()['city']

def linedetect():
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)  #设置Pin6=tx,Pin7=rx
    idsend='huanghe'
    idreceive='changjiang'
    uart.write(idsend)
    time.sleep(5)      #树莓派启动需要的时间,否则会反复重启
    receive_data=uart.readline()
    if (idreceive in str(receive_data)):
        return 1
    else:
        return 0

def onoff1(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.on()
#    p.off()
def onoff0(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.off()


def createconfigfile(filename):
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)
    time.sleep(5)
    readmsg = uart.readline()
    if 'SSID' and 'PWD' and 'START' and 'END' and 'WATERTIME' and 'changjiang' in str(readmsg):
        content = readmsg
        with open(filename,mode='w',encoding='utf-8') as f:
            f.write(content)

def readconfigfile(filename):
    with open(filename,mode='r',encoding='utf-8') as f:
        s=f.read().strip('\n').split(',')
        if 'SSID' and 'PWD' and 'START' and 'END' and 'WATERTIME' and 'changjiang' in s:
            ssid=s[0].split(':')[1]
            pwd=s[1].split(':')[1]
            startt=s[2].split(':')[1]
            endt=s[3].split(':')[1]
            watertime=s[4].split(':')[1]
            return ssid,pwd,startt,endt,watertime
        else:
            print('config file format error,will drop it')
            os.remove(filename)

# 同步时间
def sync_ntp():
     ntptime.NTP_DELTA = 3155644800   # 可选 UTC+8偏移时间(秒),不设置就是UTC0
     ntptime.host = 'ntp1.aliyun.com'  # 可选,ntp服务器,默认是"pool.ntp.org"
     try:
         ntptime.settime()   # 修改设备时间,到这就已经设置好了
     except:
         for i in range(6):
            led1.value(1)              #turn off 0是亮
            time.sleep(0.1)
            led1.value(0)             
            time.sleep(0.1)
         print('同步失败')

# 联WIFI
def WIFI_Connect(ssid,pwd):
    wlan = network.WLAN(network.STA_IF) #STA模式
    wlan.active(True)                   #激活接口
    start_time=time.time()              #记录时间做超时判断
    if not wlan.isconnected():
        print('connecting to network...'+ssid)
        wlan.connect(ssid, pwd) #输入WIFI账号密码
        time.sleep(1)
        while not wlan.isconnected():
            if time.time()-start_time > 15 :
                print('wifi Connected Timeout!')
                os.remove('config.txt')
                machine.reset()
    if wlan.isconnected():
        print('WiFi 已正常连接!')        

try:
    a,b,c,d,e=readconfigfile('config.txt')
except:
    print('can not find config.txt,will create it!')
    createconfigfile('config.txt')
    time.sleep(2)
    machine.reset()   
WIFI_Connect(a,b)
#print(a,b,c,d,e)
city=fromipgetcity(str(getpublicip()))
#Url = 'https://api.seniverse.com/v3/weather/now.json?key=SdzxOm0_G_Oi3waGC&location='+city+'&language=zh-Hans&unit=c'
Url = 'https://api.seniverse.com/v3/weather/daily.json?key=SdzxOm0_G_Oi3waGC&location='+city+'&language=zh-Hans&unit=c&start=0&days=5'

Url_sun = 'https://api.seniverse.com/v3/geo/sun.json?key=SL4MGOKv4L9OcVyNJ&location='+city+'&language=zh-Hans&days=1'
r_sun = urequests.get(Url_sun)
r_sun.json
#print(r_sun.json())
sunrise = r_sun.json()['results'][0]['sun'][0]['sunrise']
sunset = r_sun.json()['results'][0]['sun'][0]['sunset']

startwater=int(sunrise.split(':')[0])
stopwater=int(sunset.split(':')[0])+1
print(city+'今天日出时间'+str(startwater)+'点 日落时间'+str(stopwater)+'点')

while True:
    dt=time.localtime()
    po=dt[2]
    iflag=0
    iflag1=0
    try:
        r = urequests.get(Url)
    except:
        WIFI_Connect(a,b)
        r = urequests.get(Url)
    r.json()
#    print(r.json())
#    print(r.json()['results'][0]['location']['name'],r.json()['results'][0]['now']['temperature'],r.json()['results'][0]['now']['text'])
#    print(r.json()['results'][0]['daily'][0]['date'])
    weather_day=(r.json()['results'][0]['daily'][0]['text_night']+r.json()['results'][0]['daily'][0]['text_day'])            
    for i in range(5):
        sync_ntp()
        time.sleep(1)
    while True:
        dt1=time.localtime()
        if po!=dt1[2]:
            break
#        elif '雨' in r.json()['results'][0]['now']['text']:
        elif '雨' in weather_day:
            print(city+'预计今日有雨,取消浇灌计划')
            time.sleep(3)
            led1.value(1)
            time.sleep(0.05)
            led1.value(0)
            continue
        else:
            if iflag==0 and iflag1==0:
                print(city+'今日无雨,等待条件启动水泵')
            elif iflag==1 and iflag1==0:
                print('日出前已浇灌,等待日落后浇灌')
            elif iflag==0 and iflag1==1:
                print('日落后已浇灌')
            elif iflag==1 and iflag1==1:
                print('全天已浇灌')                
            led2.value(1)
            time.sleep(1)
            led2.value(0)
            time.sleep(1)
            dt1=time.localtime()
#            print(dt1)
            if (po==dt1[2] and iflag==0):
                if (dt1[3]>=startwater and dt1[3]<stopwater):
                    print('启动水泵')
                    onoff1(2)
                    led2.value(1)
                    time.sleep(int(e))
                    onoff0(2)
                    led2.value(0)
                    iflag=1
            if (po==dt1[2] and iflag1==0):
                if (dt1[3]>=stopwater):
                    print('启动水泵')
                    onoff1(2)
                    led2.value(1)
                    time.sleep(int(e))
                    onoff0(2)
                    led2.value(0)
                    iflag1=1

2022-04-29

采用淘宝接口更稳定根据ip地址获取归属地:

#coding:utf-8
from machine import UART,Pin,RTC
import machine
import time,network
import ntptime
import os
import urequests

led1=Pin(12,Pin.OUT)
led2=Pin(13,Pin.OUT)
rtc = RTC()
k=0

def getpublicip():
    t=urequests.get("http://txt.go.sohu.com/ip/soip")
    nums=(t.text.find('window.sohu_user_ip='))
    pre_ip=(t.text[(nums+21):(nums+21+15)])
    ip = pre_ip.split('"')[0]
    print(ip)
    return ip

def fromipgetcity(ip):
    result_flag=0
    url="https://ip.taobao.com//outGetIpInfo?ip=" + ip
    while True:
        time.sleep(2)
        if result_flag !=200:
            try:
                r = urequests.get(url)
                time.sleep(1)
                result_flag=r.status_code
            except:
                print('try '+url)
        elif result_flag==200:
            if r.json()['msg']!='the request over max qps for user ,the accessKey=public':
#                print(r.json())
#                print(r.json()['data']['city'])
                return r.json()['data']['city']
                break
            else:
                result_flag=0
                continue

def linedetect():
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)  #设置Pin6=tx,Pin7=rx
    idsend='huanghe'
    idreceive='changjiang'
    uart.write(idsend)
    time.sleep(5)      #树莓派启动需要的时间,否则会反复重启
    receive_data=uart.readline()
    if (idreceive in str(receive_data)):
        return 1
    else:
        return 0

def onoff1(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.on()
#    p.off()
def onoff0(pinnum):
    p = Pin(pinnum, Pin.OUT)
    p.off()


def createconfigfile(filename):
    uart=UART(1,baudrate=115200,tx=6,rx=7,timeout=1)
    time.sleep(5)
    readmsg = uart.readline()
    if 'SSID' and 'PWD' and 'START' and 'END' and 'WATERTIME' and 'changjiang' in str(readmsg):
        content = readmsg
        with open(filename,mode='w',encoding='utf-8') as f:
            f.write(content)

def readconfigfile(filename):
    with open(filename,mode='r',encoding='utf-8') as f:
        s=f.read().strip('\n').split(',')
        if 'SSID' and 'PWD' and 'START' and 'END' and 'WATERTIME' and 'changjiang' in s:
            ssid=s[0].split(':')[1]
            pwd=s[1].split(':')[1]
            startt=s[2].split(':')[1]
            endt=s[3].split(':')[1]
            watertime=s[4].split(':')[1]
            return ssid,pwd,startt,endt,watertime
        else:
            print('config file format error,will drop it')
            os.remove(filename)

# 同步时间
def sync_ntp():
     ntptime.NTP_DELTA = 3155644800   # 可选 UTC+8偏移时间(秒),不设置就是UTC0
     ntptime.host = 'ntp1.aliyun.com'  # 可选,ntp服务器,默认是"pool.ntp.org"
     try:
         ntptime.settime()   # 修改设备时间,到这就已经设置好了
     except:
         for i in range(6):
            led1.value(1)              #turn off 0是亮
            time.sleep(0.1)
            led1.value(0)             
            time.sleep(0.1)
         print('同步失败')

# 联WIFI
def WIFI_Connect(ssid,pwd):
    wlan = network.WLAN(network.STA_IF) #STA模式
    wlan.active(True)                   #激活接口
    start_time=time.time()              #记录时间做超时判断
    if not wlan.isconnected():
        print('connecting to network...'+ssid)
        wlan.connect(ssid, pwd) #输入WIFI账号密码
        time.sleep(1)
        while not wlan.isconnected():
            if time.time()-start_time > 15 :
                print('wifi Connected Timeout!')
                os.remove('config.txt')
                machine.reset()
    if wlan.isconnected():
        print('WiFi 已正常连接!')        

try:
    a,b,c,d,e=readconfigfile('config.txt')
except:
    print('can not find config.txt,will create it!')
    createconfigfile('config.txt')
    time.sleep(2)
    machine.reset()   
WIFI_Connect(a,b)
#print(a,b,c,d,e)
city=fromipgetcity(str(getpublicip()))
#Url = 'https://api.seniverse.com/v3/weather/now.json?key=SdzxOm0_G_Oi3waGC&location='+city+'&language=zh-Hans&unit=c'
Url = 'https://api.seniverse.com/v3/weather/daily.json?key=SdzxOm0_G_Oi3waGC&location='+city+'&language=zh-Hans&unit=c&start=0&days=5'

Url_sun = 'https://api.seniverse.com/v3/geo/sun.json?key=SL4MGOKv4L9OcVyNJ&location='+city+'&language=zh-Hans&days=1'
r_sun = urequests.get(Url_sun)
r_sun.json
#print(r_sun.json())
sunrise = r_sun.json()['results'][0]['sun'][0]['sunrise']
sunset = r_sun.json()['results'][0]['sun'][0]['sunset']
print(r_sun.json())
startwater=int(sunrise.split(':')[0])
stopwater=int(sunset.split(':')[0])+1
print(city+'今天日出时间'+str(startwater)+'点 日落时间'+str(stopwater)+'点')

while True:
    dt=time.localtime()
    po=dt[2]
    iflag=0
    iflag1=0
    try:
        r = urequests.get(Url)
    except:
        WIFI_Connect(a,b)
        r = urequests.get(Url)
    r.json()
#    print(r.json())
#    print(r.json()['results'][0]['location']['name'],r.json()['results'][0]['now']['temperature'],r.json()['results'][0]['now']['text'])
#    print(r.json()['results'][0]['daily'][0]['date'])
    weather_day=(r.json()['results'][0]['daily'][0]['text_night']+r.json()['results'][0]['daily'][0]['text_day'])            
    for i in range(5):
        sync_ntp()
        time.sleep(1)
    while True:
        dt1=time.localtime()
        if po!=dt1[2]:
            break
#        elif '雨' in r.json()['results'][0]['now']['text']:
        elif '雨' in weather_day:
            print(city+'预计今日有雨,取消浇灌计划')
            time.sleep(3)
            led1.value(1)
            time.sleep(0.05)
            led1.value(0)
            continue
        else:
            if iflag==0 and iflag1==0:
                print(city+'今日无雨,等待条件启动水泵')
            elif iflag==1 and iflag1==0:
                print('日出前已浇灌,等待日落后浇灌')
            elif iflag==0 and iflag1==1:
                print('日落后已浇灌')
            elif iflag==1 and iflag1==1:
                print('全天已浇灌')                
            led2.value(1)
            time.sleep(1)
            led2.value(0)
            time.sleep(1)
            dt1=time.localtime()
#            print(dt1)
            if (po==dt1[2] and iflag==0):
                if (dt1[3]>=startwater and dt1[3]<stopwater):
                    print('启动水泵')
                    onoff1(2)
                    led2.value(1)
                    time.sleep(int(e))
                    onoff0(2)
                    led2.value(0)
                    iflag=1
            if (po==dt1[2] and iflag1==0):
                if (dt1[3]>=stopwater):
                    print('启动水泵')
                    onoff1(2)
                    led2.value(1)
                    time.sleep(int(e))
                    onoff0(2)
                    led2.value(0)
                    iflag1=1

对于自动删除config.txt文件的修补,主要是修改main.py

# 1.打开文件
file_read = open("c.txt")
file_write = open("config.txt", "w")
# 2. 读、写
text = file_read.read() # 读取文件的内容
file_write.write(text) # 把读取的内容写到新的文件中
# 3.关闭文件
file_read.close()
file_write.close()

import autowater
]]>
1038 0 0 0
<![CDATA[micropython OTA]]> http://mixdiy.com/index.php/2022/04/13/micropython-ota/ Wed, 13 Apr 2022 14:29:31 +0000 http://mixdiy.com/?p=1057

(10条消息) 解决esp8266 Mircopython OTA 远程升级方案_潭影的博客-CSDN博客

]]>
1057 0 0 0
<![CDATA[ESP32 Set Up Wi-Fi Connection Using Bluetooth]]> http://mixdiy.com/index.php/2022/04/13/set-wifi-password-throw-ble/ Wed, 13 Apr 2022 15:15:50 +0000 http://mixdiy.com/?p=1059

ESP32 使用蓝牙设置 Wi-Fi 连接 – 机器人零点一 (robotzero.one)

ESP32 Set Up Wi-Fi Connection Using Bluetooth – Robot Zero One

]]>
1059 0 0 0
<![CDATA[使用 micropython 的一些技巧]]> http://mixdiy.com/index.php/2022/04/14/micropython-tips/ Thu, 14 Apr 2022 05:50:47 +0000 http://mixdiy.com/?p=1063

确认连接wifi后,用以下方式安装模块

import upip

upip.install('micropython-uasyncio')
upip.install('micropython-pkg_resources')

文件传输,先安装相关工具(ampy是文件传输工具,特别是对于有二进制文件传输需求的很有用,传统的编辑工具thonny的很好补充,pyserial是命令行串口调试工具):

pip install esptool adafruit-ampy pyserial

再编写批处理文件putfile.bat(将目录下的文件和文件夹传入esp32)

set COMPORT=COM13
ampy --port %COMPORT% --baud 115200 put boot.py
ampy --port %COMPORT% --baud 115200 put configserver.py
ampy --port %COMPORT% --baud 115200 put main.py
ampy --port %COMPORT% --baud 115200 put wifi_database.py
ampy --port %COMPORT% --baud 115200 put html
pause

使用批处理文件很方便处理命令行,节省太多时间,举几个例子:

set COMPORT=COM36
python -m esptool --port %COMPORT% erase_flash
python -m esptool --port %COMPORT% --chip esp8266 write_flash --flash_size=detect -fm dio 0 "esp8266-20191220-v1.12.bin"
pause
@echo off
set COMPORT=COM36
echo Press Reset Button
python -m serial.tools.miniterm %COMPORT% 115200
]]>
1063 0 0 0
<![CDATA[编译包含自己模块和程序的micropython固件(ESP32-C3)]]> http://mixdiy.com/index.php/2022/04/16/micropython-gujian/ Sat, 16 Apr 2022 12:36:49 +0000 http://mixdiy.com/?p=1078
1.1.2. Esp-idf开发环境

在linux子系统命令行模式下依次执行如下指令:

cd ~

git clone https://gitee.com/EspressifSystems/esp-gitee-tools.git

git clone https://gitee.com/EspressifSystems/esp-idf.git

ls

执行结束后窗口如下所示:

执行如下指令:

cd esp-idf

git checkout v4.4.1

cd ~/esp-gitee-tools

./submodule-update.sh ~/esp-idf/

./install.sh ~/esp-idf/

( . /home/peter/esp-idf/export.sh)

(

Added the following directories to PATH:
/home/peter/esp-idf/components/esptool_py/esptool
/home/peter/esp-idf/components/espcoredump
/home/peter/esp-idf/components/partition_table
/home/peter/esp-idf/components/app_update
Done! You can now compile ESP-IDF projects.
Go to the project directory and run:

idf.py build

(检查一下PATH是否包含上述路径,$PATH)

等待命令结束,接着执行如下指令:

sudo nano /etc/profile
末尾加上以下一行:
export PATH="$PATH:~/esp-idf"

cd ~/esp-idf/

source export.sh

1.1.3. 编译固件

执行如下指令

cd ~

git clone https://gitee.com/CHN_ZC/micropython.git

sudo chmod a+rwx micropython

cd ~/micropython

make -C mpy-cross

cd ports/esp32

make submodules

make

最终执行结果:

1.1.4. 测试模块

Python文件模块放在esp32下的modules文件夹,进入该文件夹:

cd modules

新建一个测试用的python文件,如下:

nano test.py

文件内容如下:

from time import sleep

def hello():
    print("hello world")

def hw(str):
    print(str)

def cycle(str):
    while True:
        print(str)
        sleep(1)

保存后回到esp32目录,执行编译操作:

cd ~/micropython   #micropython根目录

make -C mpy-cross

cd ports/esp32/

编译esp32c3固件

make clean

更改ports/esp32/Makefile文件

找到 BOARD ?= GENERIC
改为 BOARD ?= GENERIC_C3

make

留意上面的三个文件及地址,分别是烧录文件及偏移地址。

注意:

严格按照执行后的提示执行一下,否则会导致环境变量等等没有设置,为后续编译带来很多麻烦

如果安装了虚拟机,比如在windows上安装了ubuntu,则可以将编译好的文件拷回windows系统再用工具烧录,比如将xxx拷贝到windows的d盘:

$ cp xxx /mnt/d/

$ cp build-GENERIC_C3/bootloader/bootloader.bin /mnt/d/
$ cp build-GENERIC_C3/partition_table/partition-table.bin /mnt/d/
$ cp build-GENERIC_C3/micropython.bin /mnt/d/
]]>
1078 0 0 0
<![CDATA[bootloader]]> http://mixdiy.com/index.php/2022/04/16/bootloader/ Sat, 16 Apr 2022 15:30:49 +0000 http://mixdiy.com/?p=1082 ]]> 1082 0 0 0 <![CDATA[修改python环境变量(python2 to python3)]]> http://mixdiy.com/index.php/2022/04/17/env/ Sun, 17 Apr 2022 01:24:52 +0000 http://mixdiy.com/?p=1088

which python2

which python3

sudo rm /usr/bin/python

sudo ln -s /usr/bin/python3 /usr/bin/python

查看esp芯片型号及flash大小

esptool.py --chip auto --port /dev/ttyUSB0 flash_id

]]>
1088 0 0 0
<![CDATA[获取自己的ip地址数据库]]> http://mixdiy.com/index.php/2022/04/29/getipcity/ Fri, 29 Apr 2022 09:44:46 +0000 http://mixdiy.com/?p=1115
import requests
from bs4 import BeautifulSoup
import re
import time

iplist=''

def getpublicip():
    t=requests.get("http://txt.go.sohu.com/ip/soip")
    nums=(t.text.find('window.sohu_user_ip='))
    pre_ip=(t.text[(nums+21):(nums+21+15)])
    ip = pre_ip.split('"')[0]
    return ip

for a in range(0,255):
    for b in range(0,255):
        for c in range(0,255):
            for d in range(0,255):
                ip=str(a)+'.'+str(b)+'.'+str(c)+'.'+str(d)
                url = "https://www.ip138.com/iplookup.asp?ip={}&action=2".format(ip)
                headers = {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36'
                }

                # 获取响应
                response = requests.get(url=url, headers=headers)
                response.encoding = "gb2312"
                html = response.text
                time.sleep(0.05)
                for match in re.finditer('"(ip|prov|city|ct)":"(.*?)"',html):
                    print(ip+':'+match.group())

                    with open('ip2city.txt',mode='a',encoding='utf-8') as f:
                        f.write(ip+','+iplist.join(match.group())+'\n')

http://ip.bczs.net/city/320100

]]>
1115 0 0 0
<![CDATA[Fritzing 使用注意事项]]> http://mixdiy.com/index.php/2022/05/01/fritzing-tags/ Sat, 30 Apr 2022 23:42:50 +0000 http://mixdiy.com/?p=1133

使用心得

svg:g节点的copper1是电路板正面,copper0是电路板反面

svg:g的id=silkscreen默认丝印在正面

]]>
1133 0 0 0
<![CDATA[常用元器件英文翻译]]> http://mixdiy.com/index.php/2022/05/01/elect-trancelate/ Sun, 01 May 2022 01:34:24 +0000 http://mixdiy.com/?p=1145

部分分立元件名称中英对照表
AND ————与门 
ANTENNA ——天线 
BATTERY—— 直流电源
BELL ————铃,钟 
BVC—— ——同轴电缆接插件 
BRIDEG 1 ——整流桥(二极管
BRIDEG 2 ——整流桥(集成块) 
BUFFER ——缓冲器 
BUZZER—— 蜂鸣器
CAP ————电容 
CAPACITOR ——电容 
CAPACITOR POL ——有极性电容 
CAPVAR ——————可调电容 
CIRCUIT BREAKER ——熔断丝 
COAX ——————同轴电缆 
CON ——————插口 
CRYSTAL ————晶体整荡器 
DB* ————并行插口 
DIODE ————二极管 
DIODE SCHOTTKY ——稳压二极管 
DIODE VARACTOR ——变容二极管 
DPY_3-SEG ————3段LED
DPY_7-SEG ————7段LED 
DPY_7-SEG_DP ———7段LED(带小数点) 
ELECTRO ————电解电容 
FUSE ————熔断器 
INDUCTOR———— 电感 
INDUCTOR IRON ———带铁芯电感 
INDUCTOR3 ————可调电感 
JFET N ————n沟道场效应管
JFET P ————P沟道场效应管 
LAMP ————灯泡 
LAMP NEDN ——起辉器 
LED ————发光二极管 
METER ——————仪表 
MICROPHONE ————麦克风 
MOSFET———— mos管
MOTOR AC ————交流电机 
MOTOR SERVO ——伺服电机 
NAND ——————与非门
NOR ——————或非门 
NOT ——————非门 
NPN ————NPN型三极管
NPN-PHOTO—— 感光三极管 
OPAMP ————运放 
OR ————或门 
PHOTO ————感光二极管 
PNP ————PNP型三极管 
NPN DAR ————NPN三极管 
PNP DAR ————PNP三极管 
POT 1和POT2————滑线变阻器
PELAY-DPDT ————双刀双掷继电器
RES1和RES2——电阻 
RES3和RES4 ——可变电阻 
RESISTOR BRIDGE ——桥式电阻 
RESPACK1~~RESPACK4 ——排阻
SCR ———晶闸管
PLUG——插头 
PLUG AC FEMALE和PLUG AC MALE ——三相交流插头 
SOCKET——插座 
SOURCE CURRENT—— 电流源 
SOURCE VOLTAGE ——电压源 
SPEAKER—— 扬声器 
SW-DIP2~~SW-DIP9————开关
SW-DPDT ———— 双刀双掷开关 
SW-DPST ———— 双刀单掷开关 
SW-SPST————单刀单掷开关 
SW-SPPT————单刀双掷开关
SW-PB ————按钮(开关)  
THERMISTOR ————电热调节器 
TRANS1 ————变压器
TRANS2 ————可调变压器 
TRIAC———— 三端双向可控硅
TRIODE ————三极真空管 
VARISTOR ————变阻器 
ZENER1~~ZENER3————稳压二极管 
DPY_7-SEG和DPY_7-SEG_DP  ————数码管 

Protel DOS Schematic Libraries.ddb 元件库及部分元件中英对照表
Protel Dos Schematic 4000 Cmos .Lib ———— 40.系列CMOS管集成块元件库( 例:4013 D                            触发器 和4027 JK 触发器 )   
来源:xiangtan.ourtimes.ren                            
Protel Dos Schematic analog Digital.Lib ———— 模拟数字式集成块元件库(AD系列 DAC系列 HD系列 MC系列 ) 
Protel Dos Schematic Comparator.Lib ————比较放大器元件库 
Protel Dos Shcematic Intel.Lib ———————INTEL公司生产的80系列CPU集成块元件库 
Protel Dos Schematic linear.lib ————————线性元件库( 例:555 )
Protel Dos Schemattic Memory Devices.Lib ————内存存储器元件库 
Protel Dos Schematic SYnertek.Lib ————————SY系列集成块元件库 
Protes Dos Schematic Motorlla.Lib———— ————摩托罗拉公司生产的元件库 
Protes Dos Schematic NEC.lib———— ————NEC公司生产的集成块元件库 
Protes Dos Schematic Operationel Amplifers.lib ————运算放大器元件库 
Protes Dos Schematic TTL.Lib ————————晶体管集成块元件库 74系列 
Protel Dos Schematic Voltage Regulator.lib ————电压调整集成块元件库 
Protes Dos Schematic Zilog.Lib ————————齐格格公司生产的Z80系列CPU集成块元件库 

74系列:
74LS00 ————TTL 2输入端四与非门
74LS01 ————TTL 集电极开路2输入端四与非门
74LS02 ————TTL 2输入端四或非门来源:xiangtan.ourtimes.ren
74LS03 ————TTL 集电极开路2输入端四与非门
74LS122———— TTL 可再触发单稳态多谐振荡器
74LS123 ————TTL 双可再触发单稳态多谐振荡器
74LS125 ————TTL 三态输出高有效四总线缓冲门
74LS126 ————TTL 三态输出低有效四总线缓冲门
74LS13 ————TTL 4输入端双与非施密特触发器
74LS132 ————TTL 2输入端四与非施密特触发器
74LS133 ————TTL 13输入端与非门
74LS136 ————TTL 四异或门
74LS138 ————TTL 3-8线译码器/复工器
74LS139 ————TTL 双2-4线译码器/复工器
74LS14 ————TTL 六反相施密特触发器
74LS145 ————TTL BCD—十进制译码/驱动器
74LS15 ————TTL 开路输出3输入端三与门
74LS150 ————TTL 16选1数据选择/多路开关
74LS151 ————TTL 8选1数据选择器
74LS153 ————TTL 双4选1数据选择器
74LS154 ————TTL 4线—16线译码器
74LS155———— TTL 图腾柱输出译码器/分配器
74LS156 ————TTL 开路输出译码器/分配器
74LS157 ————TTL 同相输出四2选1数据选择器
74LS158———— TTL 反相输出四2选1数据选择器
74LS16 ————TTL 开路输出六反相缓冲/驱动器
74LS160———— TTL 可预置BCD异步清除计数器
74LS161 ————TTL 可予制四位二进制异步清除计数
74LS162 ————TTL 可预置BCD同步清除计数器
74LS163 ————TTL 可予制四位二进制同步清除计数器
74LS164 ————TTL 八位串行入/并行输出移位寄存器
74LS165 ————TTL 八位并行入/串行输出移位寄存器
74LS166 ————TTL 八位并入/串出移位寄存器
74LS169 ————TTL 二进制四位加/减同步计数器
74LS17 ————TTL 开路输出六同相缓冲/驱动器
74LS170 ————TTL 开路输出4×4寄存器堆
74LS173 ————TTL 三态输出四位D型寄存器
74LS174 ————TTL 带公共时钟和复位六D触发器
74LS175 ————TTL 带公共时钟和复位四D触发器
74LS180 ————TTL 9位奇数/偶数发生器/校验器
74LS181 ————TTL 算术逻辑单元/函数发生器
74LS185 ————TTL 二进制—BCD代码转换器
74LS190 ————TTL BCD同步加/减计数器
74LS191 ————TTL 二进制同步可逆计数器
74LS192———— TTL 可预置BCD双时钟可逆计数器
74LS193 ————TTL 可预置四位二进制双时钟可逆计数器
74LS194 ————TTL 四位双向通用移位寄存器
来源:xiangtan.ourtimes.ren
74LS195————TTL 四位并行通道移位寄存器
74LS196 ————TTL 十进制/二-十进制可预置计数锁存器
74LS197 ————TTL 二进制可预置锁存器/计数器
74LS20 ————TTL 4输入端双与非门
74LS21 ————TTL 4输入端双与门
74LS22 ————TTL 开路输出4输入端双与非门
74LS221 ————TTL 双/单稳态多谐振荡器
74LS240 ————TTL 八反相三态缓冲器/线驱动器
74LS241 ————TTL 八同相三态缓冲器/线驱动器
74LS243 ————TTL 四同相三态总线收发器
74LS244 ————TTL 八同相三态缓冲器/线驱动器
74LS245 ————TTL 八同相三态总线收发器
74LS247 ————TTL BCD—7段15V输出译码/驱动器
74LS248 ————TTL BCD—7段译码/升压输出驱动器
74LS249 ————TTL BCD—7段译码/开路输出驱动器
74LS251 ————TTL 三态输出8选1数据选择器/复工器
74LS253 ————TTL 三态输出双4选1数据选择器/复工器

]]>
1145 0 0 0
<![CDATA[自动浇灌电路]]> http://mixdiy.com/index.php/2022/05/01/auto-water-pub/ Sun, 01 May 2022 02:44:55 +0000 http://mixdiy.com/?p=1149
]]>
1149 0 0 0
<![CDATA[树莓派网站无人值守程序]]> http://mixdiy.com/index.php/2022/05/02/website-monitor/ Mon, 02 May 2022 07:20:20 +0000 http://mixdiy.com/?p=1162

树莓派本身的稳定性毋庸置疑,但由于其极限成本策略,导致采用了外置sd的方式,sd的质量参次不齐,另外sd的寿命也是问题,更关键的是个人爱好者往往选择便宜的ddns服务商,这些服务商的稳定性长期存在问题,在以上多种因素的叠加下,如何保证自己的网站稳定可靠地运行是个问题,这里提供一个思路,通过服务程序自动监视主机网站服务状态,一旦发现网站停止或其它死机行为,则重启相关服务,重启如还不能解决问题,则重新启动机器,从而实现无人值守。目前我的网站使用一年多,效果还不错

from lxml import etree
from pygame import mixer
import RPi.GPIO as GPIO
import requests
import os
import time
import datetime

def waring(Pin,Delay):
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(Pin,GPIO.OUT)
    GPIO.output(Pin,1)
    time.sleep(Delay)
    GPIO.cleanup()

def wlandect(s):
    import time as tm
    import os
    if '192' not in os.popen('ifconfig | grep 192').read():
        print('')
        os.system('rfkill unblock wifi')
        os.system('wpa_cli -i wlan0 enable_network 0')
        tm.sleep(s)


def playwav(wavename,Delay):
    mixer.init()
#    mixer.music.load('/home/pi/Downloads/music/01 獨角戲.wav')
    mixer.music.load(wavename)
    mixer.music.play()
    time.sleep(Delay)
    mixer.music.pause()

def restartlog(filename,content):
    f = open(filename, 'a+') # 'a+'append write
    f.write(content)
    f.close()

def webdectect(url,relaytime):
    i=0
    j=0
    while True:
        j=j+1
        i=i+1
        try:
#            url = 'http://dcar.cn'
            response = requests.get(url)
            response.encoding=response.apparent_encoding
            label=etree.HTML(response.text)
            #提取这个页面中所有的标签信息
            #content=label.xpath('//span[@class="is-text-small is-text-grey"]/text()')
            content=label.xpath('//p[@class="site-description"]/text()')

            #提取span标签中class名为"is-text-small is-text-grey"的内容信息,并且存入一个列表中
            #print(content[0])
            if content[0]=='MixDiy':
                print('网站工作正常 '+datetime.datetime.now().strftime("%Y-%m-%d %X"))
                i=0
                j=0
                time.sleep(10)
            else:
                print('网站访问失败 '+str(i))
            #打印获得的文本信息
        except:
            print('网站访问失败 '+str(i))
            time.sleep(10)
            if i==relaytime:                
                di=datetime.datetime.now().strftime("%Y-%m-%d %X")
                print(str(di))
                restartlog('/var/www/html/wp-content/uploads/2021/11/log_pacong.txt',str(di)+'\r\n')                
                playwav('/home/pi/Downloads/music/独角戏.mp3',10)
                waring(16,5)
                i=0
                wlandect(10)
                os.system('systemctl daemon-reload')
                os.system('systemctl restart rc-local')
            if j==100:
                os.system('reboot')
webdectect('http://mixdiy.com',10)

作为系统服务

import daemon

from pacong_GPIO_log_netreset_mixdiy.py import do_main_program

with daemon.DaemonContext():
    do_main_program()

修改rc.local

sudo nano /etc/rc.local

#
# In order to enable or disable this script just change the execution
# bits.
#

# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

cpolar start-all &
nohup sudo python /home/pi/Downloads/daemon_test.py start &

exit 0

附件:

一键更新树莓派

import os
def alter(file,old_str,new_str):
    """
    将替换的字符串写到一个新的文件中,然后将原文件删除,新文件改为原来文件的名字
    :param file: 文件路径
    :param old_str: 需要替换的字符串
    :param new_str: 替换的字符串
    :return: None
    """
    with open(file, "r", encoding="utf-8") as f1,open("%s.bak" % file, "w", encoding="utf-8") as f2:
        for line in f1:
            if old_str in line:
                line = line.replace(old_str, new_str)
            f2.write(line)
    os.remove(file)
    os.rename("%s.bak" % file, file)

alter("/etc/apt/sources.list", "deb http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi", "deb http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ buster main non-free contrib rpi")
alter("/etc/apt/sources.list", "#deb-src http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi", "deb-src http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ buster main non-free contrib rpi")

alter("/etc/apt/sources.list.d/raspi.list", "deb http://archive.raspberrypi.org/debian/ buster main", "deb http://mirrors.tuna.tsinghua.edu.cn/raspberrypi/ buster main ui")


os.system('sudo apt-get update')
os.system('sudo apt-get upgrade -y')
os.system('sudo apt-get install fcitx fcitx-googlepinyin fcitx-module-cloudpinyin -y')
os.system('sudo pip3 install pymysql')
os.system('reboot')

一键安装wordpress(2wordpressinstall.sh)

#!/bin/sh

sudo apt-get update -y
sudo apt-get install apache2 php  -y
sudo service apache2 restart
sudo apt-get install mariadb-server php-mysql -y
sudo service apache2 restart
cd /var/www/html/
sudo rm -rf *
sudo wget http://wordpress.org/latest.tar.gz
sudo tar xzf latest.tar.gz
sudo mv wordpress/* .
sudo rm -rf wordpress latest.tar.gz
sudo chown -R www-data: .
sudo mysql_secure_installation
#sudo mysql -uroot -p

sudo python3 prepare_wordpress_cpolarstart.py

一键安装wordpress/cpolar(2wordpressinstall_cpolar.sh)

#!/bin/sh

sudo apt-get update -y
sudo apt-get install apache2 php  -y
sudo service apache2 restart
sudo apt-get install mariadb-server php-mysql -y
sudo service apache2 restart
cd /var/www/html/
sudo rm -rf *
sudo wget http://wordpress.org/latest.tar.gz
sudo tar xzf latest.tar.gz
sudo mv wordpress/* .
sudo rm -rf wordpress latest.tar.gz
sudo chown -R www-data: .
sudo mysql_secure_installation
#sudo mysql -uroot -p
curl -L https://www.cpolar.com/static/downloads/install-release-cpolar.sh | sudo bash
sudo python3 prepare_wordpress_cpolarstart.py
]]>
1162 0 0 0
<![CDATA[去除Envo Storefront主题的底部版权信息]]> http://mixdiy.com/index.php/2022/05/05/rewrite-copywriter/ Thu, 05 May 2022 15:40:55 +0000 http://mixdiy.com/?p=1202

今天发现一个wordpress的主题不错,风格超赞,但底部的“自豪地采用wordprss及Envo Storefront”极其刺眼,到网上找了一圈,没有看到去除的方法,于是自己动手,发现这个主题厂家很鬼,将版权信息放在了一个特殊的地方,修改如下:

]]>
1202 0 0 0
<![CDATA[python 基础]]> http://mixdiy.com/index.php/2022/05/06/python-base/ Fri, 06 May 2022 09:26:13 +0000 http://mixdiy.com/?p=1211

https://www.runoob.com/python/python-reg-expressions.html

]]>
1211 0 0 0
<![CDATA[基于miropython环境下esp32的wifi配网]]> http://mixdiy.com/index.php/2022/05/08/about-esp32-wifi-manager/ Sat, 07 May 2022 22:29:31 +0000 http://mixdiy.com/?p=1215

GitHub - brainelectronics/Micropython-ESP-WiFi-Manager: Micropython based ESP WiFi Manager

OTA

GitHub - BxNxM/micrOS: micrOS - Micropython based configuration and communication framework

esp32 sniff

GitHub - NicheSecTech/esp32-micropython-wifi-sniff-inject: A ESP32 build of micropython v1.12 using ESP-IDF v4.0 that supports WiFi sniffing and injection.

]]>
1215 0 0 0
<![CDATA[更新树莓派固件]]> http://mixdiy.com/index.php/2022/05/10/rbi-update/ Tue, 10 May 2022 10:31:46 +0000 http://mixdiy.com/?p=1222

树莓派固件更新,一些版本使用sudo UPDATE_SELF=0 rpi-update失败,可以按以下方式更新:

sudo apt update && sudo apt full-upgrade -y
sudo reboot
sudo rpi-eeprom-update -a
sudo reboot 
sudo rpi-eeprom-update
]]>
1222 0 0 0
<![CDATA[esp32 wifi 配置]]> http://mixdiy.com/index.php/2022/05/15/esp32-wifi-config/ Sun, 15 May 2022 00:49:08 +0000 http://mixdiy.com/?p=1227

【摘要】 我们购买智能家居产品后,买回来拆箱后第一件事通常就是给这个新的硬件进行配网,所谓配网,也就是让这个新的物联网设备联入我们的局域网内,让这个物联网设备可以进行网络通讯。我们在上一篇文章《MicroPython(ESP32/ESP8266) 实现web控制GPIO》中已经了解到了如何使用ESP32和ESP8266通过联网来实现在Web中控制板载的 LED 灯开关。本文将介绍…

我们购买智能家居产品后,买回来拆箱后第一件事通常就是给这个新的硬件进行配网,所谓配网,也就是让这个新的物联网设备联入我们的局域网内,让这个物联网设备可以进行网络通讯。我们在上一篇文章《MicroPython(ESP32/ESP8266) 实现web控制GPIO》中已经了解到了如何使用ESP32和ESP8266通过联网来实现在Web中控制板载的 LED 灯开关。本文将介绍基于 MicroPython 来实现的 ESP32/ESP8266 Wifi配网。

准备工作

在开始代码之前,需要先准备以下:

配网流程

回想以下我们的智能家居物联网设备,以小米生态圈的设备为例,新设备开箱通电后,一般是打开米家APP,然后搜索到新买的设备,然后需要手动将wifi连接到这个设配上,然后在 APP 中填入 SSID 和 wifi密码信息,等待传输,传输完成后,就算完成配网,在 APP 的界面中就可以看到新的设备了。

在这里插入图片描述

配网的流程总结如上图所示。然而我们的使用当中,配网通常只发生在新设备加入或者网络环境改变的时候才需要,正常情况下设备重启,是不需要每一次都要来一次配网操作的。所以一般情况下,在一次配网之后,我们会将我们的Wifi信息保存下来,设备重启后如果有存在的配网信息,会自动直接联网。

在这里插入图片描述

而针对我们整个开发版的程序,我们可以在 main.py 执行在开始,就先执行网络检查,然后根据是否成功联网来判断是否需要配网操作,流程如下:

在这里插入图片描述

MicroPython Wifi 操作

上文梳理了整个配网过程的流程。在这个流程中,最开始的步骤就是判断网络是否连接。以下将介绍如何使用 MicroPython 操作开发板的 Wifi。

我们开发板(ESP32/ESP8266)的wifi有AP和STA模式,AP就是开发版上创建一个热点,其他设备连接到AP上,而STA模式和我们普通的手机电脑使用Wifi联网类似。这里的要点就是我们需要检查STA模式下开发版是否能正常联网,如果不能,我们利用开发板的AP模式,让我们的其他设备连接开发板,把我们局域网Wifi的配置信息告知开发板,从而使开发板能正常联网。

import network
wlan_sta = network.WLAN(network.STA_IF)
wlan_sta.isconnected()

  

通过调用 isconnected() 函数,可以获取到开发板是否正常联网,如果正常联网,返回结果会是 True 否则为 False 。

wlan_sta.scan()

  

scan() 函数扫描设备附近可以搜索到的 Wifi,会返回一个列表,列表中每一条为可连接wifi的信息。

[(b'WifiSSID', b'LPw\xb7\xs8\x94', 1, -48, 3, 0),...]

  

以上是省略了部分信息的返回值,可以看到,每一条记录中有6个信息,它们分别代表了 SSID名称 BSSID(MAC地址) 频道 RSSI信号强度 加密模式 是否隐藏 。其中加密模式,包含了 WEP、WPA-PSK、WPA2-PSK、WPA/WPA2-PSK等。

接下来,我们就可以尝试连接Wifi。

wlan_sta.connect('ssid', 'password')
wlan_sta.isconnected()

  

如果连接成功,则返回 True 。如果需要断开连接,可以使用 disconnect() 函数。

wlan_sta.disconnect()

  

MicroPython AP操作

完成了 Wifi 连接和检查网络是否正常后,我们开始解决利用 AP 配网的问题。

先看代码:

import network
import socket

wlan_ap = network.WLAN(network.AP_IF)
wlan_ap.active(True)
wlan_ap.config(essid='MyESP8266',authmode=0)
server_socket = socket.socket()
server_socket.bind(('0.0.0.0', 80))
server_socket.listen(3)

def web_page(): return b"""<html> <head> <title>MYESP8266 AP Test</title> </head> <body> <h1>This is MyESP8266 AP Test Page.</h1> </body> </html>"""

while True: conn, addr = server_socket.accept() print('Connection: %s ' % str(addr)) response = web_page() conn.send('HTTP/1.1 200 OK\n') conn.send('Content-Type: text/html\n') conn.send('Connection: close\n\n') conn.sendall(response) conn.close()

  

从上面的代码我们可以看到,当我们创建好AP后,就打开一个 socket ,并且绑定80端口开始监听,然后开启一个循环,当接受到连接后就给客户端发送页面代码。如果对 socket 不了解的,可以参考《快速了解Python socket编程》

这时用手机或者电脑的 wifi 连接 SSID 名为 MYESP8266 的 Wifi 热点,因为我们authmode选择了 open 所以不需要密码。连接成功后,用浏览器打开地址 192.168.4.1 ,就可以看到我们上面的页面。

针对 MicroPython 的 Web 编程

我们一般情况下,如果要进行 Web 开发,通常会使用 Flask 或者 Django 之类的框架。而针对开发版这种运算能力有限的硬件,也有对应的框架可以用。但我们这里为了能深入的了解,就通过自己完成最基本的功能来了解整个程序的运行方式。

封装HTML响应

根据上面的示例代码,我们可以了解到,如果要在客户端正常显示页面,我们需要先给客户端发一个HTTP的Header信息,然后再发送具体的页面内容。所以,为了方便日后的调用,我们对上面的代码进行修改:

import network
import socket

wlan_ap = network.WLAN(network.AP_IF)
wlan_ap.active(True)
wlan_ap.config(essid='MyESP8266',authmode=0)
server_socket = socket.socket()
server_socket.bind(('0.0.0.0', 80))
server_socket.listen(3)

def send_header(conn, status_code=200, content_length=None ): conn.sendall("HTTP/1.0 {} OK\r\n".format(status_code)) conn.sendall("Content-Type: text/html\r\n") if content_length is not None: conn.sendall("Content-Length: {}\r\n".format(content_length)) conn.sendall("\r\n")

def send_response(conn, payload, status_code=200): content_length = len(payload) send_header(conn, status_code, content_length) if content_length > 0: conn.sendall(payload) conn.close()

def config_page(): return b"""<html> <head> <title>MYESP8266 AP Test</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>Wifi 配网</h1> <form action="configure" method="post"> <div> <label>SSID</label> <input type="text" name="ssid"> </div> <div> <label>PASSWORD</label> <input type="password" name="password"> </div> <input type="submit" value="连接"> <form> </body> </html>"""

while True: conn, addr = server_socket.accept() print('Connection: %s ' % str(addr)) try: conn.settimeout(3) request = b"" try: while "\r\n\r\n" not in request: request += conn.recv(512) except OSError: pass print(request) response = config_page() send_response(conn, response)

	finally: conn.close()
  

我们添加了三个函数,分别为 send_header() send_response() config_page() 。其中 send_header() 把我们需要发送的 Header 信息打包,config_page() 则是创建我们的 HTML 页面,最后由 send_response() 将其整合,发送给客户端。

在这里插入图片描述

运行代码,如果正常,用手机连接开发板的AP,打开 192.168.4.1 ,就可以看到上图的页面。

路由

上面的代码中,页面中有一个 form ,里面可以输入 SSID 和 Wifi 密码,当我们输入完成后,点击连接,将会将我们输入的内容 POST 到 /configure 路径中。处理这个问题,在 Web 框架中,会有现成的路由模块,但这里我们需要自己用代码进行处理。

我们的代码中,当客户端连接后,我们的开发板会接受来自客户端传来的信息——request ,打印这个变量看看客户端传来的内存:

# 连接 192.168.4.1
Connection: ('192.168.4.2', 44794) 
b'GET / HTTP/1.1\r\nUser-Agent: Dalvik/2.1.0 (Linux; U; Android 9; MIX 2 MIUI/20.6.18)\r\nHost: 192.168.4.1\r\nConnection: Keep-Alive\r\nAccept-Encoding: gzip\r\n\r\n'

# 连接 192.168.4.1/test
b'GET /test HTTP/1.1\r\nHost: 192.168.4.1\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Linux; Android 9; MIX 2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.82 Mobile Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n\r\n'

# 输入信息,点击连接按钮
b'POST /configure HTTP/1.1\r\nHost: 192.168.4.1\r\nConnection: keep-alive\r\nContent-Length: 26\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nOrigin: http://192.168.4.1\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: Mozilla/5.0 (Linux; Android 9; MIX 2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.82 Mobile Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nReferer: http://192.168.4.1/\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n\r\nssid=xdbdh&password=ddjxdj'

  

可以看到,当我们连接不同的地址,开发板接受到的信息是不同的,我们就可以通过正则表达式来抓去不同的内容即可实现类似 Web 框架路由的功能。

try: url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).decode("utf-8").rstrip("/") except Exception: url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).rstrip("/") print("URL is {}".format(url))
  

我们将上面 print() 函数替换乘上面的代码,再尝试上面三个地址:

# 连接 192.168.4.1
URL is 
# 连接 192.168.4.1/test
URL is test
# 输入信息,点击连接按钮
URL is configure

  

这样,我们的精简版路由功能就完成了。

POST 传参获取

解决了页面显示和路由,剩下就是如何获取 POST 的传参了。我们再看一次当我们使用 POST 时,返回过来的信息:

b'POST /configure HTTP/1.1\r\n
Host: 192.168.4.1\r\n
Connection: keep-alive\r\n
Content-Length: 26\r\n
Cache-Control: max-age=0\r\n
Upgrade-Insecure-Requests: 1\r\n
Origin: http://192.168.4.1\r\n
Content-Type: application/x-www-form-urlencoded\r\nUser-Agent: Mozilla/5.0 (Linux; Android 9; MIX 2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.82 Mobile Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nReferer: http://192.168.4.1/\r\n
Accept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n\r\n
ssid=xdbdh&password=ddjxdj'

  

可以看到,信息开头是 POST 方法,然后结 ssid=....&password=... 就是我们传过来的参数,和处理路由的方法类似,我们使用正则表达式过滤一下,即可获取到我们需要的 ssid 和 Wifi 密码了。

# POST 参数解析
def get_wifi_conf(request): match = ure.search("ssid=([^&]*)&password=(.*)", request) if match is None: return False try: ssid = match.group(1).decode("utf-8").replace("%3F", "?").replace("%21", "!") password = match.group(2).decode("utf-8").replace("%3F", "?").replace("%21", "!") except Exception: ssid = match.group(1).replace("%3F", "?").replace("%21", "!") password = match.group(2).replace("%3F", "?").replace("%21", "!") if len(ssid) == 0: return False return (ssid, password)
  

我们再修改一下代码,添加一个新页面,用来显示 ssid 和 Wifi 密码,来确认我们的路由功能和 POST 参数正常获取。

def wifi_conf_page(ssid, passwd): return b"""<html> <head> <title>Wifi Conf Info</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>Post data:</h1> <p>SSID: %s</p> <p>PASSWD: %s</p> <a href="https://bbs.huaweicloud.com/">Return Configure Page</a> </body> </html>""" % (ssid, passwd)

  

修改后的代码:

# 前面相同的部分省略
while True: conn, addr = server_socket.accept() print('Connection: %s ' % str(addr)) try: conn.settimeout(3) request = b"" try: while "\r\n\r\n" not in request: request += conn.recv(512) except OSError: pass # url process try: url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).decode("utf-8").rstrip("/") except Exception: url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).rstrip("/") print("URL is {}".format(url)) if url == "": response = config_page() send_response(conn, response) elif url == "configure": ret = get_wifi_conf(request) response = wifi_conf_page(ret[0], ret[1]) send_response(conn, response) finally: conn.close()

  

执行代码,输入 ssid 和密码后,点击连接,应该能跳转到新页面并且显示刚才输入的 ssid 和密码。点击返回,能重新跳回信息输入的页面。

在这里插入图片描述

Wifi连接

《MicroPython(ESP32/ESP8266) 实现web控制GPIO》中,我们已经介绍了如何通过 connect() 方法来连接我们已知的 Wifi。接下来,我们要做的也很简单,就是创建一个 do_connect() 方法来处理我们上面传过来的 ssid 和密码。

def do_connect(ssid, password): wlan_sta.active(True) if wlan_sta.isconnected(): return None print('Connect to %s' % ssid) wlan_sta.connect(ssid, password) for retry in range(100): connected = wlan_sta.isconnected() if connected: break time.sleep(0.1) print('.', end='') if connected: print('\nConnected : ', wlan_sta.ifconfig()) else: print('\nFailed. Not Connected to: ' + ssid) return connected

  

可以看到,这个函数会接受传来的 wifi 配置参数,进行连接,如果成功,会返回 True。然后我们还还需要一个执行连接的方法,这个方法用于连接成功,就自动获取连如局域网后的ip地址。

def handle_wifi_configure(ssid, password): if do_connect(ssid, password): new_ip = wlan_sta.ifconfig()[0] return new_ip else: print('connect fail') return False

  

这些都完成后,我们只需要把开发板 AP 联网配置部分封装好,成为一个 start_ap() 方法,即可:

# response 的方法都为创建 HTML 代码方法,这里省略
# 可以在文末完整代码中查看
def startAP(): global server_socket stop() wlan_ap.active(True) wlan_ap.config(essid='MyEsp8266',authmode=0) server_socket = socket.socket() server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind(('0.0.0.0', 80)) server_socket.listen(3) while not wlan_sta.isconnected(): conn, addr = server_socket.accept() print('Connection: %s ' % str(addr)) try: conn.settimeout(3) request = b"" try: while "\r\n\r\n" not in request: request += conn.recv(512) except OSError: pass # url process try: url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).decode("utf-8").rstrip("/") except Exception: url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).rstrip("/") print("URL is {}".format(url)) if url == "": response = config_page() send_response(conn, response) elif url == "configure": ret = get_wifi_conf(request) ret = handle_wifi_configure(ret[0], ret[1]) if ret is not None: response = connect_sucess(ret) send_response(conn, response) print('connect sucess') elif url == "disconnect": wlan_sta.disconnect() finally: conn.close() wlan_ap.active(False) print('ap exit')

  

这里我们实现的功能为让开发板创建AP,生成一个 Wifi 信息的配置页面,然后通过路由来处理输入和参数,最后执行 Wifi 联网,如果连接成功,即退出循环,关闭 AP 热点。

我们从用手机输入完成点击连接后,如果连接成功,将会自动返回成功连接的页面:

在这里插入图片描述

到这里,我们的 wifi 配网就已经基本完成了。

总结

本文开始先从配网的需求、流程进行分析,然后一步步分别介绍 MicroPython Wifi的操作,AP的使用以及简单的 Web 实现,然后将上述的要点结合我们的配网需求,完成完整的设配配网代码开发。

但是,文章为了比较清晰的展示内容,因此在代码上可能会显得比较冗长和繁复,有很大的优化空间。大家可以根据自己的实际情况,对代码进行进一步的优化和调整,以下给出几个可以调整方法:

  • 优化代码结构,模块化部分功能
  • 将 web 部分整合成一个模块,比如带有 html 模板渲染功能的模块、路由模块灯
  • 尝试在用户体验上优化配网的流程
  • 其他创新的需求等……

此外,还存还存在一个问题,就是可能因为 ESP8266 的内存和算力问题,代码运行的时候有时会出错和跳出,需要重启或者断电,但同样的代码在 ESP32 开发板上,却没有问题。可能是 MircoPython 的问题,也有可能是因为代码设计问题,这方面需要进一步研究和尝试。

物联网开发涉及到很多硬件和软件的问题,但是在实践中,经常会遇到各种奇怪的问题,这很可能打击了学习的热情,加上网上的教程和示例不多,初学者更容易遇到问题解决不了而不得不放弃。本文尽可能的详细解释代码和原理,但由于水平经验有限,难免会有所疏漏, 望读者见谅,并且欢迎大家一起来交流进步。

希望本文对你有用。如果你觉得文章对你用,记得关注收藏。你的关注和收藏是继续更新的动力哦。

附:完整代码

import network
import socket
import ure
import time

NETWORK_PROFILES = 'wifi.dat'

wlan_ap = network.WLAN(network.AP_IF)
wlan_sta = network.WLAN(network.STA_IF)

server_socket = None
def send_header(conn, status_code=200, content_length=None ): conn.sendall("HTTP/1.0 {} OK\r\n".format(status_code)) conn.sendall("Content-Type: text/html\r\n") if content_length is not None: conn.sendall("Content-Length: {}\r\n".format(content_length)) conn.sendall("\r\n")

def send_response(conn, payload, status_code=200): content_length = len(payload) send_header(conn, status_code, content_length) if content_length > 0: conn.sendall(payload) conn.close()

def config_page(): return b"""<html> <head> <title>MYESP8266 AP Test</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>Wifi 配网</h1> <form action="configure" method="post"> <div> <label>SSID</label> <input type="text" name="ssid"> </div> <div> <label>PASSWORD</label> <input type="password" name="password"> </div> <input type="submit" value="连接"> <form> </body> </html>"""
def wifi_conf_page(ssid, passwd): return b"""<html> <head> <title>Wifi Conf Info</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>Post data:</h1> <p>SSID: %s</p> <p>PASSWD: %s</p> <a href="https://bbs.huaweicloud.com/">Return Configure Page</a> </body> </html>""" % (ssid, passwd)

def connect_sucess(new_ip): return b"""<html> <head> <title>Connect Sucess!</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <p>Wifi Connect Sucess</p> <p>IP Address: %s</p> <a href="http://%s">Home</a> <a href="https://bbs.huaweicloud.com/disconnect">Disconnect</a> </body> </html>""" % (new_ip, new_ip)

def get_wifi_conf(request): match = ure.search("ssid=([^&]*)&password=(.*)", request) if match is None: return False try: ssid = match.group(1).decode("utf-8").replace("%3F", "?").replace("%21", "!") password = match.group(2).decode("utf-8").replace("%3F", "?").replace("%21", "!") except Exception: ssid = match.group(1).replace("%3F", "?").replace("%21", "!") password = match.group(2).replace("%3F", "?").replace("%21", "!") if len(ssid) == 0: return False return (ssid, password)
def handle_wifi_configure(ssid, password): if do_connect(ssid, password):
# try:
# profiles = read_profiles()
# except OSError:
# profiles = {}
# profiles[ssid] = password
# write_profiles(profiles)
# 
# time.sleep(5)
#  new_ip = wlan_sta.ifconfig()[0] return new_ip else: print('connect fail') return False

def check_wlan_connected(): if wlan_sta.isconnected(): return True else: return False def do_connect(ssid, password): wlan_sta.active(True) if wlan_sta.isconnected(): return None print('Connect to %s' % ssid) wlan_sta.connect(ssid, password) for retry in range(100): connected = wlan_sta.isconnected() if connected: break time.sleep(0.1) print('.', end='') if connected: print('\nConnected : ', wlan_sta.ifconfig()) else: print('\nFailed. Not Connected to: ' + ssid) return connected

def read_profiles(): with open(NETWORK_PROFILES) as f: lines = f.readlines() profiles = {} for line in lines: ssid, password = line.strip("\n").split(";") profiles[ssid] = password return profiles
def write_profiles(profiles): lines = [] for ssid, password in profiles.items(): lines.append("%s;%s\n" % (ssid, password)) with open(NETWORK_PROFILES, "w") as f: f.write(''.join(lines)) def stop(): global server_socket if server_socket: server_socket.close() server_socket = None

def startAP(): global server_socket stop() wlan_ap.active(True) wlan_ap.config(essid='MyEsp8266',authmode=0) server_socket = socket.socket() server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind(('0.0.0.0', 80)) server_socket.listen(3) while not wlan_sta.isconnected(): conn, addr = server_socket.accept() print('Connection: %s ' % str(addr)) try: conn.settimeout(3) request = b"" try: while "\r\n\r\n" not in request: request += conn.recv(512) except OSError: pass # url process try: url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).decode("utf-8").rstrip("/") except Exception: url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).rstrip("/") print("URL is {}".format(url)) if url == "": response = config_page() send_response(conn, response) elif url == "configure": ret = get_wifi_conf(request) ret = handle_wifi_configure(ret[0], ret[1]) if ret is not None: response = connect_sucess(ret) send_response(conn, response) print('connect sucess') elif url == "disconnect": wlan_sta.disconnect() finally: conn.close() wlan_ap.active(False) print('ap exit')

def home(): global server_socket stop() wlan_sta.active(True) ip_addr = wlan_sta.ifconfig()[0] print('wifi connected') server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind(('0.0.0.0', 80)) server_socket.listen(3) while check_wlan_connected(): conn, addr = server_socket.accept() try: conn.settimeout(3) request = b"" try: while "\r\n\r\n" not in request: request += conn.recv(512) except OSError: pass # url process try: url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).decode("utf-8").rstrip("/") except Exception: url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).rstrip("/") if url == "": response = connect_sucess(ip_addr) send_response(conn, response) elif url == "disconnect": wlan_sta.disconnect() finally: conn.close() wlan_sta.active(False) print('sta exit')
def main(): while True: if not check_wlan_connected(): startAP() else: home() main()

  

原文链接:

https://zhuanlan.zhihu.com/p/369017239

http://www.proyy.com/885253b7289c422da561fdfdc78f3bd2.html

]]>
1227 0 0 0
<![CDATA[捕捉系统按键ctrl-c等]]> http://mixdiy.com/index.php/2022/05/20/file/ Fri, 20 May 2022 03:44:57 +0000 http://mixdiy.com/?p=1234

捕捉Ctrl-C键盘事件

import time
import signal

def signal_handler(signal,frame):
    print('You pressed Ctrl+C!')

signal.signal(signal.SIGINT,signal_handler)
print('Press Ctrl+C')
for x in range(1,100):
    time.sleep(2)
    print(x)

import time
if __name__ == "__main__":
    try:
        time.sleep(10)
    except KeyboardInterrupt:
        print("Application exit!")
# 自定义信号处理函数
def my_handler(signum, frame):
    global stop
    stop = True
    print("终止")
 
 
# 设置相应信号处理的handler
signal.signal(signal.SIGINT, my_handler)    #读取Ctrl+c信号
 
stop = False
 
while True:
    try:
        #读取到Ctrl+c前进行的操作
        if stop:
            # 中断时需要处理的代码
            pass
#            break    #break只能退出当前循坏
            #中断程序需要用 raise
    except Exception as e:
        print(str(e))
        break
]]>
1234 0 0 0
<![CDATA[树莓派更换清华源]]> http://mixdiy.com/index.php/2022/05/20/update-raspberry/ Fri, 20 May 2022 06:32:31 +0000 http://mixdiy.com/?p=1244

https://mirrors.tuna.tsinghua.edu.cn/help/raspbian/

]]>
1244 0 0 0
<![CDATA[树莓派 config.txt 文件解析]]> http://mixdiy.com/index.php/2022/05/21/rpi-config-txt/ Fri, 20 May 2022 23:39:25 +0000 http://mixdiy.com/?p=1249

由于树莓派并没有传统意义上的BIOS, 所以现在各种系统配置参数通常被存在”config.txt”这个文本文件中.

树莓派的config.txt文件会在ARM内核初始化之前被GPU读取.

这个文件存在引导分区上的.对于Linux, 路径通常是/boot/config.txt, 如果是Windows (或者OS X) 它会被识别为SD卡中可访问部分的一个普通文件.

如果想要编辑配置文件, 请查看介绍编辑树莓派配置文件.

你可以使用下列命令去获取当前激活的设置:

- 列出指定的配置参数.
- 例如: vcgencmd get_config arm_freq
vcgencmd get_config 
- 列出所有已设置的整形配置参数(非零)
vcgencmd get_config int
- 列出所有已设置的字符型配置参数(非零)
vcgencmd get_config str

文件格式

当值是整形时格式为”属性=值”. 每行只指定一个参数. 注释使用’#’井号作为一行开头.

注意: 在新版的树莓派里每行都有#注释, 要想使用该行参数只需移除#.

下面是示例文件
# Set stdv mode to PAL (as used in Europe) sdtv_mode=2
# Force the monitor to HDMI mode so that sound will be sent over HDMI cable
hdmi_drive=2
# Set monitor mode to DMT
hdmi_group=2
# Set monitor resolution to 1024x768 XGA 60Hz (HDMI_DMT_XGA_60)
hdmi_mode=16
# Make display smaller to stop text spilling off the screen
overscan_left=20
overscan_right=12
overscan_top=10
overscan_bottom=10

这是另一个示例文件, 包含了各种功能的扩展文档.

内存

disable_l2cache 禁止ARM访问GPU的二级缓存. 相应的需要在内核中关闭二级缓存. 默认为0
gpu_mem GPU内存以兆为单位. 设置ARM和GPU之间的内存分配. ARM会获得剩余所有内存. 最小设为16. 默认为64
gpu_mem_256 对于有256MB内存的树莓派的GPU内存设置. 512MB的派请忽略. 会覆盖gpu_mem. 最大设为192. 默认不设置
gpu_mem_512 对于有512MB内存的树莓派的GPU内存设置. 256MB的派请忽略.  会覆盖gpu_mem. 最大设为448. 默认不设置
disable_pvt 禁止每500毫秒调整一次RAM的刷新率 (RAM温度测量).

CMA – 动态内存分配

自2012年11月19号, 固件和内核开始支持CMA, 这意味运行时可以动态管理ARM和GPU之间的内存分配. 这儿有相关config.txt示例.
cma_lwm 当GPU可用内存低于cma_lwm所设值, 将会向ARM请求一些内存.
cma_hwm 当GPU可用内存高于cma_hwm所设值, 将会向ARM释放一些内存.
要启用CMA,下面的参数需要添加到cmdline.txt文件里:
coherent_pool=6M smsc95xx.turbo_mode=N

视频

视频模式选项
sdtv_mode 为复合信号输出设置视频制式(默认为0)
sdtv_mode=0    NTSC
sdtv_mode=1    日本版NTSC – 无基座
sdtv_mode=2    PAL
sdtv_mode=3    巴西版PAL – 副载波为525/60而不是625/50

sdtv_aspect 为复合信号输出设置宽高比(默认为1)
sdtv_aspect=1  4:3
sdtv_aspect=2  14:9
sdtv_aspect=3  16:9

sdtv_disable_colourburst 禁止复合信号输出彩色副载波群. 图片会显示为单色, 但是可能会更清晰
sdtv_disable_colourburst=1  禁止输出彩色副载波群
hdmi_safe 使用”安全模式”的设置去尝试用HDMI最大兼容性启动. 这和下面的组合是一个意思: hdmi_force_hotplug=1, config_hdmi_boost=4, hdmi_group=2, hdmi_mode=4, disable_overscan=0
hdmi_safe=1
hdmi_ignore_edid 如果你的显示器是天朝产的垃圾货, 允许系统忽略EDID显示数据
hdmi_ignore_edid=0xa5000080
hdmi_edid_file 当设为1时, 将会从edid.dat文件中读取EDID数据,而不是从显示器.
hdmi_edid_file=1
hdmi_force_edid_audio 伪装成支持所有音频格式播放, 即便报告不支持也允许通过DTS/AC3.
hdmi_force_edid_audio=1
hdmi_force_edid_3d 伪装成全部CEA模式都支持3D, 即便EDID并不支持.
hdmi_force_edid_3d=1
avoid_edid_fuzzy_match 禁止去模糊匹配EDID中描述的模式. 即便遮蔽错误, 也选用匹配分辨率和最接近帧率的标准模式.
avoid_edid_fuzzy_match=1
hdmi_ignore_cec_init 不发送初始化激活源消息. 避免在重启时使(启用CEC)TV结束待机并切换频道.
hdmi_ignore_cec_init=1
hdmi_ignore_cec 伪装成TV不支持CEC. 将不会支持任何CEC功能.
hdmi_ignore_cec=1
hdmi_force_hotplug 伪装成HDMI热插拔信号被检测到, 出现HDMI显示器被接入
hdmi_force_hotplug=1 即便没有检测到HDMI显示器也要使用HDMI模式
hdmi_ignore_hotplug 伪装成HDMI热插拔信号没有被检测到, 出现HDMI显示器未接入
hdmi_ignore_hotplug=1 即便检测到HDMI显示器也要使用混合模式
hdmi_pixel_encoding 强制像素编码模式. 默认情况下会使用EDID请求的模式, 所以不需要修改.
hdmi_pixel_encoding=0 default       (limited for CEA, full for DMT)
hdmi_pixel_encoding=1 RGB limited   (16-235)
hdmi_pixel_encoding=2 RGB full      ( 0-255)
hdmi_pixel_encoding=3 YCbCr limited (16-235)
hdmi_pixel_encoding=4 YCbCr limited ( 0-255)

hdmi_drive 选择HDMI还是DVI模式
hdmi_drive=1 DVI模式 (没声音)
hdmi_drive=2 HDMI模式 (如果支持并已启用将有声音输出)

hdmi_group 设置HDMI类型
不指定组, 或者设为0, 将会使用EDID报告的首选组.
hdmi_group=1   CEA
hdmi_group=2   DMT

hdmi_mode 设置在CEA或DMT格式下的屏幕分辨率
当hdmi_group=1 (CEA)时,下列值有效
hdmi_mode=1    VGA
hdmi_mode=2    480p  60Hz
hdmi_mode=3    480p  60Hz  H
hdmi_mode=4    720p  60Hz
hdmi_mode=5    1080i 60Hz
hdmi_mode=6    480i  60Hz
hdmi_mode=7    480i  60Hz  H
hdmi_mode=8    240p  60Hz
hdmi_mode=9    240p  60Hz  H
hdmi_mode=10   480i  60Hz  4x
hdmi_mode=11   480i  60Hz  4x H
hdmi_mode=12   240p  60Hz  4x
hdmi_mode=13   240p  60Hz  4x H
hdmi_mode=14   480p  60Hz  2x
hdmi_mode=15   480p  60Hz  2x H
hdmi_mode=16   1080p 60Hz
hdmi_mode=17   576p  50Hz
hdmi_mode=18   576p  50Hz  H
hdmi_mode=19   720p  50Hz
hdmi_mode=20   1080i 50Hz
hdmi_mode=21   576i  50Hz
hdmi_mode=22   576i  50Hz  H
hdmi_mode=23   288p  50Hz
hdmi_mode=24   288p  50Hz  H
hdmi_mode=25   576i  50Hz  4x
hdmi_mode=26   576i  50Hz  4x H
hdmi_mode=27   288p  50Hz  4x
hdmi_mode=28   288p  50Hz  4x H
hdmi_mode=29   576p  50Hz  2x
hdmi_mode=30   576p  50Hz  2x H
hdmi_mode=31   1080p 50Hz
hdmi_mode=32   1080p 24Hz
hdmi_mode=33   1080p 25Hz
hdmi_mode=34   1080p 30Hz
hdmi_mode=35   480p  60Hz  4x
hdmi_mode=36   480p  60Hz  4xH
hdmi_mode=37   576p  50Hz  4x
hdmi_mode=38   576p  50Hz  4x H
hdmi_mode=39   1080i 50Hz  reduced blanking
hdmi_mode=40   1080i 100Hz
hdmi_mode=41   720p  100Hz
hdmi_mode=42   576p  100Hz
hdmi_mode=43   576p  100Hz H
hdmi_mode=44   576i  100Hz
hdmi_mode=45   576i  100Hz H
hdmi_mode=46   1080i 120Hz
hdmi_mode=47   720p  120Hz
hdmi_mode=48   480p  120Hz
hdmi_mode=49   480p  120Hz H
hdmi_mode=50   480i  120Hz
hdmi_mode=51   480i  120Hz H
hdmi_mode=52   576p  200Hz
hdmi_mode=53   576p  200Hz H
hdmi_mode=54   576i  200Hz
hdmi_mode=55   576i  200Hz H
hdmi_mode=56   480p  240Hz
hdmi_mode=57   480p  240Hz H
hdmi_mode=58   480i  240Hz
hdmi_mode=59   480i  240Hz H
H表示16:9比例(正常是4:3).
2x表示双倍像素(即更高的像素时脉, 每个像素重复两次)
4x表示四倍像素(即更高的像素时脉, 每个像素重复四次)

当hdmi_group=2 (DMT)时,下列值有效
警告: 根据这篇帖子所述
像素时脉是有限制的, 最高支持的模式是1920x1200 @60Hz with reduced blanking.
hdmi_mode=1    640x350   85Hz
hdmi_mode=2    640x400   85Hz
hdmi_mode=3    720x400   85Hz
hdmi_mode=4    640x480   60Hz
hdmi_mode=5    640x480   72Hz
hdmi_mode=6    640x480   75Hz
hdmi_mode=7    640x480   85Hz
hdmi_mode=8    800x600   56Hz
hdmi_mode=9    800x600   60Hz
hdmi_mode=10   800x600   72Hz
hdmi_mode=11   800x600   75Hz
hdmi_mode=12   800x600   85Hz
hdmi_mode=13   800x600   120Hz
hdmi_mode=14   848x480   60Hz
hdmi_mode=15   1024x768  43Hz  DO NOT USE
hdmi_mode=16   1024x768  60Hz
hdmi_mode=17   1024x768  70Hz
hdmi_mode=18   1024x768  75Hz
hdmi_mode=19   1024x768  85Hz
hdmi_mode=20   1024x768  120Hz
hdmi_mode=21   1152x864  75Hz
hdmi_mode=22   1280x768        reduced blanking
hdmi_mode=23   1280x768  60Hz
hdmi_mode=24   1280x768  75Hz
hdmi_mode=25   1280x768  85Hz
hdmi_mode=26   1280x768  120Hz reduced blanking
hdmi_mode=27   1280x800        reduced blanking
hdmi_mode=28   1280x800  60Hz
hdmi_mode=29   1280x800  75Hz
hdmi_mode=30   1280x800  85Hz
hdmi_mode=31   1280x800  120Hz reduced blanking
hdmi_mode=32   1280x960  60Hz
hdmi_mode=33   1280x960  85Hz
hdmi_mode=34   1280x960  120Hz reduced blanking
hdmi_mode=35   1280x1024 60Hz
hdmi_mode=36   1280x1024 75Hz
hdmi_mode=37   1280x1024 85Hz
hdmi_mode=38   1280x1024 120Hz reduced blanking
hdmi_mode=39   1360x768  60Hz
hdmi_mode=40   1360x768  120Hz reduced blanking
hdmi_mode=41   1400x1050       reduced blanking
hdmi_mode=42   1400x1050 60Hz
hdmi_mode=43   1400x1050 75Hz
hdmi_mode=44   1400x1050 85Hz
hdmi_mode=45   1400x1050 120Hz reduced blanking
hdmi_mode=46   1440x900        reduced blanking
hdmi_mode=47   1440x900  60Hz
hdmi_mode=48   1440x900  75Hz
hdmi_mode=49   1440x900  85Hz
hdmi_mode=50   1440x900  120Hz reduced blanking
hdmi_mode=51   1600x1200 60Hz
hdmi_mode=52   1600x1200 65Hz
hdmi_mode=53   1600x1200 70Hz
hdmi_mode=54   1600x1200 75Hz
hdmi_mode=55   1600x1200 85Hz
hdmi_mode=56   1600x1200 120Hz reduced blanking
hdmi_mode=57   1680x1050       reduced blanking
hdmi_mode=58   1680x1050 60Hz
hdmi_mode=59   1680x1050 75Hz
hdmi_mode=60   1680x1050 85Hz
hdmi_mode=61   1680x1050 120Hz reduced blanking
hdmi_mode=62   1792x1344 60Hz
hdmi_mode=63   1792x1344 75Hz
hdmi_mode=64   1792x1344 120Hz reduced blanking
hdmi_mode=65   1856x1392 60Hz
hdmi_mode=66   1856x1392 75Hz
hdmi_mode=67   1856x1392 120Hz reduced blanking
hdmi_mode=68   1920x1200       reduced blanking
hdmi_mode=69   1920x1200 60Hz
hdmi_mode=70   1920x1200 75Hz
hdmi_mode=71   1920x1200 85Hz
hdmi_mode=72   1920x1200 120Hz reduced blanking
hdmi_mode=73   1920x1440 60Hz
hdmi_mode=74   1920x1440 75Hz
hdmi_mode=75   1920x1440 120Hz reduced blanking
hdmi_mode=76   2560x1600       reduced blanking
hdmi_mode=77   2560x1600 60Hz
hdmi_mode=78   2560x1600 75Hz
hdmi_mode=79   2560x1600 85Hz
hdmi_mode=80   2560x1600 120Hz reduced blanking
hdmi_mode=81   1366x768  60Hz
hdmi_mode=82   1080p     60Hz
hdmi_mode=83   1600x900        reduced blanking
hdmi_mode=84   2048x1152       reduced blanking
hdmi_mode=85   720p      60Hz
hdmi_mode=86   1366x768        reduced blanking

overscan_left 左侧跳过像素数
overscan_right 右侧跳过像素数
overscan_top 顶部跳过像素数
overscan_bottom 底部跳过像素数
framebuffer_width 控制台framebuffer宽度, 以像素为单位. 默认是显示器宽度减去超出扫描.
framebuffer_height 控制台framebuffer高度, 以像素为单位. 默认是显示器高度减去超出扫描.
framebuffer_depth 控制台framebuffer深度, 以位为单位. 默认是16位. 8位也是有效的, 但是默认RGB调色板会导致屏幕不可读. 24位效果更好 ,但是2012年6月15号发现有显示混乱问题. 32位没有混乱问题, 但是需要设置framebuffer_ignore_alpha=1, 并在2012年6月15号发现颜色显示错误.
framebuffer_ignore_alpha 设为1将禁用alpha通道. 仅对32位有效.
test_mode 允许在启动时做声音与图像测试.
disable_overscan 设为1将禁用超出扫描.
config_hdmi_boost 设置HDMI接口的信号强度. 默认为0. 如果出现HDMI干扰问题可以试试设为4. 最大为7.
display_rotate 顺时针旋转屏幕显示 (默认为0) 或者翻转显示.
display_rotate=0        正常
display_rotate=1        90度
display_rotate=2        180度
display_rotate=3        270度
display_rotate=0x10000  水平翻转
display_rotate=0x20000  垂直翻转

注意: 旋转90度或者270度额外需要GPU内存, 所以在GPU只分配到16M的时候旋转会无效. 可能的原因:
Crashes my RPI before Linux boots if set to “1” — REW 20120913.

哪些值对我的显示器有效?

你的HDMI显示器可能只支持一部分设置. 想要找出支持哪些设置, 可以使用下面的方法.
把输出格式设为VGA 60Hz (hdmi_group=1 hdmi_mode=1) 然后启动树莓派
输入下列命令可以获取CEA支持模式的列表
/opt/vc/bin/tvservice -m CEA
输入下列命令可以获取DMT支持模式的列表
/opt/vc/bin/tvservice -m DMT
输入下列命令可以获取当前设置状态
/opt/vc/bin/tvservice -s
输入下列命令可以从显示器获取更多详细信息
/opt/vc/bin/tvservice -d edid.dat /opt/vc/bin/edidparser edid.dat
使用默认HDMI模式去排除问题时, edid.dat文件同样会提供信息

许可的解码器

你可以购买绑定树莓派CPU序列号的证书来使用额外的硬件解码器.
decode_MPG2 可开启MPEG-2硬解的序列号.
decode_MPG2=0x12345678
decode_WVC1 可开启VC-1硬解的序列号.
decode_WVC1=0x12345678
可在多台树莓派间共享SD卡的序列号. 同时最多8个证书.
decode_XXXX=0x12345678,0xabcdabcd,0x87654321,...

启动

disable_commandline_tags 在启动内核前, 通过改写ATAGS (0x100处的内存)来阻止start.elf
cmdline (string) 命令行参数. 可用来代替cmdline.txt文件
kernel (string) 加载指定名称的内核镜像文件启动内核. 默认为”kernel.img”
kernel_address 加载kernel.img文件地址
kernel_old (bool) 为1时, 从0x0处加载内核
ramfsfile (string) 要的加载的ramfs文件
ramfsaddr 要加载的ramfs文件地址
initramfs (string address) 要加载的ramfs文件及其地址 (就是把ramfsfile+ramfsaddr合并为一项).
注意: 这项使用与其他项不同的语法 – 不要在这用”=”号. 正确示例:
initramfs initramf.gz 0x00800000
device_tree_address 加载device_tree的地址
init_uart_baud 初始化uart波特率. 默认为115200
init_uart_clock 初始化uart时序. 默认为3000000 (3Mhz)
init_emmc_clock 初始化emmc时序. 默认为100000000 (100MHz)
boot_delay 在加载内核前在start.elf等待指定秒. 总延迟=1000 * boot_delay + boot_delay_ms. 默认为1
boot_delay_ms 在加载内核前在start.elf等待指定毫秒. 默认为0
avoid_safe_mode 如果设为1, 将不以安全模式启动. 默认为0

超频

注意: 设置任何参数来超频树莓派都会在芯片中永久的储存一个保修位, 用于检测你的树莓派是否超频过. 如果设备超频过保修就无效了. 自2012年9月19号,你可以自由超频而不影响保修了.
最新的内核有一个默认开启”ondemand”调速器的cpu频率内核驱动. 未开启超频并不会有任何影响. 一旦你开超频, ARM频率将随处理器负载而变化. 只有在调速器需要时才会使用非默认值. 你可以使用*_min配置选项来调整最低值, 或者使用force_turbo=1来禁用动态超频.

当芯片温度达到85°C运行时会关闭超频及超压, 直到冷却. 即使在25°C环境温度下使用最高设置, 也不要让温度达到极限.

超频选项

参数    说明
arm_freq    ARM频率,以MHz为单位. 默认为700
gpu_freq    同时设置core_freq, h264_freq, isp_freq, v3d_freq. 默认为250
core_freq    GPU处理器核心频率,以MHz为单位. 由于GPU要驱动二级缓存, 对ARM性能会造成影响. 默认为 250
h264_freq    视频硬解模块频率,以MHz为单位. 默认为250
isp_freq    图像传感器管道模块频率,以MHz为单位. 默认为250
v3d_freq    3D模块频率,以MHz为单位. 默认为250
avoid_pwm_pll    不要把锁相环用在PWM音频. 这会略微降低模拟音频的效果. 空闲的锁相环允许从剩余GPU独立设置core_freq, 这将会比超频有更多权限. 默认为0
sdram_freq    SDRAM频率,以MHz为单位.默认为400
over_voltage    ARM/GPU核心电压调节. [-16,8]用0.025V步进等同于[0.8V,1.4V]. 默认为0 (1.2V). 只有在指定 force_turbo或current_limit_override时 (会设置保修位), 才允许数值在6以上
over_voltage_sdram    同时设置over_voltage_sdram_c, over_voltage_sdram_i, over_voltage_sdram_p
over_voltage_sdram_c    SDRAM控制器电压调节. [-16,8]用0.025V步进等同于[0.8V,1.4V]. 默认为0 (1.2V)
over_voltage_sdram_i    SDRAM I/O电压调节. [-16,8]用0.025V步进等同于[0.8V,1.4V]. 默认为0 (1.2V)
over_voltage_sdram_p    SDRAM phy电压调节. [-16,8]用0.025V步进等同于[0.8V,1.4V]. 默认为0 (1.2V)
force_turbo    关闭动态CPU频率驱动及下面的最小设置. 开启h264/v3d/isp超频. 默认为0. 会设置保修位.
initial_turbo    在启动时以指定秒数 (上限为60) 或者以CPU频率来开启急速模式. 如果已经超频, 能对SD卡错误问题有改善. 默认为0
arm_freq_min    设置动态时序的最小arm_freq. 默认为700
core_freq_min    设置动态时序的最小core_freq. 默认为250
sdram_freq_min    设置动态时序的最小sdram_freq. 默认为400
over_voltage_min    设置动态时序的最小over_voltage. 默认为0
temp_limit    过热保护. 当芯片达到指定温度就把时序和电源切换会默认值. 把此值设高于默认值将影响保修. 默认为85
current_limit_override    当设为”0x5A000020″时, 禁止SMPS限流保护. 在超频过高无法重启时设置此项会有所帮助. 会设置保修位.

force_turbo模式
force_turbo=0
开启对ARM核心,GPU核心和SDRAM的动态时序及电压. 在忙的时候ARM频率会提高到”arm_freq”并在闲的时候降低到”arm_freq_min”. “core_freq”, “sdram_freq”和”over_voltage”的行为都一样. “over_voltage”最高为6 (1.35V). h264/v3d/isp部分的非默认值将被忽略.
force_turbo=1
关闭动态时序, 因此所有频率和电压会保持高值. h264/v3d/isp GPU部分的超频也会开启, 等同于设置”over_voltage”为8 (1.4V). 

时序关系

GPU核心, h264, v3d和isp共享一个锁相环, 因此需要相关联的频率. ARM, SDRAM和GPU有各自独有的锁相环, 因此可以设为没有关联的频率.

当设了”avoid_pwm_pll=1″下列设置就没必要了.
pll_freq = floor(2400 / (2 * core_freq)) * (2 * core_freq)
gpu_freq = pll_freq / [偶数]

有效的gpu_freq会自动四舍五到到最接近的整型偶数, 所以请求core_freq为500, gpu_freq为300,算一下2000/300 = 6.666 => 6 ,结果就是333.33MHz.

已测试过的超频设置

下表显示了一些成功的超频尝试, 这些可以指导你进行超频. 这些设置不一定能在每台树莓派上都成功, 并且会缩短高通芯片的寿命.

arm_freq    gpu_freq    core_freq    h264_freq    isp_freq    v3d_freq    sdram_freq    over_voltage    over_voltage_sdram
800
900    275                    500
900        450                450
930    350                    500
1000        500                500    6
1050                            6
1150        500                600    8
这是一个表明Hynix产的RAM在超频上表现不如三星产的RAM的报告.

超频时SD卡使用

设置SD卡: http://elinux.org/RPi_Easy_SD_Card_Setup
超频时使用6速或10速的SD卡(SHDC/SHDX)会导致在一些天后树莓派读取SD卡文件系统不稳定.
不管是ext4 , NTFS 或其他格式都一样.
不管是哪家SD卡生产商都一样.
不管是哪个版本的树莓派都一样.
这与SD卡容量无关 – 实际验证出现在16G或更大的SD卡上.
! 关键是你何时让树莓派功率不足,也就是低于树莓派的基本设置需求 !
popcornmix发表在https://github.com/raspberrypi/linux/issues/280:
“超频会导致SD卡错误.这情况往往是与板子相关(就是说有些树莓派超频后SD卡没事,有些不行).
我认为通常都是core_freq导致的SD卡问题(和arm_freq,sdram_freq比)”
在2013年4月写这个提示的时候在树莓派官方论坛上一共有137个有关于SD的问题, 绝大部分与超频有关.
如果你使用6速或10速SD卡, 还想要树莓派稳定运行:  不要尝试超频,否则很可能会丢失数据

监测温度及电压

要检测树莓派的温度, 看: /sys/class/thermal/thermal_zone0/temp
要检测树莓派当前的频率, 看: /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
要检测树莓派电源装置的电压, 你需要一个万用电表, 接上电源测试点, 或者扩展头.

通常来说要保持核心温度低于70度, 电压高于4.8V. (另外请注意, 不要用那种便宜的USB电源, 那基本上是4.2V的, 这是因为那本来就是为充3.7V锂电池设计的, 根本无法为树莓派提供稳定的5V电压). 此外, 用散热片也是个好主意, 尤其是你把树莓派装到了壳子里. 一个合适的散热器是自带不干胶栅格状的 14x14x10 mm 散热片.

超频稳定性测试

大多数超频问题立马就会出现启动问题, 但还是会随时间而出现文件系统问题. 这是一个对系统,特别是SD卡进行压力测试的脚本. 如果脚本执行完成, dmesg中不提示任何错误, 你做的超频设置可能会比较稳定.

如果系统崩溃了, 在重启时按住shift键, 这会临时性关闭所有超频. 同样, 注意SD卡问题通常由core_freq造成,不要在raspi-config预设的高速(950 MHz)和超速(1 GHz)里来个大跳越(从250 MHz飞到500 MHz).
#!/bin/bash
#Simple stress test for system. If it survives this, it's probably stable.
#Free software, GPL2+

echo "Testing overclock stability..."

#Max out the CPU in the background (one core). Heats it up, loads the power-supply.
nice yes >/dev/null &

#Read the entire SD card 10x. Tests RAM and I/O
for i in `seq 1 10`; do echo reading: $i; sudo dd if=/dev/mmcblk0 of=/dev/null bs=4M; done

#Writes 512 MB test file,  10x.
for i in `seq 1 10`; do echo writing: $i; dd if=/dev/zero of=deleteme.dat bs=1M count=512; sync; done

#Clean up
killall yes
rm deleteme.dat

#Print summary. Anything nasty will appear in dmesg.
echo -n "CPU freq: " ; cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
echo -n "CPU temp: " ; cat /sys/class/thermal/thermal_zone0/temp
dmesg | tail

echo "Not crashed yet, probably stable."

]]>
1249 0 0 0
<![CDATA[开启树莓派看门狗]]> http://mixdiy.com/index.php/2022/05/25/raspberry-watch-dog/ Wed, 25 May 2022 14:48:13 +0000 http://mixdiy.com/?p=1256

树莓派内核默认没有启用看门狗功能,当内核挂死时将进入“死机”状态或kgdb调试状态,并不会自动重启系统。本文为树莓派开启看门狗功能并通过内核线程周期性喂狗,当出现系统崩溃时会自动重启Linux系统。

加载看门狗模块

sudo modprobe bcm2835_wdt

/etc/modules里添加信息

bcm2835_wdt

启用硬件看门狗

sudo su
echo 'dtparam=watchdog=on' >> /boot/config.txt
reboot

安装看门狗系统服务

sudo apt-get update
sudo apt-get install watchdog

如果安装过程报错,请看我的这篇文章对你是否有帮助
看门狗安装报错解决办法

配置看门狗服务

sudo su
echo 'watchdog-device = /dev/watchdog' >> /etc/watchdog.conf
echo 'watchdog-timeout = 15' >> /etc/watchdog.conf
echo 'max-load-1 = 24' >> /etc/watchdog.conf

启用服务

su pi
sudo systemctl enable watchdog
sudo systemctl start watchdog
sudo systemctl status watchdog

如果启用正常,会得到以下结果
在这里插入图片描述
如果要对此进行测试,可以尝试在shell上运行fork炸弹,模拟系统卡死情况:

sudo bash -c ':(){ :|:& };:'

https://diode.io/raspberry%20pi/running-forever-with-the-raspberry-pi-hardware-watchdog-20202/


启用bcm2835_wdt模块:

sudo modprobe bcm2835_wdt
在/etc/modules文件末尾添加一行:(sudo nano /etc/modules Linux下nano编辑器的使用 )

bcm2835_wdt
安装watchdog模块

sudo apt-get install watchdog
编辑/etc/watchdog.conf文件进行编辑配置看门狗: (sudo /etc/watchdog.conf Linux下nano编辑器的使用 )

#ping                   = 172.31.14.1
#ping                   = 172.26.1.255
#interface              = eth0
#file                   = /var/log/messages
#change                 = 1407
# 如果要使能,请去掉行前的注释标号,设置其中一个的值为0来禁用
# 这些值应设定为在正常使用期间不会重启你的设备的值
# (如果你的机器真的死机了,平均载入进程量应该超过25)
#max-load-m=n表示在m分钟内开超过n个进程的时候重启
max-load-1              = 24
#max-load-5             = 18
#max-load-15            = 12
# 注意这是内存分页的数量
# 请检查你的机器的分页大小以获取真实的大小
#最小剩余内存
#min-memory             = 1
#最小可分配内存
#allocatable-memory     = 1
#repair-binary          = /usr/sbin/repair
#repair-timeout         =
#test-binary            =
#test-timeout           =
#选择看门狗设备,这里用默认值
watchdog-device = /dev/watchdog
# 已经编译进二进制文件的默认值
#这个文件里面的内容就是CPU温度(摄氏温标)的一千倍
temperature-device      = /sys/class/thermal/thermal_zone0/temp
#设定为重置温度的一千倍
max-temperature = 75000
# 已经编译进二进制文件的默认值
#admin                  = root
#interval               = 1
#logtick                = 1
#log-dir                = /var/log/watchdog
# 这极大地减少了在你的机器真正加载完成前看门狗不被列入进程表的几率
realtime                = yes
priority                = 1
# 使能下面这行来检查rsyslogd是否仍然在运行
#pidfile                = /var/run/rsyslogd.pid

使看门狗服务开机自动启动:

sudo systemctl enable watchdog.service


测试看门狗:

sudo bash -c ':(){ :|:& };:'

所谓看门狗就是检测功能性软件是否还在运转的软件或硬件模块。当它检测到软件没有在工作时,就会重启机器,使得软件在重启之后能够继续工作。它的检测方式很简单,就让功能性软件定时的发信号给它(所谓的喂狗),一旦一定时间内没有接收到信号,那么就认为软件已经停止运行了,系统就需要重启了。在树莓派的硬件中就有看门狗的功能。下面我们就来玩玩这条狗。

教程目的:

用脚本的方式来喂狗,并试验当脚本停止喂狗后,树莓派会重启。(网上大量流传着下载watchdog这个软件来进行喂狗工作,本教程是自己写shell来喂狗的。当然,也是很简单的。)

教程器材及软件:

树莓派的板子。
SD卡(已经有镜像刷入)。
电源线及USB充电器。
U盘或USB硬盘
putty和psftp。
有DHCP的网线。
步骤:

先打开内核模块中的看门狗功能:
sudo modprobe bcm2708_wdog
(如果,你要在树莓派启动的时候,就启动看门狗功能。那么你需要在/etc/modules文件中加入,一行:bcm2708_wdog)
开启这个之后,你就可以在/dev目录下看到一个叫watchdog的文件。
执行完上面的命令,狗还不需要喂。但是一旦,你打开了这个文件(所谓,打开一种是c语言里的fopen,另一种就是等价的在shell中使用读写该文件的命令),就需要开始喂狗。喂狗的方式就是输入任何字符后数字除了V以外。此时,哪怕你关闭了文件,狗还是需要喂的。知道你输入V,才可以停止喂狗。有了这样子的原理,写一个喂狗的脚本就容易了。

!/bin/bash

while((1))
do
sleep 3
echo "Feed the dog"
echo "Feed the dog!" > /dev/watchdog
done
然后,运行这个脚本一段时间,一直喂狗,然后中断他。(Ctrl+C)
过了一会,树莓派就重启了。

]]>
1256 0 0 0
<![CDATA[Centos7系统编译安装PHP7.4]]> http://mixdiy.com/index.php/2022/05/26/install-php7-4-in-centos/ Thu, 26 May 2022 06:25:34 +0000 http://mixdiy.com/?p=1262
最近买了个盒子,拆开一看是用了一家已经倒闭的某公司的产品,拆了板子重新烧录了Centos7系统装了宝塔再加了个新壳子,首先应该肯定的是卖家会做生意,不敢苟同的是里面一堆坑要踩,当然想买这种板子的人都是爱折腾的,估计卖家也是吃准了这一点。我一个centos新手(拿到这个盒子之前从来没摸过centos)被折腾的够呛,本来买盒子就一个目的,架一个个人博客(当然选wordpress),结果盒子自带的php版本太低,wordpress直接警告,随后按卖家文档升级,于是坑一个接一个。。。。。说说多了,最后总结,这玩意只能编译安装,步骤如下:

安装依赖包
yum install libxml2 libxml2-devel openssl openssl-devel bzip2 bzip2-devel libcurl libcurl-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel gmp gmp-devel libmcrypt libmcrypt-devel readline readline-devel libxslt libxslt-devel zlib zlib-devel glibc glibc-devel glib2 glib2-devel ncurses curl gdbm-devel db4-devel libXpm-devel libX11-devel gd-devel gmp-devel expat-devel xmlrpc-c xmlrpc-c-devel libicu-devel libmcrypt-devel libmemcached-devel

下载安装包
wget https://www.php.net/distributions/php-7.4.10.tar.gz

解压
tar -xf php-7.4.10.tar.gz

进入目录
cd php-7.4.10

安装配置
./configure \
--prefix=/usr/local/php7.1.33 \
--enable-fpm \
--enable-inline-optimization \
--disable-debug \
--disable-rpath \
--enable-shared \
--enable-soap \
--with-libxml-dir \
--with-xmlrpc \
--with-openssl \
--with-mcrypt \
--with-mhash \
--with-pcre-regex \
--with-sqlite3 \
--with-zlib \
--enable-bcmath \
--with-iconv \
--with-bz2 \
--enable-calendar \
--with-curl \
--with-cdb \
--enable-dom \
--enable-exif \
--enable-fileinfo \
--enable-filter \
--with-pcre-dir \
--enable-ftp \
--with-gd \
--with-openssl-dir \
--with-jpeg-dir \
--with-png-dir \
--with-zlib-dir \
--with-freetype-dir \
--enable-gd-native-ttf \
--enable-gd-jis-conv \
--with-gettext \
--with-gmp \
--with-mhash \
--enable-json \
--enable-mbstring \
--enable-mbregex \
--enable-mbregex-backtrack \
--with-libmbfl \
--with-onig \
--enable-pdo \
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--with-zlib-dir \
--with-pdo-sqlite \
--with-readline \
--enable-session \
--enable-shmop \
--enable-simplexml \
--enable-sockets \
--enable-sysvmsg \
--enable-sysvsem \
--enable-sysvshm \
--enable-wddx \
--with-libxml-dir \
--with-xsl \
--enable-zip \
--enable-mysqlnd-compression-support \
--with-pear \
--enable-opcache

编译安装
make && make install



Centos8 编译安装 PHP 8.1.0 - 简书 (jianshu.com)

如果是源码安装,先运行./configure,生成makefile,再执行make,即可正常运行

Centos8 编译安装 PHP 8.1.0

安装工具包

yum -y install libtool automake libzip-devel epel-release libxml2 libxml2-devel openssl openssl-devel curl-devel libjpeg-devel libpng-devel freetype-devel libmcrypt-devel uuid libuuid-devel gcc bzip2 bzip2-devel gmp-devel  readline-devel libxslt-devel autoconf bison gcc gcc-c++ sqlite-devel cmake

下载安装 oniguruma

wget -c https://github.com/kkos/oniguruma/archive/refs/tags/v6.9.7.1.tar.gz
tar -zxvf v6.9.7.1.tar.gz
cd oniguruma-6.9.7.1
./autogen.sh && ./configure --prefix=/usr
make && make install

下载安装 php 8.1.0

wget -c  https://www.php.net/distributions/php-8.1.0.tar.gz
tar -zxvf php-8.1.0.tar.gz
cd php-8.1.0

./configure \
--prefix=/opt/php/8.1.0 \
--with-config-file-path=/opt/php/8.1.0/etc \
--with-config-file-scan-dir=/opt/php/8.1.0/etc/conf.d \
--enable-fpm \
--enable-soap \
--with-openssl \
--with-openssl-dir \
--with-zlib \
--with-iconv \
--with-bz2 \
--enable-gd \
--with-jpeg \
--with-freetype \
--with-curl \
--enable-dom \
--with-xml \
--with-zip \
--enable-mbstring \
--enable-pdo \
--with-pdo-mysql \
--with-zlib-dir \
--enable-session \
--enable-shmop \
--enable-simplexml \
--enable-sockets \
--enable-sysvmsg \
--enable-sysvsem \
--enable-sysvshm \
--with-xsl \
--enable-mysqlnd \
--with-mysqli \
--without-pear \
--disable-short-tags 

make && make install

Installing shared extensions:     /opt/php/8.1.0/lib/php/extensions/no-debug-non-zts-20210902/
Installing PHP CLI binary:        /opt/php/8.1.0/bin/
Installing PHP CLI man page:      /opt/php/8.1.0/php/man/man1/
Installing PHP FPM binary:        /opt/php/8.1.0/sbin/
Installing PHP FPM defconfig:     /opt/php/8.1.0/etc/
Installing PHP FPM man page:      /opt/php/8.1.0/php/man/man8/
Installing PHP FPM status page:   /opt/php/8.1.0/php/php/fpm/
Installing phpdbg binary:         /opt/php/8.1.0/bin/
Installing phpdbg man page:       /opt/php/8.1.0/php/man/man1/
Installing PHP CGI binary:        /opt/php/8.1.0/bin/
Installing PHP CGI man page:      /opt/php/8.1.0/php/man/man1/
Installing build environment:     /opt/php/8.1.0/lib/php/build/
Installing header files:          /opt/php/8.1.0/include/php/
Installing helper programs:       /opt/php/8.1.0/bin/
  program: phpize
  program: php-config
Installing man pages:             /opt/php/8.1.0/php/man/man1/
  page: phpize.1
  page: php-config.1
/home/centos/php-8.1.0/build/shtool install -c ext/phar/phar.phar /opt/php/8.1.0/bin/phar.phar
ln -s -f phar.phar /opt/php/8.1.0/bin/phar
Installing PDO headers:           /opt/php/8.1.0/include/php/ext/pdo/

启动和后续配件可以参考这里
Centos7 编译 PHP7.4.16 - 简书 (jianshu.com)


增加扩展

进入刚才的解压目录

#安装GD库示例
cd php-8.1.0/ext/gd
/opt/php/8.1.0/bin/phpize

Configuring for:
PHP Api Version:         20210902
Zend Module Api No:      20210902
Zend Extension Api No:   420210902

./configure --with-php-config=/opt/php/8.1.0/bin/php-config
make && make install

完成后记得在 php.ini 里加入 extension=gd.so

PHP8 主要不向后兼容的变更

  • 字符串与数字的比较
    数字与非数字形式的字符串之间的非严格比较现在将首先将数字转为字符串,然后比较这两个字符串。
    数字与数字形式的字符串之间的比较仍然像之前那样进行。

请注意,这意味着 0 == "not-a-number" 现在将被认为是 false 。

ComparisonBeforeAfter
0 == "0"truetrue
0 == "0.0"truetrue
0 == "foo"truefalse
0 == ""truefalse
42 == " 42"truetrue
42 == "42foo"truefalse
  • match 现在是一个保留字。
  • 断言(Assertion)失败现在默认抛出异常。如果想要改回之前的行为,可以在 INI 设置中设置 assert.exception=0
  • 与类名相同的方法名将不再被当做构造方法。应该使用__construct() 来取代它。
  • 不再允许通过静态调用的方式去调用非静态方法。因此is_callable()在检查一个类名与非静态方法 时将返回失败(应当检查一个类的实例)。
  • (real)(unset) 转换已被移除

PHP8 主要新特性

命名参数

新增命名参数的功能。

注解(Attributes)

新增注解的功能。

构造器属性提升(Constructor Property Promotion)

新增构造器属性提升功能 在构造函数中声明类的属性)。

联合类型

新增 联合类型

Match 表达式

新增 match 表达式

Nullsafe 运算符

新增Nullsafe 运算符(?->)。

其他新特性

  • 新增 WeakMap 类。
  • 新增 ValueError 类。
  • 现在,只要类型兼容,任意数量的函数参数都可以用一个可变参数替换。 例如允许编写下面的代码:

<?php class A {
         public function method(int $many, string $parameters, $here) {}
    }
    class B extends A {
         public function method(...$everything) {}
    } ?>
  • static ("后期静态绑定"中) 可以作为返回类型:

    <?php class Test {
         public function create(): static {
              return new static();
         }
    } ?>
  • 现在可以通过 $object::class 获取类名,返回的结果和 get_class($object) 一致。
  • newinstanceof 可用于任何表达式, 用法为 new (expression)(...$args)$obj instanceof (expression)
  • 添加对一些变量语法一致性的修复,例如现在能够编写 Foo::BAR::$baz
  • 添加 Stringable interface, 当一个类定义 __toString() 方法后会自动实现该接口。
  • Trait 可以定义私有抽象方法(abstract private method)。 类必须实现 trait 定义的该方法。
  • 可作为表达式使用 throw。 使得可以编写以下用法:

    <?php
    $fn = fn() => throw new Exception('Exception in arrow function'); $user = $session->user ?? throw new Exception('Must have user');
  • 参数列表中的末尾逗号为可选。

    <?php function functionWithLongSignature(
        Type1 $parameter1,
        Type2 $parameter2, // <-- 这个逗号也被允许了 ) {
    }
  • 现在允许 catch (Exception) 一个 exception 而无需捕获到变量中。
  • 支持 mixed 类型。
]]>
1262 0 0 0
<![CDATA[再Centos7上安装python3]]> http://mixdiy.com/index.php/2022/05/27/centos7-python3/ Fri, 27 May 2022 06:49:53 +0000 http://mixdiy.com/?p=1280

第一步:安装相关依赖包和编译环境

$>yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel gcc

(注意:这一步很重要,如果不安装相关依赖包,在使用pip安装python包时会报找不到SSL错误!)

第二步:下载python3.8.0安装包

$>wget https://www.python.org/ftp/python/3.8.0/Python-3.8.0.tar.xz

第三步:解压安装包并创建安装目录

$>xz -d Python-3.8.0.tar.xz

$>tar -xvf Python-3.8.0.tar

$>mkdir /usr/local/python3.8.0

第四步:编译安装

$>cd Python-3.8.0

$>./configure --with-ssl --prefix=/usr/local/python3.8.0

(注意:prefix后面配置第三步中创建的路径,且等号两边不能有空格,不然会报错)

$>make && make install

第五步:创建python3.6.5软链接

$>ln -s /usr/local/python3.8.0/bin/python3.8 /usr/bin/python3

$>ln -s /usr/local/python3.8.0/bin/pip3.8 /usr/bin/pip3

$>pip3 install --upgrade pip(升级pip3)

第六步:修改python2.7.5软链接(这一步可有可无)

$>mv /usr/bin/python /usr/bin/python2

第七步:验证,使用python3进入python3.8.0命令行

$>python3

]]>
1280 0 0 0
<![CDATA[朋途-putug]]> http://mixdiy.com/index.php/2022/05/28/putug/ Sat, 28 May 2022 04:02:04 +0000 http://mixdiy.com/?p=1283

http域名:uza3jlgx.hk.wknwct.top:80(绑定内网80端口,用于建站)

面板域名:uza3jlgxmb.hk.wknwct.top:80(绑定内网8888端口,用于访问面板)

Https域名:uza3jlgxs.hk.wknwct.top:443(需要开启并部署证书,未开启默认http端口)

Phpmyadmin:uza3jlgxsql.hk.wknwct.top:80/phpmyadmin_ba74697dda5af6e2 (绑定内网888端口,用于数据库管理)

SSH端口:8613(默认绑定内网22端口,用于远程链接服务器,地址为赠送或绑定的域名)

数据库端口:38613(默认绑定内网3306端口,用于远程数据库链接,地址为赠送或绑定的域名)

提示信息:请绑定自己的域名。香港线路,三网双程直连 国内访问速度飞快, 无防机房,被攻击封一小时IP

]]>
1283 0 0 0
<![CDATA[数据清洗、筛选、去重、排序、提取]]> http://mixdiy.com/index.php/2022/05/30/data-cleaning/ Mon, 30 May 2022 05:38:05 +0000 http://mixdiy.com/?p=1288
Mexicans were there enveloped5 in their sarapes; Chinamen in their large-sleeved tunics6, pointed7 shoes, and conical hats; one or two Kanucks from the coast; and even a sprinkling of Black Feet, Grosventres, or Flatheads, from the banks of the Trinity river.
 
The scene is in San Francisco, the capital of California, but not at the period when the placer-mining fever was raging—from 1849 to 1852. San Francisco was no longer what it had been then, a caravanserai, a terminus, an inn, where for a night there slept the busy men who were hastening to the gold-fields west of the Sierra Nevada. At the end of some twenty years the old unknown Yerba-Buena had given place to a town unique of its kind, peopled by 100,000 inhabitants, built under the shelter of a couple of hills, away from the shore, but stretching off to the farthest heights in the background—a city in short which has dethroned Lima, Santiago, Valparaiso, and every other rival, and which the Americans have made the queen of the Pacific, the "glory of the western coast!"
 
It was the 15th of May, and the weather was still cold. In California, subject as it is to the direct action of the polar currents, the first weeks of this month are somewhat similar to the last weeks of March in Central Europe. But the cold was hardly noticeable in the thick of the auction crowd. The bell with its incessant8 clangour had[Pg 3] brought together an enormous throng9, and quite a summer temperature caused the drops of perspiration10 to glisten11 on the foreheads of the spectators which the cold outside would have soon solidified12.
 
Do not imagine that all these folks had come to the auction-room with the intention of buying. I might say that all of them had but come to see. Who was going to be mad enough, even if he were rich enough, to purchase an isle13 of the Pacific, which the government had in some eccentric moment decided14 to sell? Would the reserve price ever be reached? Could anybody be found to work up the bidding? If not, it would scarcely be the fault of the public crier, who tried his best to tempt15 buyers by his shoutings and gestures, and the flowery metaphors16 of his harangue17. People laughed at him, but they did not seem much influenced by him.
 
"An island! an isle to sell!" repeated Gingrass.
 
"But not to buy!" answered an Irishman, whose pocket did not hold enough to pay for a single pebble18.
 
"An island which at the valuation will not fetch six dollars an acre!" said the auctioneer.
 
"And which won't pay an eighth per cent.!" replied a big farmer, who was well acquainted with agricultural speculations19.
 
"An isle which measures quite sixty-four miles round[Pg 4] and has an area of two hundred and twenty-five thousand acres!"
 
"Is it solid on its foundation?" asked a Mexican, an old customer at the liquor-bars, whose personal solidity seemed rather doubtful at the moment.
 
"An isle with forests still virgin20!" repeated the crier, "with prairies, hills, watercourses—"
 
"Warranted?" asked a Frenchman, who seemed rather inclined to nibble21.
 
"Yes! warranted!" added Felporg, much too old at his trade to be moved by the chaff22 of the public.
 
"For two years?"
 
"To the end of the world!"
 
"Beyond that?"
 
"A freehold island!" repeated the crier, "an island without a single noxious23 animal, no wild beasts, no reptiles24!—"
 
"No birds?" added a wag.
 
"No insects?" inquired another.
 
"An island for the highest bidder!" said Dean Felporg, beginning again. "Come, gentlemen, come! Have a little courage in your pockets! Who wants an island in perfect state of repair, never been used, an island in the Pacific, that ocean of oceans? The valuation is a mere25 nothing! It is put at eleven hundred thousand dollars, is there any[Pg 5] one will bid? Who speaks first? You, sir?—you, over there nodding your head like a porcelain26 mandarin27? Here is an island! a really good island! Who says an island?"
 
"Pass it round!" said a voice as if they were dealing28 with a picture or a vase.
 
And the room shouted with laughter, but not a half-dollar was bid.
 
However, if the lot could not be passed round, the map of the island was at the public disposal. The whereabouts of the portion of the globe under consideration could be accurately29 ascertained30. There was neither surprise nor disappointment to be feared in that respect. Situation, orientation31, outline, altitudes, levels, hydrography, climatology, lines of communication, all these were easily to be verified in advance. People were not buying a pig in a poke32, and most undoubtedly33 there could be no mistake as to the nature of the goods on sale. Moreover, the innumerable journals of the United States, especially those of California, with their dailies, bi-weeklies, weeklies, bi-monthlies, monthlies, their reviews, magazines, bulletins, &c., had been for several months directing constant attention to the island whose sale by auction had been authorized34 by Act of Congress.
 
The island was Spencer Island, which lies in the [Pg 6]west-south-west of the Bay of San Francisco, about 460 miles from the Californian coast, in 32° 15' north latitude35, and 145° 18' west longitude36, reckoning from Greenwich. It would be impossible to imagine a more isolated37 position, quite out of the way of all maritime38 or commercial traffic, although Spencer Island was relatively39, not very far off, and situated40 practically in American waters. But thereabouts the regular currents diverging41 to the north and south have formed a kind of lake of calms, which is sometimes known as the "Whirlpool of Fleurieu."
 
It is in the centre of this enormous eddy42, which has hardly an appreciable43 movement, that Spencer Island is situated. And so it is sighted by very few ships. The main routes of the Pacific, which join the new to the old continent, and lead away to China or Japan, run in a more southerly direction. Sailing-vessels would meet with endless calms in the Whirlpool of Fleurieu; and steamers, which always take the shortest road, would gain no advantage by crossing it. Hence ships of neither class know anything of Spencer Island, which rises above the waters like the isolated summit of one of the submarine mountains of the Pacific. Truly, for a man wishing to flee from the noise of the world, seeking quiet in solitude44, what could be better than this island, lost within a few hundred miles of the coast? For a voluntary Robinson Crusoe, it would[Pg 7] be the very ideal of its kind! Only of course he must pay for it.
 
And now, why did the United States desire to part with the island? Was it for some whim45? No! A great nation cannot act on caprice in any matter, however simple. The truth was this: situated as it was, Spencer Island had for a long time been known as a station perfectly46 useless. There could be no practical result from settling there. In a military point of view it was of no importance, for it only commanded an absolutely deserted47 portion of the Pacific. In a commercial point of view there was a similar want of importance, for the products would not pay the freight either inwards or outwards48. For a criminal colony it was too far from the coast. And to occupy it in any way, would be a very expensive undertaking49. So it had remained deserted from time immemorial, and Congress, composed of "eminently50 practical" men, had resolved to put it up for sale—on one condition only, and that was, that its purchaser should be a free American citizen. There was no intention of giving away the island for nothing, and so the reserve price had been fixed51 at $1,100,000. This amount for a financial society dealing with such matters was a mere bagatelle52, if the transaction could offer any advantages; but as we need hardly repeat, it offered none, and competent men[Pg 8] attached no more value to this detached portion of the United States, than to one of the islands lost beneath the glaciers53 of the Pole.
 
In one sense, however, the amount was considerable. A man must be rich to pay for this hobby, for in any case it would not return him a halfpenny per cent. He would even have to be immensely rich for the transaction was to be a "cash" one, and even in the United States it is as yet rare to find citizens with $1,100,000 in their pockets, who would care to throw them into the water without hope of return.
 
And Congress had decided not to sell the island under the price. Eleven hundred thousand dollars, not a cent less, or Spencer Island would remain the property of the union.
 
It was hardly likely that any one would be mad enough to buy it on the terms.
 
Besides, it was expressly reserved that the proprietor54, if one offered, should not become king of Spencer Island, but president of a republic. He would gain no right to have subjects, but only fellow-citizens, who could elect him for a fixed time, and would be free from re-electing him indefinitely. Under any circumstances he was forbidden to play at monarchy55. The union could never tolerate the foundation of a kingdom, no matter how small, in American waters.
 
[Pg 9]
 
This reservation was enough to keep off many an ambitious millionaire, many an aged56 nabob, who might like to compete with the kings of the Sandwich, the Marquesas, and the other archipelagoes of the Pacific.
 
In short, for one reason or other, nobody presented himself. Time was getting on, the crier was out of breath in his efforts to secure a buyer, the auctioneer orated without obtaining a single specimen57 of those nods which his estimable fraternity are so quick to discover; and the reserve price was not even mentioned.
 
However, if the hammer was not wearied with oscillating above the rostrum, the crowd was not wearied with waiting around it. The joking continued to increase, and the chaff never ceased for a moment. One individual offered two dollars for the island, costs included. Another said that a man ought to be paid that for taking it.
 
And all the time the crier was heard with,—
 
"An island to sell! an island for sale!"

数据清洗、筛选、去重、排序、提取


import re
#a='Beautiful, is; better*than\nugly'
a='Mexicans were there enveloped5 in their sarapes; Chinamen in their large-sleeved tunics6, pointed7 shoes, and conical hats; one or two Kanucks from the coast; and even a sprinkling of Black Feet, Grosventres, or Flatheads, from the banks of the Trinity river.The scene is in San Francisco, the capital of California, but not at the period when the placer-mining fever was raging—from 1849 to 1852. San Francisco was no longer what it had been then, a caravanserai, a terminus, an inn, where for a night there slept the busy men who were hastening to the gold-fields west of the Sierra Nevada. At the end of some twenty years the old unknown Yerba-Buena had given place to a town unique of its kind, peopled by 100,000 inhabitants, built under the shelter of a couple of hills, away from the shore, but stretching off to the farthest heights in the background—a city in short which has dethroned Lima, Santiago, Valparaiso, and every other rival, and which the Americans have made the queen of the Pacific, the "glory of the western coast!"It was the 15th of May, and the weather was still cold. In California, subject as it is to the direct action of the polar currents, the first weeks of this month are somewhat similar to the last weeks of March in Central Europe. But the cold was hardly noticeable in the thick of the auction crowd. The bell with its incessant8 clangour had[Pg 3] brought together an enormous throng9, and quite a summer temperature caused the drops of perspiration10 to glisten11 on the foreheads of the spectators which the cold outside would have soon solidified12.Do not imagine '

# 四个分隔符为:,  ;  *  \n
x= re.split(' |,|; |\*|\n',a)
for t in x:
    print(t)

去重、排序

import re
#a='Beautiful, is; better*than\nugly'
a='Mexicans were there enveloped5 in their sarapes; Chinamen in their large-sleeved tunics6, pointed7 shoes, and conical hats; one or two Kanucks from the coast; and even a sprinkling of Black Feet, Grosventres, or Flatheads, from the banks of the Trinity river.The scene is in San Francisco, the capital of California, but not at the period when the placer-mining fever was raging—from 1849 to 1852. San Francisco was no longer what it had been then, a caravanserai, a terminus, an inn, where for a night there slept the busy men who were hastening to the gold-fields west of the Sierra Nevada. At the end of some twenty years the old unknown Yerba-Buena had given place to a town unique of its kind, peopled by 100,000 inhabitants, built under the shelter of a couple of hills, away from the shore, but stretching off to the farthest heights in the background—a city in short which has dethroned Lima, Santiago, Valparaiso, and every other rival, and which the Americans have made the queen of the Pacific, the "glory of the western coast!"It was the 15th of May, and the weather was still cold. In California, subject as it is to the direct action of the polar currents, the first weeks of this month are somewhat similar to the last weeks of March in Central Europe. But the cold was hardly noticeable in the thick of the auction crowd. The bell with its incessant8 clangour had[Pg 3] brought together an enormous throng9, and quite a summer temperature caused the drops of perspiration10 to glisten11 on the foreheads of the spectators which the cold outside would have soon solidified12.Do not imagine '

# 四个分隔符为:,  ;  *  \n
x= re.split(' |,|; |\*|\n',a)

lista=list(set(x))
lista.sort(key=len)

for t in sorted(lista,key = len):
    print(t)
]]>
1288 0 0 0
<![CDATA[python图像边缘检测并生成svg图像]]> http://mixdiy.com/index.php/2022/06/02/opencv-findcontours/ Thu, 02 Jun 2022 09:38:46 +0000 http://mixdiy.com/?p=1306

https://zhuanlan.zhihu.com/p/398237689

from typing import Iterable, List, Tuple, Union
import cv2
import matplotlib.pyplot as plt
import numpy as np
from xml.dom import minidom as md
from queue import Queue
import warnings

def look_shape(a : Iterable) -> Tuple:
    # for debug
    return np.array(a).shape

def length_within_points(a : Iterable, empty_value : Union[int, float] = 0) -> int:
    """
        a simple instance:
            array : [empty_value, empty_value, empty_value, 1, empty_value, 0, 1, 2, empty_value]
            Then length_within_points(array) will return index diff between 1 and 2, which is 5
    """
    a = list(a)
    l_pivot, r_pivot = -1, -2
    for index, (l_val, r_val) in enumerate(zip(a[::1], a[::-1])):
        if l_val != empty_value and l_pivot == -1:
            l_pivot = index 
        if r_val != empty_value and r_pivot == -2:
            r_pivot = len(a) - index

    return r_pivot - l_pivot + 1

def dump_rings_from_image(image : np.ndarray, output_path : str, plot_dict : dict = {"color" : "k", "linewidth" : 0.3}, default_height : float = 8) -> List[np.ndarray]:
    # regular operation, no more explainations
    blur = cv2.GaussianBlur(image, (3, 3), 0)
    gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)
    edge = cv2.Canny(gray, 50, 150)

    # get ratio between width and height to adjust the final output
    valid_width = length_within_points(edge.sum(axis=0))
    valid_height = length_within_points(edge.sum(axis=1))
    true_ratio = valid_width / valid_height

    # get contour of the edge image
    contour_tuple = cv2.findContours(edge, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)
    contours = contour_tuple[0]
    rings = [np.array(c).reshape([-1, 2]) for c in contours]

    # adjust coordinate system to the image coordinate system
    max_x, max_y, min_x, min_y = 0, 0, 0, 0
    for ring in rings:
        max_x = max(max_x, ring.max(axis=0)[0])
        max_y = max(max_y, ring.max(axis=0)[1])
        min_x = max(min_x, ring.min(axis=0)[0])
        min_y = max(min_y, ring.min(axis=0)[1])
    
    # adjust ratio
    plt.figure(figsize=[default_height * true_ratio, default_height])

    # plot to the matplotlib
    for _, ring in enumerate(rings):
        close_ring = np.vstack((ring, ring[0]))
        xx = close_ring[..., 0]
        yy = max_y - close_ring[..., 1]
        plt.plot(xx, yy, **plot_dict)

    plt.axis("off")
    plt.savefig(output_path)

def remove_matplotlib_background(svg_file : str, bg_node_name : str = "patch_1") -> None:
    dom_tree : md.Document = md.parse(svg_file)
    svg_node = None 
    # select the svg tag
    for node in dom_tree.childNodes:
        if node.nodeName == "svg":
            svg_node : md.Element = node 
    if svg_node is None:
        raise ValueError("not find a svg node in {}".format(svg_file))

    # bfs svg node to find the background node
    q = Queue()
    q.put(svg_node)
    target_node = None  # we will remove the target node

    while not q.empty():
        cur : md.Node = q.get()
        if cur.hasChildNodes():
            for node in cur.childNodes:
                q.put(node)
        if hasattr(cur, "getAttribute"):
            # this is the id of the background node
            if cur.getAttribute("id") == bg_node_name:
                target_node = cur
    
    if target_node is None:
        warnings.warn("background node is not found, please ensure whether bg_node_name is correct")
    else:       # remove and write
        target_node.parentNode.removeChild(target_node)
        with open(svg_file, "w", encoding="utf-8") as fp:
            dom_tree.writexml(
                writer=fp,
                indent="\t"
            )

def bitmap_to_contour_svg(input_bitmap_path : str, output_svg_path : str):
    img = cv2.imread(input_bitmap_path)
    dump_rings_from_image(img, output_path=output_svg_path)
    remove_matplotlib_background(output_svg_path)

if __name__ == '__main__':
    bitmap_to_contour_svg(
        input_bitmap_path="555.png",
        output_svg_path="abc.svg"
    )

一个简单方式

from PIL import Image, ImageFilter

img = Image.open('abc.jpg')  # 打开图片文件
newimg = img.filter(ImageFilter.CONTOUR)  # 设置图片轮廓筛选器
newimg.save('轮廓效果.png', 'png')  # 保存轮廓效果的图片
]]>
1306 0 0 0
<![CDATA[Custom Styles]]> http://mixdiy.com/index.php/2022/01/26/wp-global-styles-twentyfifteen/ Wed, 26 Jan 2022 08:20:55 +0000 http://mixdiy.com/index.php/2022/01/26/wp-global-styles-twentyfifteen/ 537 0 0 0 <![CDATA[未命名可重用区块]]> http://mixdiy.com/index.php/2022/02/06/%e6%9c%aa%e5%91%bd%e5%90%8d%e5%8f%af%e9%87%8d%e7%94%a8%e5%8c%ba%e5%9d%97/ Sun, 06 Feb 2022 14:44:05 +0000 http://mixdiy.com/index.php/2022/02/06/%e6%9c%aa%e5%91%bd%e5%90%8d%e5%8f%af%e9%87%8d%e7%94%a8%e5%8c%ba%e5%9d%97/

语音提示播报
去中心化

]]>
607 0 0 0
<![CDATA[精彩的树梅派项目]]> http://mixdiy.com/index.php/2022/06/04/nice-pi-project/ Sat, 04 Jun 2022 10:39:29 +0000 http://mixdiy.com/?p=1317

https://www.tomshardware.com/features/best-raspberry-pi-projects

树莓派人脸识别

https://www.tomshardware.com/how-to/raspberry-pi-facial-recognition

树莓派系统及软件备份

https://www.tomshardware.com/how-to/back-up-raspberry-pi-as-disk-image

如何设置 Raspberry Pi Web 服务器

https://www.tomshardware.com/news/raspberry-pi-web-server,40174.html

]]>
1317 0 0 0
<![CDATA[无法访问raw.githubusercontent.com的解决]]> http://mixdiy.com/index.php/2022/06/04/raw-githubusercontent-com-error/ Sat, 04 Jun 2022 15:33:06 +0000 http://mixdiy.com/?p=1325
我们难免不会访问raw.githubusercontent.com,但因种种因素,国内很难访问这个站点,以下方法可能帮助到你:
https://ip.cn/ip/raw.githubusercontent.com.html
查找到ip为185.199.108.133
sudo nano /etc/hosts

添加

185.199.110.133 raw.githubusercontent.com

注:

如果是windows系统,则修改

C:\Windows\System32\drivers\etc
hosts文件

]]>
1325 0 0 0
<![CDATA[树莓派安装人脸训练模型 face-recognition]]> http://mixdiy.com/index.php/2022/06/05/raspberry-install-face-recognition/ Sun, 05 Jun 2022 02:43:28 +0000 http://mixdiy.com/?p=1330
pip install cmake
pip install face-recognition
]]>
1330 0 0 0
<![CDATA[树莓派系统备份并传输]]> http://mixdiy.com/index.php/2022/06/05/backup-raspberrypi/ Sun, 05 Jun 2022 04:18:10 +0000 http://mixdiy.com/?p=1334

一键备份


建立文件backup.sh

sudo nano backup.sh

#!/bin/bash

set -e #当命令以非零状态退出时,则退出shell

Color_End="\033[0m"
Color_Red="\033[31m"
Color_Green="\033[32m"

if [ `id -un` != "root" ];then
    echo -e "$Color_Red权限不足,退出脚本! $Color_End"
    exit 1
fi

# 设置文件存放目录
BACKUP_DIR=`pwd`
BACK_UP_DIR=$BACKUP_DIR/raspi-backup
FILE=$BACK_UP_DIR/raspi-backup.img  #备份后的img文件名
mkdir $BACK_UP_DIR

#安装必要的软件安装包 
echo -e "$Color_Green安装必要的软件...$Color_End"
apt-get install -qq -y dosfstools dump parted kpartx rsync
apt-get clean

#创建镜像img文件
echo -e "$Color_Green创建img文件...$Color_End"
ROOT=`df -P | grep /dev/root | awk '{print $3}'`   #获取 ROOT的文件大小
MMCBLK0P1=`df -P | grep /dev/mmcblk0p1 | awk '{print $2}'`  #获取主目录的文件大小
ALL=`echo $ROOT $MMCBLK0P1 | awk '{print int(($1+$2)*1.1)}'`  #生成一个比原文件大200M的IMG文件
echo "预计生成文件大小:$(($ALL/1024))MB"
echo "root 大小是 $(($ROOT/1024))MB"
echo "boot 大小是 $(($MMCBLK0P1/1024))MB"
echo "文件路径是 $FILE"
dd if=/dev/zero of=$FILE bs=1K count=$ALL status=progress

#格式化分区
echo -e "$Color_Green格式化root和boot...$Color_End"
P1_START=`fdisk -l /dev/mmcblk0 | grep /dev/mmcblk0p1 | awk '{print $2}'`
P1_END=`fdisk -l /dev/mmcblk0 | grep /dev/mmcblk0p1 | awk '{print $3}'`
P2_START=`fdisk -l /dev/mmcblk0 | grep /dev/mmcblk0p2 | awk '{print $2}'`
echo "boot_start is :$P1_START .boot_end is : $P1_END  .rootfs_start is :$P2_START"
parted $FILE --script -- mklabel msdos
parted $FILE --script -- mkpart primary fat32 ${P1_START}s ${P1_END}s
parted $FILE --script -- mkpart primary ext4 ${P2_START}s -1
parted $FILE --script -- quit

# mount
echo -e "$Color_Green挂载分区...$Color_End"
loopdevice_dst=`losetup -f --show $FILE` 
echo "loop分区在 $loopdevice_dst"
PART_BOOT="/dev/dm-0"
PART_ROOT="/dev/dm-1"
sleep 1
device_dst=`kpartx -va $loopdevice_dst | sed -E 's/.*(loop[0-9])p.*/\1/g' | head -1`
sleep 1
device_dst="/dev/mapper/${device_dst}"
sleep 1
mkfs.vfat ${device_dst}p1 -n boot 
sleep 1
mkfs.ext4 ${device_dst}p2 -L rootfs
sleep 1

# 复制文件到img
echo -e "$Color_Green复制文件到img...$Color_End"
echo "备份分区 /dev/boot"
dst_boot_path=$BACK_UP_DIR/dst_boot
mkdir  $dst_boot_path
mount -t vfat ${device_dst}p1 $dst_boot_path 
cp -rfp /boot/* $dst_boot_path
echo "备份boot完成"
echo "备份分区 /dev/root"
dst_root_path=$BACK_UP_DIR/dst_root
mkdir  $dst_root_path
sleep 1
mount -t ext4 ${device_dst}p2 $dst_root_path
cd $dst_root_path
chmod 777 $dst_root_path/
#通过rsync复制根目录文件到IMG镜像中,排除了一些不需要同步的文件
rsync -ax --info=progress2 --no-inc-recursive \
    --exclude="$FILE" \
    --exclude=$BACK_UP_DIR  \
    --exclude=$BACKUP_DIR/$0  \
    --exclude=/sys/* \
    --exclude=/proc/*  \
    --exclude=/tmp/* /  $dst_root_path/
echo "备份root完成"

# 设置自动扩展空间
echo -e "$Color_Green设置自动扩展空间 ...$Color_End"
sed -i 's/exit 0/sudo bash \/expand-rootfs.sh \&/' $dst_root_path/etc/rc.local 
echo "exit 0" >> $dst_root_path/etc/rc.local 
cat > $dst_root_path/expand-rootfs.sh << EOF
#!/bin/bash
sed -i '/sudo bash \/expand-rootfs.sh &/d' /etc/rc.local 
rm "\`pwd\`/\$0"
echo -e "\033[33m两秒后扩展分区空间!\033[0m"
sleep 2
raspi-config --expand-rootfs
echo -e "\033[33my一秒后重启系统!\033[0m"
sleep 1
reboot
EOF


#返回目录 $BACKUP_DIR
cd $BACKUP_DIR
sync

#替换PARTUUID 这步非常重要,liunx启动时会对PARTUUID有特定的指定,备份的时候是把旧的也同步过来,需要根据新的IMG文件来更新PARTUUID
echo -e "$Color_Green替换PARTUUID ...$Color_End"
opartuuidb=`blkid -o export /dev/mmcblk0p1 | grep PARTUUID`
opartuuidr=`blkid -o export /dev/mmcblk0p2| grep PARTUUID`
npartuuidb=`blkid -o export ${device_dst}p1 | grep PARTUUID`
npartuuidr=`blkid -o export ${device_dst}p2 | grep PARTUUID`
echo "BOOT uuid $opartuuidb 替换为 $npartuuidb"
echo "ROOT uuid $opartuuidr 替换为 $npartuuidr"
sed -i "s/$opartuuidr/$npartuuidr/g" $dst_boot_path/cmdline.txt
sed -i "s/$opartuuidb/$npartuuidb/g" $dst_root_path/etc/fstab
sed -i "s/$opartuuidr/$npartuuidr/g" $dst_root_path/etc/fstab

#清理释放装载的文件夹
echo -e "$Color_Green清理释放装载的文件夹...$Color_End"
umount $dst_boot_path
umount $dst_root_path
kpartx -d ${device_dst}p1
kpartx -d ${device_dst}p2
kpartx -d $loopdevice_dst 
losetup -d $loopdevice_dst   
rm -rf  $dst_boot_path
rm -rf  $dst_root_path
chmod 766 $FILE
mv $FILE $BACKUP_DIR
rm -rf $BACK_UP_DIR

echo -e "$Color_Green备份完成。$Color_End"
exit 0

给文件权限并执行

chmod +x backup.sh
sudo bash backup.sh

执行后将在当前路径生成备份文件:raspi-backup.img

参考链接:https://github.com/mghcool/Raspberry-backup

另一种方案(未验证)

https://github.com/nanhantianyi/rpi-backup

处理大文件无法拷贝传输问题

磁盘需要格式化为exFAT32格式,其它格式未验证,但fat32对于超过4g的文件肯定不行

https://linux.cn/article-11682-1.html

树莓派侧执行文件分割命令

# split -b1000M raspi-backup.img
split -b1G --verbose raspi-backup.img rpibackup.

远程侧执行如下指令

rsync "pi@192.168.1.2:/home/pi/rpibackup.??" /Volumes/BACKUPDISK/mixdiy.com/backup 

传输完成后执行合并指令

cat rpibackup.?? > raspi-backup.img

如遇权限问题可执行以下命令

sudo sh -c 'rpibackup.?? > raspi-backup.img'

注意:

如果将系统盘格式化为exfat32再烧入镜像,则系统无法启动

树莓派系统备份为img的最好方法

http://blog.dngz.net/813.htm

首先,不要用Win32DiskImager备份,不然备份出来文件超大。

用下面这个脚本备份文件才比实际使用空间大一点点。

将如下脚本保存为/tmp/back2img.sh

#!/bin/bash

if [ `whoami` != "root" ];then
    echo "This script must be run as root!"
    exit 1
fi

# install software
#apt update
apt install -y dosfstools parted kpartx rsync

echo ""
echo "software is ready"

file="rpi-`date +%Y%m%d%H%M%S`.img"

if [ "x$1" != "x" ];then
    file="$1"
fi

# boot mount point
boot_mnt=`findmnt -n /dev/mmcblk0p1 | awk '{print $1}'`

root_info=`df -PT / | tail -n 1`

root_type=`echo $root_info | awk '{print $2}'`

dr=`echo $root_info | awk '{print $4}'`
db=`df -P | grep /dev/mmcblk0p1 | awk '{print $2}'`
ds=`echo $dr $db |awk '{print int(($1+$2)*1.2)}'`

echo "create $file ..."

dd if=/dev/zero of=$file bs=1K count=0 seek=$ds
#truncate -s ${ds}k $file

start=`fdisk -l /dev/mmcblk0| awk 'NR==9 {print $2}'`
end=`fdisk -l /dev/mmcblk0| awk 'NR==9 {print $3}'`

if [ "$start" == "*" ];then
    start=`fdisk -l /dev/mmcblk0| awk 'NR==9 {print $3}'`
    end=`fdisk -l /dev/mmcblk0| awk 'NR==9 {print $4}'`
fi

start=`echo $start's'`
end=`echo $end's'`

end2=`fdisk -l /dev/mmcblk0| awk 'NR==10 {print $2}'`
end2=`echo $end2's'`

echo "start=$start"
echo "end=$end"
echo "end2=$end2"

parted $file --script -- mklabel msdos
parted $file --script -- mkpart primary fat32 $start $end
parted $file --script -- mkpart primary ext4 $end2 -1

loopdevice=`losetup -f --show $file`
device=`kpartx -va $loopdevice | sed -E 's/.*(loop[0-9])p.*/\1/g' | head -1`
device="/dev/mapper/${device}"

echo "device=$device"

partBoot="${device}p1"
partRoot="${device}p2"

echo "partBoot=$partBoot"
echo "partRoot=$partRoot"

sleep 5s

opartuuidb=`blkid -o export /dev/mmcblk0p1 | grep PARTUUID`
opartuuidr=`blkid -o export /dev/mmcblk0p2 | grep PARTUUID`

npartuuidb=`blkid -o export ${partBoot} | grep PARTUUID`
npartuuidr=`blkid -o export ${partRoot} | grep PARTUUID`

boot_label=`dosfslabel /dev/mmcblk0p1 | tail -n 1`
root_label=`e2label /dev/mmcblk0p2 | tail -n 1`

mkfs.vfat -F 32 -n "$boot_label" $partBoot
echo "$partBoot format success"

mkfs.ext4 $partRoot
e2label $partRoot $root_label
echo "$partRoot format success"

mount -t vfat $partBoot /mnt
cp -rfp ${boot_mnt}/* /mnt/

sed -i "s/$opartuuidr/$npartuuidr/g" /mnt/cmdline.txt

sync

umount /mnt

mount -t ext4 $partRoot /mnt

if [ -f /etc/dphys-swapfile ]; then
    SWAPFILE=`cat /etc/dphys-swapfile | grep ^CONF_SWAPFILE | cut -f 2 -d=`
    if [ "$SWAPFILE" = "" ]; then
        SWAPFILE=/var/swap
    fi
    EXCLUDE_SWAPFILE="--exclude $SWAPFILE"
fi

cd /mnt

rsync --force -rltWDEgop --delete --stats --progress \
    $EXCLUDE_SWAPFILE \
    --exclude ".gvfs" \
    --exclude "$boot_mnt" \
    --exclude "/dev" \
    --exclude "/media" \
    --exclude "/mnt" \
    --exclude "/proc" \
    --exclude "/run" \
    --exclude "/snap" \
    --exclude "/sys" \
    --exclude "/tmp" \
    --exclude "lost\+found" \
    --exclude "$file" \
    / ./

if [ ! -d $boot_mnt ]; then
    mkdir $boot_mnt
fi

if [ -d /snap ]; then
    mkdir /mnt/snap
fi

for i in boot dev media mnt proc run sys boot; do
    if [ ! -d /mnt/$i ]; then
        mkdir /mnt/$i
    fi
done

if [ ! -d /mnt/tmp ]; then
    mkdir /mnt/tmp
    chmod a+w /mnt/tmp
fi

cd

sed -i "s/$opartuuidb/$npartuuidb/g" /mnt/etc/fstab
sed -i "s/$opartuuidr/$npartuuidr/g" /mnt/etc/fstab

sync

umount /mnt

kpartx -d $loopdevice
losetup -d $loopdevice

用法,直接运行:
cd /tmp/
sudo ./back2img.sh blog.dngz.net.img

注意:如果TF卡剩余空间不够的话就挂载外部硬盘或U盘,将备份的img直接保存在外部磁盘中,并且外部磁盘请挂载到 /media 目录下,不要挂载到 /mnt!
因为脚本会对/mnt目录进行备份操作,会导致不断循环的递归备份,导致磁盘爆满。
总之记住一定不要挂载到/mnt目录!

运行时的备份日志:

Reading package lists... Done
Building dependency tree
Reading state information... Done
dosfstools is already the newest version (4.1-2).
parted is already the newest version (3.2-25).
rsync is already the newest version (3.1.3-6).
The following NEW packages will be installed:
  kpartx
0 upgraded, 1 newly installed, 0 to remove and 12 not upgraded.
Need to get 37.8 kB of archives.
After this operation, 86.0 kB of additional disk space will be used.
Get:1 https://mirrors.tuna.tsinghua.edu.cn/debian buster/main arm64 kpartx arm64 0.7.9-3+deb10u1 [37.8 kB]
Fetched 37.8 kB in 1s (47.2 kB/s)

Selecting previously unselected package kpartx.
(Reading database ... 49050 files and directories currently installed.)
Preparing to unpack .../kpartx_0.7.9-3+deb10u1_arm64.deb ...
Unpacking kpartx (0.7.9-3+deb10u1) ...
Setting up kpartx (0.7.9-3+deb10u1) ...
Processing triggers for man-db (2.8.5-2) ...

software is ready
create blog.dngz.net.img ...
0+0 records in
0+0 records out
0 bytes copied, 0.00126612 s, 0.0 kB/s
start=8192s
end=532479s
end2=532480s
device=/dev/mapper/loop0
partBoot=/dev/mapper/loop0p1
partRoot=/dev/mapper/loop0p2
mkfs.fat 4.1 (2017-01-24)
mkfs.fat: warning - lowercase labels might not work properly with DOS or Windows
/dev/mapper/loop0p1 format success
mke2fs 1.44.5 (15-Dec-2018)
Discarding device blocks: done
Creating filesystem with 739328 4k blocks and 185104 inodes
Filesystem UUID: ed9bf852-cd2f-4c1b-a9f6-d1e00bd20844
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

/dev/mapper/loop0p2 format success
sending incremental file list

rsync同步数据日志省略...

Number of files: 62,378 (reg: 48,951, dir: 5,618, link: 7,809)
Number of created files: 62,376 (reg: 48,951, dir: 5,616, link: 7,809)
Number of deleted files: 0
Number of regular files transferred: 48,951
Total file size: 2,260,121,595 bytes
Total transferred file size: 2,259,998,604 bytes
Literal data: 2,259,998,604 bytes
Matched data: 0 bytes
File list size: 1,703,776
File list generation time: 0.008 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 2,264,157,862
Total bytes received: 990,956

sent 2,264,157,862 bytes  received 990,956 bytes  6,214,400.05 bytes/sec
total size is 2,260,121,595  speedup is 1.00

备份后blog.dngz.net.img 显示3.1G(实际占用2.4G),用7z极限压缩后仅为630M。

https://zhuanlan.zhihu.com/p/66853949#:~:text=如果你有一台树莓派,你可能需要了解一个名为%20PiShrink%20的%20bash,脚本,该脚本可使树莓派镜像更小%E3%80%82%20PiShrink%20将自动缩小镜像,然后在启动时将其调整为%20SD%20卡的最大大小%E3%80%82

安装 PiShrink

要在 Linux 机器上安装 PiShrink,请先使用以下命令下载最新版本:

$ wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh

接下来,将下载的 PiShrink 变成二进制可执行文件:

$ chmod +x pishrink.sh

最后,移动到目录:

$ sudo mv pishrink.sh /usr/local/bin/

使树莓派镜像更小

你可能已经知道,Raspbian 是所有树莓派型号的官方操作系统。树莓派基金会为 PC 和 Mac 开发了树莓派桌面版本。你可以创建一个 live CD,并在虚拟机中运行它,甚至也可以将其安装在桌面上。树莓派也有少量非官方​​操作系统镜像。为了测试,我从官方下载页面下载了官方的 Raspbian 系统。

解压下载的系统镜像:

$ unzip 2019-04-08-raspbian-stretch-lite.zip

上面的命令将提取当前目录中 2019-04-08-raspbian-stretch-lite.zip 文件的内容。

让我们看下提取文件的实际大小:

$ du -h 2019-04-08-raspbian-stretch-lite.img
1.7G 2019-04-08-raspbian-stretch-lite.img

如你所见,提取的树莓派系统镜像大小为 1.7G。

现在,使用 PiShrink 缩小此文件的大小,如下所示:

$ sudo pishrink.sh 2019-04-08-raspbian-stretch-lite.img

示例输出:

Creating new /etc/rc.local
rootfs: 39795/107072 files (0.1% non-contiguous), 239386/428032 blocks
resize2fs 1.45.0 (6-Mar-2019)
resize2fs 1.45.0 (6-Mar-2019)
Resizing the filesystem on /dev/loop1 to 280763 (4k) blocks.
Begin pass 3 (max = 14)
Scanning inode table XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Begin pass 4 (max = 3728)
Updating inode references XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
The filesystem on /dev/loop1 is now 280763 (4k) blocks long.

Shrunk 2019-04-08-raspbian-stretch-lite.img from 1.7G to 1.2G

正如你在上面的输出中看到的,树莓派镜像的大小已减少到 1.2G。

你还可以使用 -s 标志跳过该过程的自动扩展部分。

$ sudo pishrink.sh -s 2019-04-08-raspbian-stretch-lite.img newpi.img

这将创建一个源镜像文件(即 2019-04-08-raspbian-stretch-lite.img)的副本到一个新镜像文件(newpi.img)并进行处理。有关更多详细信息,请查看最后给出的官方 GitHub 页面。

]]>
1334 0 0 0
<![CDATA[mix backup files]]> http://mixdiy.com/?p=1338 Sun, 05 Jun 2022 12:28:29 +0000 http://mixdiy.com/?p=1338

http://mixdiy.com/wp-content/uploads/raspi-backup.img

]]>
1338 0 0 0
<![CDATA[piaware]]> http://mixdiy.com/index.php/2022/06/10/piaware/ Fri, 10 Jun 2022 03:53:42 +0000 http://mixdiy.com/?p=1345

Howto Install Piaware 3.8.1 on Ubuntu Server 20.04 arm64 / Pi4 - FlightAware / ADS-B Flight Tracking - FlightAware Discussions


https://github.com/abcd567a/piaware-ubuntu20-amd64/blob/master/README.md

Automated Installation of piaware 7.2, dump1090-fa 7.2, piaware-web 7.2, and dump978-fa 7.2 (by building packages from source code), on following OS:
(1) Ubuntu 20 - amd64
(2) Debian 11 - amd64
(3) Kali-linux 2021 - amd64
(4) On RPI Model 3 & 4 (32-bit & 64-bit / armv7l & aarch64) Raspberry Pi OS Bullseye, DietPi OS Bullseye, Ubuntu-20 for RPi, and Kali-2021 for RPi

(1) DUMP1090-FA
Copy-paste following command in SSH console and press Enter key. The script will install dump1090-fa.

sudo bash -c "$(wget -O - https://raw.githubusercontent.com/abcd567a/piaware-ubuntu20-amd64/master/install-dump1090-fa.sh)"


(2) PIAWARE
Copy-paste following command in SSH console and press Enter key. The script will install piaware.

sudo bash -c "$(wget -O - https://raw.githubusercontent.com/abcd567a/piaware-ubuntu20-amd64/master/install-piaware.sh)"


(3) PIAWARE-WEB
Copy-paste following command in SSH console and press Enter key. The script will install piaware-web.

sudo bash -c "$(wget -O - https://raw.githubusercontent.com/abcd567a/piaware-ubuntu20-amd64/master/install-piaware-web.sh)"


(4) DUMP978-FA (For USA ONLY. Requires 2nd Dongle)
If you want to receive both ES1090 and UAT978, then two dongles are required, one for 1090 MHz and other for 978 MHz.

(4.1) Copy-paste following command in SSH console and press Enter key. The script will install dump978-fa and skyaware978:
sudo bash -c "$(wget -O - https://raw.githubusercontent.com/abcd567a/piaware-ubuntu20-amd64/master/install-dump978-fa.sh)"


(4.2) Serialize dongles as follows
If you want to receive both ES1090 and UAT978, then two dongles are required, one for 1090 MHz and other for 978 MHz. In this case you will have to serialize dongles so that correct dongle+antenna sets are used by dump1090-fa and dump978-fa.


For 1090 Mhz dongle: use serial # 00001090
For 978 Mhz dongle : use serial # 00000978


(4.1.1) Issue following command to install serialization software:
sudo apt install rtl-sdr

(4.1.2) Unplug ALL DVB-T dongles from RPi

(4.1.3) Plugin only that DVB-T dongle which you want to use for dump1090-fa. All other dongles should be unplugged.

(4.1.4) Issue following command. Say yes when asked for confirmation to chang serial number.
rtl_eeprom -s 00001090

(4.1.5) Unplug 1090 dongle

(4.1.6) Plugin only that DVB-T dongle which you want to use for dump978-fa. All other dongles should be unplugged.

(4.1.7) Issue following command. Say yes when asked for confirmation to chang serial number.
rtl_eeprom -s 00000978

(4.1.8) Unplug 978 dongle

IMPORTANT: After completing above commands, unplug and then replug both dongles.

(4.3) - Configure dump1090-fa & dump978-fa to use dongles of assigned serial numbers
sudo sed -i 's/^RECEIVER_SERIAL=.*/RECEIVER_SERIAL=00001090/' /etc/default/dump1090-fa  
sudo sed -i 's/driver=rtlsdr[^ ]* /driver=rtlsdr,serial=00000978 /' /etc/default/dump978-fa  
(4.4) - Reboot so that dump1090-fa & dump978-fa can pick their assigned dongles at boot
sudo reboot
 
]]>
1345 0 0 0
<![CDATA[piaware in raspberrypi]]> http://mixdiy.com/index.php/2022/06/11/piaware-in-raspberrypi/ Fri, 10 Jun 2022 17:12:07 +0000 http://mixdiy.com/?p=1350

https://github.com/flightaware/piaware/blob/master/Building-and-installing-PiAware-from-source.mediawiki

Table of Contents
Upgrade Raspberry Pi software to the latest
Obtain, build and install the RTL-SDR libraries
Obtain, build and install dump1090
Obtain, build and install piaware
Upgrade Raspberry Pi software to the latest
 sudo apt-get update
 sudo apt-get upgrade
 sudo rpi-update
If rpi-update reported that it downloaded firmware and said you need to reboot for it to take effect, reboot:

 sudo reboot
Obtain, build and install the RTL-SDR libraries
This is the software that can talk to the software defined radio in the RTL-SDR dongle...

 sudo apt-get install git git-core cmake libusb-1.0-0-dev build-essential
 mkdir src
 cd src
 git clone git://git.osmocom.org/rtl-sdr.git
 cd rtl-sdr
 mkdir build
 cd build
 cmake ../ -DINSTALL_UDEV_RULES=ON
 make
 sudo make install
 sudo ldconfig
 sudo cp rtl-sdr.rules /etc/udev/rules.d
Keep the system from thinking that the RTL-SDR dongle is for watching TV... cat </tmp/no-rtl.conf

 blacklist dvb_usb_rtl28xxu
 blacklist rtl2832
 blacklist rtl2830
 EOF
 sudo mv /tmp/no-rtl.conf /etc/modprobe.d/
 sudo reboot
Obtain, build and install dump1090
This is for the FlightAware version. The main difference is that the FA version of dump1090 can emit "FlightAware" format directly, avoiding piaware having to run the faup1090 translation program. Other than that it's the same as dump1090 from Malcolm Robb.

Should you want to build the FlightAware version, please follow the build/install instructions at https://github.com/flightaware/dump1090_mr/blob/master/README.md#building

Obtain, build and install piaware
Follow the instructions for building and installing PiAware from source at https://github.com/flightaware/piaware#building-and-installing-piaware-from-source
Table of ContentsUpgrade Raspberry Pi software to the latestObtain, build and install the RTL-SDR librariesObtain, build and install dump1090Obtain, build and install piaware

install

Upgrade Raspberry Pi software to the latest

 sudo apt-get update
 sudo apt-get upgrade
 sudo rpi-update

If rpi-update reported that it downloaded firmware and said you need to reboot for it to take effect, reboot:

 sudo reboot

Obtain, build and install the RTL-SDR libraries

This is the software that can talk to the software defined radio in the RTL-SDR dongle...

 sudo apt-get install git git-core cmake libusb-1.0-0-dev build-essential
 mkdir src
 cd src
 git clone git://git.osmocom.org/rtl-sdr.git
 cd rtl-sdr
 mkdir build
 cd build
 cmake ../ -DINSTALL_UDEV_RULES=ON
 make
 sudo make install
 sudo ldconfig
 sudo cp rtl-sdr.rules /etc/udev/rules.d

Keep the system from thinking that the RTL-SDR dongle is for watching TV... cat </tmp/no-rtl.conf

 blacklist dvb_usb_rtl28xxu
 blacklist rtl2832
 blacklist rtl2830
 EOF
 sudo mv /tmp/no-rtl.conf /etc/modprobe.d/
 sudo reboot

Obtain, build and install dump1090

This is for the FlightAware version. The main difference is that the FA version of dump1090 can emit "FlightAware" format directly, avoiding piaware having to run the faup1090 translation program. Other than that it's the same as dump1090 from Malcolm Robb.

Should you want to build the FlightAware version, please follow the build/install instructions at https://github.com/flightaware/dump1090_mr/blob/master/README.md#building

Obtain, build and install piaware

Follow the instructions for building and installing PiAware from source at https://github.com/flightaware/piaware#building-and-installing-piaware-from-source

https://zh.flightaware.com/adsb/piaware/build/

树莓派安装piware

https://zh.flightaware.com/adsb/piaware/install

您想将您的Raspberry Pi与 dump1090 链接到 FlightAware 吗?

如果您在 Raspberry Pi 上运行带有 dump1090 的 ADS-B 接收器,那么您可以安装 PiAware 软件包以将您的 ADS-B 接收器数据传输到 FlightAware。

PiAware 用户可以同时使用运行 dump1090 的 Raspberry Pi 中的数据,还可以通过 PiAware 将飞行数据发送到 FlightAware。与 FlightAware 共享数据的用户自动有资格免费升级到企业帐户。

什么是 PiAware?

PiAware 是一个 FlightAware 客户端程序,在 Raspberry Pi 上运行,可将 dump1090 ADS-B 和 Mode S 数据安全地传输到 FlightAware。PiAware 适用于已经使用 ADS-B 接收器和 dump1090 运行自己的 Raspberry Pi 的人。

上手又快又容易。

使用下面的简单步骤,您可以配置您的 Raspberry Pi 以提供 FlightAware。该过程需要两到三分钟。

已经在运行 PiAware?

查看

PiAware 升级页面以更新到最新版本。

1需要什么

您应该已经安装了 Raspbian 的 Raspberry Pi,并且对使用它有一定的了解。

如果您不熟悉使用 Pi 或需要从头开始构建接收器,请参阅这些说明!

2下载并安装 PiAware

下载并安装 PiAware 存储库包,它会告诉您的 Pi 的包管理器 (apt) 除了 Raspbian 提供的包之外,如何找到 FlightAware 的软件包。

对于在Raspbian Bullseye OS 上运行的 Raspberry Pi ,请从命令行执行以下命令:

wget https://zh.flightaware.com/adsb/piaware/files/packages/pool/piaware/p/piaware-support/piaware-repository_7.2_all.deb
sudo dpkg -i piaware-repository_7.2_all.deb

对于在

Raspbian Buster OS上运行的 Raspberry Pi ,请单击

此处 这将在您的 Raspberry Pi 上下载并安装 PiAware 和所需的依赖项。

sudo apt-get update
sudo apt-get install piaware

这将启用自动和手动(基于 Web,根据您的请求)PiAware 软件更新。默认情况下禁用这些更新。要禁用更新,请跳过此步骤。

sudo piaware-config allow-auto-updates yes
sudo piaware-config allow-manual-updates yes

3下载并安装 dump1090

如果您还没有安装dump1090等ADS-B接收机软件,那么您可以通过执行以下命令安装FlightAware的dump1090版本。

sudo apt-get install dump1090-fa
4下载并安装dump978
sudo apt-get install dump978-fa

有关为 978 UAT 配置接收器的更多说明,请访问我们的

高级配置页面。

5重启你的 Pi

完成安装和配置软件包后,重新启动 Raspberry Pi 以确保一切正常启动。

sudo reboot
6在 FlightAware.com上绑定您的PiAware客户端。

您可能需要等待 PiAware 启动大约四或五分钟,然后可以将您的 PiAware 设备关联您的 FlightAware 帐户来获取免费的企业级账号。

在 FlightAware.com上绑定您的PiAware客户端。

如果5分钟后您的设备还未显示为已绑定,请重新启动设备,如果这仍然不起作用,请重新确认Wi-Fi设置(如果使用Wi-Fi)正确无误。最后,您还可以通过ADSBsupport@FlightAware.com与我们联系。请检查您的统计信息页面(见以下链接)以确认其是否绑定。

7查看您的ADS-B统计数据
  1. 查看您的 ADS-B 数据统计:https://zh.flightaware.com/adsb/stats
    • FlightAware会立即处理您的数据,并在30分钟内显示统计信息。
    • 或者在登录以后点击标题栏里的“我的ADS-B”。这个链接只会在站点开始发送数据30分钟以后才会显示。
  2. 通过点击位于站点名称右侧的齿轮图标,在统计信息页面配置您的位置和天线高度。
    • 多点定位,也称为MLAT,可以使用从飞机接收消息的站点的位置来精确定位飞机的位置。
  3. FlightAware统计页面还会显示您设备的本地IP地址,并提供用于直接连接的链接。在这里您可以点击SkyAware的链接并通过这个门户网站在地图上查看接收器正在接收消息的航班。

相关链接 (PiAware, Raspberry Pi, dump1090, and more)

您想将您的Raspberry Pi与 dump1090 链接到 FlightAware 吗?
如果您在 Raspberry Pi 上运行带有 dump1090 的 ADS-B 接收器,那么您可以安装 PiAware 软件包以将您的 ADS-B 接收器数据传输到 FlightAware。

PiAware 用户可以同时使用运行 dump1090 的 Raspberry Pi 中的数据,还可以通过 PiAware 将飞行数据发送到 FlightAware。与 FlightAware 共享数据的用户自动有资格免费升级到企业帐户。

什么是 PiAware?
PiAware 是一个 FlightAware 客户端程序,在 Raspberry Pi 上运行,可将 dump1090 ADS-B 和 Mode S 数据安全地传输到 FlightAware。PiAware 适用于已经使用 ADS-B 接收器和 dump1090 运行自己的 Raspberry Pi 的人。

上手又快又容易。
使用下面的简单步骤,您可以配置您的 Raspberry Pi 以提供 FlightAware。该过程需要两到三分钟。
已经在运行 PiAware?
查看PiAware 升级页面以更新到最新版本。

1需要什么
您应该已经安装了 Raspbian 的 Raspberry Pi,并且对使用它有一定的了解。

如果您不熟悉使用 Pi 或需要从头开始构建接收器,请参阅这些说明!

树莓派
已安装Raspbian Bullseye 或 Bullseye Lite操作系统
网络连接
通过网络或使用连接的键盘和显示器访问 Pi 上的命令行 shell
必须在 Raspberry Pi 上启用 SSH 访问才能进行远程访问。有关启用 SSH 的信息,请参阅Raspberry Pi SSH 文档。
2下载并安装 PiAware
下载并安装 PiAware 存储库包,它会告诉您的 Pi 的包管理器 (apt) 除了 Raspbian 提供的包之外,如何找到 FlightAware 的软件包。

对于在Raspbian Bullseye OS 上运行的 Raspberry Pi ,请从命令行执行以下命令:

wget https://zh.flightaware.com/adsb/piaware/files/packages/pool/piaware/p/piaware-support/piaware-repository_7.2_all.deb
sudo dpkg -i piaware-repository_7.2_all.deb

对于在Raspbian Buster OS上运行的 Raspberry Pi ,请单击此处 这将在您的 Raspberry Pi 上下载并安装 PiAware 和所需的依赖项。



sudo apt-get update
sudo apt-get install piaware

这将启用自动和手动(基于 Web,根据您的请求)PiAware 软件更新。默认情况下禁用这些更新。要禁用更新,请跳过此步骤。

sudo piaware-config allow-auto-updates yes
sudo piaware-config allow-manual-updates yes

3下载并安装 dump1090
如果您还没有安装dump1090等ADS-B接收机软件,那么您可以通过执行以下命令安装FlightAware的dump1090版本。


sudo apt-get install dump1090-fa

4下载并安装dump978

sudo apt-get install dump978-fa

有关为 978 UAT 配置接收器的更多说明,请访问我们的高级配置页面。
5重启你的 Pi
完成安装和配置软件包后,重新启动 Raspberry Pi 以确保一切正常启动。


sudo reboot

6在 FlightAware.com上绑定您的PiAware客户端。
您可能需要等待 PiAware 启动大约四或五分钟,然后可以将您的 PiAware 设备关联您的 FlightAware 帐户来获取免费的企业级账号。

在 FlightAware.com上绑定您的PiAware客户端。
如果5分钟后您的设备还未显示为已绑定,请重新启动设备,如果这仍然不起作用,请重新确认Wi-Fi设置(如果使用Wi-Fi)正确无误。最后,您还可以通过ADSBsupport@FlightAware.com与我们联系。请检查您的统计信息页面(见以下链接)以确认其是否绑定。

7查看您的ADS-B统计数据
查看您的 ADS-B 数据统计:https://zh.flightaware.com/adsb/stats
FlightAware会立即处理您的数据,并在30分钟内显示统计信息。
统计页面
或者在登录以后点击标题栏里的“我的ADS-B”。这个链接只会在站点开始发送数据30分钟以后才会显示。
页眉中的我的 ADS-B
通过点击位于站点名称右侧的齿轮图标,在统计信息页面配置您的位置和天线高度。
多点定位,也称为MLAT,可以使用从飞机接收消息的站点的位置来精确定位飞机的位置。
统计页面齿轮图标位置
FlightAware统计页面还会显示您设备的本地IP地址,并提供用于直接连接的链接。在这里您可以点击SkyAware的链接并通过这个门户网站在地图上查看接收器正在接收消息的航班。
统计页面 SkyAware 链接位置
FlightAware PiAware:SkyAware ADS-B 软件网页界面
相关链接 (PiAware, Raspberry Pi, dump1090, and more)
]]>
1350 0 0 0
<![CDATA[树莓派wordpress网站无法解析 php文件问题]]> http://mixdiy.com/index.php/2022/06/14/wordpress-php-not-world/ Tue, 14 Jun 2022 04:43:46 +0000 http://mixdiy.com/?p=1356

树莓派最新64位系统安装wordpress后发现apache2可以正常运行,但无法解析php,网站显示的是php源代码

解决方案:
1.安装lib文件
sudo apt-get install libapache2-mod-php7.4 #大版本保持一致就好
2.修改配置文件

cd /etc/apache2/mods-enabled
sudo nano php7.4.conf #这个地方改成你对应的版本


在set handler 里面的require all denied改为granted
以及下面的 php|ar一串正则也改为granted

3.重启apche2

sudo service apache2 restart

]]>
1356 0 0 0
<![CDATA[test table]]> http://mixdiy.com/?p=1382 Wed, 15 Jun 2022 01:50:18 +0000 http://mixdiy.com/?p=1382

测试1

是;康健地方;老师的军方手机发送;来开发建设大雷开发及森林大可;附加;萨里看到飞机东莞东莞的防晒乳

测试2

是;康健地方;老师的军方手机发送;来开发建设大雷开发及森林大可;附加;萨里看到飞机东莞东莞的防晒乳

测试3

是;康健地方;老师的军方手机发送;来开发建设大雷开发及森林大可;附加;萨里看到飞机东莞东莞的防晒乳

测试4

测试5

测试1

测试1

]]>
1382 0 0 0
<![CDATA[测试目录]]> http://mixdiy.com/?p=1384 Wed, 15 Jun 2022 01:54:06 +0000 http://mixdiy.com/?p=1384

cal

是;康健地方;老师的军方手机发送;来开发建设大雷开发及森林大可;附加;萨里看到飞机东莞东莞的防晒乳

莉莉

是;康健地方;老师的军方手机发送;来开发建设大雷开发及森林大可;附加;萨里看到飞机东莞东莞的防晒乳

]]>
1384 0 0 0
<![CDATA[yrdy]]> http://mixdiy.com/?p=1387 Wed, 15 Jun 2022 01:55:49 +0000 http://mixdiy.com/?p=1387

测试1

是;康健地方;老师的军方手机发送;来开发建设大雷开发及森林大可;附加;萨里看到飞机东莞东莞的防晒乳

测试2

是;康健地方;老师的军方手机发送;来开发建设大雷开发及森林大可;附加;萨里看到飞机东莞东莞的防晒乳

]]>
1387 0 0 0
<![CDATA[set power flag led]]> http://mixdiy.com/index.php/2022/06/15/set-power-flag-led/ Wed, 15 Jun 2022 06:33:35 +0000 http://mixdiy.com/?p=1407

https://blog.csdn.net/zhufu86/article/details/99704074

怎样在树莓派系统启动时,就改变某一个GPIO为上拉或者下拉,现在我就来举例说明。

我有一个树莓派2B,安装的Raspbian系统。

首先确保安装了wiringpi和device-tree-compiler。

pi@raspberrypi:~ $ sudo apt-get install wiringpi device-tree-compiler
Reading package lists… Done
Building dependency tree
Reading state information… Done
device-tree-compiler is already the newest version (1.4.7-3+rpt1).
wiringpi is already the newest version (2.50).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
1
2
3
4
5
6
7
然后查看wringpi的版本,以及系统硬件的情况。

pi@raspberrypi:~ $ gpio -v
gpio version: 2.50
Copyright (c) 2012-2018 Gordon Henderson
This is free software with ABSOLUTELY NO WARRANTY.
For details type: gpio -warranty

Raspberry Pi Details:
Type: Pi 2, Revision: 01, Memory: 1024MB, Maker: Embest

  • Device tree is enabled.
    *--> Raspberry Pi 2 Model B Rev 1.1
  • This Raspberry Pi supports user-level GPIO access.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    使用gpio readall可以读出所有GPIO的状态。

pi@raspberrypi:~ $ gpio readall
+-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+
| BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM |
+-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
| | | 3.3v | | | 1 || 2 | | | 5v | | |
| 2 | 8 | SDA.1 | IN | 1 | 3 || 4 | | | 5v | | |
| 3 | 9 | SCL.1 | IN | 1 | 5 || 6 | | | 0v | | |
| 4 | 7 | GPIO. 7 | IN | 1 | 7 || 8 | 1 | ALT0 | TxD | 15 | 14 |
| | | 0v | | | 9 || 10 | 1 | ALT0 | RxD | 16 | 15 |
| 17 | 0 | GPIO. 0 | IN | 0 | 11 || 12 | 0 | IN | GPIO. 1 | 1 | 18 |
| 27 | 2 | GPIO. 2 | IN | 0 | 13 || 14 | | | 0v | | |
| 22 | 3 | GPIO. 3 | IN | 0 | 15 || 16 | 0 | IN | GPIO. 4 | 4 | 23 |
| | | 3.3v | | | 17 || 18 | 0 | IN | GPIO. 5 | 5 | 24 |
| 10 | 12 | MOSI | IN | 0 | 19 || 20 | | | 0v | | |
| 9 | 13 | MISO | IN | 0 | 21 || 22 | 0 | IN | GPIO. 6 | 6 | 25 |
| 11 | 14 | SCLK | IN | 0 | 23 || 24 | 1 | IN | CE0 | 10 | 8 |
| | | 0v | | | 25 || 26 | 1 | IN | CE1 | 11 | 7 |
| 0 | 30 | SDA.0 | IN | 1 | 27 || 28 | 1 | IN | SCL.0 | 31 | 1 |
| 5 | 21 | GPIO.21 | IN | 1 | 29 || 30 | | | 0v | | |
| 6 | 22 | GPIO.22 | IN | 1 | 31 || 32 | 0 | IN | GPIO.26 | 26 | 12 |
| 13 | 23 | GPIO.23 | IN | 0 | 33 || 34 | | | 0v | | |
| 19 | 24 | GPIO.24 | IN | 0 | 35 || 36 | 0 | IN | GPIO.27 | 27 | 16 |
| 26 | 25 | GPIO.25 | IN | 0 | 37 || 38 | 0 | IN | GPIO.28 | 28 | 20 |
| | | 0v | | | 39 || 40 | 0 | IN | GPIO.29 | 29 | 21 |
+-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
| BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM |
+-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
可以看出,GPIO#6,13,19,26(BCM编号)的默认状态如下表:(GPIO的硬件接口全部悬空的情况下)

BCM GPIO # 硬件接口 # 输入输出 上下拉 值
6 31 输入 未知 1
13 33 输入 未知 0
19 35 输入 未知 0
26 37 输入 未知 0
4个GPIO都是默认输入口,值为1的可能是上拉,值为0的可能是下拉。(只是可能)

现在我要尝试把这4个GPIO的状态改变为下表所示:

BCM GPIO # 硬件接口 # 输入输出 上下拉 值
6 31 输入 下拉 0
13 33 输入 上拉 1
19 35 输出 n.a. X
26 37 输出 n.a. X
新建一个文件mygpio-overlay.dts并编辑为如下内容:

/dts-v1/;
/plugin/;

/ {
compatible = "brcm,bcm2708";

fragment@0 {
    target = <&gpio>;
    __overlay__ {
        pinctrl-names = "default";
        pinctrl-0 = <&my_pins>;

        my_pins: my_pins {
            brcm,pins = <6 13 19 26>;     /* gpio no. */
            brcm,function = <0 0 1 1>; /* 0:in, 1:out */
            brcm,pull = <1 2 1 2>;     /* 2:up 1:down 0:none */
        };
    };
};

};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
brcm,pins = <6 13 19 26>; /* gpio no. / 一行说明要修改那几个GPIO brcm,function = <0 0 1 1>; / 0:in, 1:out / 一行为要设定的输入输出状态,0输入,1输出 brcm,pull = <1 2 1 2>; / 2:up 1:down 0:none */
一行为要设定的上拉下拉状态,2上拉,1下拉,0为悬空
我把输出也设了上拉和下拉,测试看看是什么效果。

然后将mygpio-overlay.dts文件编译为mygpio-overlay.dtb,并拷贝到/boot/overlays/目录

pi@raspberrypi:~ $ dtc -@ -I dts -O dtb -o mygpio-overlay.dtb mygpio-overlay.dts
pi@raspberrypi:~ $ sudo cp mygpio-overlay.dtb /boot/overlays/
pi@raspberrypi:~ $ sudo vi /boot/config.txt
1
2
3
编辑/boot/config.txt文件,在[pi4]部分之前加入一行device_tree_overlay=overlays/mygpio-overlay.dtb

device_tree_overlay=overlays/mygpio-overlay.dtb

[pi4]
1
2
3
如果没有[pi4],就直接在文件末尾加入这一行好了。

重启后,运行gpio readall看看。

pi@raspberrypi:~ $ gpio readall
+-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+
| BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM |
+-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
| | | 3.3v | | | 1 || 2 | | | 5v | | |
| 2 | 8 | SDA.1 | IN | 1 | 3 || 4 | | | 5v | | |
| 3 | 9 | SCL.1 | IN | 1 | 5 || 6 | | | 0v | | |
| 4 | 7 | GPIO. 7 | IN | 1 | 7 || 8 | 1 | ALT0 | TxD | 15 | 14 |
| | | 0v | | | 9 || 10 | 1 | ALT0 | RxD | 16 | 15 |
| 17 | 0 | GPIO. 0 | IN | 0 | 11 || 12 | 0 | IN | GPIO. 1 | 1 | 18 |
| 27 | 2 | GPIO. 2 | IN | 0 | 13 || 14 | | | 0v | | |
| 22 | 3 | GPIO. 3 | IN | 0 | 15 || 16 | 0 | IN | GPIO. 4 | 4 | 23 |
| | | 3.3v | | | 17 || 18 | 0 | IN | GPIO. 5 | 5 | 24 |
| 10 | 12 | MOSI | IN | 0 | 19 || 20 | | | 0v | | |
| 9 | 13 | MISO | IN | 0 | 21 || 22 | 0 | IN | GPIO. 6 | 6 | 25 |
| 11 | 14 | SCLK | IN | 0 | 23 || 24 | 1 | IN | CE0 | 10 | 8 |
| | | 0v | | | 25 || 26 | 1 | IN | CE1 | 11 | 7 |
| 0 | 30 | SDA.0 | IN | 1 | 27 || 28 | 1 | IN | SCL.0 | 31 | 1 |
| 5 | 21 | GPIO.21 | IN | 1 | 29 || 30 | | | 0v | | |
| 6 | 22 | GPIO.22 | IN | 0 | 31 || 32 | 0 | IN | GPIO.26 | 26 | 12 |
| 13 | 23 | GPIO.23 | IN | 1 | 33 || 34 | | | 0v | | |
| 19 | 24 | GPIO.24 | OUT | 0 | 35 || 36 | 0 | IN | GPIO.27 | 27 | 16 |
| 26 | 25 | GPIO.25 | OUT | 0 | 37 || 38 | 0 | IN | GPIO.28 | 28 | 20 |
| | | 0v | | | 39 || 40 | 0 | IN | GPIO.29 | 29 | 21 |
+-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
| BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM |
+-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
可以发现6,13,19,26四个GPIO的状态已经发生了变化,6的值变为0(应该是下拉了),13的值变为1(应该是上拉了)。另外可以看出,输出口指定上拉下拉是无效的,默认输出都是0。

举一反三,可以按照这个方法修改其他GPIO的状态。

【参考资料】
https://pinout.xyz
https://github.com/fivdi/onoff/wiki/Enabling-Pullup-and-Pulldown-Resistors-on-The-Raspberry-Pi

在config.txt里用gpio语法就可以给io配置的。
例如将GPIO18设为输出并置为低电平,写法如下:
gpio=18=op,dl

https://www.csdn.net/tags/OtDaAg2sMjIyMDQtYmxvZwO0O0OO0O0O.html

该指令允许在启动时将 GPIO 引脚设置为特定模式和值,这在以前需要自定义文件时是必需的。每行将相同的设置(或至少进行相同的更改)应用于一组引脚,可以是单个引脚 ()、引脚范围 (),也可以是逗号分隔的任意一个 ()。PIN 集后跟此列表中的一个或多个逗号分隔属性:gpiodt-blob.bin33-43-4,6,8=

ip- 输入

op- 输出

a0-a5- Alt0-Alt5

dh- 高电平驱动(用于输出)

dl- 低电平驱动(用于输出)

pu- 上拉

pd- 下拉

pn/np- 无拉

gpio设置按顺序应用,因此稍后出现的设置将覆盖较早出现的设置。
]]>
1407 0 0 0
<![CDATA[backup wordpress website]]> http://mixdiy.com/index.php/2022/06/15/backup-wordpress-website/ Wed, 15 Jun 2022 13:44:33 +0000 http://mixdiy.com/?p=1413

https://www.themepark.com.cn/wzdjwordpresswzsjbf.html#:~:text=一般来说wordpress网站的数据包括2个方面,这两个都需要单独备份%E3%80%82,一个是文件,也就是你上传安装的主题、插件以及文章中的图片和其他附件,这些我们在服务器或者虚拟主机上直接压缩下载备份即可%E3%80%82%20另一个是数据库,数据库是你的网站所有的设置,包括你的文章设置、菜单、主题选项、管理员账号密码等等信息都是保存在数据库中的%E3%80%82

wordpress完整的数据备份

我们在使用wordpress进行建站的时候,当网站建立完成之后,我们需要将网站的数据进行备份,以防止当网站服务器数据丢失,或者其他情况导致的数据丢失。

因此网站数据备份是非常重要的,如果遇到这种情况,我们辛辛苦苦制作好并上传好资料的网站,如果没有备份,那么就永远丢失找不回来啦。

因此这里我们介绍如何完整的备份好你的数据。

一般来说wordpress网站的数据包括2个方面,这两个都需要单独备份。

一个是文件,也就是你上传安装的主题、插件以及文章中的图片和其他附件,这些我们在服务器或者虚拟主机上直接压缩下载备份即可。

另一个是数据库,数据库是你的网站所有的设置,包括你的文章设置、菜单、主题选项、管理员账号密码等等信息都是保存在数据库中的。

完整的将网站数据进行备份

1.网站文件的备份

首先,我们需要将我们网站上所有的文件进行备份,找到你的网站根目录,一般来说根目录的名称是www、htdocs、wwwroot、public_html等等,

如果你找不到哪一个是根目录,那么可以逐个打开目录,查看目录下是否有三个文件夹wp-content、wp-admin、wp-includes三个文件夹,那么就是wordpress的根目录了。

找到根目录之后,将整个目录文件夹压缩为zip压缩包格式,下载到本地的电脑上进行保存,这样我们的文件就全部保存好了。

2.网站数据库的备份

网站数据库的备份需要你进入你的数据库管理。一般找到服务器数据库管理可以找到,大部分的服务器是使用phpmyadmin进行管理的,这里我们也使用phpmyadmin为例子进行介绍。

首先进入phpmyadmin,找到你的数据库,如果你不知道你的网站是安装在哪一个数据库的 ,那么可以看看下面的图片,这样的数据库结构就是你的wordpress网站数据:

完整的将wordpress网站数据备份

如上图所示,这就是你的wordpress网站数据,如果你安装了多了wordpress网站,有多个这样的数据库,不知道哪一个是想要备份的,那么在左侧上图找到wp-options,点击一下进入这个表

可以看到两个数据,一个是home,一个是siteurl,还有可以看到你的网站标题和副标题,这样就很容易找到了:

完整的将wordpress网站数据备份

找到你想要的数据库之后,退出这个options表,进入整个表单的列表 ,如上图所示,可以点击一下红框中的链接进入:

完整的将wordpress网站数据备份

然后点击导出按钮,进入导出界面后选择选择自定义:

完整的将wordpress网站数据备份

在下面的兼容选择一下myslq323 兼容模式:

完整的将wordpress网站数据备份

然后在最后点击导出即可得到备份的sql文件。

这里需要注意的是,因为mysql不同的版本导入的数据可能出现问题,因此最大兼容旧版本可以导出多个版本进行备份,

这是以防止你的数据要迁移到别的数据库进行导入时遇到的问题,如果你只是单纯的备份,那么这里可以不需要选择最大兼容。

这两个文件保存在自己的电脑上进行备份,这样我们的数据就完全的导入完成了,明天我们将出一篇教程,如何将我们的备份数据进行恢复,届时你可以点击下一篇文章进行阅读。

压缩目录

1、把/home目录下面的mydata目录压缩为mydata.zip
zip -r mydata.zip mydata #压缩mydata目录
2、把/home目录下面的mydata.zip解压到mydatabak目录里面
unzip mydata.zip -d mydatabak
3、把/home目录下面的abc文件夹和123.txt压缩成为abc123.zip
zip -r abc123.zip abc 123.txt
4、把/home目录下面的wwwroot.zip直接解压到/home目录里面
unzip wwwroot.zip
5、把/home目录下面的abc12.zip、abc23.zip、abc34.zip同时解压到/home目录里面
unzip abc*.zip
6、查看把/home目录下面的wwwroot.zip里面的内容
unzip -v wwwroot.zip
7、验证/home目录下面的wwwroot.zip是否完整
unzip -t wwwroot.zip
8、把/home目录下面wwwroot.zip里面的所有文件解压到第一级目录
unzip -j wwwroot.zip

主要参数

-c:将解压缩的结果
-l:显示压缩文件内所包含的文件
-p:与-c参数类似,会将解压缩的结果显示到屏幕上,但不会执行任何的转换
-t:检查压缩文件是否正确
-u:与-f参数类似,但是除了更新现有的文件外,也会将压缩文件中的其它文件解压缩到目录中
-v:执行是时显示详细的信息
-z:仅显示压缩文件的备注文字
-a:对文本文件进行必要的字符转换
-b:不要对文本文件进行字符转换
-C:压缩文件中的文件名称区分大小写
-j:不处理压缩文件中原有的目录路径
-L:将压缩文件中的全部文件名改为小写
-M:将输出结果送到more程序处理
-n:解压缩时不要覆盖原有的文件
-o:不必先询问用户,unzip执行后覆盖原有文件
-P:使用zip的密码选项
-q:执行时不显示任何信息
-s:将文件名中的空白字符转换为底线字符
-V:保留VMS的文件版本信息
-X:解压缩时同时回存文件原来的UID/GID

]]>
1413 0 0 0
<![CDATA[树莓派清华源镜像]]> http://mixdiy.com/index.php/2022/06/15/qinghua-image/ Wed, 15 Jun 2022 13:48:41 +0000 http://mixdiy.com/?p=1416

Raspbian 镜像使用帮助

Raspbian 简介

Raspbian 是专门用于 ARM 卡片式计算机 Raspberry Pi® “树莓派”的操作系统, 其基于 Debian 开发,针对 Raspberry Pi 硬件优化。

Raspbian 并非由树莓派的开发与维护机构 The Raspberry Pi Foundation “树莓派基金会” 官方支持。其维护者是一群 Raspberry Pi 硬件和 Debian 项目的爱好者。

系统架构与版本

架构:

  • armhf

版本:

  • stretch
  • buster
  • bullseye

注:Raspbian 系统由于从诞生开始就基于(为了armhf,也必须基于)当时还是 testing 版本的 7.0/wheezy,所以 Raspbian 不倾向于使用 stable/testing 表示版本。

使用说明

首先通过 uname -m 确定你使用的系统的架构。选择你的 Raspbian 对应的 Debian 版本:  Debian 9 (stretch) Debian 10 (buster) Debian 11 (bullseye) 

# armv7l 用户:编辑 `/etc/apt/sources.list` 文件,删除原文件所有内容,用以下内容取代
deb http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ bullseye main non-free contrib rpi
# deb-src http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ bullseye main non-free contrib rpi

# armv7l 用户如果需要开启 multi-arch 使用 arm64 软件源,需要在 `/etc/apt/sources.list` 中加上
deb [arch=arm64] http://mirrors.tuna.tsinghua.edu.cn/raspbian/multiarch/ bullseye main

# aarch64 用户:编辑 `/etc/apt/sources.list` 文件,用以下内容取代:
# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-updates main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-updates main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-backports main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-backports main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian-security bullseye-security main contrib non-free
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian-security bullseye-security main contrib non-free

# 对于两个架构,编辑 `/etc/apt/sources.list.d/raspi.list` 文件,删除原文件所有内容,用以下内容取代:
deb http://mirrors.tuna.tsinghua.edu.cn/raspberrypi/ bullseye main

注意:网址末尾的raspbian重复两次是必须的。因为 Raspbian 的仓库中除了APT软件源还包含其他代码。APT软件源不在仓库的根目录,而在raspbian/子目录下。

编辑镜像站后,请使用sudo apt-get update命令,更新软件源列表,同时检查您的编辑是否正确。

相关链接

Raspbian 链接

树莓派链接

关于本文档

本文档内容的原始版本由 Raspberry Pi 中文社区“树莓爱好者论坛”提供。按照知识共享署名-非商业性使用 3.0 中国大陆许可协议授权清华大学镜像站使用。

TUNA 提供的修改版本同样使用知识共享署名-非商业性使用 3.0 中国大陆许可协议

]]>
1416 0 0 0
<![CDATA[wordpressxml]]> http://mixdiy.com/index.php/2022/06/15/wordpressxml/ Wed, 15 Jun 2022 14:15:56 +0000 http://mixdiy.com/?p=1422 ]]> 1422 0 0 0 <![CDATA[如何修复WordPress图片裁剪错误]]> http://mixdiy.com/index.php/2022/06/16/wordpress-picture-cut-error/ Thu, 16 Jun 2022 04:06:04 +0000 http://mixdiy.com/?p=1430

我们现在知道错误的样子。让我们进一步了解如何修复图像裁剪错误。

作为故障排除的第一步,我们检查服务器的PHP版本以及GD库。如果缺少GD库,我们安装它。但是,GD安装步骤因服务器类型而异。

对于RedHat/CentOS主机,我们运行命令

yum install php-gd

或者,如果是Ubuntu服务器,GD安装使用

apt-get install php-gd

在这里,我们还要确保WordPress软件包的版本与主机的PHP版本相匹配。

最后,要制作新安装的GD库,需要重启web服务器。我们在服务器中通过

/etc/init.d/httpd restart

修复错误后,在“媒体库”中,用户可以选择图像并“插入文章”,完美解决问题。

]]>
1430 0 0 0