diff --git a/translated_images/tw/pi-camera-module.4278753c31bd6e75.webp b/translated_images/tw/pi-camera-module.4278753c31bd6e75.webp deleted file mode 100644 index ccaf32cfe..000000000 Binary files a/translated_images/tw/pi-camera-module.4278753c31bd6e75.webp and /dev/null differ diff --git a/translated_images/tw/pi-camera-ribbon-cable.0bf82acd251611c2.webp b/translated_images/tw/pi-camera-ribbon-cable.0bf82acd251611c2.webp deleted file mode 100644 index aff488c57..000000000 Binary files a/translated_images/tw/pi-camera-ribbon-cable.0bf82acd251611c2.webp and /dev/null differ diff --git a/translated_images/tw/pi-camera-socket-ribbon-cable.a18309920b118009.webp b/translated_images/tw/pi-camera-socket-ribbon-cable.a18309920b118009.webp deleted file mode 100644 index 49ec5cda4..000000000 Binary files a/translated_images/tw/pi-camera-socket-ribbon-cable.a18309920b118009.webp and /dev/null differ diff --git a/translated_images/tw/pi-camera-upside-down.5376961ba3145988.webp b/translated_images/tw/pi-camera-upside-down.5376961ba3145988.webp deleted file mode 100644 index 43a74781b..000000000 Binary files a/translated_images/tw/pi-camera-upside-down.5376961ba3145988.webp and /dev/null differ diff --git a/translated_images/tw/pi-gps-sensor.1f99ee2b2f652891.webp b/translated_images/tw/pi-gps-sensor.1f99ee2b2f652891.webp deleted file mode 100644 index 4dc3e89aa..000000000 Binary files a/translated_images/tw/pi-gps-sensor.1f99ee2b2f652891.webp and /dev/null differ diff --git a/translated_images/tw/pi-hardware-kit.26dbadaedb7dd44c.webp b/translated_images/tw/pi-hardware-kit.26dbadaedb7dd44c.webp deleted file mode 100644 index 960227d5d..000000000 Binary files a/translated_images/tw/pi-hardware-kit.26dbadaedb7dd44c.webp and /dev/null differ diff --git a/translated_images/tw/pi-led.97f1d474981dc35d.webp b/translated_images/tw/pi-led.97f1d474981dc35d.webp deleted file mode 100644 index a8fb6e48e..000000000 Binary files a/translated_images/tw/pi-led.97f1d474981dc35d.webp and /dev/null differ diff --git a/translated_images/tw/pi-light-sensor.66cc1e31fa48cd7d.webp b/translated_images/tw/pi-light-sensor.66cc1e31fa48cd7d.webp deleted file mode 100644 index 8dfea1d17..000000000 Binary files a/translated_images/tw/pi-light-sensor.66cc1e31fa48cd7d.webp and /dev/null differ diff --git a/translated_images/tw/pi-relay-and-soil-moisture-sensor.02f3198975b8c53e.webp b/translated_images/tw/pi-relay-and-soil-moisture-sensor.02f3198975b8c53e.webp deleted file mode 100644 index bfbd22d50..000000000 Binary files a/translated_images/tw/pi-relay-and-soil-moisture-sensor.02f3198975b8c53e.webp and /dev/null differ diff --git a/translated_images/tw/pi-respeaker-hat.f00fabe7dd039a93.webp b/translated_images/tw/pi-respeaker-hat.f00fabe7dd039a93.webp deleted file mode 100644 index 509b300d5..000000000 Binary files a/translated_images/tw/pi-respeaker-hat.f00fabe7dd039a93.webp and /dev/null differ diff --git a/translated_images/tw/pi-soil-moisture-sensor.fdd7eb2393792cf6.webp b/translated_images/tw/pi-soil-moisture-sensor.fdd7eb2393792cf6.webp deleted file mode 100644 index 879a34e45..000000000 Binary files a/translated_images/tw/pi-soil-moisture-sensor.fdd7eb2393792cf6.webp and /dev/null differ diff --git a/translated_images/tw/pi-temperature-sensor.3ff82fff672c8e56.webp b/translated_images/tw/pi-temperature-sensor.3ff82fff672c8e56.webp deleted file mode 100644 index fc8593576..000000000 Binary files a/translated_images/tw/pi-temperature-sensor.3ff82fff672c8e56.webp and /dev/null differ diff --git a/translated_images/tw/pi-time-of-flight-sensor.58c8dc04eb3bfb57.webp b/translated_images/tw/pi-time-of-flight-sensor.58c8dc04eb3bfb57.webp deleted file mode 100644 index 95d012e07..000000000 Binary files a/translated_images/tw/pi-time-of-flight-sensor.58c8dc04eb3bfb57.webp and /dev/null differ diff --git a/translated_images/tw/plant-growth-temp-graph.c6d69c9478e6ca83.webp b/translated_images/tw/plant-growth-temp-graph.c6d69c9478e6ca83.webp deleted file mode 100644 index 6d0346345..000000000 Binary files a/translated_images/tw/plant-growth-temp-graph.c6d69c9478e6ca83.webp and /dev/null differ diff --git a/translated_images/tw/polygon-points.302193da381cb415.webp b/translated_images/tw/polygon-points.302193da381cb415.webp deleted file mode 100644 index 20c598bf0..000000000 Binary files a/translated_images/tw/polygon-points.302193da381cb415.webp and /dev/null differ diff --git a/translated_images/tw/potentiometer.35a348b9ce22f6ec.webp b/translated_images/tw/potentiometer.35a348b9ce22f6ec.webp deleted file mode 100644 index 1c6b0d863..000000000 Binary files a/translated_images/tw/potentiometer.35a348b9ce22f6ec.webp and /dev/null differ diff --git a/translated_images/tw/proximity-sensor.f5cd752c77fb62fe.webp b/translated_images/tw/proximity-sensor.f5cd752c77fb62fe.webp deleted file mode 100644 index d7173838b..000000000 Binary files a/translated_images/tw/proximity-sensor.f5cd752c77fb62fe.webp and /dev/null differ diff --git a/translated_images/tw/pub-sub.7c7ed43fe9fd15d4.webp b/translated_images/tw/pub-sub.7c7ed43fe9fd15d4.webp deleted file mode 100644 index 704df209a..000000000 Binary files a/translated_images/tw/pub-sub.7c7ed43fe9fd15d4.webp and /dev/null differ diff --git a/translated_images/tw/pump-wired-to-relay.66c5cfc0d8918990.webp b/translated_images/tw/pump-wired-to-relay.66c5cfc0d8918990.webp deleted file mode 100644 index 3954889bf..000000000 Binary files a/translated_images/tw/pump-wired-to-relay.66c5cfc0d8918990.webp and /dev/null differ diff --git a/translated_images/tw/pwm-motor-150rpm.83347ac04ca38482.webp b/translated_images/tw/pwm-motor-150rpm.83347ac04ca38482.webp deleted file mode 100644 index 363c1811e..000000000 Binary files a/translated_images/tw/pwm-motor-150rpm.83347ac04ca38482.webp and /dev/null differ diff --git a/translated_images/tw/pwm-motor-75rpm.a5e4c939934b6e14.webp b/translated_images/tw/pwm-motor-75rpm.a5e4c939934b6e14.webp deleted file mode 100644 index c99e78e67..000000000 Binary files a/translated_images/tw/pwm-motor-75rpm.a5e4c939934b6e14.webp and /dev/null differ diff --git a/translated_images/tw/ram-comparison.6beb73541b42ac6f.webp b/translated_images/tw/ram-comparison.6beb73541b42ac6f.webp deleted file mode 100644 index 26629855d..000000000 Binary files a/translated_images/tw/ram-comparison.6beb73541b42ac6f.webp and /dev/null differ diff --git a/translated_images/tw/raspberry-pi-4.fd4590d308c3d456.webp b/translated_images/tw/raspberry-pi-4.fd4590d308c3d456.webp deleted file mode 100644 index b9bf322c1..000000000 Binary files a/translated_images/tw/raspberry-pi-4.fd4590d308c3d456.webp and /dev/null differ diff --git a/translated_images/tw/raspberry-pi-imager.24aedeab9e233d84.webp b/translated_images/tw/raspberry-pi-imager.24aedeab9e233d84.webp deleted file mode 100644 index bcfabda45..000000000 Binary files a/translated_images/tw/raspberry-pi-imager.24aedeab9e233d84.webp and /dev/null differ diff --git a/translated_images/tw/raspberry-pi-logo.4efaa16605cee054.webp b/translated_images/tw/raspberry-pi-logo.4efaa16605cee054.webp deleted file mode 100644 index f943ff624..000000000 Binary files a/translated_images/tw/raspberry-pi-logo.4efaa16605cee054.webp and /dev/null differ diff --git a/translated_images/tw/raspberry-pi-zero.f7a4133e1e7d54bb.webp b/translated_images/tw/raspberry-pi-zero.f7a4133e1e7d54bb.webp deleted file mode 100644 index 030efe07c..000000000 Binary files a/translated_images/tw/raspberry-pi-zero.f7a4133e1e7d54bb.webp and /dev/null differ diff --git a/translated_images/tw/relay-off.c34a178a2960fecd.webp b/translated_images/tw/relay-off.c34a178a2960fecd.webp deleted file mode 100644 index e92c9b885..000000000 Binary files a/translated_images/tw/relay-off.c34a178a2960fecd.webp and /dev/null differ diff --git a/translated_images/tw/relay-on.4db16a0fd6b66926.webp b/translated_images/tw/relay-on.4db16a0fd6b66926.webp deleted file mode 100644 index 5ce252076..000000000 Binary files a/translated_images/tw/relay-on.4db16a0fd6b66926.webp and /dev/null differ diff --git a/translated_images/tw/resistive-soil-moisture-sensor.728a138a3d109e06.webp b/translated_images/tw/resistive-soil-moisture-sensor.728a138a3d109e06.webp deleted file mode 100644 index d145640ab..000000000 Binary files a/translated_images/tw/resistive-soil-moisture-sensor.728a138a3d109e06.webp and /dev/null differ diff --git a/translated_images/tw/respeaker-35mm-speaker.ad79ef4f128c7751.webp b/translated_images/tw/respeaker-35mm-speaker.ad79ef4f128c7751.webp deleted file mode 100644 index 503d6d8d8..000000000 Binary files a/translated_images/tw/respeaker-35mm-speaker.ad79ef4f128c7751.webp and /dev/null differ diff --git a/translated_images/tw/respeaker-jst-speaker.a441d177809df945.webp b/translated_images/tw/respeaker-jst-speaker.a441d177809df945.webp deleted file mode 100644 index 6a9073c24..000000000 Binary files a/translated_images/tw/respeaker-jst-speaker.a441d177809df945.webp and /dev/null differ diff --git a/translated_images/tw/respeaker.f5d19d1c6b14ab16.webp b/translated_images/tw/respeaker.f5d19d1c6b14ab16.webp deleted file mode 100644 index aa90b1fb5..000000000 Binary files a/translated_images/tw/respeaker.f5d19d1c6b14ab16.webp and /dev/null differ diff --git a/translated_images/tw/ribbon-mic.eacc8e092c7441ca.webp b/translated_images/tw/ribbon-mic.eacc8e092c7441ca.webp deleted file mode 100644 index 19b706457..000000000 Binary files a/translated_images/tw/ribbon-mic.eacc8e092c7441ca.webp and /dev/null differ diff --git a/translated_images/tw/rpi-stock-with-bounding-boxes.b5540e2ecb7cd49f.webp b/translated_images/tw/rpi-stock-with-bounding-boxes.b5540e2ecb7cd49f.webp deleted file mode 100644 index 1637d8b52..000000000 Binary files a/translated_images/tw/rpi-stock-with-bounding-boxes.b5540e2ecb7cd49f.webp and /dev/null differ diff --git a/translated_images/tw/sampling.6f4fadb3f2d9dfe7.webp b/translated_images/tw/sampling.6f4fadb3f2d9dfe7.webp deleted file mode 100644 index 6970fbdb2..000000000 Binary files a/translated_images/tw/sampling.6f4fadb3f2d9dfe7.webp and /dev/null differ diff --git a/translated_images/tw/save-telemetry-database.ddc9c6bea0c5ba39.webp b/translated_images/tw/save-telemetry-database.ddc9c6bea0c5ba39.webp deleted file mode 100644 index 89d3b8d3c..000000000 Binary files a/translated_images/tw/save-telemetry-database.ddc9c6bea0c5ba39.webp and /dev/null differ diff --git a/translated_images/tw/save-telemetry-to-storage-from-functions.ed3b1820980097f1.webp b/translated_images/tw/save-telemetry-to-storage-from-functions.ed3b1820980097f1.webp deleted file mode 100644 index 657a90b91..000000000 Binary files a/translated_images/tw/save-telemetry-to-storage-from-functions.ed3b1820980097f1.webp and /dev/null differ diff --git a/translated_images/tw/search-buffer-and-distance.e6a79af3898183c7.webp b/translated_images/tw/search-buffer-and-distance.e6a79af3898183c7.webp deleted file mode 100644 index 1ad069a8c..000000000 Binary files a/translated_images/tw/search-buffer-and-distance.e6a79af3898183c7.webp and /dev/null differ diff --git a/translated_images/tw/seeed-logo.74732b6b482b6e8e.webp b/translated_images/tw/seeed-logo.74732b6b482b6e8e.webp deleted file mode 100644 index 4ae6ba57a..000000000 Binary files a/translated_images/tw/seeed-logo.74732b6b482b6e8e.webp and /dev/null differ diff --git a/translated_images/tw/select-the-random-checkbox-and-set-a-range.32cf4bc7c12e797f.webp b/translated_images/tw/select-the-random-checkbox-and-set-a-range.32cf4bc7c12e797f.webp deleted file mode 100644 index 1d43f3ba7..000000000 Binary files a/translated_images/tw/select-the-random-checkbox-and-set-a-range.32cf4bc7c12e797f.webp and /dev/null differ diff --git a/translated_images/tw/send-message-asymmetric.7abe327c62615b8c.webp b/translated_images/tw/send-message-asymmetric.7abe327c62615b8c.webp deleted file mode 100644 index 9b88b3e75..000000000 Binary files a/translated_images/tw/send-message-asymmetric.7abe327c62615b8c.webp and /dev/null differ diff --git a/translated_images/tw/send-message-certificate.9cc576ac1e46b76e.webp b/translated_images/tw/send-message-certificate.9cc576ac1e46b76e.webp deleted file mode 100644 index e1291b596..000000000 Binary files a/translated_images/tw/send-message-certificate.9cc576ac1e46b76e.webp and /dev/null differ diff --git a/translated_images/tw/send-message-symmetric-key-hacker.e7cb53db1707adfb.webp b/translated_images/tw/send-message-symmetric-key-hacker.e7cb53db1707adfb.webp deleted file mode 100644 index 30b7e4c44..000000000 Binary files a/translated_images/tw/send-message-symmetric-key-hacker.e7cb53db1707adfb.webp and /dev/null differ diff --git a/translated_images/tw/send-message-symmetric-key.a2e8ad0d495896ff.webp b/translated_images/tw/send-message-symmetric-key.a2e8ad0d495896ff.webp deleted file mode 100644 index 928fb386c..000000000 Binary files a/translated_images/tw/send-message-symmetric-key.a2e8ad0d495896ff.webp and /dev/null differ diff --git a/translated_images/tw/sentence-as-intent-entities.301401696f992259.webp b/translated_images/tw/sentence-as-intent-entities.301401696f992259.webp deleted file mode 100644 index 31cd0cc72..000000000 Binary files a/translated_images/tw/sentence-as-intent-entities.301401696f992259.webp and /dev/null differ diff --git a/translated_images/tw/serverless-scaling.f8c769adf0413fd1.webp b/translated_images/tw/serverless-scaling.f8c769adf0413fd1.webp deleted file mode 100644 index c7e8e0e02..000000000 Binary files a/translated_images/tw/serverless-scaling.f8c769adf0413fd1.webp and /dev/null differ diff --git a/translated_images/tw/shapes-to-images.1a309f0ea88dd66f.webp b/translated_images/tw/shapes-to-images.1a309f0ea88dd66f.webp deleted file mode 100644 index 043cea68f..000000000 Binary files a/translated_images/tw/shapes-to-images.1a309f0ea88dd66f.webp and /dev/null differ diff --git a/translated_images/tw/smarter-thermostat.a75855f15d2d9e63.webp b/translated_images/tw/smarter-thermostat.a75855f15d2d9e63.webp deleted file mode 100644 index 4ba64fb4a..000000000 Binary files a/translated_images/tw/smarter-thermostat.a75855f15d2d9e63.webp and /dev/null differ diff --git a/translated_images/tw/soil-moisture-delay.865f3fae206db01d.webp b/translated_images/tw/soil-moisture-delay.865f3fae206db01d.webp deleted file mode 100644 index 4d35be2a7..000000000 Binary files a/translated_images/tw/soil-moisture-delay.865f3fae206db01d.webp and /dev/null differ diff --git a/translated_images/tw/soil-moisture-sensor-in-soil.bfad91002bda5e96.webp b/translated_images/tw/soil-moisture-sensor-in-soil.bfad91002bda5e96.webp deleted file mode 100644 index 5990e3c87..000000000 Binary files a/translated_images/tw/soil-moisture-sensor-in-soil.bfad91002bda5e96.webp and /dev/null differ diff --git a/translated_images/tw/soil-moisture-to-voltage-with-reading.681cb3e1f8b68caf.webp b/translated_images/tw/soil-moisture-to-voltage-with-reading.681cb3e1f8b68caf.webp deleted file mode 100644 index 7db671707..000000000 Binary files a/translated_images/tw/soil-moisture-to-voltage-with-reading.681cb3e1f8b68caf.webp and /dev/null differ diff --git a/translated_images/tw/soil-moisture-to-voltage.df86d80cda158700.webp b/translated_images/tw/soil-moisture-to-voltage.df86d80cda158700.webp deleted file mode 100644 index 2e3aaaa1c..000000000 Binary files a/translated_images/tw/soil-moisture-to-voltage.df86d80cda158700.webp and /dev/null differ diff --git a/translated_images/tw/soil-moisture-travel.a0e31af222cf1438.webp b/translated_images/tw/soil-moisture-travel.a0e31af222cf1438.webp deleted file mode 100644 index 6610ea4bc..000000000 Binary files a/translated_images/tw/soil-moisture-travel.a0e31af222cf1438.webp and /dev/null differ diff --git a/translated_images/tw/spi.297431d6f98b386b.webp b/translated_images/tw/spi.297431d6f98b386b.webp deleted file mode 100644 index c536325ab..000000000 Binary files a/translated_images/tw/spi.297431d6f98b386b.webp and /dev/null differ diff --git a/translated_images/tw/sql-database.be160f12bfccefd3.webp b/translated_images/tw/sql-database.be160f12bfccefd3.webp deleted file mode 100644 index 82828db35..000000000 Binary files a/translated_images/tw/sql-database.be160f12bfccefd3.webp and /dev/null differ diff --git a/translated_images/tw/stock-7-cans-tomato-paste.f86059cc573d7bec.webp b/translated_images/tw/stock-7-cans-tomato-paste.f86059cc573d7bec.webp deleted file mode 100644 index d63beb157..000000000 Binary files a/translated_images/tw/stock-7-cans-tomato-paste.f86059cc573d7bec.webp and /dev/null differ diff --git a/translated_images/tw/stock-rogue-corn.be1f3ada8c457854.webp b/translated_images/tw/stock-rogue-corn.be1f3ada8c457854.webp deleted file mode 100644 index fccee657b..000000000 Binary files a/translated_images/tw/stock-rogue-corn.be1f3ada8c457854.webp and /dev/null differ diff --git a/translated_images/tw/strawberry-with-pump.b410fc72ac6aabad.webp b/translated_images/tw/strawberry-with-pump.b410fc72ac6aabad.webp deleted file mode 100644 index 749a908d4..000000000 Binary files a/translated_images/tw/strawberry-with-pump.b410fc72ac6aabad.webp and /dev/null differ diff --git a/translated_images/tw/telemetry.21e5d8b97649d2eb.webp b/translated_images/tw/telemetry.21e5d8b97649d2eb.webp deleted file mode 100644 index 250f80824..000000000 Binary files a/translated_images/tw/telemetry.21e5d8b97649d2eb.webp and /dev/null differ diff --git a/translated_images/tw/temperature-as-digital.85004491b977bae1.webp b/translated_images/tw/temperature-as-digital.85004491b977bae1.webp deleted file mode 100644 index b533bada6..000000000 Binary files a/translated_images/tw/temperature-as-digital.85004491b977bae1.webp and /dev/null differ diff --git a/translated_images/tw/time-of-flight-banana.079921ad8b1496e4.webp b/translated_images/tw/time-of-flight-banana.079921ad8b1496e4.webp deleted file mode 100644 index b2e7dc67d..000000000 Binary files a/translated_images/tw/time-of-flight-banana.079921ad8b1496e4.webp and /dev/null differ diff --git a/translated_images/tw/traditional-vs-ml.5c20c169621fa539.webp b/translated_images/tw/traditional-vs-ml.5c20c169621fa539.webp deleted file mode 100644 index b449c9390..000000000 Binary files a/translated_images/tw/traditional-vs-ml.5c20c169621fa539.webp and /dev/null differ diff --git a/translated_images/tw/translated-smart-timer.08ac20057fdc5c37.webp b/translated_images/tw/translated-smart-timer.08ac20057fdc5c37.webp deleted file mode 100644 index 5619019cd..000000000 Binary files a/translated_images/tw/translated-smart-timer.08ac20057fdc5c37.webp and /dev/null differ diff --git a/translated_images/tw/transpiration.b735aa34e4372e65.webp b/translated_images/tw/transpiration.b735aa34e4372e65.webp deleted file mode 100644 index 4df9bf295..000000000 Binary files a/translated_images/tw/transpiration.b735aa34e4372e65.webp and /dev/null differ diff --git a/translated_images/tw/tts-overview.193843cf3f5ee09f.webp b/translated_images/tw/tts-overview.193843cf3f5ee09f.webp deleted file mode 100644 index fbe0a956d..000000000 Binary files a/translated_images/tw/tts-overview.193843cf3f5ee09f.webp and /dev/null differ diff --git a/translated_images/tw/uart.d0dbd3fb9e3728c6.webp b/translated_images/tw/uart.d0dbd3fb9e3728c6.webp deleted file mode 100644 index 7acead167..000000000 Binary files a/translated_images/tw/uart.d0dbd3fb9e3728c6.webp and /dev/null differ diff --git a/translated_images/tw/vscode-azure-functions-init-notification.bd19b49229963edb.webp b/translated_images/tw/vscode-azure-functions-init-notification.bd19b49229963edb.webp deleted file mode 100644 index b3f2ca23d..000000000 Binary files a/translated_images/tw/vscode-azure-functions-init-notification.bd19b49229963edb.webp and /dev/null differ diff --git a/translated_images/tw/vscode-kill-terminal.1cc4de7c6f25ee08.webp b/translated_images/tw/vscode-kill-terminal.1cc4de7c6f25ee08.webp deleted file mode 100644 index 49acb87e1..000000000 Binary files a/translated_images/tw/vscode-kill-terminal.1cc4de7c6f25ee08.webp and /dev/null differ diff --git a/translated_images/tw/vscode-new-file-button.182702340fe6723c.webp b/translated_images/tw/vscode-new-file-button.182702340fe6723c.webp deleted file mode 100644 index 03e37860e..000000000 Binary files a/translated_images/tw/vscode-new-file-button.182702340fe6723c.webp and /dev/null differ diff --git a/translated_images/tw/vscode-new-terminal.77db8fc0f9cd3182.webp b/translated_images/tw/vscode-new-terminal.77db8fc0f9cd3182.webp deleted file mode 100644 index de71202e4..000000000 Binary files a/translated_images/tw/vscode-new-terminal.77db8fc0f9cd3182.webp and /dev/null differ diff --git a/translated_images/tw/vscode-open-nightlight-remote.d3d2a4011e30d535.webp b/translated_images/tw/vscode-open-nightlight-remote.d3d2a4011e30d535.webp deleted file mode 100644 index 637904798..000000000 Binary files a/translated_images/tw/vscode-open-nightlight-remote.d3d2a4011e30d535.webp and /dev/null differ diff --git a/translated_images/tw/vscode-platformio-build-command-palette.7708e7ec7d75d7ee.webp b/translated_images/tw/vscode-platformio-build-command-palette.7708e7ec7d75d7ee.webp deleted file mode 100644 index 48b21c273..000000000 Binary files a/translated_images/tw/vscode-platformio-build-command-palette.7708e7ec7d75d7ee.webp and /dev/null differ diff --git a/translated_images/tw/vscode-platformio-home-open.3f9a41bfd3f4da1c.webp b/translated_images/tw/vscode-platformio-home-open.3f9a41bfd3f4da1c.webp deleted file mode 100644 index bc4eaa5a0..000000000 Binary files a/translated_images/tw/vscode-platformio-home-open.3f9a41bfd3f4da1c.webp and /dev/null differ diff --git a/translated_images/tw/vscode-platformio-menu.297be26b9733e5c4.webp b/translated_images/tw/vscode-platformio-menu.297be26b9733e5c4.webp deleted file mode 100644 index 3e8441491..000000000 Binary files a/translated_images/tw/vscode-platformio-menu.297be26b9733e5c4.webp and /dev/null differ diff --git a/translated_images/tw/vscode-platformio-nightlight-project-wizard.5c64db4da6037420.webp b/translated_images/tw/vscode-platformio-nightlight-project-wizard.5c64db4da6037420.webp deleted file mode 100644 index 2d3637aff..000000000 Binary files a/translated_images/tw/vscode-platformio-nightlight-project-wizard.5c64db4da6037420.webp and /dev/null differ diff --git a/translated_images/tw/vscode-platformio-serial-monitor-command-palette.b348ec841b8a1c14.webp b/translated_images/tw/vscode-platformio-serial-monitor-command-palette.b348ec841b8a1c14.webp deleted file mode 100644 index 18a697cf9..000000000 Binary files a/translated_images/tw/vscode-platformio-serial-monitor-command-palette.b348ec841b8a1c14.webp and /dev/null differ diff --git a/translated_images/tw/vscode-platformio-upload-command-palette.9e0f49cf80d1f1c3.webp b/translated_images/tw/vscode-platformio-upload-command-palette.9e0f49cf80d1f1c3.webp deleted file mode 100644 index 63e367754..000000000 Binary files a/translated_images/tw/vscode-platformio-upload-command-palette.9e0f49cf80d1f1c3.webp and /dev/null differ diff --git a/translated_images/tw/vscode-platformio-welcome-new-button.ba6fc8a4c7b78cc8.webp b/translated_images/tw/vscode-platformio-welcome-new-button.ba6fc8a4c7b78cc8.webp deleted file mode 100644 index b5cbeb76c..000000000 Binary files a/translated_images/tw/vscode-platformio-welcome-new-button.ba6fc8a4c7b78cc8.webp and /dev/null differ diff --git a/translated_images/tw/vscode-virtual-env.8ba42e04c3d533cf.webp b/translated_images/tw/vscode-virtual-env.8ba42e04c3d533cf.webp deleted file mode 100644 index 3512f7825..000000000 Binary files a/translated_images/tw/vscode-virtual-env.8ba42e04c3d533cf.webp and /dev/null differ diff --git a/translated_images/tw/what-is-azure-video-thumbnail.20174db09e03bbb8.webp b/translated_images/tw/what-is-azure-video-thumbnail.20174db09e03bbb8.webp deleted file mode 100644 index f58ebd172..000000000 Binary files a/translated_images/tw/what-is-azure-video-thumbnail.20174db09e03bbb8.webp and /dev/null differ diff --git a/translated_images/tw/wio-gps-sensor.19fd52b81ce58095.webp b/translated_images/tw/wio-gps-sensor.19fd52b81ce58095.webp deleted file mode 100644 index a616960d3..000000000 Binary files a/translated_images/tw/wio-gps-sensor.19fd52b81ce58095.webp and /dev/null differ diff --git a/translated_images/tw/wio-hardware-kit.4c70c48b85e4283a.webp b/translated_images/tw/wio-hardware-kit.4c70c48b85e4283a.webp deleted file mode 100644 index ba2b64272..000000000 Binary files a/translated_images/tw/wio-hardware-kit.4c70c48b85e4283a.webp and /dev/null differ diff --git a/translated_images/tw/wio-led.265a1897e72d7f21.webp b/translated_images/tw/wio-led.265a1897e72d7f21.webp deleted file mode 100644 index a73c0a32d..000000000 Binary files a/translated_images/tw/wio-led.265a1897e72d7f21.webp and /dev/null differ diff --git a/translated_images/tw/wio-light-sensor.b1f529f3c95f5165.webp b/translated_images/tw/wio-light-sensor.b1f529f3c95f5165.webp deleted file mode 100644 index 0afe1cf9f..000000000 Binary files a/translated_images/tw/wio-light-sensor.b1f529f3c95f5165.webp and /dev/null differ diff --git a/translated_images/tw/wio-mic.3f8c843dbe8ad917.webp b/translated_images/tw/wio-mic.3f8c843dbe8ad917.webp deleted file mode 100644 index 56ece147e..000000000 Binary files a/translated_images/tw/wio-mic.3f8c843dbe8ad917.webp and /dev/null differ diff --git a/translated_images/tw/wio-relay-and-soil-moisture-sensor.ed722202d42babe0.webp b/translated_images/tw/wio-relay-and-soil-moisture-sensor.ed722202d42babe0.webp deleted file mode 100644 index 12427b0a6..000000000 Binary files a/translated_images/tw/wio-relay-and-soil-moisture-sensor.ed722202d42babe0.webp and /dev/null differ diff --git a/translated_images/tw/wio-respeaker-hat.bd54917d446e6f6f.webp b/translated_images/tw/wio-respeaker-hat.bd54917d446e6f6f.webp deleted file mode 100644 index e28fc51b7..000000000 Binary files a/translated_images/tw/wio-respeaker-hat.bd54917d446e6f6f.webp and /dev/null differ diff --git a/translated_images/tw/wio-respeaker-wiring-0.767f80aa65081038.webp b/translated_images/tw/wio-respeaker-wiring-0.767f80aa65081038.webp deleted file mode 100644 index 94da52acf..000000000 Binary files a/translated_images/tw/wio-respeaker-wiring-0.767f80aa65081038.webp and /dev/null differ diff --git a/translated_images/tw/wio-respeaker-wiring-1.8d894727f2ba2400.webp b/translated_images/tw/wio-respeaker-wiring-1.8d894727f2ba2400.webp deleted file mode 100644 index e21cb4005..000000000 Binary files a/translated_images/tw/wio-respeaker-wiring-1.8d894727f2ba2400.webp and /dev/null differ diff --git a/translated_images/tw/wio-respeaker-wiring-2.329e1cbd306e754f.webp b/translated_images/tw/wio-respeaker-wiring-2.329e1cbd306e754f.webp deleted file mode 100644 index 2afb18d12..000000000 Binary files a/translated_images/tw/wio-respeaker-wiring-2.329e1cbd306e754f.webp and /dev/null differ diff --git a/translated_images/tw/wio-respeaker-wiring-3.75b0be447e2fa930.webp b/translated_images/tw/wio-respeaker-wiring-3.75b0be447e2fa930.webp deleted file mode 100644 index fc3fe014b..000000000 Binary files a/translated_images/tw/wio-respeaker-wiring-3.75b0be447e2fa930.webp and /dev/null differ diff --git a/translated_images/tw/wio-respeaker-wiring-4.aa9cd434d8779437.webp b/translated_images/tw/wio-respeaker-wiring-4.aa9cd434d8779437.webp deleted file mode 100644 index c8b223f85..000000000 Binary files a/translated_images/tw/wio-respeaker-wiring-4.aa9cd434d8779437.webp and /dev/null differ diff --git a/translated_images/tw/wio-respeaker-wiring-5.af117c20acf622f3.webp b/translated_images/tw/wio-respeaker-wiring-5.af117c20acf622f3.webp deleted file mode 100644 index 7687eb1dc..000000000 Binary files a/translated_images/tw/wio-respeaker-wiring-5.af117c20acf622f3.webp and /dev/null differ diff --git a/translated_images/tw/wio-sd-card.acdcbe322fa4ee7f.webp b/translated_images/tw/wio-sd-card.acdcbe322fa4ee7f.webp deleted file mode 100644 index 6691aef09..000000000 Binary files a/translated_images/tw/wio-sd-card.acdcbe322fa4ee7f.webp and /dev/null differ diff --git a/translated_images/tw/wio-soil-moisture-sensor.46919b61c3f6cb74.webp b/translated_images/tw/wio-soil-moisture-sensor.46919b61c3f6cb74.webp deleted file mode 100644 index e8a617ff3..000000000 Binary files a/translated_images/tw/wio-soil-moisture-sensor.46919b61c3f6cb74.webp and /dev/null differ diff --git a/translated_images/tw/wio-temperature-sensor.2934928f38c7f79a.webp b/translated_images/tw/wio-temperature-sensor.2934928f38c7f79a.webp deleted file mode 100644 index 64bd38365..000000000 Binary files a/translated_images/tw/wio-temperature-sensor.2934928f38c7f79a.webp and /dev/null differ diff --git a/translated_images/tw/wio-terminal-c-button.73df3cb1c1445ea0.webp b/translated_images/tw/wio-terminal-c-button.73df3cb1c1445ea0.webp deleted file mode 100644 index 946a7ce5b..000000000 Binary files a/translated_images/tw/wio-terminal-c-button.73df3cb1c1445ea0.webp and /dev/null differ diff --git a/translated_images/tw/wio-terminal-pin-sticker.b90b1535937b84bd.webp b/translated_images/tw/wio-terminal-pin-sticker.b90b1535937b84bd.webp deleted file mode 100644 index fbb9e1b3c..000000000 Binary files a/translated_images/tw/wio-terminal-pin-sticker.b90b1535937b84bd.webp and /dev/null differ diff --git a/translated_images/tw/wio-terminal.b8299ee16587db9a.webp b/translated_images/tw/wio-terminal.b8299ee16587db9a.webp deleted file mode 100644 index 439d1e984..000000000 Binary files a/translated_images/tw/wio-terminal.b8299ee16587db9a.webp and /dev/null differ diff --git a/translated_images/tw/wio-time-of-flight-sensor.c4c182131d2ea73d.webp b/translated_images/tw/wio-time-of-flight-sensor.c4c182131d2ea73d.webp deleted file mode 100644 index 7d0b1af71..000000000 Binary files a/translated_images/tw/wio-time-of-flight-sensor.c4c182131d2ea73d.webp and /dev/null differ diff --git a/translated_images/zh-HK/.co-op-translator.json b/translated_images/zh-HK/.co-op-translator.json new file mode 100644 index 000000000..f65c6dcbd --- /dev/null +++ b/translated_images/zh-HK/.co-op-translator.json @@ -0,0 +1,2198 @@ +{ + "IMG_5305.aa291c8812a9f1e5.webp": { + "original_hash": "fc0711c2be2774ab6089d59dc597260b", + "translation_date": "2026-01-16T09:41:14+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/testing/IMG_5305.png", + "language_code": "zh-HK" + }, + "IMG_5306.d575b9ab7025877b.webp": { + "original_hash": "4e37695a9ad976c862b785afb2bb7c2e", + "translation_date": "2026-01-16T09:40:54+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/testing/IMG_5306.png", + "language_code": "zh-HK" + }, + "IMG_5307.f9c9b8361a8aa734.webp": { + "original_hash": "cab390c38e0b27852ef90122d6dc4a8a", + "translation_date": "2026-01-16T09:42:22+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5307.png", + "language_code": "zh-HK" + }, + "IMG_5308.cbd6ed7007e69060.webp": { + "original_hash": "0ba29462f9494cbdec73138d13555eeb", + "translation_date": "2026-01-16T09:45:26+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5308.png", + "language_code": "zh-HK" + }, + "IMG_5309.23fbc3b6667bfb64.webp": { + "original_hash": "489a940aac2fae4a3a1e497781cba5a8", + "translation_date": "2026-01-16T09:42:27+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5309.png", + "language_code": "zh-HK" + }, + "IMG_5310.0ee0eb2fbc1c2d0e.webp": { + "original_hash": "44821d181f6a6dfc59fce6430ea543c3", + "translation_date": "2026-01-16T09:44:46+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5310.png", + "language_code": "zh-HK" + }, + "IMG_5311.8c90da6446c2d8c2.webp": { + "original_hash": "68d840516ae36a03715a432e9dac3856", + "translation_date": "2026-01-16T09:43:36+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5311.png", + "language_code": "zh-HK" + }, + "IMG_5312.a45550ddd8ce8e65.webp": { + "original_hash": "254b4fd54e9fe80a51a191ce9f654c85", + "translation_date": "2026-01-16T09:42:01+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5312.png", + "language_code": "zh-HK" + }, + "IMG_5313.ed1b45211271dbd2.webp": { + "original_hash": "86bea69b269add926a2da7a91bd68191", + "translation_date": "2026-01-16T09:42:05+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5313.png", + "language_code": "zh-HK" + }, + "IMG_5314.c2203206a05a74b5.webp": { + "original_hash": "168cc384e8f57b7bdd2469769fddced6", + "translation_date": "2026-01-16T09:43:54+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5314.png", + "language_code": "zh-HK" + }, + "IMG_5315.f698228e0e031bd4.webp": { + "original_hash": "4975f6e8e46204674362e3686f994433", + "translation_date": "2026-01-16T09:44:41+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5315.png", + "language_code": "zh-HK" + }, + "IMG_5316.29dc70d802ce8349.webp": { + "original_hash": "ba7dbbd6975e2bd39c4b37e1a12867da", + "translation_date": "2026-01-16T09:41:30+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5316.png", + "language_code": "zh-HK" + }, + "IMG_5317.ed81e4c1ca5046dc.webp": { + "original_hash": "1418135512d05bb5a5e8295c3ca27e4e", + "translation_date": "2026-01-16T09:42:41+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5317.png", + "language_code": "zh-HK" + }, + "IMG_5318.15dfffeb7f47abf7.webp": { + "original_hash": "6d55d50694cfb9d88f84167f245a5d51", + "translation_date": "2026-01-16T09:41:55+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5318.png", + "language_code": "zh-HK" + }, + "IMG_5319.b549b1fff0dcf143.webp": { + "original_hash": "cd73355ae901d57470404f12f3e14d8f", + "translation_date": "2026-01-16T09:44:27+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5319.png", + "language_code": "zh-HK" + }, + "IMG_5320.8268d3f61972f348.webp": { + "original_hash": "a6db3613b3615430929eec1d26541f23", + "translation_date": "2026-01-16T09:44:07+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5320.png", + "language_code": "zh-HK" + }, + "IMG_5321.b207cf143a59458d.webp": { + "original_hash": "d5626c8d5686c0fb0b71774b18b5799e", + "translation_date": "2026-01-16T09:41:08+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/testing/IMG_5321.png", + "language_code": "zh-HK" + }, + "IMG_5322.974809b9461a9e20.webp": { + "original_hash": "12e8057af8ca375668ac69dd83d3df9e", + "translation_date": "2026-01-16T09:44:36+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5322.png", + "language_code": "zh-HK" + }, + "IMG_5323.4939fa17958f291b.webp": { + "original_hash": "e7b6ccd025636efe16234fde1583ac2f", + "translation_date": "2026-01-16T09:45:00+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5323.png", + "language_code": "zh-HK" + }, + "IMG_5324.0afbc6f0caceb1a3.webp": { + "original_hash": "2d32f160ad94adf82b14722b5a4750de", + "translation_date": "2026-01-16T09:42:34+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5324.png", + "language_code": "zh-HK" + }, + "IMG_5325.9e9d9e9b85a10b06.webp": { + "original_hash": "797e68cbe795ca2b3bcefb017cde1e88", + "translation_date": "2026-01-16T09:43:48+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5325.png", + "language_code": "zh-HK" + }, + "IMG_5326.35bbc9e054c704d0.webp": { + "original_hash": "34cd6fb13bd85e2bc35a2113b89aecd1", + "translation_date": "2026-01-16T09:42:16+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5326.png", + "language_code": "zh-HK" + }, + "IMG_5327.804b63a605b5a77a.webp": { + "original_hash": "7f20dfc038fa8b756cf381c0d13cc764", + "translation_date": "2026-01-16T09:43:12+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5327.png", + "language_code": "zh-HK" + }, + "IMG_5328.925a9da23d96759f.webp": { + "original_hash": "d2038a13e44588a9bfbf8901b11d00e2", + "translation_date": "2026-01-16T09:41:48+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5328.png", + "language_code": "zh-HK" + }, + "IMG_5329.27da5fcbc3336773.webp": { + "original_hash": "fd75c4e9e16aeb231b2f10c94d10d608", + "translation_date": "2026-01-16T09:44:54+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5329.png", + "language_code": "zh-HK" + }, + "IMG_5330.3db3d5ea01c8cca2.webp": { + "original_hash": "148147aa52a66e9d9e77c17a4b6fb50b", + "translation_date": "2026-01-16T09:43:31+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5330.png", + "language_code": "zh-HK" + }, + "IMG_5331.181987d3d094472f.webp": { + "original_hash": "2272482bed9dbf36b7798a41324d008f", + "translation_date": "2026-01-16T09:45:08+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5331.png", + "language_code": "zh-HK" + }, + "IMG_5332.4beed56eaa5158e3.webp": { + "original_hash": "445a83687357d2bf3749e23583768407", + "translation_date": "2026-01-16T09:45:15+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5332.png", + "language_code": "zh-HK" + }, + "IMG_5333.53c3364c2ee0e7bb.webp": { + "original_hash": "018ac6ada7c30b62d168689b8865c5cd", + "translation_date": "2026-01-16T09:44:16+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5333.png", + "language_code": "zh-HK" + }, + "IMG_5334.979472e433948c10.webp": { + "original_hash": "13d55902d7f4025e4c0b4ec92d7e5c38", + "translation_date": "2026-01-16T09:43:59+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5334.png", + "language_code": "zh-HK" + }, + "IMG_5335.9f5bfba69a8d5a90.webp": { + "original_hash": "d896d7a763d29c0da589c8dbc77c9ed3", + "translation_date": "2026-01-16T09:42:58+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5335.png", + "language_code": "zh-HK" + }, + "IMG_5336.1b46586fdec95767.webp": { + "original_hash": "e40e7f6dcb13a7f78508a3751ce2f071", + "translation_date": "2026-01-16T09:42:47+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5336.png", + "language_code": "zh-HK" + }, + "IMG_5337.91c82be4d37029b0.webp": { + "original_hash": "17697be963b005f6961b32d72072d6af", + "translation_date": "2026-01-16T09:42:53+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5337.png", + "language_code": "zh-HK" + }, + "IMG_5338.9ab356541448923b.webp": { + "original_hash": "c6112e9cf630cc63b828dcbdde845de3", + "translation_date": "2026-01-16T09:45:21+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5338.png", + "language_code": "zh-HK" + }, + "IMG_5340.8440651b3ce1dd35.webp": { + "original_hash": "24b34e210b680dcdd7f84c7b819e5af2", + "translation_date": "2026-01-16T09:41:41+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5340.png", + "language_code": "zh-HK" + }, + "IMG_5341.a1aa041c42dbc534.webp": { + "original_hash": "a5bce966ff8f378c7ac2c3ae3263f629", + "translation_date": "2026-01-16T09:43:24+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5341.png", + "language_code": "zh-HK" + }, + "IMG_5342.9f3b93d9fab90d3b.webp": { + "original_hash": "5d05e1e08f6e5aaf21891368d22fcfb5", + "translation_date": "2026-01-16T09:43:05+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5342.png", + "language_code": "zh-HK" + }, + "IMG_5343.fa30105d3c10cad2.webp": { + "original_hash": "a41290e37e030928a002529f04f9387f", + "translation_date": "2026-01-16T09:41:00+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/testing/IMG_5343.png", + "language_code": "zh-HK" + }, + "IMG_5344.acfcd61ee5281321.webp": { + "original_hash": "a376d539df9a174bfdaadbab9dbc1cfd", + "translation_date": "2026-01-16T09:43:17+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5344.png", + "language_code": "zh-HK" + }, + "IMG_5345.3d305ee2bb4ee804.webp": { + "original_hash": "a511a224fa17db01684e0e63921ff020", + "translation_date": "2026-01-16T09:41:18+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5345.png", + "language_code": "zh-HK" + }, + "IMG_5346.281dcc267f69c70b.webp": { + "original_hash": "4772f5cabda119b12f676ad35147320e", + "translation_date": "2026-01-16T09:44:21+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5346.png", + "language_code": "zh-HK" + }, + "IMG_5347.48a4a981e76910ae.webp": { + "original_hash": "7b0d15e3f5e8ec289aff74d74a6dbf6e", + "translation_date": "2026-01-16T09:42:09+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5347.png", + "language_code": "zh-HK" + }, + "IMG_5348.3961d3b444537c13.webp": { + "original_hash": "312f940de37e3d8b548a5ff7e0ef59ce", + "translation_date": "2026-01-16T09:43:41+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5348.png", + "language_code": "zh-HK" + }, + "IMG_5349.1f0ec7fbf8807b3b.webp": { + "original_hash": "d12db74e18cfb01344a6f47e93341d6e", + "translation_date": "2026-01-16T09:41:22+00:00", + "source_file": "5-retail/lessons/1-train-stock-detector/images/training/IMG_5349.png", + "language_code": "zh-HK" + }, + "Roadmap.bb1dec285dda0eda.webp": { + "original_hash": "c49112726cdadd69ca0ec203a13e4689", + "translation_date": "2026-01-16T09:31:01+00:00", + "source_file": "sketchnotes/Roadmap.jpg", + "language_code": "zh-HK" + }, + "amqp.804bd4fce8330157.webp": { + "original_hash": "692b3edd9d3704d946d527027dd7c551", + "translation_date": "2026-01-16T09:25:40+00:00", + "source_file": "images/amqp.png", + "language_code": "zh-HK" + }, + "analog-sensor-voltage.3b6f315392247399.webp": { + "original_hash": "04c17740d5a42b6ee113d71605d6a0a8", + "translation_date": "2026-01-16T09:19:12+00:00", + "source_file": "images/analog-sensor-voltage.png", + "language_code": "zh-HK" + }, + "arducam-wio-terminal-connections.a4d5a4049bdb5ab8.webp": { + "original_hash": "01f69e3dc5f29479230a13a7c0f0efe0", + "translation_date": "2026-01-16T09:25:36+00:00", + "source_file": "images/arducam-wio-terminal-connections.png", + "language_code": "zh-HK" + }, + "arducam.20e4e4cbb2682965.webp": { + "original_hash": "f17393656663c080b68a3ed2b41c0fc2", + "translation_date": "2026-01-16T09:24:40+00:00", + "source_file": "images/arducam.png", + "language_code": "zh-HK" + }, + "arduino-sketch.79590cb837ff7a7c.webp": { + "original_hash": "a1ff6ddb0db8a676992dd9f958c6a5af", + "translation_date": "2026-01-16T09:24:50+00:00", + "source_file": "images/arduino-sketch.png", + "language_code": "zh-HK" + }, + "assignment-1-flow.7552a51acb1a5ec8.webp": { + "original_hash": "b3991eeb7b7d7ab00b21d1b95cbbd2c1", + "translation_date": "2026-01-16T09:15:01+00:00", + "source_file": "images/assignment-1-flow.png", + "language_code": "zh-HK" + }, + "assignment-1-internet-flow.3256feab5f052fd2.webp": { + "original_hash": "46d80a9e43b81a385c223c1fc12f39c6", + "translation_date": "2026-01-16T09:06:34+00:00", + "source_file": "images/assignment-1-internet-flow.png", + "language_code": "zh-HK" + }, + "azure-container-registry-logo.09494206991d4b29.webp": { + "original_hash": "adbc9655bf044bdf906e0f58fb36fbe1", + "translation_date": "2026-01-16T09:13:50+00:00", + "source_file": "images/azure-container-registry-logo.png", + "language_code": "zh-HK" + }, + "azure-functions-logo.1cfc8e3204c9c44a.webp": { + "original_hash": "7e62e90fe4cbd4f81b7db92c1f52933a", + "translation_date": "2026-01-16T09:20:37+00:00", + "source_file": "images/azure-functions-logo.png", + "language_code": "zh-HK" + }, + "azure-iot-edge-logo.c1c076749b5cba2e.webp": { + "original_hash": "bda41e5883dffedc3baf5efd20f09b49", + "translation_date": "2026-01-16T09:24:47+00:00", + "source_file": "images/azure-iot-edge-logo.png", + "language_code": "zh-HK" + }, + "azure-iot-hub-logo.28a19de76d0a1932.webp": { + "original_hash": "25526337ac38c5da2187ec953308a1a0", + "translation_date": "2026-01-16T09:21:29+00:00", + "source_file": "images/azure-iot-hub-logo.png", + "language_code": "zh-HK" + }, + "azure-maps-logo.35d01dcfbd81fe61.webp": { + "original_hash": "0b44d4f0596a033eb687e0a23d231428", + "translation_date": "2026-01-16T09:02:28+00:00", + "source_file": "images/azure-maps-logo.png", + "language_code": "zh-HK" + }, + "azure-region-existing.73f704604f2aa6cb.webp": { + "original_hash": "663095f6754e78da000aecf8b255f56a", + "translation_date": "2026-01-16T09:15:43+00:00", + "source_file": "images/azure-region-existing.png", + "language_code": "zh-HK" + }, + "azure-region-planned-expansion.a5074a1e8af74f15.webp": { + "original_hash": "e7b04b7641a855f6ab0461b9814c5bfa", + "translation_date": "2026-01-16T09:19:39+00:00", + "source_file": "images/azure-region-planned-expansion.png", + "language_code": "zh-HK" + }, + "azure-speech-logo.a1f08c4befb0159f.webp": { + "original_hash": "c0515d2fc1d34b7de4da2ae6f6af07e6", + "translation_date": "2026-01-16T09:15:25+00:00", + "source_file": "images/azure-speech-logo.png", + "language_code": "zh-HK" + }, + "azure-storage-logo.605c0f602c640d48.webp": { + "original_hash": "1b40b8e787f4058533cbfe7371dc400c", + "translation_date": "2026-01-16T09:11:56+00:00", + "source_file": "images/azure-storage-logo.png", + "language_code": "zh-HK" + }, + "azure-translator-logo.c6ed3a4a433edfd2.webp": { + "original_hash": "e6f188b64083c3f1814719c5b31a2128", + "translation_date": "2026-01-16T09:13:08+00:00", + "source_file": "images/azure-translator-logo.png", + "language_code": "zh-HK" + }, + "banana-arducam.be1b32d4267a8194.webp": { + "original_hash": "ab405c9f67c6a168e156dd3de2e4c9be", + "translation_date": "2026-01-16T09:25:12+00:00", + "source_file": "images/banana-arducam.jpg", + "language_code": "zh-HK" + }, + "banana-picture-compare.174df164dc326a42.webp": { + "original_hash": "1c539ac9dde011622cb841c9d1ac7991", + "translation_date": "2026-01-16T09:14:47+00:00", + "source_file": "images/banana-picture-compare.png", + "language_code": "zh-HK" + }, + "banana-ripe-1.6ed72365ffc92300.webp": { + "original_hash": "b669c7b079f11e40ccf403bca27420a6", + "translation_date": "2026-01-16T09:45:29+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/testing/ripe/banana-ripe-1.png", + "language_code": "zh-HK" + }, + "banana-ripe-1.780e9be3a60d8879.webp": { + "original_hash": "e1657ae830ee7346983e1ca04f185e0e", + "translation_date": "2026-01-16T09:45:48+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-1.png", + "language_code": "zh-HK" + }, + "banana-ripe-10.c3d98eb280e7464f.webp": { + "original_hash": "1fd32bdc31ba975c91484494cc3ef395", + "translation_date": "2026-01-16T09:46:51+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-10.png", + "language_code": "zh-HK" + }, + "banana-ripe-11.3d932f292b95b9a2.webp": { + "original_hash": "10d419a7eb4916eb20ab86a2d5704359", + "translation_date": "2026-01-16T09:45:32+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-11.png", + "language_code": "zh-HK" + }, + "banana-ripe-12.9f87e663b9da6c85.webp": { + "original_hash": "8e8d0158457b243d442b06713947d089", + "translation_date": "2026-01-16T09:45:45+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-12.png", + "language_code": "zh-HK" + }, + "banana-ripe-13.b7402e05160f4543.webp": { + "original_hash": "9f3ebf2203aaa657357ba262990bbb21", + "translation_date": "2026-01-16T09:45:36+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-13.png", + "language_code": "zh-HK" + }, + "banana-ripe-14.85cf309aa702cfa8.webp": { + "original_hash": "c45e36f248a3dace32791bfca32d6aed", + "translation_date": "2026-01-16T09:45:43+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-14.png", + "language_code": "zh-HK" + }, + "banana-ripe-15.933412eb14a3f8e4.webp": { + "original_hash": "efaa40a12e710aa6bbb1a1d0bb2052a4", + "translation_date": "2026-01-16T09:45:45+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-15.png", + "language_code": "zh-HK" + }, + "banana-ripe-16.781a68a01401f89e.webp": { + "original_hash": "29f95cc922f22691d9804dae4672b387", + "translation_date": "2026-01-16T09:45:38+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-16.png", + "language_code": "zh-HK" + }, + "banana-ripe-17.f8e2853a40d3ac45.webp": { + "original_hash": "8f0a9f7b93505876f7bd290b3679099d", + "translation_date": "2026-01-16T09:45:41+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-17.png", + "language_code": "zh-HK" + }, + "banana-ripe-18.3d05f5f78ecf6f0d.webp": { + "original_hash": "cae1792a648b1486d2e3590866d11980", + "translation_date": "2026-01-16T09:45:40+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-18.png", + "language_code": "zh-HK" + }, + "banana-ripe-19.ac3b0fc6cf6fdfd5.webp": { + "original_hash": "2390f2cc5023984a9682ec5f96f2bd6e", + "translation_date": "2026-01-16T09:45:46+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-19.png", + "language_code": "zh-HK" + }, + "banana-ripe-2.8ab96da692df69f1.webp": { + "original_hash": "ae966eeff29fbf1a867a864baa149780", + "translation_date": "2026-01-16T09:45:28+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/testing/ripe/banana-ripe-2.png", + "language_code": "zh-HK" + }, + "banana-ripe-2.96ddc53114c5f020.webp": { + "original_hash": "c7c3b976043accf6fff757e50afed132", + "translation_date": "2026-01-16T09:45:35+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-2.png", + "language_code": "zh-HK" + }, + "banana-ripe-20.a4d0ad33a7e6f037.webp": { + "original_hash": "aa2539f64cd978be437a294ef56b38ec", + "translation_date": "2026-01-16T09:45:39+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-20.png", + "language_code": "zh-HK" + }, + "banana-ripe-21.07e03d64f265d55d.webp": { + "original_hash": "2c1528b1de4d0572dd8e3813ec38a591", + "translation_date": "2026-01-16T09:45:33+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-21.png", + "language_code": "zh-HK" + }, + "banana-ripe-22.a63c05aeb7f866fc.webp": { + "original_hash": "1b4c8cac0fb4d88e2fac6e10a401b567", + "translation_date": "2026-01-16T09:46:52+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-22.png", + "language_code": "zh-HK" + }, + "banana-ripe-23.6f3364afcab19e57.webp": { + "original_hash": "28dcfffcd92c957991aca002f7afb9f5", + "translation_date": "2026-01-16T09:45:44+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-23.png", + "language_code": "zh-HK" + }, + "banana-ripe-24.ff2c02cc80a9c430.webp": { + "original_hash": "cffff187f53978527516f37fbdb528b6", + "translation_date": "2026-01-16T09:45:47+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-24.png", + "language_code": "zh-HK" + }, + "banana-ripe-25.65ce63418cdc4de2.webp": { + "original_hash": "773b4a997e31a2a512b75f51941d594d", + "translation_date": "2026-01-16T09:45:36+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-25.png", + "language_code": "zh-HK" + }, + "banana-ripe-3.4fae05a0a1d2b5f0.webp": { + "original_hash": "4810eae16d8cd35405bd23a7630d4c1c", + "translation_date": "2026-01-16T09:45:42+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-3.png", + "language_code": "zh-HK" + }, + "banana-ripe-5.c762086879ccec4c.webp": { + "original_hash": "b70a07b7c7fe7a6e38624ecf409ecb39", + "translation_date": "2026-01-16T09:45:32+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-5.png", + "language_code": "zh-HK" + }, + "banana-ripe-6.5131bcbf492980cb.webp": { + "original_hash": "54da747610520d477d2ce3ab576ac125", + "translation_date": "2026-01-16T09:45:34+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-6.png", + "language_code": "zh-HK" + }, + "banana-ripe-7.5fc18dfe7b7ae9dc.webp": { + "original_hash": "ab45157090d3fb253fc262e1ac44cd36", + "translation_date": "2026-01-16T09:46:53+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-7.png", + "language_code": "zh-HK" + }, + "banana-ripe-8.dba1d33bd34d4830.webp": { + "original_hash": "12afa64eebbc95b9536e6f3bbccdb345", + "translation_date": "2026-01-16T09:45:37+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-8.png", + "language_code": "zh-HK" + }, + "banana-ripe-9.32f91462c8b0e2d3.webp": { + "original_hash": "a380e69917059d3401d09c9e00e289f6", + "translation_date": "2026-01-16T09:45:40+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/ripe/banana-ripe-9.png", + "language_code": "zh-HK" + }, + "banana-training-images.530eb203346d73bc.webp": { + "original_hash": "c93a7b2b58562500f7eaf9385e7c0d58", + "translation_date": "2026-01-16T09:12:09+00:00", + "source_file": "images/banana-training-images.png", + "language_code": "zh-HK" + }, + "banana-unripe-1.910c8606a300fa20.webp": { + "original_hash": "6d91493fcc6ed717266d0a58ed4ba97d", + "translation_date": "2026-01-16T09:47:12+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-1.png", + "language_code": "zh-HK" + }, + "banana-unripe-1.b2c7051d9c8a4e61.webp": { + "original_hash": "a4cc0cccd0bd9e129367a314c3d97e3a", + "translation_date": "2026-01-16T09:45:31+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/testing/unripe/banana-unripe-1.png", + "language_code": "zh-HK" + }, + "banana-unripe-10.38dab0db918a2487.webp": { + "original_hash": "3fbe312ca683ffb4130c21bb5f8faf67", + "translation_date": "2026-01-16T09:46:58+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-10.png", + "language_code": "zh-HK" + }, + "banana-unripe-11.ec8d0eefe63e10b9.webp": { + "original_hash": "fafe54a9660b5ea8811d8df8dfe375ad", + "translation_date": "2026-01-16T09:47:16+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-11.png", + "language_code": "zh-HK" + }, + "banana-unripe-12.b95a088ccae935db.webp": { + "original_hash": "26ff82ce86b8b10f54fc97c7227aa131", + "translation_date": "2026-01-16T09:47:05+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-12.png", + "language_code": "zh-HK" + }, + "banana-unripe-13.37e5a7cac5aa0920.webp": { + "original_hash": "ad62ee1af2830d972b9d933e94c8f592", + "translation_date": "2026-01-16T09:47:02+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-13.png", + "language_code": "zh-HK" + }, + "banana-unripe-14.d19ddd6bbf63a259.webp": { + "original_hash": "df691d75f8bdf20269d642704fa81233", + "translation_date": "2026-01-16T09:47:10+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-14.png", + "language_code": "zh-HK" + }, + "banana-unripe-15.274e48544326077a.webp": { + "original_hash": "1bf43279e1d99527a05e3fd05b6ac035", + "translation_date": "2026-01-16T09:47:13+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-15.png", + "language_code": "zh-HK" + }, + "banana-unripe-16.bd058f64bd7ec014.webp": { + "original_hash": "a57dba0a4221e68a916b8d6b39ee88b3", + "translation_date": "2026-01-16T09:46:55+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-16.png", + "language_code": "zh-HK" + }, + "banana-unripe-17.408382d679bfa079.webp": { + "original_hash": "9b487f26ef0c7cfb561c472902c5d96b", + "translation_date": "2026-01-16T09:47:07+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-17.png", + "language_code": "zh-HK" + }, + "banana-unripe-18.39c0eb79d7b3b9ba.webp": { + "original_hash": "5e5522a34ce418404df666003c42058e", + "translation_date": "2026-01-16T09:47:03+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-18.png", + "language_code": "zh-HK" + }, + "banana-unripe-19.e61e6d7efaf2d8c8.webp": { + "original_hash": "9412ba91c9eca98b84c86de22b51631f", + "translation_date": "2026-01-16T09:47:06+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-19.png", + "language_code": "zh-HK" + }, + "banana-unripe-2.43a73b544521afc7.webp": { + "original_hash": "191f77cd53ad268a98fc833365c67fc5", + "translation_date": "2026-01-16T09:45:30+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/testing/unripe/banana-unripe-2.png", + "language_code": "zh-HK" + }, + "banana-unripe-2.9591d1a6aa27deeb.webp": { + "original_hash": "b414558382a2640d15611bd29ed8f5f0", + "translation_date": "2026-01-16T09:47:04+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-2.png", + "language_code": "zh-HK" + }, + "banana-unripe-20.85b7a74eaab5634e.webp": { + "original_hash": "cf0067ebc713ffa4be85f95463639d68", + "translation_date": "2026-01-16T09:47:01+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-20.png", + "language_code": "zh-HK" + }, + "banana-unripe-21.ccc1333439b344bc.webp": { + "original_hash": "6ee44eae72d211d164828835be1dd7c5", + "translation_date": "2026-01-16T09:47:08+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-21.png", + "language_code": "zh-HK" + }, + "banana-unripe-22.27dff4b438163080.webp": { + "original_hash": "a9a930c9ba5f72daa2205105248afd86", + "translation_date": "2026-01-16T09:47:00+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-22.png", + "language_code": "zh-HK" + }, + "banana-unripe-23.c4c9067f23370e90.webp": { + "original_hash": "141fdca6d816f53efd73fe0ae59d0a43", + "translation_date": "2026-01-16T09:46:54+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-23.png", + "language_code": "zh-HK" + }, + "banana-unripe-24.6f0b781c309da62f.webp": { + "original_hash": "c36781ac98c350de305cc9864e4e4b67", + "translation_date": "2026-01-16T09:47:14+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-24.png", + "language_code": "zh-HK" + }, + "banana-unripe-25.21d553d84880ac4f.webp": { + "original_hash": "534e54b877de9b3bc7c5e67e6fb426b5", + "translation_date": "2026-01-16T09:47:11+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-25.png", + "language_code": "zh-HK" + }, + "banana-unripe-26.823c48b61feb1d5c.webp": { + "original_hash": "f541d556eff5be42991bba984ca305f9", + "translation_date": "2026-01-16T09:47:15+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-26.png", + "language_code": "zh-HK" + }, + "banana-unripe-27.f98fd272deeb02d9.webp": { + "original_hash": "32ffe72a780f2ae97ac1b189d0fb9a44", + "translation_date": "2026-01-16T09:46:56+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-27.png", + "language_code": "zh-HK" + }, + "banana-unripe-28.5331ba409ce41c07.webp": { + "original_hash": "75f6d0f1ad3d2ca875d31421b8f35deb", + "translation_date": "2026-01-16T09:46:59+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-28.png", + "language_code": "zh-HK" + }, + "banana-unripe-29.84e126f389bf627e.webp": { + "original_hash": "fad512ccb4e4129d91a10b915bc55829", + "translation_date": "2026-01-16T09:47:09+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-29.png", + "language_code": "zh-HK" + }, + "banana-unripe-3.896df8fb2c3b8f51.webp": { + "original_hash": "16d53ba9a1f72a431e04901214d53f2e", + "translation_date": "2026-01-16T09:47:02+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-3.png", + "language_code": "zh-HK" + }, + "banana-unripe-4.483e740d6fd7b5a6.webp": { + "original_hash": "e0ff72cd82b8c7c4595e1beec806b8cb", + "translation_date": "2026-01-16T09:46:57+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-4.png", + "language_code": "zh-HK" + }, + "banana-unripe-5.e9923cf1ffcfc1c9.webp": { + "original_hash": "c46c19087503e1173c9c214750b06c8f", + "translation_date": "2026-01-16T09:47:10+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-5.png", + "language_code": "zh-HK" + }, + "banana-unripe-6.e3a73307558caecc.webp": { + "original_hash": "29d58bf3f0290ac98d5c748f467ab900", + "translation_date": "2026-01-16T09:46:55+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-6.png", + "language_code": "zh-HK" + }, + "banana-unripe-7.634ca89acc17d68f.webp": { + "original_hash": "90677247facc68c33c17c37f03d6531d", + "translation_date": "2026-01-16T09:46:58+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-7.png", + "language_code": "zh-HK" + }, + "banana-unripe-8.75720b4cdebac8c3.webp": { + "original_hash": "b2c3f312e05c258ea969ea1510c9e8d7", + "translation_date": "2026-01-16T09:47:07+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-8.png", + "language_code": "zh-HK" + }, + "banana-unripe-9.e8076983351d2f54.webp": { + "original_hash": "93eac8be89da4c1db40c38166f18d515", + "translation_date": "2026-01-16T09:47:14+00:00", + "source_file": "4-manufacturing/lessons/1-train-fruit-detector/images/training/unripe/banana-unripe-9.png", + "language_code": "zh-HK" + }, + "banana-unripe-quick-test-prediction.dae9b5e1c4ef7c64.webp": { + "original_hash": "7351b0cf725cfdc62e6b44d3c25ece0b", + "translation_date": "2026-01-16T09:16:40+00:00", + "source_file": "images/banana-unripe-quick-test-prediction.png", + "language_code": "zh-HK" + }, + "bananas-ripe-vs-unripe-predictions.8d0e2034014aa50e.webp": { + "original_hash": "43cbf29c310e7358f9ac1baa54fcf34f", + "translation_date": "2026-01-16T09:24:44+00:00", + "source_file": "images/bananas-ripe-vs-unripe-predictions.png", + "language_code": "zh-HK" + }, + "basic-thermostat.a923217fd1f37e5a.webp": { + "original_hash": "50447b3cb751fb0fe9d4d79b89e2b923", + "translation_date": "2026-01-16T09:20:44+00:00", + "source_file": "images/basic-thermostat.png", + "language_code": "zh-HK" + }, + "bing-translate.348aa796d6efe2a9.webp": { + "original_hash": "357040ee1345ab2473e9b5284f85e81b", + "translation_date": "2026-01-16T09:02:35+00:00", + "source_file": "images/bing-translate.png", + "language_code": "zh-HK" + }, + "bounding-box.1420a7ea0d3d15f7.webp": { + "original_hash": "6e8d5968c9badbf125027b02a8f5cacd", + "translation_date": "2026-01-16T09:22:30+00:00", + "source_file": "images/bounding-box.png", + "language_code": "zh-HK" + }, + "bricked-car.dc38f8efadc6c59d.webp": { + "original_hash": "67716470d43e5717406c6ef7ce6f51e4", + "translation_date": "2026-01-16T09:19:58+00:00", + "source_file": "images/bricked-car.png", + "language_code": "zh-HK" + }, + "button-with-digital.3749edea8eb885af.webp": { + "original_hash": "c71d7fe1263b112c755f3d0442406df8", + "translation_date": "2026-01-16T09:12:07+00:00", + "source_file": "images/button-with-digital.png", + "language_code": "zh-HK" + }, + "button.eadb560b77ac45e5.webp": { + "original_hash": "85b6123798803252b7f30ffce7dc58c8", + "translation_date": "2026-01-16T09:02:20+00:00", + "source_file": "images/button.png", + "language_code": "zh-HK" + }, + "chart-soil-moisture-relay.fbb391236d34a64d.webp": { + "original_hash": "2528b9288bbb1201b87058b75d516b0a", + "translation_date": "2026-01-16T09:03:26+00:00", + "source_file": "images/chart-soil-moisture-relay.png", + "language_code": "zh-HK" + }, + "chart-soil-moisture.fd6d9d0cdc0b5f75.webp": { + "original_hash": "7dfa7af1913187ec9700073ccfac2d7e", + "translation_date": "2026-01-16T09:12:53+00:00", + "source_file": "images/chart-soil-moisture.png", + "language_code": "zh-HK" + }, + "child-watering-garden.9a5d3f1bfe6d0d8d.webp": { + "original_hash": "c547237141a5201756db33e5e977a363", + "translation_date": "2026-01-16T09:04:46+00:00", + "source_file": "images/child-watering-garden.jpg", + "language_code": "zh-HK" + }, + "cloud-with-edge.1e26462c62c126fe.webp": { + "original_hash": "5f1262d7eaa6699d85c7b7c89ed6eeb0", + "translation_date": "2026-01-16T09:04:10+00:00", + "source_file": "images/cloud-with-edge.png", + "language_code": "zh-HK" + }, + "cloud-without-edge.b4da641f6022c95e.webp": { + "original_hash": "7788335416c9ad8abd402188a02680b2", + "translation_date": "2026-01-16T09:14:19+00:00", + "source_file": "images/cloud-without-edge.png", + "language_code": "zh-HK" + }, + "cmos-sensor.75f9cd74decb1371.webp": { + "original_hash": "955bcd3e34765f989cd7c0df5909560d", + "translation_date": "2026-01-16T09:21:23+00:00", + "source_file": "images/cmos-sensor.png", + "language_code": "zh-HK" + }, + "commands.d6c06bbbb3a02cce.webp": { + "original_hash": "8390a0fb6de9ed7ea485aa8b7885d107", + "translation_date": "2026-01-16T09:26:28+00:00", + "source_file": "images/commands.png", + "language_code": "zh-HK" + }, + "condenser-mic.6f6ed5b76ca19e0e.webp": { + "original_hash": "b1b138069d34d7c5c124b0d8e69a8089", + "translation_date": "2026-01-16T09:16:15+00:00", + "source_file": "images/condenser-mic.jpg", + "language_code": "zh-HK" + }, + "consumer-groups.a3262e26fc27ba20.webp": { + "original_hash": "052b922d0ba2c50c83132074572c68e3", + "translation_date": "2026-01-16T09:17:15+00:00", + "source_file": "images/consumer-groups.png", + "language_code": "zh-HK" + }, + "container-edge-flow.c246050dd60ceefd.webp": { + "original_hash": "82e97f8c96fa7aa5acddb9e2bace544b", + "translation_date": "2026-01-16T09:14:41+00:00", + "source_file": "images/container-edge-flow.png", + "language_code": "zh-HK" + }, + "container-web-browser.4ee81dd4f0d8838c.webp": { + "original_hash": "ed428a4061a9c8a7bd3b46eb92709603", + "translation_date": "2026-01-16T09:11:45+00:00", + "source_file": "images/container-web-browser.png", + "language_code": "zh-HK" + }, + "counterfit-camera-options.eb3bd5150a8e7dff.webp": { + "original_hash": "bad454364357d90492676e610b7bfaaf", + "translation_date": "2026-01-16T09:17:53+00:00", + "source_file": "images/counterfit-camera-options.png", + "language_code": "zh-HK" + }, + "counterfit-camera.001ec52194c8ee5d.webp": { + "original_hash": "ce27314b8b055207bd6d020cd0f16403", + "translation_date": "2026-01-16T09:15:22+00:00", + "source_file": "images/counterfit-camera.png", + "language_code": "zh-HK" + }, + "counterfit-connected.ed30b46d8f79b092.webp": { + "original_hash": "9deaaeb5131e9a263fbbecb01f7997f6", + "translation_date": "2026-01-16T09:19:04+00:00", + "source_file": "images/counterfit-connected.png", + "language_code": "zh-HK" + }, + "counterfit-create-camera.a5de97f59c0bd3cb.webp": { + "original_hash": "d90405c8dbb6738e0e11a8ed2fea2b56", + "translation_date": "2026-01-16T09:05:56+00:00", + "source_file": "images/counterfit-create-camera.png", + "language_code": "zh-HK" + }, + "counterfit-create-distance-sensor.967c9fb98f27888d.webp": { + "original_hash": "fc393748f2ddbae005330e640d841c41", + "translation_date": "2026-01-16T09:06:41+00:00", + "source_file": "images/counterfit-create-distance-sensor.png", + "language_code": "zh-HK" + }, + "counterfit-create-gps-sensor.6385dc9357d85ad1.webp": { + "original_hash": "57eef4bdcf582fa280abe6860d6605c9", + "translation_date": "2026-01-16T09:06:45+00:00", + "source_file": "images/counterfit-create-gps-sensor.png", + "language_code": "zh-HK" + }, + "counterfit-create-humidity-sensor.2750e27b6f30e09c.webp": { + "original_hash": "2678e71d9f5f0b887059244ceb0747e2", + "translation_date": "2026-01-16T09:21:37+00:00", + "source_file": "images/counterfit-create-humidity-sensor.png", + "language_code": "zh-HK" + }, + "counterfit-create-led.ba9db1c9b8c622a6.webp": { + "original_hash": "748880e7a96e6fe87be2d253398d1d8a", + "translation_date": "2026-01-16T09:10:13+00:00", + "source_file": "images/counterfit-create-led.png", + "language_code": "zh-HK" + }, + "counterfit-create-light-sensor.9f36a5e0d4458d8d.webp": { + "original_hash": "8906d361868d0342fccec9bb4952c74b", + "translation_date": "2026-01-16T09:20:15+00:00", + "source_file": "images/counterfit-create-light-sensor.png", + "language_code": "zh-HK" + }, + "counterfit-create-relay.fa7c40fd0f2f6afc.webp": { + "original_hash": "69d09420bf11407584ef8f7c2a4b6934", + "translation_date": "2026-01-16T09:14:14+00:00", + "source_file": "images/counterfit-create-relay.png", + "language_code": "zh-HK" + }, + "counterfit-create-soil-moisture-sensor.35266135a5e0ae68.webp": { + "original_hash": "0d5b89d4e4a3e0fcd2b30cb15aa9c37f", + "translation_date": "2026-01-16T09:20:57+00:00", + "source_file": "images/counterfit-create-soil-moisture-sensor.png", + "language_code": "zh-HK" + }, + "counterfit-create-temperature-sensor.199350ed34f7343d.webp": { + "original_hash": "24a54b9c88c3efc0a31bb9d9db572985", + "translation_date": "2026-01-16T09:11:41+00:00", + "source_file": "images/counterfit-create-temperature-sensor.png", + "language_code": "zh-HK" + }, + "counterfit-distance-sensor.079eefeeea0b68af.webp": { + "original_hash": "87337c291c694bf955c4ea27d16b527b", + "translation_date": "2026-01-16T09:04:58+00:00", + "source_file": "images/counterfit-distance-sensor.png", + "language_code": "zh-HK" + }, + "counterfit-first-run.433326358b669b31.webp": { + "original_hash": "af69e87134afcc0b94a0ee13d7127e83", + "translation_date": "2026-01-16T09:03:57+00:00", + "source_file": "images/counterfit-first-run.png", + "language_code": "zh-HK" + }, + "counterfit-gps-sensor-gpxfile.8310b063ce8a425c.webp": { + "original_hash": "0160890d86b609909702f86540b4d7df", + "translation_date": "2026-01-16T09:06:06+00:00", + "source_file": "images/counterfit-gps-sensor-gpxfile.png", + "language_code": "zh-HK" + }, + "counterfit-gps-sensor-latlon.008c867d75464fbe.webp": { + "original_hash": "176d91894e0da37cc78d56aed5858fac", + "translation_date": "2026-01-16T09:19:52+00:00", + "source_file": "images/counterfit-gps-sensor-latlon.png", + "language_code": "zh-HK" + }, + "counterfit-gps-sensor-nmea.c62eea442171e17e.webp": { + "original_hash": "afbf9dca4a4c89c0b21abb6a6d163635", + "translation_date": "2026-01-16T09:03:06+00:00", + "source_file": "images/counterfit-gps-sensor-nmea.png", + "language_code": "zh-HK" + }, + "counterfit-gps-sensor.3fbb15af0a536756.webp": { + "original_hash": "f5317788426731587cefceac202102ed", + "translation_date": "2026-01-16T09:14:25+00:00", + "source_file": "images/counterfit-gps-sensor.png", + "language_code": "zh-HK" + }, + "counterfit-humidity-sensor.7b12f7f339e430cb.webp": { + "original_hash": "7bb1b1afc76c63ba7cf4c7f7bf9a134c", + "translation_date": "2026-01-16T09:26:25+00:00", + "source_file": "images/counterfit-humidity-sensor.png", + "language_code": "zh-HK" + }, + "counterfit-led.c0ab02de6d256ad8.webp": { + "original_hash": "c03cf80f7745d1caf7b7ece187e3ac7b", + "translation_date": "2026-01-16T09:25:53+00:00", + "source_file": "images/counterfit-led.png", + "language_code": "zh-HK" + }, + "counterfit-light-sensor.5d0f5584df56b90f.webp": { + "original_hash": "938da9f71fb2820061acca73bb053a68", + "translation_date": "2026-01-16T09:06:54+00:00", + "source_file": "images/counterfit-light-sensor.png", + "language_code": "zh-HK" + }, + "counterfit-relay.bbf74c1dbdc8b9ac.webp": { + "original_hash": "0a28fe42b27b9898e0eba6e65faef94a", + "translation_date": "2026-01-16T09:25:56+00:00", + "source_file": "images/counterfit-relay.png", + "language_code": "zh-HK" + }, + "counterfit-soil-moisture-sensor.81742b2de0e9de60.webp": { + "original_hash": "54613df49c9e3c09be6ddc351dae1875", + "translation_date": "2026-01-16T09:21:43+00:00", + "source_file": "images/counterfit-soil-moisture-sensor.png", + "language_code": "zh-HK" + }, + "counterfit-temperature-sensor.f0560236c96a9016.webp": { + "original_hash": "52e9ae650e824d9c36c419fcf6febef7", + "translation_date": "2026-01-16T09:04:43+00:00", + "source_file": "images/counterfit-temperature-sensor.png", + "language_code": "zh-HK" + }, + "custom-vision-banana-prediction.30cdff4e1d72db5d.webp": { + "original_hash": "1962f0cce26ba3d8a180acaec2972895", + "translation_date": "2026-01-16T09:20:20+00:00", + "source_file": "images/custom-vision-banana-prediction.png", + "language_code": "zh-HK" + }, + "custom-vision-create-object-detector-project.32d4fb9aa8e7e737.webp": { + "original_hash": "052a80c8a99c7f2a0400338775c8c132", + "translation_date": "2026-01-16T09:05:49+00:00", + "source_file": "images/custom-vision-create-object-detector-project.png", + "language_code": "zh-HK" + }, + "custom-vision-create-project.cf46325b92d8b131.webp": { + "original_hash": "4471516b6b46ce7920ac178930397ccc", + "translation_date": "2026-01-16T09:12:27+00:00", + "source_file": "images/custom-vision-create-project.png", + "language_code": "zh-HK" + }, + "custom-vision-logo.d3d4e7c8a87ec9da.webp": { + "original_hash": "b67626a2dbc98e461f19f42db2de3a5b", + "translation_date": "2026-01-16T09:20:54+00:00", + "source_file": "images/custom-vision-logo.png", + "language_code": "zh-HK" + }, + "custom-vision-object-detector-publish-button.34ee379fc650ccb9.webp": { + "original_hash": "dd33f3d305ce96a1831bd1726dc9353b", + "translation_date": "2026-01-16T09:18:43+00:00", + "source_file": "images/custom-vision-object-detector-publish-button.png", + "language_code": "zh-HK" + }, + "custom-vision-prediction-key-endpoint.30c569ffd0338864.webp": { + "original_hash": "99d1145bc97ed96e187d6a5590c4332c", + "translation_date": "2026-01-16T09:07:46+00:00", + "source_file": "images/custom-vision-prediction-key-endpoint.png", + "language_code": "zh-HK" + }, + "custom-vision-publish-button.b7174e1977b0c33b.webp": { + "original_hash": "60c4e9f0efc81552bf0ad33db2688757", + "translation_date": "2026-01-16T09:06:21+00:00", + "source_file": "images/custom-vision-publish-button.png", + "language_code": "zh-HK" + }, + "custom-vision-stock-prediction.942266ab1bcca341.webp": { + "original_hash": "0c70587ccdba9496866b14ffe90cfd7b", + "translation_date": "2026-01-16T09:17:25+00:00", + "source_file": "images/custom-vision-stock-prediction.png", + "language_code": "zh-HK" + }, + "dimmable-light.9ceffeb195dec1a8.webp": { + "original_hash": "d64c8b23c012ebdbcacfb9bc0ac88b78", + "translation_date": "2026-01-16T09:17:48+00:00", + "source_file": "images/dimmable-light.png", + "language_code": "zh-HK" + }, + "dmac-adc-buffers.4509aee49145c90b.webp": { + "original_hash": "1351cbaad314ff7f81234448cdc5a1c9", + "translation_date": "2026-01-16T09:09:53+00:00", + "source_file": "images/dmac-adc-buffers.png", + "language_code": "zh-HK" + }, + "dynamic-mic.8babac890a2d80df.webp": { + "original_hash": "56710f289e88457d2e977fc9d3806569", + "translation_date": "2026-01-16T09:21:06+00:00", + "source_file": "images/dynamic-mic.jpg", + "language_code": "zh-HK" + }, + "favicon.37b561214b36d454.webp": { + "original_hash": "228faa6584f8ba1f7e9a75e3200112e9", + "translation_date": "2026-01-16T09:04:46+00:00", + "source_file": "images/favicon.png", + "language_code": "zh-HK" + }, + "fetch-decode-execute.2fd6f150f6280392.webp": { + "original_hash": "5bf630278697e98b9d322229d98d403c", + "translation_date": "2026-01-16T09:01:35+00:00", + "source_file": "images/fetch-decode-execute.png", + "language_code": "zh-HK" + }, + "fruit-quality-detector-message-flow.adf2a65da8fd8741.webp": { + "original_hash": "a7dab1140d82034626bb8de5f186131e", + "translation_date": "2026-01-16T09:10:32+00:00", + "source_file": "images/fruit-quality-detector-message-flow.png", + "language_code": "zh-HK" + }, + "gdd-calculation-corn.64a58b7a7afcd0df.webp": { + "original_hash": "313f7af564decf12984266f48531ca25", + "translation_date": "2026-01-16T09:15:45+00:00", + "source_file": "images/gdd-calculation-corn.png", + "language_code": "zh-HK" + }, + "gdd-calculation-strawberries.59f57db94b22adb8.webp": { + "original_hash": "8bbc10fc92e798cafb77786f7fee2fec", + "translation_date": "2026-01-16T09:06:00+00:00", + "source_file": "images/gdd-calculation-strawberries.png", + "language_code": "zh-HK" + }, + "gdd-calculation.79b3660f9c5757aa.webp": { + "original_hash": "bf4227ba3a7cfaae2a832050d904960e", + "translation_date": "2026-01-16T09:12:58+00:00", + "source_file": "images/gdd-calculation.png", + "language_code": "zh-HK" + }, + "gdd-jupyter-notebook.c5b52cf21094f158.webp": { + "original_hash": "85379646b4bef7edbfb10f7f457a3fda", + "translation_date": "2026-01-16T09:07:30+00:00", + "source_file": "images/gdd-jupyter-notebook.png", + "language_code": "zh-HK" + }, + "geofence-crossing-inaccurate-gps.6a3ed911202ad9ca.webp": { + "original_hash": "478511d89755e68ad1b0a8cd6cb72eef", + "translation_date": "2026-01-16T09:18:20+00:00", + "source_file": "images/geofence-crossing-inaccurate-gps.png", + "language_code": "zh-HK" + }, + "geofence-examples.172fbc534665769f.webp": { + "original_hash": "e836773107dcaf3aa2dcbf4b072bf1d0", + "translation_date": "2026-01-16T09:05:25+00:00", + "source_file": "images/geofence-examples.png", + "language_code": "zh-HK" + }, + "gps-satellites.04acf1148fe25fbf.webp": { + "original_hash": "af44157de12f3de1e84ea34fc9da6647", + "translation_date": "2026-01-16T09:21:47+00:00", + "source_file": "images/gps-satellites.png", + "language_code": "zh-HK" + }, + "gps-telemetry-iot-hub-functions.24d3fa5592455e9f.webp": { + "original_hash": "fe6ecba627bb1ac7c0f7123dcdc89c1c", + "translation_date": "2026-01-16T09:02:31+00:00", + "source_file": "images/gps-telemetry-iot-hub-functions.png", + "language_code": "zh-HK" + }, + "gps-telemetry-iot-hub.8115335d51cd2c12.webp": { + "original_hash": "594448866b50836b5d5d664571ae8144", + "translation_date": "2026-01-16T09:14:10+00:00", + "source_file": "images/gps-telemetry-iot-hub.png", + "language_code": "zh-HK" + }, + "grove-base-hat-ribbon-cable.501fed202fcf73b1.webp": { + "original_hash": "aa2a325edef2b1fbd2d7639853d310fd", + "translation_date": "2026-01-16T09:17:37+00:00", + "source_file": "images/grove-base-hat-ribbon-cable.png", + "language_code": "zh-HK" + }, + "grove-button.a70cfbb809a85636.webp": { + "original_hash": "63615c1ec61dcf1763b1c406298bb67c", + "translation_date": "2026-01-16T09:18:13+00:00", + "source_file": "images/grove-button.png", + "language_code": "zh-HK" + }, + "grove-capacitive-soil-moisture-sensor.e7f0776cce30e78b.webp": { + "original_hash": "f1a5ee679e4c05c2b3047ef0d69f85f0", + "translation_date": "2026-01-16T09:18:57+00:00", + "source_file": "images/grove-capacitive-soil-moisture-sensor.png", + "language_code": "zh-HK" + }, + "grove-dht11.07f8eafceee17004.webp": { + "original_hash": "e66c4f14e29b96bcd1d5a8c86c19288d", + "translation_date": "2026-01-16T09:02:50+00:00", + "source_file": "images/grove-dht11.png", + "language_code": "zh-HK" + }, + "grove-gps-sensor.247943bf69b03f0d.webp": { + "original_hash": "2cdf74787c6cf892a6b1a0f1753f8b24", + "translation_date": "2026-01-16T09:11:13+00:00", + "source_file": "images/grove-gps-sensor.png", + "language_code": "zh-HK" + }, + "grove-led.6c853be93f473cf2.webp": { + "original_hash": "ccf9fb22df3755582b81bae02335135b", + "translation_date": "2026-01-16T09:18:50+00:00", + "source_file": "images/grove-led.png", + "language_code": "zh-HK" + }, + "grove-light-sensor.b8127b7c434e632d.webp": { + "original_hash": "1e6e9a840b1f0b0dd777593c38d31109", + "translation_date": "2026-01-16T09:10:20+00:00", + "source_file": "images/grove-light-sensor.png", + "language_code": "zh-HK" + }, + "grove-relay-labelled.293e068f5c3c2a19.webp": { + "original_hash": "b75dcabfa78bcfca34b6cce74323e178", + "translation_date": "2026-01-16T09:05:42+00:00", + "source_file": "images/grove-relay-labelled.png", + "language_code": "zh-HK" + }, + "grove-relay.d426958ca210fbd0.webp": { + "original_hash": "ceed414ceb65226b0e91bf1147d1fa01", + "translation_date": "2026-01-16T09:10:51+00:00", + "source_file": "images/grove-relay.png", + "language_code": "zh-HK" + }, + "grove-time-of-flight-sensor.d82ff2165bfded9f.webp": { + "original_hash": "dcc2dfd53862a2533a34b9bb7913ae90", + "translation_date": "2026-01-16T09:22:15+00:00", + "source_file": "images/grove-time-of-flight-sensor.png", + "language_code": "zh-HK" + }, + "gsm-calculation-example.99f9803b4f29e976.webp": { + "original_hash": "0421a50ee61d76fda1380f899c4fd99c", + "translation_date": "2026-01-16T09:24:57+00:00", + "source_file": "images/gsm-calculation-example.png", + "language_code": "zh-HK" + }, + "gsm-calculation.6da38c6201eec14e.webp": { + "original_hash": "9b2287a653a1fd6ee0480021a98bca3b", + "translation_date": "2026-01-16T09:22:10+00:00", + "source_file": "images/gsm-calculation.png", + "language_code": "zh-HK" + }, + "i2c.83da845dde02256b.webp": { + "original_hash": "198d21e02d4c69b84028bd69172ffa8e", + "translation_date": "2026-01-16T09:14:04+00:00", + "source_file": "images/i2c.png", + "language_code": "zh-HK" + }, + "image-classifier-cashews-tomato.bc2e16ab8f05cf9a.webp": { + "original_hash": "1b245f1520995579839a00cb93a09242", + "translation_date": "2026-01-16T09:20:48+00:00", + "source_file": "images/image-classifier-cashews-tomato.png", + "language_code": "zh-HK" + }, + "image-upload-bananas.0751639f3815e0ec.webp": { + "original_hash": "a293096ce03324bb0282c712610f6892", + "translation_date": "2026-01-16T09:16:50+00:00", + "source_file": "images/image-upload-bananas.png", + "language_code": "zh-HK" + }, + "image-upload-object-detector.77c7892c3093cb59.webp": { + "original_hash": "d66ff14cd73c4acfbb2ce4b15a256850", + "translation_date": "2026-01-16T09:02:25+00:00", + "source_file": "images/image-upload-object-detector.png", + "language_code": "zh-HK" + }, + "iot-device-and-hacked-device-connecting-encryption.5941aff601fc978f.webp": { + "original_hash": "b55ce16301f8b389703a495e87d30f89", + "translation_date": "2026-01-16T09:24:34+00:00", + "source_file": "images/iot-device-and-hacked-device-connecting-encryption.png", + "language_code": "zh-HK" + }, + "iot-device-and-hacked-device-connecting.e0671675df74d6d9.webp": { + "original_hash": "4424245692fededc88fa35415899b600", + "translation_date": "2026-01-16T09:21:02+00:00", + "source_file": "images/iot-device-and-hacked-device-connecting.png", + "language_code": "zh-HK" + }, + "iot-for-beginners.95958e2ed1900917.webp": { + "original_hash": "0ee1737da60391e3b1d48686e1d7c893", + "translation_date": "2026-01-16T09:05:37+00:00", + "source_file": "images/iot-for-beginners.png", + "language_code": "zh-HK" + }, + "iot-hub-cloud-to-device-message.f4f21fea772cc20b.webp": { + "original_hash": "d5e70a4a54a0038278c5bdc2b4988f8e", + "translation_date": "2026-01-16T09:26:33+00:00", + "source_file": "images/iot-hub-cloud-to-device-message.png", + "language_code": "zh-HK" + }, + "iot-hub-device-to-cloud-message.e46e584d87f35fd9.webp": { + "original_hash": "f080414808052183955ae04971260ebb", + "translation_date": "2026-01-16T09:25:49+00:00", + "source_file": "images/iot-hub-device-to-cloud-message.png", + "language_code": "zh-HK" + }, + "iot-hub-device-twins.7055a60fc5e2331c.webp": { + "original_hash": "ddaade630edadb655820875bc6a7a398", + "translation_date": "2026-01-16T09:03:11+00:00", + "source_file": "images/iot-hub-device-twins.png", + "language_code": "zh-HK" + }, + "iot-hub-direct-method-request.86a5026e91f4ca18.webp": { + "original_hash": "8940e81543500cb4f065eed262888ba0", + "translation_date": "2026-01-16T09:21:15+00:00", + "source_file": "images/iot-hub-direct-method-request.png", + "language_code": "zh-HK" + }, + "iot-messages-to-serverless.0194da1cc0732bb7.webp": { + "original_hash": "b845f5d76ec996b99ee19d76c7254e4f", + "translation_date": "2026-01-16T09:11:02+00:00", + "source_file": "images/iot-messages-to-serverless.png", + "language_code": "zh-HK" + }, + "iot-reference-architecture-azure.0b8d2161af924cb1.webp": { + "original_hash": "cf394dd153db645ed55d362824b4beff", + "translation_date": "2026-01-16T09:13:28+00:00", + "source_file": "images/iot-reference-architecture-azure.png", + "language_code": "zh-HK" + }, + "iot-reference-architecture-fruit-quality.cc705f121c3b6fa7.webp": { + "original_hash": "23d41791f8c62c0f91da0979160cada7", + "translation_date": "2026-01-16T09:07:04+00:00", + "source_file": "images/iot-reference-architecture-fruit-quality.png", + "language_code": "zh-HK" + }, + "iot-reference-architecture.2278b98b55c6d4e8.webp": { + "original_hash": "a06f575f400e2323f68d231b4867cee4", + "translation_date": "2026-01-16T09:16:57+00:00", + "source_file": "images/iot-reference-architecture.png", + "language_code": "zh-HK" + }, + "iot-service-allowed-denied-connection.818b0063ac213fb8.webp": { + "original_hash": "e39609b2a6e0a6f3a09f869f30654e4c", + "translation_date": "2026-01-16T09:21:10+00:00", + "source_file": "images/iot-service-allowed-denied-connection.png", + "language_code": "zh-HK" + }, + "iot-service-connectivity.7e873847921a5d6f.webp": { + "original_hash": "b69786f576a7862abc86d0d03a68ebb7", + "translation_date": "2026-01-16T09:12:13+00:00", + "source_file": "images/iot-service-connectivity.png", + "language_code": "zh-HK" + }, + "latitude-equator.feccc3214b7d9fb1.webp": { + "original_hash": "56a4f9ad1e573c57538578c32f9d86da", + "translation_date": "2026-01-16T09:10:37+00:00", + "source_file": "images/latitude-equator.png", + "language_code": "zh-HK" + }, + "latitude-lines.11d8d91dfb2014a5.webp": { + "original_hash": "a0c885ec44be899480f5e591ff23f911", + "translation_date": "2026-01-16T09:04:04+00:00", + "source_file": "images/latitude-lines.png", + "language_code": "zh-HK" + }, + "led-digital-control.13b9be14077ea49f.webp": { + "original_hash": "48875cd4b26e07780599d8a068c0bf83", + "translation_date": "2026-01-16T09:16:12+00:00", + "source_file": "images/led-digital-control.png", + "language_code": "zh-HK" + }, + "led.ec6d94f66676a174.webp": { + "original_hash": "8dd1438a3970c2bd893f35af372b1a60", + "translation_date": "2026-01-16T09:20:03+00:00", + "source_file": "images/led.png", + "language_code": "zh-HK" + }, + "lesson-1.2606670fa61ee904.webp": { + "original_hash": "84859c69fb3a2f18ffbe095a2102d7f6", + "translation_date": "2026-01-16T09:39:37+00:00", + "source_file": "sketchnotes/lesson-1.jpg", + "language_code": "zh-HK" + }, + "lesson-10.829c86b80b9403bb.webp": { + "original_hash": "d01fb71820808d72515cf0b7d68bab41", + "translation_date": "2026-01-16T09:40:31+00:00", + "source_file": "sketchnotes/lesson-10.jpg", + "language_code": "zh-HK" + }, + "lesson-11.9fddbac4b664c6d5.webp": { + "original_hash": "b1f802eab377a9252f62e9309f8e592a", + "translation_date": "2026-01-16T09:40:48+00:00", + "source_file": "sketchnotes/lesson-11.jpg", + "language_code": "zh-HK" + }, + "lesson-12.ca7f53039712a3ec.webp": { + "original_hash": "967e8e3853bc164050b097ea16490057", + "translation_date": "2026-01-16T09:32:32+00:00", + "source_file": "sketchnotes/lesson-12.jpg", + "language_code": "zh-HK" + }, + "lesson-13.a259db1485021be7.webp": { + "original_hash": "49048659f9a44917256f7b4b7f864f62", + "translation_date": "2026-01-16T09:31:49+00:00", + "source_file": "sketchnotes/lesson-13.jpg", + "language_code": "zh-HK" + }, + "lesson-14.63980c5150ae3c15.webp": { + "original_hash": "b6ce8e2bb49d1616057ee45f2e37fe8f", + "translation_date": "2026-01-16T09:37:39+00:00", + "source_file": "sketchnotes/lesson-14.jpg", + "language_code": "zh-HK" + }, + "lesson-15.843d21afdc6fb2bb.webp": { + "original_hash": "c44ff172164fdfbafabf0c806f5e8593", + "translation_date": "2026-01-16T09:26:52+00:00", + "source_file": "sketchnotes/lesson-15.jpg", + "language_code": "zh-HK" + }, + "lesson-16.215daf18b00631fb.webp": { + "original_hash": "0bd07a79a71954ad4f5b585972d4deb9", + "translation_date": "2026-01-16T09:32:43+00:00", + "source_file": "sketchnotes/lesson-16.jpg", + "language_code": "zh-HK" + }, + "lesson-17.bc333c3c35ba8e42.webp": { + "original_hash": "5f7d869576300be85ed519ef16ff6a39", + "translation_date": "2026-01-16T09:32:12+00:00", + "source_file": "sketchnotes/lesson-17.jpg", + "language_code": "zh-HK" + }, + "lesson-18.92c32ed1d354caa5.webp": { + "original_hash": "89d2f47ae6201d70aec617d32cd09f53", + "translation_date": "2026-01-16T09:40:40+00:00", + "source_file": "sketchnotes/lesson-18.jpg", + "language_code": "zh-HK" + }, + "lesson-19.cf6973cecadf080c.webp": { + "original_hash": "7e8769bcee6aeb9ebbb9c1752847f908", + "translation_date": "2026-01-16T09:29:28+00:00", + "source_file": "sketchnotes/lesson-19.jpg", + "language_code": "zh-HK" + }, + "lesson-2.324b0580d620c25e.webp": { + "original_hash": "ec89ab3c7a4154d500a4249e4b611ce3", + "translation_date": "2026-01-16T09:36:38+00:00", + "source_file": "sketchnotes/lesson-2.jpg", + "language_code": "zh-HK" + }, + "lesson-20.0211df9551a8abb3.webp": { + "original_hash": "6955631846f6c3a04436440cbe55ec9e", + "translation_date": "2026-01-16T09:26:45+00:00", + "source_file": "sketchnotes/lesson-20.jpg", + "language_code": "zh-HK" + }, + "lesson-21.e34de51354d6606f.webp": { + "original_hash": "918fab6c83b650cf67a39f5a9e740609", + "translation_date": "2026-01-16T09:31:55+00:00", + "source_file": "sketchnotes/lesson-21.jpg", + "language_code": "zh-HK" + }, + "lesson-22.6148ea28500d9e00.webp": { + "original_hash": "744a23c671043c5538445857370ea60e", + "translation_date": "2026-01-16T09:32:03+00:00", + "source_file": "sketchnotes/lesson-22.jpg", + "language_code": "zh-HK" + }, + "lesson-23.f38483e1d4df4828.webp": { + "original_hash": "cef842f7b80b336838233fcf1be1029c", + "translation_date": "2026-01-16T09:37:53+00:00", + "source_file": "sketchnotes/lesson-23.jpg", + "language_code": "zh-HK" + }, + "lesson-24.4246968ed058510a.webp": { + "original_hash": "08ff9d2f7cd0d595eb30e96375add9f3", + "translation_date": "2026-01-16T09:32:24+00:00", + "source_file": "sketchnotes/lesson-24.jpg", + "language_code": "zh-HK" + }, + "lesson-3.cc3b7b4cd646de59.webp": { + "original_hash": "4ef30b3fb0ed4914bfc4753db2e99a7b", + "translation_date": "2026-01-16T09:28:31+00:00", + "source_file": "sketchnotes/lesson-3.jpg", + "language_code": "zh-HK" + }, + "lesson-4.7344e074ea68fa54.webp": { + "original_hash": "8fec62a3c5722c6c9adeb713c30d685c", + "translation_date": "2026-01-16T09:34:19+00:00", + "source_file": "sketchnotes/lesson-4.jpg", + "language_code": "zh-HK" + }, + "lesson-5.42b234299279d263.webp": { + "original_hash": "691cf783fe7ef6b40bf300481e7326d2", + "translation_date": "2026-01-16T09:26:56+00:00", + "source_file": "sketchnotes/lesson-5.jpg", + "language_code": "zh-HK" + }, + "lesson-6.3e493b60eee85adc.webp": { + "original_hash": "94d827c7b07c857cf86360f7a42734e0", + "translation_date": "2026-01-16T09:37:32+00:00", + "source_file": "sketchnotes/lesson-6.jpg", + "language_code": "zh-HK" + }, + "lesson-7.30b5f577d3cb8e03.webp": { + "original_hash": "65e26ac18d8f2455e8c5241431133288", + "translation_date": "2026-01-16T09:26:38+00:00", + "source_file": "sketchnotes/lesson-7.jpg", + "language_code": "zh-HK" + }, + "lesson-8.3f21f3c11159e6a0.webp": { + "original_hash": "43f49f9a260610f3e5493dfdc6dc0d7f", + "translation_date": "2026-01-16T09:32:38+00:00", + "source_file": "sketchnotes/lesson-8.jpg", + "language_code": "zh-HK" + }, + "lesson-9.dfe99c8e891f48e1.webp": { + "original_hash": "7d6641c90a95897e05e60033cf61367f", + "translation_date": "2026-01-16T09:37:45+00:00", + "source_file": "sketchnotes/lesson-9.jpg", + "language_code": "zh-HK" + }, + "light-switch.760317ad6ab8bd6d.webp": { + "original_hash": "41d49f69f3e43729085745abb67167b8", + "translation_date": "2026-01-16T09:01:41+00:00", + "source_file": "images/light-switch.png", + "language_code": "zh-HK" + }, + "lines-of-longitude-and-latitude.032aca9d3e402c4e.webp": { + "original_hash": "f8604251b9319c765df2a2046e4d5f93", + "translation_date": "2026-01-16T09:20:42+00:00", + "source_file": "images/lines-of-longitude-and-latitude.png", + "language_code": "zh-HK" + }, + "longitude-meridians.ab4ef1c91c064586.webp": { + "original_hash": "dc8d5df51066d4009ed1696d40ae016d", + "translation_date": "2026-01-16T09:06:49+00:00", + "source_file": "images/longitude-meridians.png", + "language_code": "zh-HK" + }, + "longitude-prime-meridian.33b01b41ce615f9d.webp": { + "original_hash": "470be2d3dacf49b2a2a6ce93ac32169a", + "translation_date": "2026-01-16T09:22:12+00:00", + "source_file": "images/longitude-prime-meridian.png", + "language_code": "zh-HK" + }, + "luis-intent-examples.25716580b2d2723c.webp": { + "original_hash": "10c4351eab0a8d50545a8ae5dab7e893", + "translation_date": "2026-01-16T09:14:32+00:00", + "source_file": "images/luis-intent-examples.png", + "language_code": "zh-HK" + }, + "luis-logo.5cb4f3e88c020ee6.webp": { + "original_hash": "501332df88cd55536be18754a26caffd", + "translation_date": "2026-01-16T09:13:19+00:00", + "source_file": "images/luis-logo.png", + "language_code": "zh-HK" + }, + "map-image.8fb2c53eb23ef39c.webp": { + "original_hash": "77c5609ccd32add2358d07375e1fb4ae", + "translation_date": "2026-01-16T09:22:00+00:00", + "source_file": "images/map-image.png", + "language_code": "zh-HK" + }, + "map-path.896832e72dc696ff.webp": { + "original_hash": "208641a804743048d5172bdcb674c6ce", + "translation_date": "2026-01-16T09:26:09+00:00", + "source_file": "images/map-path.png", + "language_code": "zh-HK" + }, + "mems-microphone.80574019e1f5e4d9.webp": { + "original_hash": "9fcdab93cb1a5f075fd7a7b87ee53ebd", + "translation_date": "2026-01-16T09:13:01+00:00", + "source_file": "images/mems-microphone.png", + "language_code": "zh-HK" + }, + "microsoft-gps-location-world.a321d481b010f6ad.webp": { + "original_hash": "deb0e7f79cc481afac238c73149d2f22", + "translation_date": "2026-01-16T09:04:27+00:00", + "source_file": "images/microsoft-gps-location-world.png", + "language_code": "zh-HK" + }, + "microsoft-gps-location.9eb77a13b22b7e70.webp": { + "original_hash": "657b30303221896c044fd9e1b9477c95", + "translation_date": "2026-01-16T09:11:32+00:00", + "source_file": "images/microsoft-gps-location.png", + "language_code": "zh-HK" + }, + "mobile-controlled-thermostat.4a994010473d8d6a.webp": { + "original_hash": "e5d3e7697b231ed03c0c9fcb05a79862", + "translation_date": "2026-01-16T09:26:21+00:00", + "source_file": "images/mobile-controlled-thermostat.png", + "language_code": "zh-HK" + }, + "mqtt.cbf7f21d9adc3e17.webp": { + "original_hash": "2622674075f6379bb851806552374257", + "translation_date": "2026-01-16T09:25:01+00:00", + "source_file": "images/mqtt.png", + "language_code": "zh-HK" + }, + "noqsl-database.62d24ccf5b73f60d.webp": { + "original_hash": "64109b6306a3c464b6e2b8bfb6a45665", + "translation_date": "2026-01-16T09:13:53+00:00", + "source_file": "images/noqsl-database.png", + "language_code": "zh-HK" + }, + "object-detector-cashews-tomato.1af7c26686b4db0e.webp": { + "original_hash": "3cb4f511cff4896b333181b8c0d19aa1", + "translation_date": "2026-01-16T09:20:30+00:00", + "source_file": "images/object-detector-cashews-tomato.png", + "language_code": "zh-HK" + }, + "object-detector-detected-tomato-paste.52656fe87af4c37b.webp": { + "original_hash": "5957b9bcae573e01c045354fc978f5e8", + "translation_date": "2026-01-16T09:12:40+00:00", + "source_file": "images/object-detector-detected-tomato-paste.png", + "language_code": "zh-HK" + }, + "object-detector-tag-tomato-paste.f47c362fb0f0eb58.webp": { + "original_hash": "453f6250e67659d363e7933e3999cd64", + "translation_date": "2026-01-16T09:13:45+00:00", + "source_file": "images/object-detector-tag-tomato-paste.png", + "language_code": "zh-HK" + }, + "optical-tomato-sorting.61aa134bdda4e5b1.webp": { + "original_hash": "ded722d4e89f50699032f038b439866d", + "translation_date": "2026-01-16T09:01:44+00:00", + "source_file": "images/optical-tomato-sorting.png", + "language_code": "zh-HK" + }, + "overlap-object-detection.d431e03cae75072a.webp": { + "original_hash": "9948ca331a7cbbf2ac9d7bb692603b20", + "translation_date": "2026-01-16T09:17:08+00:00", + "source_file": "images/overlap-object-detection.png", + "language_code": "zh-HK" + }, + "pi-button.c7a1a4f55943341c.webp": { + "original_hash": "94a08cd32cf5d198961e75bfb4ccd2d1", + "translation_date": "2026-01-16T09:13:16+00:00", + "source_file": "images/pi-button.png", + "language_code": "zh-HK" + }, + "pi-camera-module.4278753c31bd6e75.webp": { + "original_hash": "296dbc7764a334c9eea6e07d2bdf5228", + "translation_date": "2026-01-16T09:16:09+00:00", + "source_file": "images/pi-camera-module.png", + "language_code": "zh-HK" + }, + "pi-camera-ribbon-cable.0bf82acd251611c2.webp": { + "original_hash": "3ac274f1a38658e40c3c392746941cbd", + "translation_date": "2026-01-16T09:14:45+00:00", + "source_file": "images/pi-camera-ribbon-cable.png", + "language_code": "zh-HK" + }, + "pi-camera-socket-ribbon-cable.a18309920b118009.webp": { + "original_hash": "b34d7c3590218945fe8ac3cae8e30a50", + "translation_date": "2026-01-16T09:04:51+00:00", + "source_file": "images/pi-camera-socket-ribbon-cable.png", + "language_code": "zh-HK" + }, + "pi-camera-upside-down.5376961ba3145988.webp": { + "original_hash": "b242672b0a16425dc0c42e3458102e8c", + "translation_date": "2026-01-16T09:22:23+00:00", + "source_file": "images/pi-camera-upside-down.png", + "language_code": "zh-HK" + }, + "pi-gps-sensor.1f99ee2b2f652891.webp": { + "original_hash": "8096ac9ce04e5268b552024983283a21", + "translation_date": "2026-01-16T09:13:05+00:00", + "source_file": "images/pi-gps-sensor.png", + "language_code": "zh-HK" + }, + "pi-hardware-kit.26dbadaedb7dd44c.webp": { + "original_hash": "ff01874bf7ab355748ac69e0d18cc3a8", + "translation_date": "2026-01-16T09:03:43+00:00", + "source_file": "images/pi-hardware-kit.png", + "language_code": "zh-HK" + }, + "pi-led.97f1d474981dc35d.webp": { + "original_hash": "8c9c44daca0bb84fff271932128548e7", + "translation_date": "2026-01-16T09:15:48+00:00", + "source_file": "images/pi-led.png", + "language_code": "zh-HK" + }, + "pi-light-sensor.66cc1e31fa48cd7d.webp": { + "original_hash": "24c2a1d065061a13b890b6808abf158f", + "translation_date": "2026-01-16T09:14:07+00:00", + "source_file": "images/pi-light-sensor.png", + "language_code": "zh-HK" + }, + "pi-relay-and-soil-moisture-sensor.02f3198975b8c53e.webp": { + "original_hash": "dc129413981ef906b6edd50f837a9e01", + "translation_date": "2026-01-16T09:06:29+00:00", + "source_file": "images/pi-relay-and-soil-moisture-sensor.png", + "language_code": "zh-HK" + }, + "pi-respeaker-hat.f00fabe7dd039a93.webp": { + "original_hash": "831331e5010f14df7262034ca67a316d", + "translation_date": "2026-01-16T09:05:03+00:00", + "source_file": "images/pi-respeaker-hat.png", + "language_code": "zh-HK" + }, + "pi-soil-moisture-sensor.fdd7eb2393792cf6.webp": { + "original_hash": "c50b9b78b749af1958d574aa38253e66", + "translation_date": "2026-01-16T09:17:03+00:00", + "source_file": "images/pi-soil-moisture-sensor.png", + "language_code": "zh-HK" + }, + "pi-temperature-sensor.3ff82fff672c8e56.webp": { + "original_hash": "feb99d03e3c82faf6a583cafea990fba", + "translation_date": "2026-01-16T09:20:05+00:00", + "source_file": "images/pi-temperature-sensor.png", + "language_code": "zh-HK" + }, + "pi-time-of-flight-sensor.58c8dc04eb3bfb57.webp": { + "original_hash": "ddc354c7922b02d7be67a7bbca792ad8", + "translation_date": "2026-01-16T09:19:14+00:00", + "source_file": "images/pi-time-of-flight-sensor.png", + "language_code": "zh-HK" + }, + "plant-growth-temp-graph copy.65baa28afd9b7f5f.webp": { + "original_hash": "af7d47772297c3a84f2c2f4c519a98ee", + "translation_date": "2026-01-16T09:03:51+00:00", + "source_file": "images/plant-growth-temp-graph copy.png", + "language_code": "zh-HK" + }, + "plant-growth-temp-graph.c6d69c9478e6ca83.webp": { + "original_hash": "af7d47772297c3a84f2c2f4c519a98ee", + "translation_date": "2026-01-16T09:21:26+00:00", + "source_file": "images/plant-growth-temp-graph.png", + "language_code": "zh-HK" + }, + "polygon-points.302193da381cb415.webp": { + "original_hash": "01b6fa2d97b4fecb638b5162873f5ea9", + "translation_date": "2026-01-16T09:12:03+00:00", + "source_file": "images/polygon-points.png", + "language_code": "zh-HK" + }, + "potentiometer.35a348b9ce22f6ec.webp": { + "original_hash": "1fb25de33028ef905bf4cce330a9e6df", + "translation_date": "2026-01-16T09:25:29+00:00", + "source_file": "images/potentiometer.png", + "language_code": "zh-HK" + }, + "proximity-sensor.f5cd752c77fb62fe.webp": { + "original_hash": "46a4e21d2d633afa6120896b66b1b1f3", + "translation_date": "2026-01-16T09:25:14+00:00", + "source_file": "images/proximity-sensor.png", + "language_code": "zh-HK" + }, + "pub-sub.7c7ed43fe9fd15d4.webp": { + "original_hash": "e5ec0def10a72676eb757b830b8ae56e", + "translation_date": "2026-01-16T09:19:01+00:00", + "source_file": "images/pub-sub.png", + "language_code": "zh-HK" + }, + "pump-wired-to-relay.66c5cfc0d8918990.webp": { + "original_hash": "84a8c5f06d634c142ee4764b91ec4f59", + "translation_date": "2026-01-16T09:25:05+00:00", + "source_file": "images/pump-wired-to-relay.png", + "language_code": "zh-HK" + }, + "pwm-motor-150rpm.83347ac04ca38482.webp": { + "original_hash": "f829c583c9b05918631c9afec52dfad9", + "translation_date": "2026-01-16T09:11:53+00:00", + "source_file": "images/pwm-motor-150rpm.png", + "language_code": "zh-HK" + }, + "pwm-motor-75rpm.a5e4c939934b6e14.webp": { + "original_hash": "27174930eae0dd1d408ca7474099ebed", + "translation_date": "2026-01-16T09:18:25+00:00", + "source_file": "images/pwm-motor-75rpm.png", + "language_code": "zh-HK" + }, + "ram-comparison.6beb73541b42ac6f.webp": { + "original_hash": "178bedf746f370deb9b3600fe15afeb1", + "translation_date": "2026-01-16T09:11:59+00:00", + "source_file": "images/ram-comparison.png", + "language_code": "zh-HK" + }, + "raspberry-pi-4.fd4590d308c3d456.webp": { + "original_hash": "a6546a4c48ae28af3154546daeec43b2", + "translation_date": "2026-01-16T09:01:38+00:00", + "source_file": "images/raspberry-pi-4.jpg", + "language_code": "zh-HK" + }, + "raspberry-pi-imager.24aedeab9e233d84.webp": { + "original_hash": "d60a21f44e34a41ca0b925837b7822d2", + "translation_date": "2026-01-16T09:16:02+00:00", + "source_file": "images/raspberry-pi-imager.png", + "language_code": "zh-HK" + }, + "raspberry-pi-logo.4efaa16605cee054.webp": { + "original_hash": "6dc3ec9908411f01f40bdb144eb0352d", + "translation_date": "2026-01-16T09:20:39+00:00", + "source_file": "images/raspberry-pi-logo.png", + "language_code": "zh-HK" + }, + "raspberry-pi-zero.f7a4133e1e7d54bb.webp": { + "original_hash": "a65b4f80e337bb75907ba9668d0ec4ec", + "translation_date": "2026-01-16T09:15:14+00:00", + "source_file": "images/raspberry-pi-zero.jpg", + "language_code": "zh-HK" + }, + "relay-off.c34a178a2960fecd.webp": { + "original_hash": "695bf246ae769e65b44448d48b762bd1", + "translation_date": "2026-01-16T09:02:18+00:00", + "source_file": "images/relay-off.png", + "language_code": "zh-HK" + }, + "relay-on.4db16a0fd6b66926.webp": { + "original_hash": "9845f9c1489a850aa549e224cbe72d2a", + "translation_date": "2026-01-16T09:04:02+00:00", + "source_file": "images/relay-on.png", + "language_code": "zh-HK" + }, + "resistive-soil-moisture-sensor.728a138a3d109e06.webp": { + "original_hash": "10868a092da0bc909f5cc4639401c811", + "translation_date": "2026-01-16T09:18:29+00:00", + "source_file": "images/resistive-soil-moisture-sensor.png", + "language_code": "zh-HK" + }, + "respeaker-35mm-speaker.ad79ef4f128c7751.webp": { + "original_hash": "8df5ce34dedc6d09c1b32061af983e1a", + "translation_date": "2026-01-16T09:20:12+00:00", + "source_file": "images/respeaker-35mm-speaker.png", + "language_code": "zh-HK" + }, + "respeaker-jst-speaker.a441d177809df945.webp": { + "original_hash": "c4dff6a9a83d65415477d8b77818c184", + "translation_date": "2026-01-16T09:15:11+00:00", + "source_file": "images/respeaker-jst-speaker.png", + "language_code": "zh-HK" + }, + "respeaker.f5d19d1c6b14ab16.webp": { + "original_hash": "f096205f8ed79a62c0b3223ca9760485", + "translation_date": "2026-01-16T09:03:16+00:00", + "source_file": "images/respeaker.png", + "language_code": "zh-HK" + }, + "ribbon-mic.eacc8e092c7441ca.webp": { + "original_hash": "2d5dac0dede2c3bee039d05cb8d5eb01", + "translation_date": "2026-01-16T09:06:38+00:00", + "source_file": "images/ribbon-mic.jpg", + "language_code": "zh-HK" + }, + "rpi-stock-with-bounding-boxes.b5540e2ecb7cd49f.webp": { + "original_hash": "d801ce817c0839b0f611e0b73bf4f05d", + "translation_date": "2026-01-16T09:15:31+00:00", + "source_file": "images/rpi-stock-with-bounding-boxes.jpg", + "language_code": "zh-HK" + }, + "sampling.6f4fadb3f2d9dfe7.webp": { + "original_hash": "0873e1919ecfcd1fac59791d08b2458f", + "translation_date": "2026-01-16T09:19:54+00:00", + "source_file": "images/sampling.png", + "language_code": "zh-HK" + }, + "save-telemetry-database.ddc9c6bea0c5ba39.webp": { + "original_hash": "161b3f7c079d04498fbc0d900788510e", + "translation_date": "2026-01-16T09:25:32+00:00", + "source_file": "images/save-telemetry-database.png", + "language_code": "zh-HK" + }, + "save-telemetry-to-storage-from-functions.ed3b1820980097f1.webp": { + "original_hash": "a03847815181206801649c4168cec3a1", + "translation_date": "2026-01-16T09:19:46+00:00", + "source_file": "images/save-telemetry-to-storage-from-functions.png", + "language_code": "zh-HK" + }, + "search-buffer-and-distance.e6a79af3898183c7.webp": { + "original_hash": "4fa75893d571e0dd38a6d9835877b318", + "translation_date": "2026-01-16T09:25:21+00:00", + "source_file": "images/search-buffer-and-distance.png", + "language_code": "zh-HK" + }, + "seeed-logo.74732b6b482b6e8e.webp": { + "original_hash": "c7479fe98cfdb3be95dda1ceee7a2b0f", + "translation_date": "2026-01-16T09:19:06+00:00", + "source_file": "images/seeed-logo.png", + "language_code": "zh-HK" + }, + "select-the-random-checkbox-and-set-a-range.32cf4bc7c12e797f.webp": { + "original_hash": "38727cd537789a1b4e18ca8277ee85b1", + "translation_date": "2026-01-16T09:26:18+00:00", + "source_file": "images/select-the-random-checkbox-and-set-a-range.png", + "language_code": "zh-HK" + }, + "send-message-asymmetric.7abe327c62615b8c.webp": { + "original_hash": "0873f3deafa225ede589e282db569fd3", + "translation_date": "2026-01-16T09:17:44+00:00", + "source_file": "images/send-message-asymmetric.png", + "language_code": "zh-HK" + }, + "send-message-certificate.9cc576ac1e46b76e.webp": { + "original_hash": "df8c0a10f8e11172061fb4bbde04cec0", + "translation_date": "2026-01-16T09:10:09+00:00", + "source_file": "images/send-message-certificate.png", + "language_code": "zh-HK" + }, + "send-message-symmetric-key-hacker.e7cb53db1707adfb.webp": { + "original_hash": "da84d1704f66fc3573b68394cd8d63ab", + "translation_date": "2026-01-16T09:15:38+00:00", + "source_file": "images/send-message-symmetric-key-hacker.png", + "language_code": "zh-HK" + }, + "send-message-symmetric-key.a2e8ad0d495896ff.webp": { + "original_hash": "ea8f8a45fea06564fb75f7444db90d6d", + "translation_date": "2026-01-16T09:10:58+00:00", + "source_file": "images/send-message-symmetric-key.png", + "language_code": "zh-HK" + }, + "sentence-as-intent-entities.301401696f992259.webp": { + "original_hash": "20bc069192582a5b8ba4a45d60362dd1", + "translation_date": "2026-01-16T09:21:33+00:00", + "source_file": "images/sentence-as-intent-entities.png", + "language_code": "zh-HK" + }, + "serverless-scaling.f8c769adf0413fd1.webp": { + "original_hash": "bfa04832aefaa493dbb81bda0fe64bf8", + "translation_date": "2026-01-16T09:02:11+00:00", + "source_file": "images/serverless-scaling.png", + "language_code": "zh-HK" + }, + "shapes-to-images.1a309f0ea88dd66f.webp": { + "original_hash": "4673701d4985ae9110e41a793f89d7a2", + "translation_date": "2026-01-16T09:24:54+00:00", + "source_file": "images/shapes-to-images.png", + "language_code": "zh-HK" + }, + "smarter-thermostat.a75855f15d2d9e63.webp": { + "original_hash": "7e07ad6473af5b68b49b1fb8b18be243", + "translation_date": "2026-01-16T09:25:43+00:00", + "source_file": "images/smarter-thermostat.png", + "language_code": "zh-HK" + }, + "soil-moisture-delay.865f3fae206db01d.webp": { + "original_hash": "2fbb54ffd35ea4b7bab4232986add7ff", + "translation_date": "2026-01-16T09:15:07+00:00", + "source_file": "images/soil-moisture-delay.png", + "language_code": "zh-HK" + }, + "soil-moisture-sensor-in-soil.bfad91002bda5e96.webp": { + "original_hash": "2b3a8b0115e4a957fc188dcd60aecbae", + "translation_date": "2026-01-16T09:13:39+00:00", + "source_file": "images/soil-moisture-sensor-in-soil.png", + "language_code": "zh-HK" + }, + "soil-moisture-to-voltage-with-reading.681cb3e1f8b68caf.webp": { + "original_hash": "feedc1062e7650b1033eae28a4718f5c", + "translation_date": "2026-01-16T09:19:09+00:00", + "source_file": "images/soil-moisture-to-voltage-with-reading.png", + "language_code": "zh-HK" + }, + "soil-moisture-to-voltage.df86d80cda158700.webp": { + "original_hash": "a8deda9a89889040b0802aacaf02c0db", + "translation_date": "2026-01-16T09:13:23+00:00", + "source_file": "images/soil-moisture-to-voltage.png", + "language_code": "zh-HK" + }, + "soil-moisture-travel.a0e31af222cf1438.webp": { + "original_hash": "dacaf2385f73dadd43abdacd4812399d", + "translation_date": "2026-01-16T09:20:52+00:00", + "source_file": "images/soil-moisture-travel.png", + "language_code": "zh-HK" + }, + "spi.297431d6f98b386b.webp": { + "original_hash": "69b6cd30118f966b9a2646d6ec34944f", + "translation_date": "2026-01-16T09:13:11+00:00", + "source_file": "images/spi.png", + "language_code": "zh-HK" + }, + "sql-database.be160f12bfccefd3.webp": { + "original_hash": "eab9c93e1f46ee7f1520b61a9486c9c0", + "translation_date": "2026-01-16T09:25:26+00:00", + "source_file": "images/sql-database.png", + "language_code": "zh-HK" + }, + "stock-7-cans-tomato-paste.f86059cc573d7bec.webp": { + "original_hash": "01dac6278467a7ebc669ccde8d957954", + "translation_date": "2026-01-16T09:02:00+00:00", + "source_file": "images/stock-7-cans-tomato-paste.png", + "language_code": "zh-HK" + }, + "stock-rogue-corn.be1f3ada8c457854.webp": { + "original_hash": "76a508a11e7a91138b7a515e6c1cd93c", + "translation_date": "2026-01-16T09:16:30+00:00", + "source_file": "images/stock-rogue-corn.png", + "language_code": "zh-HK" + }, + "strawberry-with-pump.b410fc72ac6aabad.webp": { + "original_hash": "af6fbb23fcab01f826392b6944dea49e", + "translation_date": "2026-01-16T09:13:48+00:00", + "source_file": "images/strawberry-with-pump.png", + "language_code": "zh-HK" + }, + "telemetry.21e5d8b97649d2eb.webp": { + "original_hash": "65fa3e3b915dbebffbab7d72f200e4e7", + "translation_date": "2026-01-16T09:06:57+00:00", + "source_file": "images/telemetry.png", + "language_code": "zh-HK" + }, + "temperature-as-digital.85004491b977bae1.webp": { + "original_hash": "cd1fed630adeb671a51171136afe0a11", + "translation_date": "2026-01-16T09:15:17+00:00", + "source_file": "images/temperature-as-digital.png", + "language_code": "zh-HK" + }, + "time-of-flight-banana.079921ad8b1496e4.webp": { + "original_hash": "2617486ad1d365e9272698f6c8e80523", + "translation_date": "2026-01-16T09:17:58+00:00", + "source_file": "images/time-of-flight-banana.png", + "language_code": "zh-HK" + }, + "traditional-vs-ml.5c20c169621fa539.webp": { + "original_hash": "7ba80f8ce61fc58a79619972640123b7", + "translation_date": "2026-01-16T09:11:50+00:00", + "source_file": "images/traditional-vs-ml.png", + "language_code": "zh-HK" + }, + "translated-smart-timer.08ac20057fdc5c37.webp": { + "original_hash": "f0bc8a93e3e880f4362f14db64377b44", + "translation_date": "2026-01-16T09:11:08+00:00", + "source_file": "images/translated-smart-timer.png", + "language_code": "zh-HK" + }, + "transpiration.b735aa34e4372e65.webp": { + "original_hash": "cdaa5c2392fbae09e27cecbd2163fead", + "translation_date": "2026-01-16T09:21:20+00:00", + "source_file": "images/transpiration.png", + "language_code": "zh-HK" + }, + "tts-overview.193843cf3f5ee09f.webp": { + "original_hash": "60bcfe389948c72835cba3d41cc92643", + "translation_date": "2026-01-16T09:20:08+00:00", + "source_file": "images/tts-overview.png", + "language_code": "zh-HK" + }, + "uart.d0dbd3fb9e3728c6.webp": { + "original_hash": "69f7d23a9049bf351ed17aaf49400cce", + "translation_date": "2026-01-16T09:17:57+00:00", + "source_file": "images/uart.png", + "language_code": "zh-HK" + }, + "vscode-azure-functions-init-notification.bd19b49229963edb.webp": { + "original_hash": "631bfd744e57eadbdf63c0bf59b5addf", + "translation_date": "2026-01-16T09:25:10+00:00", + "source_file": "images/vscode-azure-functions-init-notification.png", + "language_code": "zh-HK" + }, + "vscode-kill-terminal.1cc4de7c6f25ee08.webp": { + "original_hash": "b22d79cb8cfc5bfb5adea2ab797d1498", + "translation_date": "2026-01-16T09:15:24+00:00", + "source_file": "images/vscode-kill-terminal.png", + "language_code": "zh-HK" + }, + "vscode-new-file-button.182702340fe6723c.webp": { + "original_hash": "f53b77090608588ce1aea6e28ac7534b", + "translation_date": "2026-01-16T09:19:42+00:00", + "source_file": "images/vscode-new-file-button.png", + "language_code": "zh-HK" + }, + "vscode-new-terminal.77db8fc0f9cd3182.webp": { + "original_hash": "a692e413071b4224d251842fc4a95050", + "translation_date": "2026-01-16T09:10:24+00:00", + "source_file": "images/vscode-new-terminal.png", + "language_code": "zh-HK" + }, + "vscode-open-nightlight-remote.d3d2a4011e30d535.webp": { + "original_hash": "863d50888f94713346469b8afa0a56b3", + "translation_date": "2026-01-16T09:02:44+00:00", + "source_file": "images/vscode-open-nightlight-remote.png", + "language_code": "zh-HK" + }, + "vscode-platformio-build-command-palette.7708e7ec7d75d7ee.webp": { + "original_hash": "24236a76bf00a861dbf80d37b29265ce", + "translation_date": "2026-01-16T09:11:16+00:00", + "source_file": "images/vscode-platformio-build-command-palette.png", + "language_code": "zh-HK" + }, + "vscode-platformio-home-open.3f9a41bfd3f4da1c.webp": { + "original_hash": "68285f83d33fcd0a4b1e3141ef0afead", + "translation_date": "2026-01-16T09:06:10+00:00", + "source_file": "images/vscode-platformio-home-open.png", + "language_code": "zh-HK" + }, + "vscode-platformio-menu.297be26b9733e5c4.webp": { + "original_hash": "690ba1bf727223328bae1da7aef65b59", + "translation_date": "2026-01-16T09:12:10+00:00", + "source_file": "images/vscode-platformio-menu.png", + "language_code": "zh-HK" + }, + "vscode-platformio-nightlight-project-wizard.5c64db4da6037420.webp": { + "original_hash": "d55f8f702d005d082ea301248f7b70f9", + "translation_date": "2026-01-16T09:10:02+00:00", + "source_file": "images/vscode-platformio-nightlight-project-wizard.png", + "language_code": "zh-HK" + }, + "vscode-platformio-serial-monitor-command-palette.b348ec841b8a1c14.webp": { + "original_hash": "02340f15877c75b8ca3f08222c32b679", + "translation_date": "2026-01-16T09:18:54+00:00", + "source_file": "images/vscode-platformio-serial-monitor-command-palette.png", + "language_code": "zh-HK" + }, + "vscode-platformio-upload-command-palette.9e0f49cf80d1f1c3.webp": { + "original_hash": "ad07a4dbd27f86bd480dcfbbc1a56a01", + "translation_date": "2026-01-16T09:14:36+00:00", + "source_file": "images/vscode-platformio-upload-command-palette.png", + "language_code": "zh-HK" + }, + "vscode-platformio-welcome-new-button.ba6fc8a4c7b78cc8.webp": { + "original_hash": "c3bd57b44fe7628cc688f4d4fd814416", + "translation_date": "2026-01-16T09:02:56+00:00", + "source_file": "images/vscode-platformio-welcome-new-button.png", + "language_code": "zh-HK" + }, + "vscode-virtual-env.8ba42e04c3d533cf.webp": { + "original_hash": "f79cd090b385dfdfe774452b7d89eb84", + "translation_date": "2026-01-16T09:14:28+00:00", + "source_file": "images/vscode-virtual-env.png", + "language_code": "zh-HK" + }, + "what-is-azure-video-thumbnail.20174db09e03bbb8.webp": { + "original_hash": "ca9324cf5205832042b050d641f904b3", + "translation_date": "2026-01-16T09:06:59+00:00", + "source_file": "images/what-is-azure-video-thumbnail.png", + "language_code": "zh-HK" + }, + "wio-gps-sensor.19fd52b81ce58095.webp": { + "original_hash": "71ac9146b7ea28e48f6ee5b636cd1109", + "translation_date": "2026-01-16T09:13:56+00:00", + "source_file": "images/wio-gps-sensor.png", + "language_code": "zh-HK" + }, + "wio-hardware-kit.4c70c48b85e4283a.webp": { + "original_hash": "e428f5e0754412af1a8664ea7baff212", + "translation_date": "2026-01-16T09:18:07+00:00", + "source_file": "images/wio-hardware-kit.png", + "language_code": "zh-HK" + }, + "wio-led.265a1897e72d7f21.webp": { + "original_hash": "047202c8d9ddd146add1c33301f91d0e", + "translation_date": "2026-01-16T09:25:45+00:00", + "source_file": "images/wio-led.png", + "language_code": "zh-HK" + }, + "wio-light-sensor.b1f529f3c95f5165.webp": { + "original_hash": "7d3c253db81dc7f1b0fcd4554ae4bb1f", + "translation_date": "2026-01-16T09:14:55+00:00", + "source_file": "images/wio-light-sensor.png", + "language_code": "zh-HK" + }, + "wio-mic.3f8c843dbe8ad917.webp": { + "original_hash": "eb90afd4b3953d38218422601ae677b5", + "translation_date": "2026-01-16T09:10:26+00:00", + "source_file": "images/wio-mic.png", + "language_code": "zh-HK" + }, + "wio-relay-and-soil-moisture-sensor.ed722202d42babe0.webp": { + "original_hash": "ca90c734c88176a89d703b243c280333", + "translation_date": "2026-01-16T09:02:15+00:00", + "source_file": "images/wio-relay-and-soil-moisture-sensor.png", + "language_code": "zh-HK" + }, + "wio-respeaker-hat.bd54917d446e6f6f.webp": { + "original_hash": "040868428b85d90d767351cbbc89a23d", + "translation_date": "2026-01-16T09:19:40+00:00", + "source_file": "images/wio-respeaker-hat.png", + "language_code": "zh-HK" + }, + "wio-respeaker-wiring-0.767f80aa65081038.webp": { + "original_hash": "47daeac252937c129a4000dd216c04d8", + "translation_date": "2026-01-16T09:19:31+00:00", + "source_file": "images/wio-respeaker-wiring-0.png", + "language_code": "zh-HK" + }, + "wio-respeaker-wiring-1.8d894727f2ba2400.webp": { + "original_hash": "192d03a7a2cd1eab078ef6afa28a39c9", + "translation_date": "2026-01-16T09:13:33+00:00", + "source_file": "images/wio-respeaker-wiring-1.png", + "language_code": "zh-HK" + }, + "wio-respeaker-wiring-2.329e1cbd306e754f.webp": { + "original_hash": "76a795bfad251f217e7822f14ebd85e3", + "translation_date": "2026-01-16T09:17:11+00:00", + "source_file": "images/wio-respeaker-wiring-2.png", + "language_code": "zh-HK" + }, + "wio-respeaker-wiring-3.75b0be447e2fa930.webp": { + "original_hash": "430c8ab68fc9ee2c6bedae1d41325002", + "translation_date": "2026-01-16T09:10:17+00:00", + "source_file": "images/wio-respeaker-wiring-3.png", + "language_code": "zh-HK" + }, + "wio-respeaker-wiring-4.aa9cd434d8779437.webp": { + "original_hash": "c8febc847beb4a69180b745d5e718281", + "translation_date": "2026-01-16T09:01:46+00:00", + "source_file": "images/wio-respeaker-wiring-4.png", + "language_code": "zh-HK" + }, + "wio-respeaker-wiring-5.af117c20acf622f3.webp": { + "original_hash": "1953600b09247d9302e7011dceed8f28", + "translation_date": "2026-01-16T09:13:59+00:00", + "source_file": "images/wio-respeaker-wiring-5.png", + "language_code": "zh-HK" + }, + "wio-sd-card.acdcbe322fa4ee7f.webp": { + "original_hash": "88bd7dd4bc8b4c181e7b3743945a2edd", + "translation_date": "2026-01-16T09:06:12+00:00", + "source_file": "images/wio-sd-card.png", + "language_code": "zh-HK" + }, + "wio-soil-moisture-sensor.46919b61c3f6cb74.webp": { + "original_hash": "19186ea8705744d6f7ef80dd49a31037", + "translation_date": "2026-01-16T09:12:17+00:00", + "source_file": "images/wio-soil-moisture-sensor.png", + "language_code": "zh-HK" + }, + "wio-temperature-sensor.2934928f38c7f79a.webp": { + "original_hash": "9e8e3bc7a39149547dcfcce2493e151e", + "translation_date": "2026-01-16T09:15:04+00:00", + "source_file": "images/wio-temperature-sensor.png", + "language_code": "zh-HK" + }, + "wio-terminal-c-button.73df3cb1c1445ea0.webp": { + "original_hash": "7e8c595998491210a3ffd21ee338afe6", + "translation_date": "2026-01-16T09:15:03+00:00", + "source_file": "images/wio-terminal-c-button.png", + "language_code": "zh-HK" + }, + "wio-terminal-pin-sticker.b90b1535937b84bd.webp": { + "original_hash": "beeae0399efc567f78b9771eb986710a", + "translation_date": "2026-01-16T09:10:45+00:00", + "source_file": "images/wio-terminal-pin-sticker.png", + "language_code": "zh-HK" + }, + "wio-terminal.b8299ee16587db9a.webp": { + "original_hash": "e031f14d743539d41b871e8da6c516f7", + "translation_date": "2026-01-16T09:03:19+00:00", + "source_file": "images/wio-terminal.png", + "language_code": "zh-HK" + }, + "wio-time-of-flight-sensor.c4c182131d2ea73d.webp": { + "original_hash": "fe6b9bb1afee16f03bab8f6ddc3512b3", + "translation_date": "2026-01-16T09:11:55+00:00", + "source_file": "images/wio-time-of-flight-sensor.png", + "language_code": "zh-HK" + } +} \ No newline at end of file diff --git a/translated_images/zh-HK/IMG_5305.aa291c8812a9f1e5.webp b/translated_images/zh-HK/IMG_5305.aa291c8812a9f1e5.webp new file mode 100644 index 000000000..8b4726d4f Binary files /dev/null and b/translated_images/zh-HK/IMG_5305.aa291c8812a9f1e5.webp differ diff --git a/translated_images/zh-HK/IMG_5306.d575b9ab7025877b.webp b/translated_images/zh-HK/IMG_5306.d575b9ab7025877b.webp new file mode 100644 index 000000000..cae739de4 Binary files /dev/null and b/translated_images/zh-HK/IMG_5306.d575b9ab7025877b.webp differ diff --git a/translated_images/zh-HK/IMG_5307.f9c9b8361a8aa734.webp b/translated_images/zh-HK/IMG_5307.f9c9b8361a8aa734.webp new file mode 100644 index 000000000..ec6bd0a5a Binary files /dev/null and b/translated_images/zh-HK/IMG_5307.f9c9b8361a8aa734.webp differ diff --git a/translated_images/zh-HK/IMG_5308.cbd6ed7007e69060.webp b/translated_images/zh-HK/IMG_5308.cbd6ed7007e69060.webp new file mode 100644 index 000000000..3cd82137e Binary files /dev/null and b/translated_images/zh-HK/IMG_5308.cbd6ed7007e69060.webp differ diff --git a/translated_images/zh-HK/IMG_5309.23fbc3b6667bfb64.webp b/translated_images/zh-HK/IMG_5309.23fbc3b6667bfb64.webp new file mode 100644 index 000000000..829cebe03 Binary files /dev/null and b/translated_images/zh-HK/IMG_5309.23fbc3b6667bfb64.webp differ diff --git a/translated_images/zh-HK/IMG_5310.0ee0eb2fbc1c2d0e.webp b/translated_images/zh-HK/IMG_5310.0ee0eb2fbc1c2d0e.webp new file mode 100644 index 000000000..0f893c4d1 Binary files /dev/null and b/translated_images/zh-HK/IMG_5310.0ee0eb2fbc1c2d0e.webp differ diff --git a/translated_images/zh-HK/IMG_5311.8c90da6446c2d8c2.webp b/translated_images/zh-HK/IMG_5311.8c90da6446c2d8c2.webp new file mode 100644 index 000000000..180743b3c Binary files /dev/null and b/translated_images/zh-HK/IMG_5311.8c90da6446c2d8c2.webp differ diff --git a/translated_images/zh-HK/IMG_5312.a45550ddd8ce8e65.webp b/translated_images/zh-HK/IMG_5312.a45550ddd8ce8e65.webp new file mode 100644 index 000000000..05f50f386 Binary files /dev/null and b/translated_images/zh-HK/IMG_5312.a45550ddd8ce8e65.webp differ diff --git a/translated_images/zh-HK/IMG_5313.ed1b45211271dbd2.webp b/translated_images/zh-HK/IMG_5313.ed1b45211271dbd2.webp new file mode 100644 index 000000000..6f736fec5 Binary files /dev/null and b/translated_images/zh-HK/IMG_5313.ed1b45211271dbd2.webp differ diff --git a/translated_images/zh-HK/IMG_5314.c2203206a05a74b5.webp b/translated_images/zh-HK/IMG_5314.c2203206a05a74b5.webp new file mode 100644 index 000000000..a080861ff Binary files /dev/null and b/translated_images/zh-HK/IMG_5314.c2203206a05a74b5.webp differ diff --git a/translated_images/zh-HK/IMG_5315.f698228e0e031bd4.webp b/translated_images/zh-HK/IMG_5315.f698228e0e031bd4.webp new file mode 100644 index 000000000..a2958dbfd Binary files /dev/null and b/translated_images/zh-HK/IMG_5315.f698228e0e031bd4.webp differ diff --git a/translated_images/zh-HK/IMG_5316.29dc70d802ce8349.webp b/translated_images/zh-HK/IMG_5316.29dc70d802ce8349.webp new file mode 100644 index 000000000..b6875428e Binary files /dev/null and b/translated_images/zh-HK/IMG_5316.29dc70d802ce8349.webp differ diff --git a/translated_images/zh-HK/IMG_5317.ed81e4c1ca5046dc.webp b/translated_images/zh-HK/IMG_5317.ed81e4c1ca5046dc.webp new file mode 100644 index 000000000..66a917596 Binary files /dev/null and b/translated_images/zh-HK/IMG_5317.ed81e4c1ca5046dc.webp differ diff --git a/translated_images/zh-HK/IMG_5318.15dfffeb7f47abf7.webp b/translated_images/zh-HK/IMG_5318.15dfffeb7f47abf7.webp new file mode 100644 index 000000000..72b158586 Binary files /dev/null and b/translated_images/zh-HK/IMG_5318.15dfffeb7f47abf7.webp differ diff --git a/translated_images/zh-HK/IMG_5319.b549b1fff0dcf143.webp b/translated_images/zh-HK/IMG_5319.b549b1fff0dcf143.webp new file mode 100644 index 000000000..b1ea39b31 Binary files /dev/null and b/translated_images/zh-HK/IMG_5319.b549b1fff0dcf143.webp differ diff --git a/translated_images/zh-HK/IMG_5320.8268d3f61972f348.webp b/translated_images/zh-HK/IMG_5320.8268d3f61972f348.webp new file mode 100644 index 000000000..fbc4feaa9 Binary files /dev/null and b/translated_images/zh-HK/IMG_5320.8268d3f61972f348.webp differ diff --git a/translated_images/zh-HK/IMG_5321.b207cf143a59458d.webp b/translated_images/zh-HK/IMG_5321.b207cf143a59458d.webp new file mode 100644 index 000000000..eb2815d51 Binary files /dev/null and b/translated_images/zh-HK/IMG_5321.b207cf143a59458d.webp differ diff --git a/translated_images/zh-HK/IMG_5322.974809b9461a9e20.webp b/translated_images/zh-HK/IMG_5322.974809b9461a9e20.webp new file mode 100644 index 000000000..187adfd43 Binary files /dev/null and b/translated_images/zh-HK/IMG_5322.974809b9461a9e20.webp differ diff --git a/translated_images/zh-HK/IMG_5323.4939fa17958f291b.webp b/translated_images/zh-HK/IMG_5323.4939fa17958f291b.webp new file mode 100644 index 000000000..6aab64b09 Binary files /dev/null and b/translated_images/zh-HK/IMG_5323.4939fa17958f291b.webp differ diff --git a/translated_images/zh-HK/IMG_5324.0afbc6f0caceb1a3.webp b/translated_images/zh-HK/IMG_5324.0afbc6f0caceb1a3.webp new file mode 100644 index 000000000..601dc8926 Binary files /dev/null and b/translated_images/zh-HK/IMG_5324.0afbc6f0caceb1a3.webp differ diff --git a/translated_images/zh-HK/IMG_5325.9e9d9e9b85a10b06.webp b/translated_images/zh-HK/IMG_5325.9e9d9e9b85a10b06.webp new file mode 100644 index 000000000..9a8b7a12e Binary files /dev/null and b/translated_images/zh-HK/IMG_5325.9e9d9e9b85a10b06.webp differ diff --git a/translated_images/zh-HK/IMG_5326.35bbc9e054c704d0.webp b/translated_images/zh-HK/IMG_5326.35bbc9e054c704d0.webp new file mode 100644 index 000000000..3227df7e7 Binary files /dev/null and b/translated_images/zh-HK/IMG_5326.35bbc9e054c704d0.webp differ diff --git a/translated_images/zh-HK/IMG_5327.804b63a605b5a77a.webp b/translated_images/zh-HK/IMG_5327.804b63a605b5a77a.webp new file mode 100644 index 000000000..a80ae41ca Binary files /dev/null and b/translated_images/zh-HK/IMG_5327.804b63a605b5a77a.webp differ diff --git a/translated_images/zh-HK/IMG_5328.925a9da23d96759f.webp b/translated_images/zh-HK/IMG_5328.925a9da23d96759f.webp new file mode 100644 index 000000000..8d40b87b5 Binary files /dev/null and b/translated_images/zh-HK/IMG_5328.925a9da23d96759f.webp differ diff --git a/translated_images/zh-HK/IMG_5329.27da5fcbc3336773.webp b/translated_images/zh-HK/IMG_5329.27da5fcbc3336773.webp new file mode 100644 index 000000000..82530a434 Binary files /dev/null and b/translated_images/zh-HK/IMG_5329.27da5fcbc3336773.webp differ diff --git a/translated_images/zh-HK/IMG_5330.3db3d5ea01c8cca2.webp b/translated_images/zh-HK/IMG_5330.3db3d5ea01c8cca2.webp new file mode 100644 index 000000000..13468426b Binary files /dev/null and b/translated_images/zh-HK/IMG_5330.3db3d5ea01c8cca2.webp differ diff --git a/translated_images/zh-HK/IMG_5331.181987d3d094472f.webp b/translated_images/zh-HK/IMG_5331.181987d3d094472f.webp new file mode 100644 index 000000000..0428805ae Binary files /dev/null and b/translated_images/zh-HK/IMG_5331.181987d3d094472f.webp differ diff --git a/translated_images/zh-HK/IMG_5332.4beed56eaa5158e3.webp b/translated_images/zh-HK/IMG_5332.4beed56eaa5158e3.webp new file mode 100644 index 000000000..03dfb9e95 Binary files /dev/null and b/translated_images/zh-HK/IMG_5332.4beed56eaa5158e3.webp differ diff --git a/translated_images/zh-HK/IMG_5333.53c3364c2ee0e7bb.webp b/translated_images/zh-HK/IMG_5333.53c3364c2ee0e7bb.webp new file mode 100644 index 000000000..ef6eef844 Binary files /dev/null and b/translated_images/zh-HK/IMG_5333.53c3364c2ee0e7bb.webp differ diff --git a/translated_images/zh-HK/IMG_5334.979472e433948c10.webp b/translated_images/zh-HK/IMG_5334.979472e433948c10.webp new file mode 100644 index 000000000..9b207811c Binary files /dev/null and b/translated_images/zh-HK/IMG_5334.979472e433948c10.webp differ diff --git a/translated_images/zh-HK/IMG_5335.9f5bfba69a8d5a90.webp b/translated_images/zh-HK/IMG_5335.9f5bfba69a8d5a90.webp new file mode 100644 index 000000000..15843ee26 Binary files /dev/null and b/translated_images/zh-HK/IMG_5335.9f5bfba69a8d5a90.webp differ diff --git a/translated_images/zh-HK/IMG_5336.1b46586fdec95767.webp b/translated_images/zh-HK/IMG_5336.1b46586fdec95767.webp new file mode 100644 index 000000000..6e57dfe7f Binary files /dev/null and b/translated_images/zh-HK/IMG_5336.1b46586fdec95767.webp differ diff --git a/translated_images/zh-HK/IMG_5337.91c82be4d37029b0.webp b/translated_images/zh-HK/IMG_5337.91c82be4d37029b0.webp new file mode 100644 index 000000000..85809b7d1 Binary files /dev/null and b/translated_images/zh-HK/IMG_5337.91c82be4d37029b0.webp differ diff --git a/translated_images/zh-HK/IMG_5338.9ab356541448923b.webp b/translated_images/zh-HK/IMG_5338.9ab356541448923b.webp new file mode 100644 index 000000000..1a38d1320 Binary files /dev/null and b/translated_images/zh-HK/IMG_5338.9ab356541448923b.webp differ diff --git a/translated_images/zh-HK/IMG_5340.8440651b3ce1dd35.webp b/translated_images/zh-HK/IMG_5340.8440651b3ce1dd35.webp new file mode 100644 index 000000000..022dd7753 Binary files /dev/null and b/translated_images/zh-HK/IMG_5340.8440651b3ce1dd35.webp differ diff --git a/translated_images/zh-HK/IMG_5341.a1aa041c42dbc534.webp b/translated_images/zh-HK/IMG_5341.a1aa041c42dbc534.webp new file mode 100644 index 000000000..c35945a90 Binary files /dev/null and b/translated_images/zh-HK/IMG_5341.a1aa041c42dbc534.webp differ diff --git a/translated_images/zh-HK/IMG_5342.9f3b93d9fab90d3b.webp b/translated_images/zh-HK/IMG_5342.9f3b93d9fab90d3b.webp new file mode 100644 index 000000000..ea4a49d63 Binary files /dev/null and b/translated_images/zh-HK/IMG_5342.9f3b93d9fab90d3b.webp differ diff --git a/translated_images/zh-HK/IMG_5343.fa30105d3c10cad2.webp b/translated_images/zh-HK/IMG_5343.fa30105d3c10cad2.webp new file mode 100644 index 000000000..bf810c4be Binary files /dev/null and b/translated_images/zh-HK/IMG_5343.fa30105d3c10cad2.webp differ diff --git a/translated_images/zh-HK/IMG_5344.acfcd61ee5281321.webp b/translated_images/zh-HK/IMG_5344.acfcd61ee5281321.webp new file mode 100644 index 000000000..46749f95c Binary files /dev/null and b/translated_images/zh-HK/IMG_5344.acfcd61ee5281321.webp differ diff --git a/translated_images/zh-HK/IMG_5345.3d305ee2bb4ee804.webp b/translated_images/zh-HK/IMG_5345.3d305ee2bb4ee804.webp new file mode 100644 index 000000000..40049d560 Binary files /dev/null and b/translated_images/zh-HK/IMG_5345.3d305ee2bb4ee804.webp differ diff --git a/translated_images/zh-HK/IMG_5346.281dcc267f69c70b.webp b/translated_images/zh-HK/IMG_5346.281dcc267f69c70b.webp new file mode 100644 index 000000000..bd623a979 Binary files /dev/null and b/translated_images/zh-HK/IMG_5346.281dcc267f69c70b.webp differ diff --git a/translated_images/zh-HK/IMG_5347.48a4a981e76910ae.webp b/translated_images/zh-HK/IMG_5347.48a4a981e76910ae.webp new file mode 100644 index 000000000..ec07602fe Binary files /dev/null and b/translated_images/zh-HK/IMG_5347.48a4a981e76910ae.webp differ diff --git a/translated_images/zh-HK/IMG_5348.3961d3b444537c13.webp b/translated_images/zh-HK/IMG_5348.3961d3b444537c13.webp new file mode 100644 index 000000000..07d7b1b58 Binary files /dev/null and b/translated_images/zh-HK/IMG_5348.3961d3b444537c13.webp differ diff --git a/translated_images/zh-HK/IMG_5349.1f0ec7fbf8807b3b.webp b/translated_images/zh-HK/IMG_5349.1f0ec7fbf8807b3b.webp new file mode 100644 index 000000000..cf7b3d078 Binary files /dev/null and b/translated_images/zh-HK/IMG_5349.1f0ec7fbf8807b3b.webp differ diff --git a/translated_images/zh-HK/Roadmap.bb1dec285dda0eda.webp b/translated_images/zh-HK/Roadmap.bb1dec285dda0eda.webp new file mode 100644 index 000000000..c5eefcc61 Binary files /dev/null and b/translated_images/zh-HK/Roadmap.bb1dec285dda0eda.webp differ diff --git a/translated_images/zh-HK/amqp.804bd4fce8330157.webp b/translated_images/zh-HK/amqp.804bd4fce8330157.webp new file mode 100644 index 000000000..2fbad4526 Binary files /dev/null and b/translated_images/zh-HK/amqp.804bd4fce8330157.webp differ diff --git a/translated_images/zh-HK/analog-sensor-voltage.3b6f315392247399.webp b/translated_images/zh-HK/analog-sensor-voltage.3b6f315392247399.webp new file mode 100644 index 000000000..a5ea61864 Binary files /dev/null and b/translated_images/zh-HK/analog-sensor-voltage.3b6f315392247399.webp differ diff --git a/translated_images/zh-HK/arducam-wio-terminal-connections.a4d5a4049bdb5ab8.webp b/translated_images/zh-HK/arducam-wio-terminal-connections.a4d5a4049bdb5ab8.webp new file mode 100644 index 000000000..c0edc40e7 Binary files /dev/null and b/translated_images/zh-HK/arducam-wio-terminal-connections.a4d5a4049bdb5ab8.webp differ diff --git a/translated_images/zh-HK/arducam.20e4e4cbb2682965.webp b/translated_images/zh-HK/arducam.20e4e4cbb2682965.webp new file mode 100644 index 000000000..5a361fb45 Binary files /dev/null and b/translated_images/zh-HK/arducam.20e4e4cbb2682965.webp differ diff --git a/translated_images/zh-HK/arduino-sketch.79590cb837ff7a7c.webp b/translated_images/zh-HK/arduino-sketch.79590cb837ff7a7c.webp new file mode 100644 index 000000000..4a3c4114e Binary files /dev/null and b/translated_images/zh-HK/arduino-sketch.79590cb837ff7a7c.webp differ diff --git a/translated_images/zh-HK/assignment-1-flow.7552a51acb1a5ec8.webp b/translated_images/zh-HK/assignment-1-flow.7552a51acb1a5ec8.webp new file mode 100644 index 000000000..9b7b54974 Binary files /dev/null and b/translated_images/zh-HK/assignment-1-flow.7552a51acb1a5ec8.webp differ diff --git a/translated_images/zh-HK/assignment-1-internet-flow.3256feab5f052fd2.webp b/translated_images/zh-HK/assignment-1-internet-flow.3256feab5f052fd2.webp new file mode 100644 index 000000000..3c4010181 Binary files /dev/null and b/translated_images/zh-HK/assignment-1-internet-flow.3256feab5f052fd2.webp differ diff --git a/translated_images/zh-HK/azure-container-registry-logo.09494206991d4b29.webp b/translated_images/zh-HK/azure-container-registry-logo.09494206991d4b29.webp new file mode 100644 index 000000000..1d3e5c014 Binary files /dev/null and b/translated_images/zh-HK/azure-container-registry-logo.09494206991d4b29.webp differ diff --git a/translated_images/zh-HK/azure-functions-logo.1cfc8e3204c9c44a.webp b/translated_images/zh-HK/azure-functions-logo.1cfc8e3204c9c44a.webp new file mode 100644 index 000000000..1ce27bf99 Binary files /dev/null and b/translated_images/zh-HK/azure-functions-logo.1cfc8e3204c9c44a.webp differ diff --git a/translated_images/zh-HK/azure-iot-edge-logo.c1c076749b5cba2e.webp b/translated_images/zh-HK/azure-iot-edge-logo.c1c076749b5cba2e.webp new file mode 100644 index 000000000..771d5e6f8 Binary files /dev/null and b/translated_images/zh-HK/azure-iot-edge-logo.c1c076749b5cba2e.webp differ diff --git a/translated_images/zh-HK/azure-iot-hub-logo.28a19de76d0a1932.webp b/translated_images/zh-HK/azure-iot-hub-logo.28a19de76d0a1932.webp new file mode 100644 index 000000000..91843530b Binary files /dev/null and b/translated_images/zh-HK/azure-iot-hub-logo.28a19de76d0a1932.webp differ diff --git a/translated_images/zh-HK/azure-maps-logo.35d01dcfbd81fe61.webp b/translated_images/zh-HK/azure-maps-logo.35d01dcfbd81fe61.webp new file mode 100644 index 000000000..9a9853be7 Binary files /dev/null and b/translated_images/zh-HK/azure-maps-logo.35d01dcfbd81fe61.webp differ diff --git a/translated_images/zh-HK/azure-region-existing.73f704604f2aa6cb.webp b/translated_images/zh-HK/azure-region-existing.73f704604f2aa6cb.webp new file mode 100644 index 000000000..70da057ed Binary files /dev/null and b/translated_images/zh-HK/azure-region-existing.73f704604f2aa6cb.webp differ diff --git a/translated_images/zh-HK/azure-region-planned-expansion.a5074a1e8af74f15.webp b/translated_images/zh-HK/azure-region-planned-expansion.a5074a1e8af74f15.webp new file mode 100644 index 000000000..e0ee60053 Binary files /dev/null and b/translated_images/zh-HK/azure-region-planned-expansion.a5074a1e8af74f15.webp differ diff --git a/translated_images/zh-HK/azure-speech-logo.a1f08c4befb0159f.webp b/translated_images/zh-HK/azure-speech-logo.a1f08c4befb0159f.webp new file mode 100644 index 000000000..61c7dfd95 Binary files /dev/null and b/translated_images/zh-HK/azure-speech-logo.a1f08c4befb0159f.webp differ diff --git a/translated_images/zh-HK/azure-storage-logo.605c0f602c640d48.webp b/translated_images/zh-HK/azure-storage-logo.605c0f602c640d48.webp new file mode 100644 index 000000000..49832fd21 Binary files /dev/null and b/translated_images/zh-HK/azure-storage-logo.605c0f602c640d48.webp differ diff --git a/translated_images/zh-HK/azure-translator-logo.c6ed3a4a433edfd2.webp b/translated_images/zh-HK/azure-translator-logo.c6ed3a4a433edfd2.webp new file mode 100644 index 000000000..065b3e087 Binary files /dev/null and b/translated_images/zh-HK/azure-translator-logo.c6ed3a4a433edfd2.webp differ diff --git a/translated_images/zh-HK/banana-arducam.be1b32d4267a8194.webp b/translated_images/zh-HK/banana-arducam.be1b32d4267a8194.webp new file mode 100644 index 000000000..e0e788e08 Binary files /dev/null and b/translated_images/zh-HK/banana-arducam.be1b32d4267a8194.webp differ diff --git a/translated_images/zh-HK/banana-picture-compare.174df164dc326a42.webp b/translated_images/zh-HK/banana-picture-compare.174df164dc326a42.webp new file mode 100644 index 000000000..85618ec5d Binary files /dev/null and b/translated_images/zh-HK/banana-picture-compare.174df164dc326a42.webp differ diff --git a/translated_images/zh-HK/banana-ripe-1.6ed72365ffc92300.webp b/translated_images/zh-HK/banana-ripe-1.6ed72365ffc92300.webp new file mode 100644 index 000000000..c0714bbc3 Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-1.6ed72365ffc92300.webp differ diff --git a/translated_images/zh-HK/banana-ripe-1.780e9be3a60d8879.webp b/translated_images/zh-HK/banana-ripe-1.780e9be3a60d8879.webp new file mode 100644 index 000000000..8075dc4ab Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-1.780e9be3a60d8879.webp differ diff --git a/translated_images/zh-HK/banana-ripe-10.c3d98eb280e7464f.webp b/translated_images/zh-HK/banana-ripe-10.c3d98eb280e7464f.webp new file mode 100644 index 000000000..6d828b371 Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-10.c3d98eb280e7464f.webp differ diff --git a/translated_images/zh-HK/banana-ripe-11.3d932f292b95b9a2.webp b/translated_images/zh-HK/banana-ripe-11.3d932f292b95b9a2.webp new file mode 100644 index 000000000..7dfafb15a Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-11.3d932f292b95b9a2.webp differ diff --git a/translated_images/zh-HK/banana-ripe-12.9f87e663b9da6c85.webp b/translated_images/zh-HK/banana-ripe-12.9f87e663b9da6c85.webp new file mode 100644 index 000000000..f0e9b2d77 Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-12.9f87e663b9da6c85.webp differ diff --git a/translated_images/zh-HK/banana-ripe-13.b7402e05160f4543.webp b/translated_images/zh-HK/banana-ripe-13.b7402e05160f4543.webp new file mode 100644 index 000000000..3d0ed4bb1 Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-13.b7402e05160f4543.webp differ diff --git a/translated_images/zh-HK/banana-ripe-14.85cf309aa702cfa8.webp b/translated_images/zh-HK/banana-ripe-14.85cf309aa702cfa8.webp new file mode 100644 index 000000000..bdb2f1b6a Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-14.85cf309aa702cfa8.webp differ diff --git a/translated_images/zh-HK/banana-ripe-15.933412eb14a3f8e4.webp b/translated_images/zh-HK/banana-ripe-15.933412eb14a3f8e4.webp new file mode 100644 index 000000000..4ecc1c8d5 Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-15.933412eb14a3f8e4.webp differ diff --git a/translated_images/zh-HK/banana-ripe-16.781a68a01401f89e.webp b/translated_images/zh-HK/banana-ripe-16.781a68a01401f89e.webp new file mode 100644 index 000000000..b77a66a97 Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-16.781a68a01401f89e.webp differ diff --git a/translated_images/zh-HK/banana-ripe-17.f8e2853a40d3ac45.webp b/translated_images/zh-HK/banana-ripe-17.f8e2853a40d3ac45.webp new file mode 100644 index 000000000..9ffb20deb Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-17.f8e2853a40d3ac45.webp differ diff --git a/translated_images/zh-HK/banana-ripe-18.3d05f5f78ecf6f0d.webp b/translated_images/zh-HK/banana-ripe-18.3d05f5f78ecf6f0d.webp new file mode 100644 index 000000000..733a99b5a Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-18.3d05f5f78ecf6f0d.webp differ diff --git a/translated_images/zh-HK/banana-ripe-19.ac3b0fc6cf6fdfd5.webp b/translated_images/zh-HK/banana-ripe-19.ac3b0fc6cf6fdfd5.webp new file mode 100644 index 000000000..aa4e5e453 Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-19.ac3b0fc6cf6fdfd5.webp differ diff --git a/translated_images/zh-HK/banana-ripe-2.8ab96da692df69f1.webp b/translated_images/zh-HK/banana-ripe-2.8ab96da692df69f1.webp new file mode 100644 index 000000000..debf28d24 Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-2.8ab96da692df69f1.webp differ diff --git a/translated_images/zh-HK/banana-ripe-2.96ddc53114c5f020.webp b/translated_images/zh-HK/banana-ripe-2.96ddc53114c5f020.webp new file mode 100644 index 000000000..5262b2b85 Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-2.96ddc53114c5f020.webp differ diff --git a/translated_images/zh-HK/banana-ripe-20.a4d0ad33a7e6f037.webp b/translated_images/zh-HK/banana-ripe-20.a4d0ad33a7e6f037.webp new file mode 100644 index 000000000..9f6572948 Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-20.a4d0ad33a7e6f037.webp differ diff --git a/translated_images/zh-HK/banana-ripe-21.07e03d64f265d55d.webp b/translated_images/zh-HK/banana-ripe-21.07e03d64f265d55d.webp new file mode 100644 index 000000000..958809792 Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-21.07e03d64f265d55d.webp differ diff --git a/translated_images/zh-HK/banana-ripe-22.a63c05aeb7f866fc.webp b/translated_images/zh-HK/banana-ripe-22.a63c05aeb7f866fc.webp new file mode 100644 index 000000000..07de2a760 Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-22.a63c05aeb7f866fc.webp differ diff --git a/translated_images/zh-HK/banana-ripe-23.6f3364afcab19e57.webp b/translated_images/zh-HK/banana-ripe-23.6f3364afcab19e57.webp new file mode 100644 index 000000000..e1656d2f0 Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-23.6f3364afcab19e57.webp differ diff --git a/translated_images/zh-HK/banana-ripe-24.ff2c02cc80a9c430.webp b/translated_images/zh-HK/banana-ripe-24.ff2c02cc80a9c430.webp new file mode 100644 index 000000000..fda478f0f Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-24.ff2c02cc80a9c430.webp differ diff --git a/translated_images/zh-HK/banana-ripe-25.65ce63418cdc4de2.webp b/translated_images/zh-HK/banana-ripe-25.65ce63418cdc4de2.webp new file mode 100644 index 000000000..50a1e1b38 Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-25.65ce63418cdc4de2.webp differ diff --git a/translated_images/zh-HK/banana-ripe-3.4fae05a0a1d2b5f0.webp b/translated_images/zh-HK/banana-ripe-3.4fae05a0a1d2b5f0.webp new file mode 100644 index 000000000..9baa7684d Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-3.4fae05a0a1d2b5f0.webp differ diff --git a/translated_images/zh-HK/banana-ripe-5.c762086879ccec4c.webp b/translated_images/zh-HK/banana-ripe-5.c762086879ccec4c.webp new file mode 100644 index 000000000..9e996d31d Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-5.c762086879ccec4c.webp differ diff --git a/translated_images/zh-HK/banana-ripe-6.5131bcbf492980cb.webp b/translated_images/zh-HK/banana-ripe-6.5131bcbf492980cb.webp new file mode 100644 index 000000000..5c8d85b0c Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-6.5131bcbf492980cb.webp differ diff --git a/translated_images/zh-HK/banana-ripe-7.5fc18dfe7b7ae9dc.webp b/translated_images/zh-HK/banana-ripe-7.5fc18dfe7b7ae9dc.webp new file mode 100644 index 000000000..a90c4fe6c Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-7.5fc18dfe7b7ae9dc.webp differ diff --git a/translated_images/zh-HK/banana-ripe-8.dba1d33bd34d4830.webp b/translated_images/zh-HK/banana-ripe-8.dba1d33bd34d4830.webp new file mode 100644 index 000000000..48f5aab3d Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-8.dba1d33bd34d4830.webp differ diff --git a/translated_images/zh-HK/banana-ripe-9.32f91462c8b0e2d3.webp b/translated_images/zh-HK/banana-ripe-9.32f91462c8b0e2d3.webp new file mode 100644 index 000000000..5e86630cd Binary files /dev/null and b/translated_images/zh-HK/banana-ripe-9.32f91462c8b0e2d3.webp differ diff --git a/translated_images/zh-HK/banana-training-images.530eb203346d73bc.webp b/translated_images/zh-HK/banana-training-images.530eb203346d73bc.webp new file mode 100644 index 000000000..5e012ab99 Binary files /dev/null and b/translated_images/zh-HK/banana-training-images.530eb203346d73bc.webp differ diff --git a/translated_images/zh-HK/banana-unripe-1.910c8606a300fa20.webp b/translated_images/zh-HK/banana-unripe-1.910c8606a300fa20.webp new file mode 100644 index 000000000..e045a73db Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-1.910c8606a300fa20.webp differ diff --git a/translated_images/zh-HK/banana-unripe-1.b2c7051d9c8a4e61.webp b/translated_images/zh-HK/banana-unripe-1.b2c7051d9c8a4e61.webp new file mode 100644 index 000000000..5e88485a0 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-1.b2c7051d9c8a4e61.webp differ diff --git a/translated_images/zh-HK/banana-unripe-10.38dab0db918a2487.webp b/translated_images/zh-HK/banana-unripe-10.38dab0db918a2487.webp new file mode 100644 index 000000000..d863e575b Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-10.38dab0db918a2487.webp differ diff --git a/translated_images/zh-HK/banana-unripe-11.ec8d0eefe63e10b9.webp b/translated_images/zh-HK/banana-unripe-11.ec8d0eefe63e10b9.webp new file mode 100644 index 000000000..d77f907da Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-11.ec8d0eefe63e10b9.webp differ diff --git a/translated_images/zh-HK/banana-unripe-12.b95a088ccae935db.webp b/translated_images/zh-HK/banana-unripe-12.b95a088ccae935db.webp new file mode 100644 index 000000000..39fe8050a Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-12.b95a088ccae935db.webp differ diff --git a/translated_images/zh-HK/banana-unripe-13.37e5a7cac5aa0920.webp b/translated_images/zh-HK/banana-unripe-13.37e5a7cac5aa0920.webp new file mode 100644 index 000000000..4991d4f65 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-13.37e5a7cac5aa0920.webp differ diff --git a/translated_images/zh-HK/banana-unripe-14.d19ddd6bbf63a259.webp b/translated_images/zh-HK/banana-unripe-14.d19ddd6bbf63a259.webp new file mode 100644 index 000000000..eff19a63b Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-14.d19ddd6bbf63a259.webp differ diff --git a/translated_images/zh-HK/banana-unripe-15.274e48544326077a.webp b/translated_images/zh-HK/banana-unripe-15.274e48544326077a.webp new file mode 100644 index 000000000..e16197d1f Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-15.274e48544326077a.webp differ diff --git a/translated_images/zh-HK/banana-unripe-16.bd058f64bd7ec014.webp b/translated_images/zh-HK/banana-unripe-16.bd058f64bd7ec014.webp new file mode 100644 index 000000000..4abdad3e5 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-16.bd058f64bd7ec014.webp differ diff --git a/translated_images/zh-HK/banana-unripe-17.408382d679bfa079.webp b/translated_images/zh-HK/banana-unripe-17.408382d679bfa079.webp new file mode 100644 index 000000000..81be6d428 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-17.408382d679bfa079.webp differ diff --git a/translated_images/zh-HK/banana-unripe-18.39c0eb79d7b3b9ba.webp b/translated_images/zh-HK/banana-unripe-18.39c0eb79d7b3b9ba.webp new file mode 100644 index 000000000..a012cd4eb Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-18.39c0eb79d7b3b9ba.webp differ diff --git a/translated_images/zh-HK/banana-unripe-19.e61e6d7efaf2d8c8.webp b/translated_images/zh-HK/banana-unripe-19.e61e6d7efaf2d8c8.webp new file mode 100644 index 000000000..da22c29fe Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-19.e61e6d7efaf2d8c8.webp differ diff --git a/translated_images/zh-HK/banana-unripe-2.43a73b544521afc7.webp b/translated_images/zh-HK/banana-unripe-2.43a73b544521afc7.webp new file mode 100644 index 000000000..10a0ab56d Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-2.43a73b544521afc7.webp differ diff --git a/translated_images/zh-HK/banana-unripe-2.9591d1a6aa27deeb.webp b/translated_images/zh-HK/banana-unripe-2.9591d1a6aa27deeb.webp new file mode 100644 index 000000000..049459245 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-2.9591d1a6aa27deeb.webp differ diff --git a/translated_images/zh-HK/banana-unripe-20.85b7a74eaab5634e.webp b/translated_images/zh-HK/banana-unripe-20.85b7a74eaab5634e.webp new file mode 100644 index 000000000..80c5effcb Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-20.85b7a74eaab5634e.webp differ diff --git a/translated_images/zh-HK/banana-unripe-21.ccc1333439b344bc.webp b/translated_images/zh-HK/banana-unripe-21.ccc1333439b344bc.webp new file mode 100644 index 000000000..a99702656 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-21.ccc1333439b344bc.webp differ diff --git a/translated_images/zh-HK/banana-unripe-22.27dff4b438163080.webp b/translated_images/zh-HK/banana-unripe-22.27dff4b438163080.webp new file mode 100644 index 000000000..87a3ab30c Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-22.27dff4b438163080.webp differ diff --git a/translated_images/zh-HK/banana-unripe-23.c4c9067f23370e90.webp b/translated_images/zh-HK/banana-unripe-23.c4c9067f23370e90.webp new file mode 100644 index 000000000..8c554da6d Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-23.c4c9067f23370e90.webp differ diff --git a/translated_images/zh-HK/banana-unripe-24.6f0b781c309da62f.webp b/translated_images/zh-HK/banana-unripe-24.6f0b781c309da62f.webp new file mode 100644 index 000000000..7998990dc Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-24.6f0b781c309da62f.webp differ diff --git a/translated_images/zh-HK/banana-unripe-25.21d553d84880ac4f.webp b/translated_images/zh-HK/banana-unripe-25.21d553d84880ac4f.webp new file mode 100644 index 000000000..be2233b77 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-25.21d553d84880ac4f.webp differ diff --git a/translated_images/zh-HK/banana-unripe-26.823c48b61feb1d5c.webp b/translated_images/zh-HK/banana-unripe-26.823c48b61feb1d5c.webp new file mode 100644 index 000000000..ab902dbf6 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-26.823c48b61feb1d5c.webp differ diff --git a/translated_images/zh-HK/banana-unripe-27.f98fd272deeb02d9.webp b/translated_images/zh-HK/banana-unripe-27.f98fd272deeb02d9.webp new file mode 100644 index 000000000..ec3f75024 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-27.f98fd272deeb02d9.webp differ diff --git a/translated_images/zh-HK/banana-unripe-28.5331ba409ce41c07.webp b/translated_images/zh-HK/banana-unripe-28.5331ba409ce41c07.webp new file mode 100644 index 000000000..addfa4127 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-28.5331ba409ce41c07.webp differ diff --git a/translated_images/zh-HK/banana-unripe-29.84e126f389bf627e.webp b/translated_images/zh-HK/banana-unripe-29.84e126f389bf627e.webp new file mode 100644 index 000000000..0a17c8f7b Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-29.84e126f389bf627e.webp differ diff --git a/translated_images/zh-HK/banana-unripe-3.896df8fb2c3b8f51.webp b/translated_images/zh-HK/banana-unripe-3.896df8fb2c3b8f51.webp new file mode 100644 index 000000000..b9c30fe66 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-3.896df8fb2c3b8f51.webp differ diff --git a/translated_images/zh-HK/banana-unripe-4.483e740d6fd7b5a6.webp b/translated_images/zh-HK/banana-unripe-4.483e740d6fd7b5a6.webp new file mode 100644 index 000000000..eefb7b624 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-4.483e740d6fd7b5a6.webp differ diff --git a/translated_images/zh-HK/banana-unripe-5.e9923cf1ffcfc1c9.webp b/translated_images/zh-HK/banana-unripe-5.e9923cf1ffcfc1c9.webp new file mode 100644 index 000000000..acfe59606 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-5.e9923cf1ffcfc1c9.webp differ diff --git a/translated_images/zh-HK/banana-unripe-6.e3a73307558caecc.webp b/translated_images/zh-HK/banana-unripe-6.e3a73307558caecc.webp new file mode 100644 index 000000000..72ae0174f Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-6.e3a73307558caecc.webp differ diff --git a/translated_images/zh-HK/banana-unripe-7.634ca89acc17d68f.webp b/translated_images/zh-HK/banana-unripe-7.634ca89acc17d68f.webp new file mode 100644 index 000000000..283cae548 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-7.634ca89acc17d68f.webp differ diff --git a/translated_images/zh-HK/banana-unripe-8.75720b4cdebac8c3.webp b/translated_images/zh-HK/banana-unripe-8.75720b4cdebac8c3.webp new file mode 100644 index 000000000..f09e18518 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-8.75720b4cdebac8c3.webp differ diff --git a/translated_images/zh-HK/banana-unripe-9.e8076983351d2f54.webp b/translated_images/zh-HK/banana-unripe-9.e8076983351d2f54.webp new file mode 100644 index 000000000..c7f53d90f Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-9.e8076983351d2f54.webp differ diff --git a/translated_images/zh-HK/banana-unripe-quick-test-prediction.dae9b5e1c4ef7c64.webp b/translated_images/zh-HK/banana-unripe-quick-test-prediction.dae9b5e1c4ef7c64.webp new file mode 100644 index 000000000..feb843f96 Binary files /dev/null and b/translated_images/zh-HK/banana-unripe-quick-test-prediction.dae9b5e1c4ef7c64.webp differ diff --git a/translated_images/zh-HK/bananas-ripe-vs-unripe-predictions.8d0e2034014aa50e.webp b/translated_images/zh-HK/bananas-ripe-vs-unripe-predictions.8d0e2034014aa50e.webp new file mode 100644 index 000000000..b289110aa Binary files /dev/null and b/translated_images/zh-HK/bananas-ripe-vs-unripe-predictions.8d0e2034014aa50e.webp differ diff --git a/translated_images/zh-HK/basic-thermostat.a923217fd1f37e5a.webp b/translated_images/zh-HK/basic-thermostat.a923217fd1f37e5a.webp new file mode 100644 index 000000000..b112cc52e Binary files /dev/null and b/translated_images/zh-HK/basic-thermostat.a923217fd1f37e5a.webp differ diff --git a/translated_images/zh-HK/bing-translate.348aa796d6efe2a9.webp b/translated_images/zh-HK/bing-translate.348aa796d6efe2a9.webp new file mode 100644 index 000000000..6c324ca84 Binary files /dev/null and b/translated_images/zh-HK/bing-translate.348aa796d6efe2a9.webp differ diff --git a/translated_images/zh-HK/bounding-box.1420a7ea0d3d15f7.webp b/translated_images/zh-HK/bounding-box.1420a7ea0d3d15f7.webp new file mode 100644 index 000000000..0d2a0e898 Binary files /dev/null and b/translated_images/zh-HK/bounding-box.1420a7ea0d3d15f7.webp differ diff --git a/translated_images/zh-HK/bricked-car.dc38f8efadc6c59d.webp b/translated_images/zh-HK/bricked-car.dc38f8efadc6c59d.webp new file mode 100644 index 000000000..b3f32058c Binary files /dev/null and b/translated_images/zh-HK/bricked-car.dc38f8efadc6c59d.webp differ diff --git a/translated_images/zh-HK/button-with-digital.3749edea8eb885af.webp b/translated_images/zh-HK/button-with-digital.3749edea8eb885af.webp new file mode 100644 index 000000000..45eeb7f3e Binary files /dev/null and b/translated_images/zh-HK/button-with-digital.3749edea8eb885af.webp differ diff --git a/translated_images/zh-HK/button.eadb560b77ac45e5.webp b/translated_images/zh-HK/button.eadb560b77ac45e5.webp new file mode 100644 index 000000000..f538d2f36 Binary files /dev/null and b/translated_images/zh-HK/button.eadb560b77ac45e5.webp differ diff --git a/translated_images/zh-HK/chart-soil-moisture-relay.fbb391236d34a64d.webp b/translated_images/zh-HK/chart-soil-moisture-relay.fbb391236d34a64d.webp new file mode 100644 index 000000000..a7efcef6f Binary files /dev/null and b/translated_images/zh-HK/chart-soil-moisture-relay.fbb391236d34a64d.webp differ diff --git a/translated_images/zh-HK/chart-soil-moisture.fd6d9d0cdc0b5f75.webp b/translated_images/zh-HK/chart-soil-moisture.fd6d9d0cdc0b5f75.webp new file mode 100644 index 000000000..314840664 Binary files /dev/null and b/translated_images/zh-HK/chart-soil-moisture.fd6d9d0cdc0b5f75.webp differ diff --git a/translated_images/zh-HK/child-watering-garden.9a5d3f1bfe6d0d8d.webp b/translated_images/zh-HK/child-watering-garden.9a5d3f1bfe6d0d8d.webp new file mode 100644 index 000000000..5aee7f4c6 Binary files /dev/null and b/translated_images/zh-HK/child-watering-garden.9a5d3f1bfe6d0d8d.webp differ diff --git a/translated_images/zh-HK/cloud-with-edge.1e26462c62c126fe.webp b/translated_images/zh-HK/cloud-with-edge.1e26462c62c126fe.webp new file mode 100644 index 000000000..6a5c52a64 Binary files /dev/null and b/translated_images/zh-HK/cloud-with-edge.1e26462c62c126fe.webp differ diff --git a/translated_images/zh-HK/cloud-without-edge.b4da641f6022c95e.webp b/translated_images/zh-HK/cloud-without-edge.b4da641f6022c95e.webp new file mode 100644 index 000000000..7167594f4 Binary files /dev/null and b/translated_images/zh-HK/cloud-without-edge.b4da641f6022c95e.webp differ diff --git a/translated_images/zh-HK/cmos-sensor.75f9cd74decb1371.webp b/translated_images/zh-HK/cmos-sensor.75f9cd74decb1371.webp new file mode 100644 index 000000000..ccf8737de Binary files /dev/null and b/translated_images/zh-HK/cmos-sensor.75f9cd74decb1371.webp differ diff --git a/translated_images/zh-HK/commands.d6c06bbbb3a02cce.webp b/translated_images/zh-HK/commands.d6c06bbbb3a02cce.webp new file mode 100644 index 000000000..5ce548847 Binary files /dev/null and b/translated_images/zh-HK/commands.d6c06bbbb3a02cce.webp differ diff --git a/translated_images/zh-HK/condenser-mic.6f6ed5b76ca19e0e.webp b/translated_images/zh-HK/condenser-mic.6f6ed5b76ca19e0e.webp new file mode 100644 index 000000000..598ab4294 Binary files /dev/null and b/translated_images/zh-HK/condenser-mic.6f6ed5b76ca19e0e.webp differ diff --git a/translated_images/zh-HK/consumer-groups.a3262e26fc27ba20.webp b/translated_images/zh-HK/consumer-groups.a3262e26fc27ba20.webp new file mode 100644 index 000000000..db9cf40de Binary files /dev/null and b/translated_images/zh-HK/consumer-groups.a3262e26fc27ba20.webp differ diff --git a/translated_images/zh-HK/container-edge-flow.c246050dd60ceefd.webp b/translated_images/zh-HK/container-edge-flow.c246050dd60ceefd.webp new file mode 100644 index 000000000..2265a2b1a Binary files /dev/null and b/translated_images/zh-HK/container-edge-flow.c246050dd60ceefd.webp differ diff --git a/translated_images/zh-HK/container-web-browser.4ee81dd4f0d8838c.webp b/translated_images/zh-HK/container-web-browser.4ee81dd4f0d8838c.webp new file mode 100644 index 000000000..adbfbea1f Binary files /dev/null and b/translated_images/zh-HK/container-web-browser.4ee81dd4f0d8838c.webp differ diff --git a/translated_images/zh-HK/counterfit-camera-options.eb3bd5150a8e7dff.webp b/translated_images/zh-HK/counterfit-camera-options.eb3bd5150a8e7dff.webp new file mode 100644 index 000000000..825636af3 Binary files /dev/null and b/translated_images/zh-HK/counterfit-camera-options.eb3bd5150a8e7dff.webp differ diff --git a/translated_images/zh-HK/counterfit-camera.001ec52194c8ee5d.webp b/translated_images/zh-HK/counterfit-camera.001ec52194c8ee5d.webp new file mode 100644 index 000000000..5b1687cc7 Binary files /dev/null and b/translated_images/zh-HK/counterfit-camera.001ec52194c8ee5d.webp differ diff --git a/translated_images/zh-HK/counterfit-connected.ed30b46d8f79b092.webp b/translated_images/zh-HK/counterfit-connected.ed30b46d8f79b092.webp new file mode 100644 index 000000000..080b967c0 Binary files /dev/null and b/translated_images/zh-HK/counterfit-connected.ed30b46d8f79b092.webp differ diff --git a/translated_images/zh-HK/counterfit-create-camera.a5de97f59c0bd3cb.webp b/translated_images/zh-HK/counterfit-create-camera.a5de97f59c0bd3cb.webp new file mode 100644 index 000000000..e09fe142f Binary files /dev/null and b/translated_images/zh-HK/counterfit-create-camera.a5de97f59c0bd3cb.webp differ diff --git a/translated_images/zh-HK/counterfit-create-distance-sensor.967c9fb98f27888d.webp b/translated_images/zh-HK/counterfit-create-distance-sensor.967c9fb98f27888d.webp new file mode 100644 index 000000000..cf18b749d Binary files /dev/null and b/translated_images/zh-HK/counterfit-create-distance-sensor.967c9fb98f27888d.webp differ diff --git a/translated_images/zh-HK/counterfit-create-gps-sensor.6385dc9357d85ad1.webp b/translated_images/zh-HK/counterfit-create-gps-sensor.6385dc9357d85ad1.webp new file mode 100644 index 000000000..227f14afc Binary files /dev/null and b/translated_images/zh-HK/counterfit-create-gps-sensor.6385dc9357d85ad1.webp differ diff --git a/translated_images/zh-HK/counterfit-create-humidity-sensor.2750e27b6f30e09c.webp b/translated_images/zh-HK/counterfit-create-humidity-sensor.2750e27b6f30e09c.webp new file mode 100644 index 000000000..ed523ae6b Binary files /dev/null and b/translated_images/zh-HK/counterfit-create-humidity-sensor.2750e27b6f30e09c.webp differ diff --git a/translated_images/zh-HK/counterfit-create-led.ba9db1c9b8c622a6.webp b/translated_images/zh-HK/counterfit-create-led.ba9db1c9b8c622a6.webp new file mode 100644 index 000000000..1b71a2c69 Binary files /dev/null and b/translated_images/zh-HK/counterfit-create-led.ba9db1c9b8c622a6.webp differ diff --git a/translated_images/zh-HK/counterfit-create-light-sensor.9f36a5e0d4458d8d.webp b/translated_images/zh-HK/counterfit-create-light-sensor.9f36a5e0d4458d8d.webp new file mode 100644 index 000000000..e789fd9a4 Binary files /dev/null and b/translated_images/zh-HK/counterfit-create-light-sensor.9f36a5e0d4458d8d.webp differ diff --git a/translated_images/zh-HK/counterfit-create-relay.fa7c40fd0f2f6afc.webp b/translated_images/zh-HK/counterfit-create-relay.fa7c40fd0f2f6afc.webp new file mode 100644 index 000000000..161e5ab8f Binary files /dev/null and b/translated_images/zh-HK/counterfit-create-relay.fa7c40fd0f2f6afc.webp differ diff --git a/translated_images/zh-HK/counterfit-create-soil-moisture-sensor.35266135a5e0ae68.webp b/translated_images/zh-HK/counterfit-create-soil-moisture-sensor.35266135a5e0ae68.webp new file mode 100644 index 000000000..ac47f85b9 Binary files /dev/null and b/translated_images/zh-HK/counterfit-create-soil-moisture-sensor.35266135a5e0ae68.webp differ diff --git a/translated_images/zh-HK/counterfit-create-temperature-sensor.199350ed34f7343d.webp b/translated_images/zh-HK/counterfit-create-temperature-sensor.199350ed34f7343d.webp new file mode 100644 index 000000000..0581dd083 Binary files /dev/null and b/translated_images/zh-HK/counterfit-create-temperature-sensor.199350ed34f7343d.webp differ diff --git a/translated_images/zh-HK/counterfit-distance-sensor.079eefeeea0b68af.webp b/translated_images/zh-HK/counterfit-distance-sensor.079eefeeea0b68af.webp new file mode 100644 index 000000000..b336e4e84 Binary files /dev/null and b/translated_images/zh-HK/counterfit-distance-sensor.079eefeeea0b68af.webp differ diff --git a/translated_images/zh-HK/counterfit-first-run.433326358b669b31.webp b/translated_images/zh-HK/counterfit-first-run.433326358b669b31.webp new file mode 100644 index 000000000..6675385db Binary files /dev/null and b/translated_images/zh-HK/counterfit-first-run.433326358b669b31.webp differ diff --git a/translated_images/zh-HK/counterfit-gps-sensor-gpxfile.8310b063ce8a425c.webp b/translated_images/zh-HK/counterfit-gps-sensor-gpxfile.8310b063ce8a425c.webp new file mode 100644 index 000000000..ba69c5a05 Binary files /dev/null and b/translated_images/zh-HK/counterfit-gps-sensor-gpxfile.8310b063ce8a425c.webp differ diff --git a/translated_images/zh-HK/counterfit-gps-sensor-latlon.008c867d75464fbe.webp b/translated_images/zh-HK/counterfit-gps-sensor-latlon.008c867d75464fbe.webp new file mode 100644 index 000000000..634eada36 Binary files /dev/null and b/translated_images/zh-HK/counterfit-gps-sensor-latlon.008c867d75464fbe.webp differ diff --git a/translated_images/zh-HK/counterfit-gps-sensor-nmea.c62eea442171e17e.webp b/translated_images/zh-HK/counterfit-gps-sensor-nmea.c62eea442171e17e.webp new file mode 100644 index 000000000..25817e25d Binary files /dev/null and b/translated_images/zh-HK/counterfit-gps-sensor-nmea.c62eea442171e17e.webp differ diff --git a/translated_images/zh-HK/counterfit-gps-sensor.3fbb15af0a536756.webp b/translated_images/zh-HK/counterfit-gps-sensor.3fbb15af0a536756.webp new file mode 100644 index 000000000..e1dca4364 Binary files /dev/null and b/translated_images/zh-HK/counterfit-gps-sensor.3fbb15af0a536756.webp differ diff --git a/translated_images/zh-HK/counterfit-humidity-sensor.7b12f7f339e430cb.webp b/translated_images/zh-HK/counterfit-humidity-sensor.7b12f7f339e430cb.webp new file mode 100644 index 000000000..21ad5ccca Binary files /dev/null and b/translated_images/zh-HK/counterfit-humidity-sensor.7b12f7f339e430cb.webp differ diff --git a/translated_images/zh-HK/counterfit-led.c0ab02de6d256ad8.webp b/translated_images/zh-HK/counterfit-led.c0ab02de6d256ad8.webp new file mode 100644 index 000000000..ddf345804 Binary files /dev/null and b/translated_images/zh-HK/counterfit-led.c0ab02de6d256ad8.webp differ diff --git a/translated_images/zh-HK/counterfit-light-sensor.5d0f5584df56b90f.webp b/translated_images/zh-HK/counterfit-light-sensor.5d0f5584df56b90f.webp new file mode 100644 index 000000000..22a34d88b Binary files /dev/null and b/translated_images/zh-HK/counterfit-light-sensor.5d0f5584df56b90f.webp differ diff --git a/translated_images/zh-HK/counterfit-relay.bbf74c1dbdc8b9ac.webp b/translated_images/zh-HK/counterfit-relay.bbf74c1dbdc8b9ac.webp new file mode 100644 index 000000000..f210c049f Binary files /dev/null and b/translated_images/zh-HK/counterfit-relay.bbf74c1dbdc8b9ac.webp differ diff --git a/translated_images/zh-HK/counterfit-soil-moisture-sensor.81742b2de0e9de60.webp b/translated_images/zh-HK/counterfit-soil-moisture-sensor.81742b2de0e9de60.webp new file mode 100644 index 000000000..169e31e67 Binary files /dev/null and b/translated_images/zh-HK/counterfit-soil-moisture-sensor.81742b2de0e9de60.webp differ diff --git a/translated_images/zh-HK/counterfit-temperature-sensor.f0560236c96a9016.webp b/translated_images/zh-HK/counterfit-temperature-sensor.f0560236c96a9016.webp new file mode 100644 index 000000000..1b8db5f58 Binary files /dev/null and b/translated_images/zh-HK/counterfit-temperature-sensor.f0560236c96a9016.webp differ diff --git a/translated_images/zh-HK/custom-vision-banana-prediction.30cdff4e1d72db5d.webp b/translated_images/zh-HK/custom-vision-banana-prediction.30cdff4e1d72db5d.webp new file mode 100644 index 000000000..0052205b8 Binary files /dev/null and b/translated_images/zh-HK/custom-vision-banana-prediction.30cdff4e1d72db5d.webp differ diff --git a/translated_images/zh-HK/custom-vision-create-object-detector-project.32d4fb9aa8e7e737.webp b/translated_images/zh-HK/custom-vision-create-object-detector-project.32d4fb9aa8e7e737.webp new file mode 100644 index 000000000..2cdb63729 Binary files /dev/null and b/translated_images/zh-HK/custom-vision-create-object-detector-project.32d4fb9aa8e7e737.webp differ diff --git a/translated_images/zh-HK/custom-vision-create-project.cf46325b92d8b131.webp b/translated_images/zh-HK/custom-vision-create-project.cf46325b92d8b131.webp new file mode 100644 index 000000000..fb1e1b760 Binary files /dev/null and b/translated_images/zh-HK/custom-vision-create-project.cf46325b92d8b131.webp differ diff --git a/translated_images/zh-HK/custom-vision-logo.d3d4e7c8a87ec9da.webp b/translated_images/zh-HK/custom-vision-logo.d3d4e7c8a87ec9da.webp new file mode 100644 index 000000000..33f20090a Binary files /dev/null and b/translated_images/zh-HK/custom-vision-logo.d3d4e7c8a87ec9da.webp differ diff --git a/translated_images/zh-HK/custom-vision-object-detector-publish-button.34ee379fc650ccb9.webp b/translated_images/zh-HK/custom-vision-object-detector-publish-button.34ee379fc650ccb9.webp new file mode 100644 index 000000000..2ef6bf985 Binary files /dev/null and b/translated_images/zh-HK/custom-vision-object-detector-publish-button.34ee379fc650ccb9.webp differ diff --git a/translated_images/zh-HK/custom-vision-prediction-key-endpoint.30c569ffd0338864.webp b/translated_images/zh-HK/custom-vision-prediction-key-endpoint.30c569ffd0338864.webp new file mode 100644 index 000000000..f5a8d32f1 Binary files /dev/null and b/translated_images/zh-HK/custom-vision-prediction-key-endpoint.30c569ffd0338864.webp differ diff --git a/translated_images/zh-HK/custom-vision-publish-button.b7174e1977b0c33b.webp b/translated_images/zh-HK/custom-vision-publish-button.b7174e1977b0c33b.webp new file mode 100644 index 000000000..9e64ffced Binary files /dev/null and b/translated_images/zh-HK/custom-vision-publish-button.b7174e1977b0c33b.webp differ diff --git a/translated_images/zh-HK/custom-vision-stock-prediction.942266ab1bcca341.webp b/translated_images/zh-HK/custom-vision-stock-prediction.942266ab1bcca341.webp new file mode 100644 index 000000000..42da52c1d Binary files /dev/null and b/translated_images/zh-HK/custom-vision-stock-prediction.942266ab1bcca341.webp differ diff --git a/translated_images/zh-HK/dimmable-light.9ceffeb195dec1a8.webp b/translated_images/zh-HK/dimmable-light.9ceffeb195dec1a8.webp new file mode 100644 index 000000000..77b5d8f81 Binary files /dev/null and b/translated_images/zh-HK/dimmable-light.9ceffeb195dec1a8.webp differ diff --git a/translated_images/zh-HK/dmac-adc-buffers.4509aee49145c90b.webp b/translated_images/zh-HK/dmac-adc-buffers.4509aee49145c90b.webp new file mode 100644 index 000000000..53b81671e Binary files /dev/null and b/translated_images/zh-HK/dmac-adc-buffers.4509aee49145c90b.webp differ diff --git a/translated_images/zh-HK/dynamic-mic.8babac890a2d80df.webp b/translated_images/zh-HK/dynamic-mic.8babac890a2d80df.webp new file mode 100644 index 000000000..c00572014 Binary files /dev/null and b/translated_images/zh-HK/dynamic-mic.8babac890a2d80df.webp differ diff --git a/translated_images/zh-HK/favicon.37b561214b36d454.webp b/translated_images/zh-HK/favicon.37b561214b36d454.webp new file mode 100644 index 000000000..48a53960d Binary files /dev/null and b/translated_images/zh-HK/favicon.37b561214b36d454.webp differ diff --git a/translated_images/zh-HK/fetch-decode-execute.2fd6f150f6280392.webp b/translated_images/zh-HK/fetch-decode-execute.2fd6f150f6280392.webp new file mode 100644 index 000000000..def349a60 Binary files /dev/null and b/translated_images/zh-HK/fetch-decode-execute.2fd6f150f6280392.webp differ diff --git a/translated_images/zh-HK/fruit-quality-detector-message-flow.adf2a65da8fd8741.webp b/translated_images/zh-HK/fruit-quality-detector-message-flow.adf2a65da8fd8741.webp new file mode 100644 index 000000000..97637240c Binary files /dev/null and b/translated_images/zh-HK/fruit-quality-detector-message-flow.adf2a65da8fd8741.webp differ diff --git a/translated_images/zh-HK/gdd-calculation-corn.64a58b7a7afcd0df.webp b/translated_images/zh-HK/gdd-calculation-corn.64a58b7a7afcd0df.webp new file mode 100644 index 000000000..c39379de7 Binary files /dev/null and b/translated_images/zh-HK/gdd-calculation-corn.64a58b7a7afcd0df.webp differ diff --git a/translated_images/zh-HK/gdd-calculation-strawberries.59f57db94b22adb8.webp b/translated_images/zh-HK/gdd-calculation-strawberries.59f57db94b22adb8.webp new file mode 100644 index 000000000..4d95505e5 Binary files /dev/null and b/translated_images/zh-HK/gdd-calculation-strawberries.59f57db94b22adb8.webp differ diff --git a/translated_images/zh-HK/gdd-calculation.79b3660f9c5757aa.webp b/translated_images/zh-HK/gdd-calculation.79b3660f9c5757aa.webp new file mode 100644 index 000000000..19224e262 Binary files /dev/null and b/translated_images/zh-HK/gdd-calculation.79b3660f9c5757aa.webp differ diff --git a/translated_images/zh-HK/gdd-jupyter-notebook.c5b52cf21094f158.webp b/translated_images/zh-HK/gdd-jupyter-notebook.c5b52cf21094f158.webp new file mode 100644 index 000000000..80bddf038 Binary files /dev/null and b/translated_images/zh-HK/gdd-jupyter-notebook.c5b52cf21094f158.webp differ diff --git a/translated_images/zh-HK/geofence-crossing-inaccurate-gps.6a3ed911202ad9ca.webp b/translated_images/zh-HK/geofence-crossing-inaccurate-gps.6a3ed911202ad9ca.webp new file mode 100644 index 000000000..7c5f83baa Binary files /dev/null and b/translated_images/zh-HK/geofence-crossing-inaccurate-gps.6a3ed911202ad9ca.webp differ diff --git a/translated_images/zh-HK/geofence-examples.172fbc534665769f.webp b/translated_images/zh-HK/geofence-examples.172fbc534665769f.webp new file mode 100644 index 000000000..c7543e551 Binary files /dev/null and b/translated_images/zh-HK/geofence-examples.172fbc534665769f.webp differ diff --git a/translated_images/zh-HK/gps-satellites.04acf1148fe25fbf.webp b/translated_images/zh-HK/gps-satellites.04acf1148fe25fbf.webp new file mode 100644 index 000000000..2675c5476 Binary files /dev/null and b/translated_images/zh-HK/gps-satellites.04acf1148fe25fbf.webp differ diff --git a/translated_images/zh-HK/gps-telemetry-iot-hub-functions.24d3fa5592455e9f.webp b/translated_images/zh-HK/gps-telemetry-iot-hub-functions.24d3fa5592455e9f.webp new file mode 100644 index 000000000..9a558cce1 Binary files /dev/null and b/translated_images/zh-HK/gps-telemetry-iot-hub-functions.24d3fa5592455e9f.webp differ diff --git a/translated_images/zh-HK/gps-telemetry-iot-hub.8115335d51cd2c12.webp b/translated_images/zh-HK/gps-telemetry-iot-hub.8115335d51cd2c12.webp new file mode 100644 index 000000000..73d99d1a8 Binary files /dev/null and b/translated_images/zh-HK/gps-telemetry-iot-hub.8115335d51cd2c12.webp differ diff --git a/translated_images/zh-HK/grove-base-hat-ribbon-cable.501fed202fcf73b1.webp b/translated_images/zh-HK/grove-base-hat-ribbon-cable.501fed202fcf73b1.webp new file mode 100644 index 000000000..1e244bd43 Binary files /dev/null and b/translated_images/zh-HK/grove-base-hat-ribbon-cable.501fed202fcf73b1.webp differ diff --git a/translated_images/zh-HK/grove-button.a70cfbb809a85636.webp b/translated_images/zh-HK/grove-button.a70cfbb809a85636.webp new file mode 100644 index 000000000..83a150907 Binary files /dev/null and b/translated_images/zh-HK/grove-button.a70cfbb809a85636.webp differ diff --git a/translated_images/zh-HK/grove-capacitive-soil-moisture-sensor.e7f0776cce30e78b.webp b/translated_images/zh-HK/grove-capacitive-soil-moisture-sensor.e7f0776cce30e78b.webp new file mode 100644 index 000000000..e6039552b Binary files /dev/null and b/translated_images/zh-HK/grove-capacitive-soil-moisture-sensor.e7f0776cce30e78b.webp differ diff --git a/translated_images/zh-HK/grove-dht11.07f8eafceee17004.webp b/translated_images/zh-HK/grove-dht11.07f8eafceee17004.webp new file mode 100644 index 000000000..839e0b054 Binary files /dev/null and b/translated_images/zh-HK/grove-dht11.07f8eafceee17004.webp differ diff --git a/translated_images/zh-HK/grove-gps-sensor.247943bf69b03f0d.webp b/translated_images/zh-HK/grove-gps-sensor.247943bf69b03f0d.webp new file mode 100644 index 000000000..8a66bb0c3 Binary files /dev/null and b/translated_images/zh-HK/grove-gps-sensor.247943bf69b03f0d.webp differ diff --git a/translated_images/zh-HK/grove-led.6c853be93f473cf2.webp b/translated_images/zh-HK/grove-led.6c853be93f473cf2.webp new file mode 100644 index 000000000..dc76a7b13 Binary files /dev/null and b/translated_images/zh-HK/grove-led.6c853be93f473cf2.webp differ diff --git a/translated_images/zh-HK/grove-light-sensor.b8127b7c434e632d.webp b/translated_images/zh-HK/grove-light-sensor.b8127b7c434e632d.webp new file mode 100644 index 000000000..2cc04c436 Binary files /dev/null and b/translated_images/zh-HK/grove-light-sensor.b8127b7c434e632d.webp differ diff --git a/translated_images/zh-HK/grove-relay-labelled.293e068f5c3c2a19.webp b/translated_images/zh-HK/grove-relay-labelled.293e068f5c3c2a19.webp new file mode 100644 index 000000000..a2ff416fa Binary files /dev/null and b/translated_images/zh-HK/grove-relay-labelled.293e068f5c3c2a19.webp differ diff --git a/translated_images/zh-HK/grove-relay.d426958ca210fbd0.webp b/translated_images/zh-HK/grove-relay.d426958ca210fbd0.webp new file mode 100644 index 000000000..6a01f1c4c Binary files /dev/null and b/translated_images/zh-HK/grove-relay.d426958ca210fbd0.webp differ diff --git a/translated_images/zh-HK/grove-time-of-flight-sensor.d82ff2165bfded9f.webp b/translated_images/zh-HK/grove-time-of-flight-sensor.d82ff2165bfded9f.webp new file mode 100644 index 000000000..b0650a687 Binary files /dev/null and b/translated_images/zh-HK/grove-time-of-flight-sensor.d82ff2165bfded9f.webp differ diff --git a/translated_images/zh-HK/gsm-calculation-example.99f9803b4f29e976.webp b/translated_images/zh-HK/gsm-calculation-example.99f9803b4f29e976.webp new file mode 100644 index 000000000..2153e8bb7 Binary files /dev/null and b/translated_images/zh-HK/gsm-calculation-example.99f9803b4f29e976.webp differ diff --git a/translated_images/zh-HK/gsm-calculation.6da38c6201eec14e.webp b/translated_images/zh-HK/gsm-calculation.6da38c6201eec14e.webp new file mode 100644 index 000000000..c2ab63235 Binary files /dev/null and b/translated_images/zh-HK/gsm-calculation.6da38c6201eec14e.webp differ diff --git a/translated_images/zh-HK/i2c.83da845dde02256b.webp b/translated_images/zh-HK/i2c.83da845dde02256b.webp new file mode 100644 index 000000000..b3bac0aef Binary files /dev/null and b/translated_images/zh-HK/i2c.83da845dde02256b.webp differ diff --git a/translated_images/zh-HK/image-classifier-cashews-tomato.bc2e16ab8f05cf9a.webp b/translated_images/zh-HK/image-classifier-cashews-tomato.bc2e16ab8f05cf9a.webp new file mode 100644 index 000000000..7dda3168e Binary files /dev/null and b/translated_images/zh-HK/image-classifier-cashews-tomato.bc2e16ab8f05cf9a.webp differ diff --git a/translated_images/zh-HK/image-upload-bananas.0751639f3815e0ec.webp b/translated_images/zh-HK/image-upload-bananas.0751639f3815e0ec.webp new file mode 100644 index 000000000..b2e25c180 Binary files /dev/null and b/translated_images/zh-HK/image-upload-bananas.0751639f3815e0ec.webp differ diff --git a/translated_images/zh-HK/image-upload-object-detector.77c7892c3093cb59.webp b/translated_images/zh-HK/image-upload-object-detector.77c7892c3093cb59.webp new file mode 100644 index 000000000..f2ae99a02 Binary files /dev/null and b/translated_images/zh-HK/image-upload-object-detector.77c7892c3093cb59.webp differ diff --git a/translated_images/zh-HK/iot-device-and-hacked-device-connecting-encryption.5941aff601fc978f.webp b/translated_images/zh-HK/iot-device-and-hacked-device-connecting-encryption.5941aff601fc978f.webp new file mode 100644 index 000000000..520993819 Binary files /dev/null and b/translated_images/zh-HK/iot-device-and-hacked-device-connecting-encryption.5941aff601fc978f.webp differ diff --git a/translated_images/zh-HK/iot-device-and-hacked-device-connecting.e0671675df74d6d9.webp b/translated_images/zh-HK/iot-device-and-hacked-device-connecting.e0671675df74d6d9.webp new file mode 100644 index 000000000..a03e96a5d Binary files /dev/null and b/translated_images/zh-HK/iot-device-and-hacked-device-connecting.e0671675df74d6d9.webp differ diff --git a/translated_images/zh-HK/iot-for-beginners.95958e2ed1900917.webp b/translated_images/zh-HK/iot-for-beginners.95958e2ed1900917.webp new file mode 100644 index 000000000..2b4b17391 Binary files /dev/null and b/translated_images/zh-HK/iot-for-beginners.95958e2ed1900917.webp differ diff --git a/translated_images/zh-HK/iot-hub-cloud-to-device-message.f4f21fea772cc20b.webp b/translated_images/zh-HK/iot-hub-cloud-to-device-message.f4f21fea772cc20b.webp new file mode 100644 index 000000000..00485801d Binary files /dev/null and b/translated_images/zh-HK/iot-hub-cloud-to-device-message.f4f21fea772cc20b.webp differ diff --git a/translated_images/zh-HK/iot-hub-device-to-cloud-message.e46e584d87f35fd9.webp b/translated_images/zh-HK/iot-hub-device-to-cloud-message.e46e584d87f35fd9.webp new file mode 100644 index 000000000..85765ccd8 Binary files /dev/null and b/translated_images/zh-HK/iot-hub-device-to-cloud-message.e46e584d87f35fd9.webp differ diff --git a/translated_images/zh-HK/iot-hub-device-twins.7055a60fc5e2331c.webp b/translated_images/zh-HK/iot-hub-device-twins.7055a60fc5e2331c.webp new file mode 100644 index 000000000..2516eec11 Binary files /dev/null and b/translated_images/zh-HK/iot-hub-device-twins.7055a60fc5e2331c.webp differ diff --git a/translated_images/zh-HK/iot-hub-direct-method-request.86a5026e91f4ca18.webp b/translated_images/zh-HK/iot-hub-direct-method-request.86a5026e91f4ca18.webp new file mode 100644 index 000000000..422c3689e Binary files /dev/null and b/translated_images/zh-HK/iot-hub-direct-method-request.86a5026e91f4ca18.webp differ diff --git a/translated_images/zh-HK/iot-messages-to-serverless.0194da1cc0732bb7.webp b/translated_images/zh-HK/iot-messages-to-serverless.0194da1cc0732bb7.webp new file mode 100644 index 000000000..e845c3b4b Binary files /dev/null and b/translated_images/zh-HK/iot-messages-to-serverless.0194da1cc0732bb7.webp differ diff --git a/translated_images/zh-HK/iot-reference-architecture-azure.0b8d2161af924cb1.webp b/translated_images/zh-HK/iot-reference-architecture-azure.0b8d2161af924cb1.webp new file mode 100644 index 000000000..c70a8b329 Binary files /dev/null and b/translated_images/zh-HK/iot-reference-architecture-azure.0b8d2161af924cb1.webp differ diff --git a/translated_images/zh-HK/iot-reference-architecture-fruit-quality.cc705f121c3b6fa7.webp b/translated_images/zh-HK/iot-reference-architecture-fruit-quality.cc705f121c3b6fa7.webp new file mode 100644 index 000000000..1584be9df Binary files /dev/null and b/translated_images/zh-HK/iot-reference-architecture-fruit-quality.cc705f121c3b6fa7.webp differ diff --git a/translated_images/zh-HK/iot-reference-architecture.2278b98b55c6d4e8.webp b/translated_images/zh-HK/iot-reference-architecture.2278b98b55c6d4e8.webp new file mode 100644 index 000000000..299817fc2 Binary files /dev/null and b/translated_images/zh-HK/iot-reference-architecture.2278b98b55c6d4e8.webp differ diff --git a/translated_images/zh-HK/iot-service-allowed-denied-connection.818b0063ac213fb8.webp b/translated_images/zh-HK/iot-service-allowed-denied-connection.818b0063ac213fb8.webp new file mode 100644 index 000000000..c9bd1b378 Binary files /dev/null and b/translated_images/zh-HK/iot-service-allowed-denied-connection.818b0063ac213fb8.webp differ diff --git a/translated_images/zh-HK/iot-service-connectivity.7e873847921a5d6f.webp b/translated_images/zh-HK/iot-service-connectivity.7e873847921a5d6f.webp new file mode 100644 index 000000000..7020b1fe3 Binary files /dev/null and b/translated_images/zh-HK/iot-service-connectivity.7e873847921a5d6f.webp differ diff --git a/translated_images/zh-HK/latitude-equator.feccc3214b7d9fb1.webp b/translated_images/zh-HK/latitude-equator.feccc3214b7d9fb1.webp new file mode 100644 index 000000000..a1fe083b3 Binary files /dev/null and b/translated_images/zh-HK/latitude-equator.feccc3214b7d9fb1.webp differ diff --git a/translated_images/zh-HK/latitude-lines.11d8d91dfb2014a5.webp b/translated_images/zh-HK/latitude-lines.11d8d91dfb2014a5.webp new file mode 100644 index 000000000..4179f3af5 Binary files /dev/null and b/translated_images/zh-HK/latitude-lines.11d8d91dfb2014a5.webp differ diff --git a/translated_images/zh-HK/led-digital-control.13b9be14077ea49f.webp b/translated_images/zh-HK/led-digital-control.13b9be14077ea49f.webp new file mode 100644 index 000000000..768359f2e Binary files /dev/null and b/translated_images/zh-HK/led-digital-control.13b9be14077ea49f.webp differ diff --git a/translated_images/zh-HK/led.ec6d94f66676a174.webp b/translated_images/zh-HK/led.ec6d94f66676a174.webp new file mode 100644 index 000000000..d7f91c36f Binary files /dev/null and b/translated_images/zh-HK/led.ec6d94f66676a174.webp differ diff --git a/translated_images/zh-HK/lesson-1.2606670fa61ee904.webp b/translated_images/zh-HK/lesson-1.2606670fa61ee904.webp new file mode 100644 index 000000000..2ecc89aa5 Binary files /dev/null and b/translated_images/zh-HK/lesson-1.2606670fa61ee904.webp differ diff --git a/translated_images/zh-HK/lesson-10.829c86b80b9403bb.webp b/translated_images/zh-HK/lesson-10.829c86b80b9403bb.webp new file mode 100644 index 000000000..072842076 Binary files /dev/null and b/translated_images/zh-HK/lesson-10.829c86b80b9403bb.webp differ diff --git a/translated_images/zh-HK/lesson-11.9fddbac4b664c6d5.webp b/translated_images/zh-HK/lesson-11.9fddbac4b664c6d5.webp new file mode 100644 index 000000000..98393a355 Binary files /dev/null and b/translated_images/zh-HK/lesson-11.9fddbac4b664c6d5.webp differ diff --git a/translated_images/zh-HK/lesson-12.ca7f53039712a3ec.webp b/translated_images/zh-HK/lesson-12.ca7f53039712a3ec.webp new file mode 100644 index 000000000..2b05a9865 Binary files /dev/null and b/translated_images/zh-HK/lesson-12.ca7f53039712a3ec.webp differ diff --git a/translated_images/zh-HK/lesson-13.a259db1485021be7.webp b/translated_images/zh-HK/lesson-13.a259db1485021be7.webp new file mode 100644 index 000000000..e8bffc62d Binary files /dev/null and b/translated_images/zh-HK/lesson-13.a259db1485021be7.webp differ diff --git a/translated_images/zh-HK/lesson-14.63980c5150ae3c15.webp b/translated_images/zh-HK/lesson-14.63980c5150ae3c15.webp new file mode 100644 index 000000000..baa1ab9cb Binary files /dev/null and b/translated_images/zh-HK/lesson-14.63980c5150ae3c15.webp differ diff --git a/translated_images/zh-HK/lesson-15.843d21afdc6fb2bb.webp b/translated_images/zh-HK/lesson-15.843d21afdc6fb2bb.webp new file mode 100644 index 000000000..103d0c56e Binary files /dev/null and b/translated_images/zh-HK/lesson-15.843d21afdc6fb2bb.webp differ diff --git a/translated_images/zh-HK/lesson-16.215daf18b00631fb.webp b/translated_images/zh-HK/lesson-16.215daf18b00631fb.webp new file mode 100644 index 000000000..82b18ac4c Binary files /dev/null and b/translated_images/zh-HK/lesson-16.215daf18b00631fb.webp differ diff --git a/translated_images/zh-HK/lesson-17.bc333c3c35ba8e42.webp b/translated_images/zh-HK/lesson-17.bc333c3c35ba8e42.webp new file mode 100644 index 000000000..0649c025d Binary files /dev/null and b/translated_images/zh-HK/lesson-17.bc333c3c35ba8e42.webp differ diff --git a/translated_images/zh-HK/lesson-18.92c32ed1d354caa5.webp b/translated_images/zh-HK/lesson-18.92c32ed1d354caa5.webp new file mode 100644 index 000000000..671bcdd9d Binary files /dev/null and b/translated_images/zh-HK/lesson-18.92c32ed1d354caa5.webp differ diff --git a/translations/hk/1-getting-started/README.md b/translations/hk/1-getting-started/README.md deleted file mode 100644 index 9c8999f80..000000000 --- a/translations/hk/1-getting-started/README.md +++ /dev/null @@ -1,30 +0,0 @@ - -# 開始學習物聯網 - -在這部分課程中,你將會接觸到物聯網(IoT),並學習基本概念,包括建立你的第一個「Hello World」物聯網項目,將設備連接到雲端。這個項目是一個夜燈,當感測器測量到光線水平下降時,燈會亮起。 - -![LED 連接到 WIO,隨著光線水平變化而開啟和關閉](../../../images/wio-running-assignment-1-1.gif) - -## 主題 - -1. [物聯網簡介](lessons/1-introduction-to-iot/README.md) -1. [深入了解物聯網](lessons/2-deeper-dive/README.md) -1. [使用感測器和致動器與物理世界互動](lessons/3-sensors-and-actuators/README.md) -1. [將你的設備連接到互聯網](lessons/4-connect-internet/README.md) - -## 鳴謝 - -所有課程由 [Jim Bennett](https://GitHub.com/JimBobBennett) 用 ♥️ 編寫 - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/1-introduction-to-iot/README.md b/translations/hk/1-getting-started/lessons/1-introduction-to-iot/README.md deleted file mode 100644 index 1a3fd9346..000000000 --- a/translations/hk/1-getting-started/lessons/1-introduction-to-iot/README.md +++ /dev/null @@ -1,242 +0,0 @@ - -# 物聯網簡介 - -![本課程的手繪筆記概覽](../../../../../translated_images/zh-HK/lesson-1.2606670fa61ee904687da5d6fa4e726639d524d064c895117da1b95b9ff6251d.jpg) - -> 手繪筆記由 [Nitya Narasimhan](https://github.com/nitya) 提供。點擊圖片查看更大版本。 - -本課程是 [Microsoft Reactor](https://developer.microsoft.com/reactor/?WT.mc_id=academic-17441-jabenn) 的 [Hello IoT 系列](https://youtube.com/playlist?list=PLmsFUfdnGr3xRts0TIwyaHyQuHaNQcb6-) 的一部分。課程分為兩段影片進行教學——一小時的課程和一小時的辦公時間,深入探討課程內容並回答問題。 - -[![課程 1:物聯網簡介](https://img.youtube.com/vi/bVFfcYh6UBw/0.jpg)](https://youtu.be/bVFfcYh6UBw) - -[![課程 1:物聯網簡介 - 辦公時間](https://img.youtube.com/vi/YI772q5v3yI/0.jpg)](https://youtu.be/YI772q5v3yI) - -> 🎥 點擊上方圖片觀看影片 - -## 課前測驗 - -[課前測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/1) - -## 簡介 - -本課程涵蓋了一些關於物聯網的入門主題,並幫助你開始設置硬件。 - -在本課程中,我們將討論: - -* [什麼是「物聯網」?](../../../../../1-getting-started/lessons/1-introduction-to-iot) -* [物聯網設備](../../../../../1-getting-started/lessons/1-introduction-to-iot) -* [設置你的設備](../../../../../1-getting-started/lessons/1-introduction-to-iot) -* [物聯網的應用](../../../../../1-getting-started/lessons/1-introduction-to-iot) -* [你周圍可能擁有的物聯網設備範例](../../../../../1-getting-started/lessons/1-introduction-to-iot) - -## 什麼是「物聯網」? - -「物聯網」這個術語由 [Kevin Ashton](https://wikipedia.org/wiki/Kevin_Ashton) 在 1999 年提出,用來描述通過感應器將互聯網連接到物理世界。自那時起,這個術語被用來描述任何與周圍物理世界互動的設備,無論是通過感應器收集數據,還是通過執行器(例如打開開關或點亮 LED 的設備)進行實際操作,通常這些設備會連接到其他設備或互聯網。 - -> **感應器** 從世界中收集信息,例如測量速度、溫度或位置。 -> -> **執行器** 將電信號轉換為實際操作,例如觸發開關、打開燈光、發出聲音,或向其他硬件發送控制信號,例如打開電源插座。 - -物聯網作為一個技術領域,不僅僅是設備本身——它還包括基於雲的服務,這些服務可以處理感應器數據,或向連接到物聯網設備的執行器發送請求。它還包括不具備或不需要互聯網連接的設備,通常被稱為邊緣設備。這些設備可以自行處理和響應感應器數據,通常使用在雲端訓練的 AI 模型。 - -物聯網是一個快速增長的技術領域。據估計,到 2020 年底,已有 300 億個物聯網設備被部署並連接到互聯網。展望未來,預計到 2025 年,物聯網設備將收集近 80 澤字節(zettabytes)的數據,相當於 80 萬億 GB。這是一個巨大的數據量! - -![一張顯示活躍物聯網設備隨時間增長的圖表,從 2015 年的不到 50 億增長到 2025 年的超過 300 億](../../../../../images/connected-iot-devices.svg) - -✅ 做一些研究:物聯網設備生成的數據中有多少實際被使用,又有多少被浪費?為什麼會有這麼多數據被忽略? - -這些數據是物聯網成功的關鍵。要成為一名成功的物聯網開發者,你需要了解需要收集的數據、如何收集數據、如何基於數據做出決策,以及在需要時如何利用這些決策與物理世界互動。 - -## 物聯網設備 - -物聯網中的 **T** 代表 **Things(物件)**——這些設備通過感應器收集數據或通過執行器進行實際操作,與周圍的物理世界互動。 - -用於生產或商業用途的設備,例如消費者健身追蹤器或工業機器控制器,通常是定制製造的。它們使用定制的電路板,甚至可能是定制的處理器,以滿足特定任務的需求,無論是小到可以戴在手腕上,還是堅固到可以在高溫、高壓或高振動的工廠環境中運行。 - -作為一名學習物聯網或創建設備原型的開發者,你需要從開發套件開始。這些是為開發者設計的通用物聯網設備,通常具有生產設備不會有的功能,例如一組外部引腳,用於連接感應器或執行器,支持調試的硬件,或在大規模生產中會增加不必要成本的額外資源。 - -這些開發套件通常分為兩類——微控制器和單板電腦。我們會在這裡介紹它們,並在下一課中進一步詳細說明。 - -> 💁 你的手機也可以被視為一種通用的物聯網設備,內置了感應器和執行器,不同的應用程序以不同的方式使用這些感應器和執行器,並與不同的雲服務交互。你甚至可以找到一些使用手機應用作為物聯網設備的教程。 - -### 微控制器 - -微控制器(也稱為 MCU,Microcontroller Unit 的縮寫)是一種小型計算機,由以下部分組成: - -🧠 一個或多個中央處理單元(CPU)——微控制器的「大腦」,運行你的程序 - -💾 記憶體(RAM 和程序記憶體)——存儲你的程序、數據和變量 - -🔌 可編程輸入/輸出(I/O)連接——與外部外設(連接的設備)如感應器和執行器通信 - -微控制器通常是低成本的計算設備,用於定制硬件的微控制器平均價格約為 0.50 美元,有些設備甚至低至 0.03 美元。開發套件的起價約為 4 美元,隨著功能的增加,成本也會上升。[Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html) 是 [Seeed Studios](https://www.seeedstudio.com) 的一款微控制器開發套件,內置感應器、執行器、WiFi 和屏幕,價格約為 30 美元。 - -![Wio Terminal](../../../../../translated_images/zh-HK/wio-terminal.b8299ee16587db9a.webp) - -> 💁 在網上搜索微控制器時,請注意搜索 **MCU** 這個術語,因為這可能會帶回大量與漫威電影宇宙(Marvel Cinematic Universe)相關的結果,而不是微控制器。 - -微控制器的設計目的是執行有限數量的非常特定的任務,而不是像 PC 或 Mac 那樣的通用計算機。除非是非常特定的場景,否則你無法連接顯示器、鍵盤和滑鼠來用它們執行通用任務。 - -微控制器開發套件通常內置額外的感應器和執行器。大多數開發板會有一個或多個可編程的 LED,以及其他設備,例如標準插頭,用於通過各製造商的生態系統添加更多感應器或執行器,或內置感應器(通常是最受歡迎的,例如溫度感應器)。一些微控制器內置無線連接功能,例如藍牙或 WiFi,或者在開發板上有額外的微控制器來添加這些連接功能。 - -> 💁 微控制器通常使用 C/C++ 編程。 - -### 單板電腦 - -單板電腦是一種小型計算設備,將完整計算機的所有元素包含在一塊小型電路板上。這些設備的規格接近桌面或筆記本電腦,運行完整的操作系統,但體積更小,功耗更低,價格也便宜得多。 - -![Raspberry Pi 4](../../../../../translated_images/zh-HK/raspberry-pi-4.fd4590d308c3d456.webp) - -Raspberry Pi 是最受歡迎的單板電腦之一。 - -與微控制器類似,單板電腦具有 CPU、記憶體和輸入/輸出引腳,但它們還具有額外的功能,例如圖形芯片,可以連接顯示器、音頻輸出和 USB 埠,用於連接鍵盤、滑鼠和其他標準 USB 設備,如網絡攝像頭或外部存儲。程序和操作系統存儲在 SD 卡或硬碟上,而不是內置在電路板上的記憶體芯片中。 - -> 🎓 你可以將單板電腦視為你正在使用的 PC 或 Mac 的更小、更便宜版本,並額外配備了 GPIO(通用輸入/輸出)引腳,用於與感應器和執行器交互。 - -單板電腦是功能齊全的計算機,因此可以使用任何語言進行編程。物聯網設備通常使用 Python 編程。 - -### 後續課程的硬件選擇 - -所有後續課程都包括使用物聯網設備與物理世界交互並與雲通信的作業。每節課支持三種設備選擇——Arduino(使用 Seeed Studios Wio Terminal),或單板電腦(實體設備 Raspberry Pi 4 或在 PC 或 Mac 上運行的虛擬單板電腦)。 - -你可以在 [硬件指南](../../../hardware.md) 中閱讀完成所有作業所需的硬件。 - -> 💁 你不需要購買任何物聯網硬件來完成作業,你可以使用虛擬單板電腦完成所有內容。 - -選擇哪種硬件取決於你家中或學校中可用的設備,以及你已經掌握或計劃學習的編程語言。兩種硬件變體將使用相同的感應器生態系統,因此如果你選擇了一條路徑,之後可以切換到另一條路徑,而無需更換大部分設備。虛擬單板電腦相當於在 Raspberry Pi 上學習,大部分代碼可以轉移到實體 Pi 上使用感應器。 - -### Arduino 開發套件 - -如果你對學習微控制器開發感興趣,可以使用 Arduino 設備完成作業。你需要對 C/C++ 編程有基本的了解,因為課程只會教授與 Arduino 框架、感應器和執行器以及與雲交互的庫相關的代碼。 - -作業將使用 [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-17441-jabenn) 和 [PlatformIO 微控制器開發擴展](https://platformio.org)。如果你熟悉 Arduino IDE,也可以使用它,但課程不會提供相關指導。 - -### 單板電腦開發套件 - -如果你對使用單板電腦進行物聯網開發感興趣,可以使用 Raspberry Pi 或在 PC 或 Mac 上運行的虛擬設備完成作業。 - -你需要對 Python 編程有基本的了解,因為課程只會教授與感應器和執行器以及與雲交互的庫相關的代碼。 - -> 💁 如果你想學習 Python 編程,可以參考以下兩個視頻系列: -> -> * [Python 初學者教程](https://channel9.msdn.com/Series/Intro-to-Python-Development?WT.mc_id=academic-17441-jabenn) -> * [進階 Python 初學者教程](https://channel9.msdn.com/Series/More-Python-for-Beginners?WT.mc_id=academic-7372-jabenn) - -作業將使用 [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-17441-jabenn)。 - -如果你使用 Raspberry Pi,可以選擇運行完整桌面版 Raspberry Pi OS,直接在 Pi 上使用 [Raspberry Pi OS 版 VS Code](https://code.visualstudio.com/docs/setup/raspberry-pi?WT.mc_id=academic-17441-jabenn) 進行編碼,或者將 Pi 作為無頭設備運行,並使用 VS Code 的 [Remote SSH 擴展](https://code.visualstudio.com/docs/remote/ssh?WT.mc_id=academic-17441-jabenn) 從 PC 或 Mac 連接到 Pi,像直接在 Pi 上編碼一樣進行編輯、調試和運行代碼。 - -如果你選擇虛擬設備選項,你將直接在電腦上編碼。與其訪問感應器和執行器,你將使用一個工具來模擬這些硬件,提供你可以定義的感應器值,並在屏幕上顯示執行器的結果。 - -## 設置你的設備 - -在開始編寫物聯網設備的程序之前,你需要進行一些基本的設置。根據你將使用的設備,請遵循相關的指導說明。 -💁 如果你尚未擁有設備,請參考 [硬件指南](../../../hardware.md),以幫助你決定使用哪種設備,以及需要購買哪些額外硬件。你不需要購買硬件,因為所有項目都可以在虛擬硬件上運行。 -這些指引包括了硬件或工具創作者提供的第三方網站鏈接,目的是確保你使用的是最新的工具和硬件指引。 - -按照相關指南設置你的設備並完成一個 "Hello World" 項目。這將是你在這四節課的入門部分中創建 IoT 夜燈的第一步。 - -* [Arduino - Wio Terminal](wio-terminal.md) -* [單板電腦 - Raspberry Pi](pi.md) -* [單板電腦 - 虛擬設備](virtual-device.md) - -✅ 你將使用 VS Code 來操作 Arduino 和單板電腦。如果你之前未使用過,請在 [VS Code 網站](https://code.visualstudio.com?WT.mc_id=academic-17441-jabenn) 上了解更多。 - -## IoT 的應用 - -IoT 涵蓋了廣泛的使用場景,分為幾個主要類別: - -* 消費型 IoT -* 商業型 IoT -* 工業型 IoT -* 基礎設施型 IoT - -✅ 做一些研究:對於以下描述的每個領域,找到一個文中未提到的具體例子。 - -### 消費型 IoT - -消費型 IoT 指的是消費者在家中購買和使用的 IoT 設備。其中一些設備非常有用,例如智能音箱、智能供暖系統和機器人吸塵器。另一些設備的實用性則值得商榷,例如語音控制的水龍頭,當水聲太大時語音控制無法正常工作,導致無法關閉水龍頭。 - -消費型 IoT 設備幫助人們在日常生活中實現更多,尤其是全球 10 億有殘疾的人群。機器人吸塵器可以幫助行動不便的人保持地板清潔,語音控制的烤箱讓視力或運動能力有限的人僅用語音即可操作烤箱,健康監測設備可以讓患者更頻繁、更詳細地監測慢性病情況。這些設備已經變得如此普及,甚至連小孩都在日常生活中使用它們,例如在 COVID 疫情期間進行虛擬學習的學生使用智能家居設備設置計時器來跟蹤學校作業或提醒即將開始的課堂會議。 - -✅ 你身上或家中有哪些消費型 IoT 設備? - -### 商業型 IoT - -商業型 IoT 涵蓋了 IoT 在工作場所的使用。在辦公室環境中,可能會有佔用感應器和動作檢測器來管理照明和供暖,僅在需要時開啟燈光和供暖,從而降低成本和碳排放。在工廠中,IoT 設備可以監測安全隱患,例如工人未佩戴安全帽或噪音達到危險水平。在零售業,IoT 設備可以測量冷藏設備的溫度,當冰箱或冷凍櫃超出所需溫度範圍時提醒店主,或者監測貨架上的商品,指導員工補充已售出的商品。交通行業越來越依賴 IoT 來監測車輛位置、追蹤道路里程以進行道路使用費計算、追蹤駕駛員的工作時間和休息合規性,或者在車輛接近倉庫時通知員工準備裝卸貨物。 - -✅ 你的學校或工作場所有哪些商業型 IoT 設備? - -### 工業型 IoT (IIoT) - -工業型 IoT,或 IIoT,是指在大規模上使用 IoT 設備來控制和管理機械設備。這涵蓋了從工廠到數字農業的廣泛使用場景。 - -工廠以多種方式使用 IoT 設備。機械設備可以通過多個感應器監測溫度、振動和旋轉速度等數據。這些數據可以被監測,當設備超出某些容差範圍時停止運行,例如過熱時關閉設備。這些數據還可以被收集並進行長期分析,用於預測性維護,AI 模型可以分析故障前的數據,並用於預測其他故障以便提前處理。 - -數字農業對於養活日益增長的人口至關重要,尤其是全球 20 億人中 5 億個家庭依靠[自給農業](https://wikipedia.org/wiki/Subsistence_agriculture)生存。數字農業可以從幾美元的感應器到大型商業設置不等。農民可以通過監測溫度並使用[生長度日](https://wikipedia.org/wiki/Growing_degree-day)來預測作物的收穫時間。他們可以將土壤濕度監測連接到自動灌溉系統,為作物提供所需的水分,但不浪費水資源,確保作物不會乾枯。農民甚至可以進一步使用無人機、衛星數據和 AI 來監測大面積農田的作物生長、病害和土壤質量。 - -✅ 還有哪些 IoT 設備可以幫助農民? - -### 基礎設施型 IoT - -基礎設施型 IoT 是指監測和控制人們每天使用的本地和全球基礎設施。 - -[智慧城市](https://wikipedia.org/wiki/Smart_city)是使用 IoT 設備收集城市數據並利用這些數據改善城市運行的城市。這些城市通常由地方政府、學術界和當地企業合作運營,追蹤和管理交通、停車和污染等多方面。例如,在丹麥哥本哈根,空氣污染對當地居民非常重要,因此會進行測量,並利用數據提供最清潔的騎行和慢跑路線。 - -[智慧電網](https://wikipedia.org/wiki/Smart_grid)通過收集個人家庭的用電數據來提供更好的電力需求分析。這些數據可以指導國家層面的決策,例如建設新電站的位置,也可以在個人層面提供用電量、用電時間的洞察,甚至提供降低成本的建議,例如在夜間為電動汽車充電。 - -✅ 如果你可以在居住地添加 IoT 設備來測量任何東西,你會選擇什麼? - -## 你周圍可能擁有的 IoT 設備示例 - -你可能會驚訝於你周圍有多少 IoT 設備。我正在家中寫這篇文章,我擁有以下連接到互聯網並具有智能功能的設備,例如應用程序控制、語音控制或通過手機向我發送數據: - -* 多個智能音箱 -* 冰箱、洗碗機、烤箱和微波爐 -* 用於太陽能板的電力監測器 -* 智能插座 -* 視頻門鈴和安全攝像頭 -* 帶有多個智能房間感應器的智能恆溫器 -* 車庫門開啟器 -* 家庭娛樂系統和語音控制電視 -* 照明設備 -* 健身和健康追蹤器 - -所有這些類型的設備都有感應器和/或執行器,並與互聯網通信。我可以通過手機查看我的車庫門是否開著,並要求我的智能音箱幫我關上。我甚至可以設置定時器,如果車庫門在晚上仍然開著,它會自動關閉。當門鈴響起時,我可以通過手機查看無論我身處世界何地的訪客,並通過門鈴內置的揚聲器和麥克風與他們交談。我可以監測我的血糖、心率和睡眠模式,尋找數據中的模式以改善我的健康。我可以通過雲端控制我的燈光,但當我的網絡連接中斷時,我只能坐在黑暗中。 - ---- - -## 🚀 挑戰 - -列出你家中、學校或工作場所的所有 IoT 設備——可能比你想像的還要多! - -## 課後測驗 - -[課後測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/2) - -## 回顧與自學 - -閱讀消費型 IoT 項目的優勢和失敗案例。查看新聞網站上的文章,了解 IoT 出現問題的情況,例如隱私問題、硬件故障或因缺乏連接而引發的問題。 - -一些例子: - -* 查看 Twitter 帳戶 **[Internet of Sh*t](https://twitter.com/internetofshit)** *(有粗言警告)*,了解一些消費型 IoT 失敗的好例子。 -* [c|net - 我的 Apple Watch 救了我的命:5 人分享他們的故事](https://www.cnet.com/news/apple-watch-lifesaving-health-features-read-5-peoples-stories/) -* [c|net - ADT 技術員承認多年來監視客戶攝像頭](https://www.cnet.com/news/adt-home-security-technician-pleads-guilty-to-spying-on-customer-camera-feeds-for-years/) *(觸發警告 - 非自願窺探)* - -## 作業 - -[調查一個 IoT 項目](assignment.md) - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/1-introduction-to-iot/assignment.md b/translations/hk/1-getting-started/lessons/1-introduction-to-iot/assignment.md deleted file mode 100644 index 09ec4eb59..000000000 --- a/translations/hk/1-getting-started/lessons/1-introduction-to-iot/assignment.md +++ /dev/null @@ -1,27 +0,0 @@ - -# 調查一個物聯網項目 - -## 指引 - -全球正在推行許多大大小小的物聯網項目,從智慧農場到智慧城市,涵蓋醫療監測、交通運輸以及公共空間的使用。 - -在網上搜尋一個你感興趣的項目,最好是離你居住地較近的。解釋該項目的優點和缺點,例如它帶來了什麼好處、可能引發的問題,以及如何考慮隱私問題。 - -## 評分標準 - -| 評分標準 | 優秀 | 合格 | 需要改進 | -| -------- | ---- | ---- | -------- | -| 解釋優點和缺點 | 清楚地解釋了該項目的優點和缺點 | 簡要地解釋了該項目的優點和缺點 | 沒有解釋該項目的優點或缺點 | - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業的人類翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/1-introduction-to-iot/pi.md b/translations/hk/1-getting-started/lessons/1-introduction-to-iot/pi.md deleted file mode 100644 index faabd4067..000000000 --- a/translations/hk/1-getting-started/lessons/1-introduction-to-iot/pi.md +++ /dev/null @@ -1,286 +0,0 @@ - -# 樹莓派 - -[樹莓派](https://raspberrypi.org) 是一款單板電腦。你可以使用各種設備和生態系統添加感應器和執行器,這些課程將使用一個名為 [Grove](https://www.seeedstudio.com/category/Grove-c-1003.html) 的硬件生態系統。你將使用 Python 為樹莓派編寫程式碼並訪問 Grove 感應器。 - -![樹莓派 4](../../../../../translated_images/zh-HK/raspberry-pi-4.fd4590d308c3d456.webp) - -## 設置 - -如果你使用樹莓派作為物聯網硬件,你有兩個選擇——你可以直接在樹莓派上完成所有這些課程並編寫程式碼,或者你可以遠端連接到一個「無頭」的樹莓派,並從你的電腦上進行編程。 - -在開始之前,你還需要將 Grove 基座帽連接到樹莓派。 - -### 任務 - 設置 - -將 Grove 基座帽安裝到樹莓派並配置樹莓派 - -1. 將 Grove 基座帽連接到樹莓派。基座帽上的插槽與樹莓派的所有 GPIO 引腳對應,將其滑到底部,牢牢固定在基座上。基座帽覆蓋在樹莓派上。 - - ![安裝 Grove 基座帽](../../../../../images/pi-grove-hat-fitting.gif) - -1. 決定你想如何編程樹莓派,然後前往以下相關部分: - - * [直接在樹莓派上工作](../../../../../1-getting-started/lessons/1-introduction-to-iot) - * [遠端訪問以編寫樹莓派程式碼](../../../../../1-getting-started/lessons/1-introduction-to-iot) - -### 直接在樹莓派上工作 - -如果你想直接在樹莓派上工作,可以使用樹莓派 OS 的桌面版本並安裝所有需要的工具。 - -#### 任務 - 直接在樹莓派上工作 - -為開發設置樹莓派。 - -1. 按照 [樹莓派設置指南](https://projects.raspberrypi.org/en/projects/raspberry-pi-setting-up) 的說明設置樹莓派,連接鍵盤/滑鼠/顯示器,連接 WiFi 或乙太網絡,並更新軟件。 - -為了使用 Grove 感應器和執行器編寫樹莓派程式碼,你需要安裝一個編輯器來編寫設備程式碼,以及與 Grove 硬件交互的各種庫和工具。 - -1. 當樹莓派重新啟動後,點擊頂部菜單欄上的 **Terminal** 圖標啟動終端,或者選擇 *Menu -> Accessories -> Terminal* - -1. 運行以下命令以確保操作系統和已安裝的軟件是最新的: - - ```sh - sudo apt update && sudo apt full-upgrade --yes - ``` - -1. 運行以下命令以安裝 Grove 硬件所需的所有庫: - - ```sh - sudo apt install git python3-dev python3-pip --yes - - git clone https://github.com/Seeed-Studio/grove.py - cd grove.py - sudo pip3 install . - - sudo raspi-config nonint do_i2c 0 - ``` - - 這首先安裝 Git 和 Pip,用於安裝 Python 套件。 - - Python 的一個強大功能是能夠安裝 [Pip 套件](https://pypi.org)——這些是其他人編寫並發布到互聯網上的程式碼包。你可以用一條命令將 Pip 套件安裝到你的電腦上,然後在程式碼中使用該套件。 - - Seeed Grove 的 Python 套件需要從源代碼安裝。這些命令將克隆包含該套件源代碼的倉庫,然後在本地安裝它。 - - > 💁 默認情況下,當你安裝一個套件時,它在電腦上的任何地方都可用,這可能會導致套件版本的問題——例如,一個應用程序依賴於某個版本的套件,而當你為另一個應用程序安裝新版本時,可能會導致問題。為了解決這個問題,你可以使用 [Python 虛擬環境](https://docs.python.org/3/library/venv.html),它本質上是 Python 的一個副本,存放在一個專用文件夾中,當你安裝 Pip 套件時,它們只會安裝到該文件夾中。在使用樹莓派時,你不會使用虛擬環境。Grove 的安裝腳本會全局安裝 Grove 的 Python 套件,因此如果你想使用虛擬環境,需要先設置虛擬環境,然後手動重新安裝 Grove 套件到該環境中。使用全局套件更簡單,特別是因為許多樹莓派開發者會為每個項目重新刷新乾淨的 SD 卡。 - - 最後,這將啟用 I2C 接口。 - -1. 使用菜單或在終端中運行以下命令重新啟動樹莓派: - - ```sh - sudo reboot - ``` - -1. 當樹莓派重新啟動後,重新打開終端並運行以下命令安裝 [Visual Studio Code (VS Code)](https://code.visualstudio.com?WT.mc_id=academic-17441-jabenn)——這是你將用來用 Python 編寫設備程式碼的編輯器。 - - ```sh - sudo apt install code - ``` - - 安裝完成後,VS Code 將可從頂部菜單中訪問。 - - > 💁 如果你有偏好的工具,可以自由選擇任何 Python IDE 或編輯器來完成這些課程,但課程中的指導將基於使用 VS Code。 - -1. 安裝 Pylance。這是 VS Code 的一個擴展,提供 Python 語言支持。請參考 [Pylance 擴展文檔](https://marketplace.visualstudio.com/items?WT.mc_id=academic-17441-jabenn&itemName=ms-python.vscode-pylance) 了解如何在 VS Code 中安裝此擴展。 - -### 遠端訪問以編寫樹莓派程式碼 - -與其直接在樹莓派上編程,你可以讓它以「無頭」模式運行,也就是不連接鍵盤/滑鼠/顯示器,並通過 Visual Studio Code 從你的電腦上配置和編程。 - -#### 設置樹莓派操作系統 - -要遠端編程,需要將樹莓派操作系統安裝到 SD 卡上。 - -##### 任務 - 設置樹莓派操作系統 - -設置無頭樹莓派操作系統。 - -1. 從 [樹莓派操作系統軟件頁面](https://www.raspberrypi.org/software/) 下載 **Raspberry Pi Imager** 並安裝 - -1. 將 SD 卡插入電腦,如有需要使用適配器 - -1. 啟動 Raspberry Pi Imager - -1. 在 Raspberry Pi Imager 中,選擇 **CHOOSE OS** 按鈕,然後選擇 *Raspberry Pi OS (Other)*,接著選擇 *Raspberry Pi OS Lite (32-bit)* - - ![Raspberry Pi Imager 選擇 Raspberry Pi OS Lite](../../../../../translated_images/zh-HK/raspberry-pi-imager.24aedeab9e233d84.webp) - - > 💁 Raspberry Pi OS Lite 是樹莓派操作系統的一個版本,沒有桌面 UI 或基於 UI 的工具。這些對於無頭樹莓派來說並不需要,並且使安裝更小,啟動時間更快。 - -1. 選擇 **CHOOSE STORAGE** 按鈕,然後選擇你的 SD 卡 - -1. 按下 `Ctrl+Shift+X` 打開 **Advanced Options**。這些選項允許在將操作系統寫入 SD 卡之前進行一些預配置。 - - 1. 勾選 **Enable SSH** 選框,並為 `pi` 用戶設置密碼。這是你稍後登錄樹莓派時使用的密碼。 - - 1. 如果你計劃通過 WiFi 連接樹莓派,勾選 **Configure WiFi** 選框,並輸入你的 WiFi SSID 和密碼,以及選擇你的 WiFi 國家。如果你將使用乙太網線,則不需要這樣做。確保你連接的網絡與你的電腦在同一網絡上。 - - 1. 勾選 **Set locale settings** 選框,並設置你的國家和時區 - - 1. 選擇 **SAVE** 按鈕 - -1. 選擇 **WRITE** 按鈕將操作系統寫入 SD 卡。如果你使用 macOS,系統會要求你輸入密碼,因為寫入磁碟映像的底層工具需要特權訪問。 - -操作系統將被寫入 SD 卡,完成後操作系統會彈出該卡,並通知你。從電腦中取出 SD 卡,插入樹莓派,啟動樹莓派並等待約 2 分鐘讓其正常啟動。 - -#### 連接到樹莓派 - -下一步是遠端訪問樹莓派。你可以使用 `ssh`,該工具在 macOS、Linux 和最近版本的 Windows 上可用。 - -##### 任務 - 連接到樹莓派 - -遠端訪問樹莓派。 - -1. 啟動終端或命令提示符,輸入以下命令連接到樹莓派: - - ```sh - ssh pi@raspberrypi.local - ``` - - 如果你使用的是較舊版本的 Windows,沒有安裝 `ssh`,可以使用 OpenSSH。你可以在 [OpenSSH 安裝文檔](https://docs.microsoft.com//windows-server/administration/openssh/openssh_install_firstuse?WT.mc_id=academic-17441-jabenn) 中找到安裝說明。 - -1. 這應該會連接到樹莓派並要求輸入密碼。 - - 能夠通過 `.local` 在網絡上找到電腦是 Linux 和 Windows 最近新增的一個功能。如果你使用的是 Linux 或 Windows,並且收到有關找不到主機名的錯誤,你需要安裝額外的軟件來啟用 ZeroConf 網絡(Apple 稱之為 Bonjour): - - 1. 如果你使用的是 Linux,使用以下命令安裝 Avahi: - - ```sh - sudo apt-get install avahi-daemon - ``` - - 1. 如果你使用的是 Windows,啟用 ZeroConf 最簡單的方法是安裝 [Bonjour Print Services for Windows](http://support.apple.com/kb/DL999)。你也可以安裝 [iTunes for Windows](https://www.apple.com/itunes/download/) 以獲得更新版本的工具(該工具無法單獨下載)。 - - > 💁 如果你無法使用 `raspberrypi.local` 連接,則可以使用樹莓派的 IP 地址。請參考 [樹莓派 IP 地址文檔](https://www.raspberrypi.org/documentation/remote-access/ip-address.md) 獲取多種方法來獲取 IP 地址的說明。 - -1. 輸入你在 Raspberry Pi Imager 高級選項中設置的密碼 - -#### 配置樹莓派上的軟件 - -連接到樹莓派後,你需要確保操作系統是最新的,並安裝與 Grove 硬件交互的各種庫和工具。 - -##### 任務 - 配置樹莓派上的軟件 - -配置已安裝的樹莓派軟件並安裝 Grove 庫。 - -1. 在你的 `ssh` 會話中,運行以下命令以更新並重新啟動樹莓派: - - ```sh - sudo apt update && sudo apt full-upgrade --yes && sudo reboot - ``` - - 樹莓派將被更新並重新啟動。當樹莓派重新啟動時,`ssh` 會話將結束,因此等待約 30 秒後重新連接。 - -1. 在重新連接的 `ssh` 會話中,運行以下命令以安裝 Grove 硬件所需的所有庫: - - ```sh - sudo apt install git python3-dev python3-pip --yes - - git clone https://github.com/Seeed-Studio/grove.py - cd grove.py - sudo pip3 install . - - sudo raspi-config nonint do_i2c 0 - ``` - - 這首先安裝 Git 和 Pip,用於安裝 Python 套件。 - - Python 的一個強大功能是能夠安裝 [Pip 套件](https://pypi.org)——這些是其他人編寫並發布到互聯網上的程式碼包。你可以用一條命令將 Pip 套件安裝到你的電腦上,然後在程式碼中使用該套件。 - - Seeed Grove 的 Python 套件需要從源代碼安裝。這些命令將克隆包含該套件源代碼的倉庫,然後在本地安裝它。 - - > 💁 默認情況下,當你安裝一個套件時,它在電腦上的任何地方都可用,這可能會導致套件版本的問題——例如,一個應用程序依賴於某個版本的套件,而當你為另一個應用程序安裝新版本時,可能會導致問題。為了解決這個問題,你可以使用 [Python 虛擬環境](https://docs.python.org/3/library/venv.html),它本質上是 Python 的一個副本,存放在一個專用文件夾中,當你安裝 Pip 套件時,它們只會安裝到該文件夾中。在使用樹莓派時,你不會使用虛擬環境。Grove 的安裝腳本會全局安裝 Grove 的 Python 套件,因此如果你想使用虛擬環境,需要先設置虛擬環境,然後手動重新安裝 Grove 套件到該環境中。使用全局套件更簡單,特別是因為許多樹莓派開發者會為每個項目重新刷新乾淨的 SD 卡。 - - 最後,這將啟用 I2C 接口。 - -1. 運行以下命令重新啟動樹莓派: - - ```sh - sudo reboot - ``` - - 當樹莓派重新啟動時,`ssh` 會話將結束。無需重新連接。 - -#### 配置 VS Code 以進行遠端訪問 - -樹莓派配置完成後,你可以使用 Visual Studio Code (VS Code) 從電腦連接到它——這是一個免費的開發者文本編輯器,你將用它來用 Python 編寫設備程式碼。 - -##### 任務 - 配置 VS Code 以進行遠端訪問 - -安裝所需軟件並遠端連接到樹莓派。 - -1. 按照 [VS Code 文檔](https://code.visualstudio.com?WT.mc_id=academic-17441-jabenn) 的說明在你的電腦上安裝 VS Code - -1. 按照 [VS Code 遠端開發使用 SSH 文檔](https://code.visualstudio.com/docs/remote/ssh?WT.mc_id=academic-17441-jabenn) 的說明安裝所需組件 - -1. 按照相同的說明,將 VS Code 連接到樹莓派 - -1. 連接後,按照 [管理擴展](https://code.visualstudio.com/docs/remote/ssh#_managing-extensions?WT.mc_id=academic-17441-jabenn) 的說明,將 [Pylance 擴展](https://marketplace.visualstudio.com/items?WT.mc_id=academic-17441-jabenn&itemName=ms-python.vscode-pylance) 遠端安裝到樹莓派 - -## Hello world -在學習一種新的程式語言或技術時,通常會從建立一個「Hello World」應用程式開始。這是一個簡單的小應用程式,會輸出類似 `"Hello World"` 的文字,來確認所有工具都已正確配置。 - -針對樹莓派的 Hello World 應用程式,將確保你已正確安裝 Python 和 Visual Studio Code。 - -這個應用程式將放在一個名為 `nightlight` 的資料夾中,並且在這次作業的後續部分會重複使用該資料夾,加入不同的程式碼來構建夜燈應用程式。 - -### 任務 - Hello World - -建立 Hello World 應用程式。 - -1. 啟動 VS Code,可以直接在樹莓派上啟動,或者在你的電腦上啟動並使用 Remote SSH 擴展連接到樹莓派。 - -1. 在 VS Code 中開啟終端機,選擇 *Terminal -> New Terminal*,或者按下快捷鍵 `` CTRL+` ``。終端機將會開啟在 `pi` 使用者的主目錄。 - -1. 執行以下指令來為你的程式碼建立一個資料夾,並在該資料夾中建立一個名為 `app.py` 的 Python 檔案: - - ```sh - mkdir nightlight - cd nightlight - touch app.py - ``` - -1. 在 VS Code 中開啟這個資料夾,選擇 *File -> Open...*,然後選擇 *nightlight* 資料夾,接著點擊 **OK**。 - - ![VS Code 的開啟對話框顯示 nightlight 資料夾](../../../../../translated_images/zh-HK/vscode-open-nightlight-remote.d3d2a4011e30d535.webp) - -1. 從 VS Code 的檔案總管中開啟 `app.py` 檔案,並加入以下程式碼: - - ```python - print('Hello World!') - ``` - - `print` 函數會將傳遞給它的內容輸出到終端機。 - -1. 在 VS Code 的終端機中執行以下指令來運行你的 Python 應用程式: - - ```sh - python app.py - ``` - - > 💁 如果你的系統同時安裝了 Python 2 和 Python 3,可能需要明確使用 `python3` 來執行這段程式碼。因為如果執行 `python`,系統可能會預設使用 Python 2 而非 Python 3。在最新版本的 Raspberry Pi OS 中,預設只安裝了 Python 3。 - - 終端機中將會出現以下輸出: - - ```output - pi@raspberrypi:~/nightlight $ python3 app.py - Hello World! - ``` - -> 💁 你可以在 [code/pi](../../../../../1-getting-started/lessons/1-introduction-to-iot/code/pi) 資料夾中找到這段程式碼。 - -😀 恭喜!你的「Hello World」程式成功運行了! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/1-introduction-to-iot/virtual-device.md b/translations/hk/1-getting-started/lessons/1-introduction-to-iot/virtual-device.md deleted file mode 100644 index cafab06aa..000000000 --- a/translations/hk/1-getting-started/lessons/1-introduction-to-iot/virtual-device.md +++ /dev/null @@ -1,245 +0,0 @@ - -# 虛擬單板電腦 - -與其購買物聯網設備以及感應器和執行器,你可以使用你的電腦來模擬物聯網硬件。[CounterFit 專案](https://github.com/CounterFit-IoT/CounterFit) 允許你在本地運行一個應用程式,模擬像感應器和執行器這樣的物聯網硬件,並通過本地的 Python 程式碼訪問這些感應器和執行器。這些程式碼的編寫方式與你在 Raspberry Pi 上使用實體硬件時的編寫方式相同。 - -## 設置 - -要使用 CounterFit,你需要在電腦上安裝一些免費的軟件。 - -### 任務 - -安裝所需的軟件。 - -1. 安裝 Python。請參考 [Python 下載頁面](https://www.python.org/downloads/) 以獲取安裝最新版本 Python 的指引。 - -1. 安裝 Visual Studio Code (VS Code)。這是你將用來編寫虛擬設備 Python 程式碼的編輯器。請參考 [VS Code 文件](https://code.visualstudio.com?WT.mc_id=academic-17441-jabenn) 以獲取安裝 VS Code 的指引。 - - > 💁 如果你有偏好的工具,可以自由選擇任何 Python IDE 或編輯器來完成這些課程,但課程中的指引將基於使用 VS Code。 - -1. 安裝 VS Code 的 Pylance 擴展。這是一個為 VS Code 提供 Python 語言支持的擴展。請參考 [Pylance 擴展文件](https://marketplace.visualstudio.com/items?WT.mc_id=academic-17441-jabenn&itemName=ms-python.vscode-pylance) 以獲取在 VS Code 中安裝此擴展的指引。 - -CounterFit 應用程式的安裝和配置指引將在相關的作業指令中提供,因為它是按專案安裝的。 - -## Hello World - -在學習一種新的程式語言或技術時,通常會先建立一個 "Hello World" 應用程式——一個小型應用程式,輸出類似 `"Hello World"` 的文字,以確認所有工具已正確配置。 - -虛擬物聯網硬件的 Hello World 應用程式將確保你已正確安裝 Python 和 Visual Studio Code。它還會連接到 CounterFit 以模擬物聯網感應器和執行器。這個應用程式不會使用任何硬件,只是進行連接以確認一切正常。 - -此應用程式將位於名為 `nightlight` 的資料夾中,並在作業的後續部分中重複使用不同的程式碼來建立夜燈應用程式。 - -### 配置 Python 虛擬環境 - -Python 的一個強大功能是能夠安裝 [Pip 套件](https://pypi.org)——這些是由其他人編寫並發布到互聯網上的程式碼套件。你可以通過一條指令在電腦上安裝 Pip 套件,然後在程式碼中使用該套件。你將使用 Pip 安裝一個套件來與 CounterFit 通訊。 - -默認情況下,當你安裝一個套件時,它在電腦的所有地方都可用,這可能會導致套件版本的問題——例如,一個應用程式依賴於某個版本的套件,而安裝新版本可能會導致另一個應用程式出現問題。為了解決這個問題,你可以使用 [Python 虛擬環境](https://docs.python.org/3/library/venv.html),它本質上是 Python 的一個副本,存放在一個專用資料夾中,當你安裝 Pip 套件時,它們只會安裝到該資料夾中。 - -> 💁 如果你使用的是 Raspberry Pi,那麼你不需要在該設備上設置虛擬環境來管理 Pip 套件,而是使用全局套件,因為 Grove 套件是通過安裝腳本全局安裝的。 - -#### 任務 - 配置 Python 虛擬環境 - -配置 Python 虛擬環境並安裝 CounterFit 的 Pip 套件。 - -1. 在終端或命令行中,運行以下指令以在你選擇的位置建立並進入一個新目錄: - - ```sh - mkdir nightlight - cd nightlight - ``` - -1. 現在運行以下指令以在 `.venv` 資料夾中建立虛擬環境: - - ```sh - python3 -m venv .venv - ``` - - > 💁 你需要明確調用 `python3` 來建立虛擬環境,以防你的系統同時安裝了 Python 2 和 Python 3(最新版本)。如果安裝了 Python 2,調用 `python` 會使用 Python 2 而不是 Python 3。 - -1. 啟動虛擬環境: - - * 在 Windows 上: - * 如果你使用的是命令提示符或 Windows Terminal 中的命令提示符,運行: - - ```cmd - .venv\Scripts\activate.bat - ``` - - * 如果你使用的是 PowerShell,運行: - - ```powershell - .\.venv\Scripts\Activate.ps1 - ``` - - > 如果你收到關於系統禁用腳本運行的錯誤,你需要通過設置適當的執行策略來啟用腳本運行。你可以以管理員身份啟動 PowerShell,然後運行以下指令: - - ```powershell - Set-ExecutionPolicy -ExecutionPolicy Unrestricted - ``` - - 當被要求確認時輸入 `Y`。然後重新啟動 PowerShell 並再次嘗試。 - - 如果需要,你可以稍後重置此執行策略。你可以在 [Microsoft Docs 的執行策略頁面](https://docs.microsoft.com/powershell/module/microsoft.powershell.core/about/about_execution_policies?WT.mc_id=academic-17441-jabenn) 上了解更多。 - - * 在 macOS 或 Linux 上,運行: - - ```cmd - source ./.venv/bin/activate - ``` - - > 💁 這些指令應在你運行建立虛擬環境指令的同一位置執行。你永遠不需要進入 `.venv` 資料夾,應始終在建立虛擬環境的資料夾中運行啟動指令以及任何安裝套件或運行程式的指令。 - -1. 啟動虛擬環境後,默認的 `python` 指令將運行用於建立虛擬環境的 Python 版本。運行以下指令以檢查版本: - - ```sh - python --version - ``` - - 輸出應包含以下內容: - - ```output - (.venv) ➜ nightlight python --version - Python 3.9.1 - ``` - - > 💁 你的 Python 版本可能不同——只要是 3.6 或更高版本即可。如果不是,刪除該資料夾,安裝更新版本的 Python 並重試。 - -1. 運行以下指令以安裝 CounterFit 的 Pip 套件。這些套件包括主要的 CounterFit 應用程式以及 Grove 硬件的 shims。這些 shims 允許你像編寫 Grove 生態系統的實體感應器和執行器程式碼一樣編寫程式碼,但連接的是虛擬物聯網設備。 - - ```sh - pip install CounterFit - pip install counterfit-connection - pip install counterfit-shims-grove - ``` - - 這些 Pip 套件只會安裝在虛擬環境中,無法在虛擬環境外部使用。 - -### 編寫程式碼 - -當 Python 虛擬環境準備好後,你可以編寫 "Hello World" 應用程式的程式碼。 - -#### 任務 - 編寫程式碼 - -建立一個 Python 應用程式,將 `"Hello World"` 輸出到控制台。 - -1. 在虛擬環境中,通過終端或命令行運行以下指令以建立名為 `app.py` 的 Python 文件: - - * 在 Windows 上運行: - - ```cmd - type nul > app.py - ``` - - * 在 macOS 或 Linux 上運行: - - ```cmd - touch app.py - ``` - -1. 在 VS Code 中打開當前資料夾: - - ```sh - code . - ``` - - > 💁 如果你的終端在 macOS 上返回 `command not found`,這意味著 VS Code 尚未添加到 PATH。你可以按照 [VS Code 文件中從命令行啟動部分](https://code.visualstudio.com/docs/setup/mac?WT.mc_id=academic-17441-jabenn#_launching-from-the-command-line) 的指引將 VS Code 添加到 PATH,然後再次運行指令。VS Code 在 Windows 和 Linux 上默認添加到 PATH。 - -1. 當 VS Code 啟動時,它將激活 Python 虛擬環境。選定的虛擬環境將顯示在底部狀態欄中: - - ![VS Code 顯示選定的虛擬環境](../../../../../translated_images/zh-HK/vscode-virtual-env.8ba42e04c3d533cf.webp) - -1. 如果 VS Code Terminal 在啟動時已運行,它將不會激活虛擬環境。最簡單的方法是使用 **Kill the active terminal instance** 按鈕關閉終端: - - ![VS Code Kill the active terminal instance 按鈕](../../../../../translated_images/zh-HK/vscode-kill-terminal.1cc4de7c6f25ee08.webp) - - 你可以通過終端提示的前綴來判斷終端是否激活了虛擬環境。例如,它可能是: - - ```sh - (.venv) ➜ nightlight - ``` - - 如果提示中沒有 `.venv` 作為前綴,則虛擬環境未在終端中激活。 - -1. 通過選擇 *Terminal -> New Terminal* 或按 `` CTRL+` `` 啟動新的 VS Code 終端。新終端將加載虛擬環境,並在終端中顯示激活指令。提示也會包含虛擬環境的名稱(`.venv`): - - ```output - ➜ nightlight source .venv/bin/activate - (.venv) ➜ nightlight - ``` - -1. 從 VS Code 的資源管理器中打開 `app.py` 文件,並添加以下程式碼: - - ```python - print('Hello World!') - ``` - - `print` 函數將其接收的內容輸出到控制台。 - -1. 從 VS Code 終端運行以下指令以運行你的 Python 應用程式: - - ```sh - python app.py - ``` - - 輸出將包含以下內容: - - ```output - (.venv) ➜ nightlight python app.py - Hello World! - ``` - -😀 你的 "Hello World" 程式成功了! - -### 連接 "硬件" - -作為第二步的 "Hello World",你將運行 CounterFit 應用程式並將程式碼連接到它。這是虛擬等效於將一些物聯網硬件插入開發套件。 - -#### 任務 - 連接 "硬件" - -1. 從 VS Code 終端運行以下指令啟動 CounterFit 應用程式: - - ```sh - counterfit - ``` - - 應用程式將開始運行並在你的網頁瀏覽器中打開: - - ![Counter Fit 應用程式在瀏覽器中運行](../../../../../translated_images/zh-HK/counterfit-first-run.433326358b669b31d0e99c3513cb01bfbb13724d162c99cdcc8f51ecf5f9c779.png) - - 它將顯示為 *Disconnected*,右上角的 LED 是熄滅的。 - -1. 在 `app.py` 文件的頂部添加以下程式碼: - - ```python - from counterfit_connection import CounterFitConnection - CounterFitConnection.init('127.0.0.1', 5000) - ``` - - 此程式碼從 `counterfit_connection` 模組中導入 `CounterFitConnection` 類,該模組來自你之前安裝的 `counterfit-connection` Pip 套件。然後它初始化了一個連接到運行在 `127.0.0.1` 的 CounterFit 應用程式,該 IP 地址始終可用於訪問你的本地電腦(通常稱為 *localhost*),端口為 5000。 - - > 💁 如果你有其他應用程式在端口 5000 上運行,你可以通過更新程式碼中的端口來更改此設置,並使用 `CounterFit --port ` 運行 CounterFit,將 `` 替換為你想使用的端口。 - -1. 你需要通過選擇 **Create a new integrated terminal** 按鈕啟動新的 VS Code 終端。這是因為 CounterFit 應用程式正在當前終端中運行。 - - ![VS Code Create a new integrated terminal 按鈕](../../../../../translated_images/zh-HK/vscode-new-terminal.77db8fc0f9cd3182.webp) - -1. 在這個新終端中,像之前一樣運行 `app.py` 文件。CounterFit 的狀態將變為 **Connected**,LED 會亮起。 - - ![Counter Fit 顯示為已連接](../../../../../translated_images/zh-HK/counterfit-connected.ed30b46d8f79b0921f3fc70be10366e596a89dca3f80c2224a9d9fc98fccf884.png) - -> 💁 你可以在 [code/virtual-device](../../../../../1-getting-started/lessons/1-introduction-to-iot/code/virtual-device) 資料夾中找到這段程式碼。 - -😀 你成功連接到硬件了! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/1-introduction-to-iot/wio-terminal.md b/translations/hk/1-getting-started/lessons/1-introduction-to-iot/wio-terminal.md deleted file mode 100644 index b749ba2e9..000000000 --- a/translations/hk/1-getting-started/lessons/1-introduction-to-iot/wio-terminal.md +++ /dev/null @@ -1,222 +0,0 @@ - -# Wio Terminal - -[Seeed Studios 的 Wio Terminal](https://www.seeedstudio.com/Wio-Terminal-p-4509.html) 是一款兼容 Arduino 的微控制器,內建 WiFi 以及一些感測器和執行器,並且可以透過名為 [Grove](https://www.seeedstudio.com/category/Grove-c-1003.html) 的硬體生態系統添加更多感測器和執行器。 - -![Seeed Studios 的 Wio Terminal](../../../../../translated_images/zh-HK/wio-terminal.b8299ee16587db9a.webp) - -## 設置 - -要使用 Wio Terminal,您需要在電腦上安裝一些免費軟件。此外,在連接 WiFi 之前,您需要更新 Wio Terminal 的韌體。 - -### 任務 - 設置 - -安裝所需的軟件並更新韌體。 - -1. 安裝 Visual Studio Code (VS Code)。這是您將用來編寫 C/C++ 設備代碼的編輯器。請參考 [VS Code 文件](https://code.visualstudio.com?WT.mc_id=academic-17441-jabenn) 了解安裝 VS Code 的說明。 - - > 💁 另一個流行的 Arduino 開發 IDE 是 [Arduino IDE](https://www.arduino.cc/en/software)。如果您已熟悉此工具,則可以使用它代替 VS Code 和 PlatformIO,但課程將基於使用 VS Code 的指導。 - -1. 安裝 VS Code 的 PlatformIO 擴展。這是 VS Code 的一個擴展,支持用 C/C++ 編程微控制器。請參考 [PlatformIO 擴展文件](https://marketplace.visualstudio.com/items?WT.mc_id=academic-17441-jabenn&itemName=platformio.platformio-ide) 了解如何在 VS Code 中安裝此擴展。此擴展依賴於 Microsoft C/C++ 擴展來處理 C 和 C++ 代碼,安裝 PlatformIO 時會自動安裝 C/C++ 擴展。 - -1. 將 Wio Terminal 連接到您的電腦。Wio Terminal 底部有一個 USB-C 端口,需要連接到電腦上的 USB 端口。Wio Terminal 附帶 USB-C 至 USB-A 線纜,但如果您的電腦只有 USB-C 端口,則需要 USB-C 線纜或 USB-A 至 USB-C 轉接器。 - -1. 按照 [Wio Terminal Wiki WiFi 概述文件](https://wiki.seeedstudio.com/Wio-Terminal-Network-Overview/) 中的說明設置 Wio Terminal 並更新韌體。 - -## Hello World - -在開始學習新的編程語言或技術時,通常會創建一個 "Hello World" 應用程序——一個小型應用程序,輸出類似 `"Hello World"` 的文本,以確保所有工具已正確配置。 - -Wio Terminal 的 Hello World 應用程序將確保您已正確安裝 Visual Studio Code 和 PlatformIO,並設置好微控制器開發環境。 - -### 創建 PlatformIO 項目 - -第一步是使用 PlatformIO 創建一個新項目,並配置為 Wio Terminal。 - -#### 任務 - 創建 PlatformIO 項目 - -創建 PlatformIO 項目。 - -1. 將 Wio Terminal 連接到您的電腦 - -1. 啟動 VS Code - -1. PlatformIO 圖標將顯示在側邊菜單欄: - - ![Platform IO 菜單選項](../../../../../translated_images/zh-HK/vscode-platformio-menu.297be26b9733e5c4.webp) - - 選擇此菜單項,然後選擇 *PIO Home -> Open* - - ![Platform IO 打開選項](../../../../../translated_images/zh-HK/vscode-platformio-home-open.3f9a41bfd3f4da1c.webp) - -1. 在歡迎屏幕中,選擇 **+ New Project** 按鈕 - - ![新項目按鈕](../../../../../translated_images/zh-HK/vscode-platformio-welcome-new-button.ba6fc8a4c7b78cc8.webp) - -1. 在 *Project Wizard* 中配置項目: - - 1. 將項目命名為 `nightlight` - - 1. 在 *Board* 下拉菜單中輸入 `WIO` 以篩選板子,並選擇 *Seeeduino Wio Terminal* - - 1. 保持 *Framework* 為 *Arduino* - - 1. 可以保持 *Use default location* 勾選,或者取消勾選並選擇項目位置 - - 1. 選擇 **Finish** 按鈕 - - ![完成的項目向導](../../../../../translated_images/zh-HK/vscode-platformio-nightlight-project-wizard.5c64db4da6037420.webp) - - PlatformIO 會下載所需的組件以編譯 Wio Terminal 的代碼並創建您的項目。這可能需要幾分鐘。 - -### 探索 PlatformIO 項目 - -VS Code 的資源管理器將顯示由 PlatformIO 向導創建的多個文件和文件夾。 - -#### 文件夾 - -* `.pio` - 此文件夾包含 PlatformIO 所需的臨時數據,例如庫或編譯代碼。如果刪除,它會自動重新創建,並且如果您在 GitHub 等網站上共享項目,則不需要將其添加到源代碼控制中。 -* `.vscode` - 此文件夾包含 PlatformIO 和 VS Code 使用的配置。如果刪除,它會自動重新創建,並且如果您在 GitHub 等網站上共享項目,則不需要將其添加到源代碼控制中。 -* `include` - 此文件夾用於添加額外庫時需要的外部頭文件。在這些課程中,您不會使用此文件夾。 -* `lib` - 此文件夾用於您希望從代碼中調用的外部庫。在這些課程中,您不會使用此文件夾。 -* `src` - 此文件夾包含應用程序的主要源代碼。最初,它只包含一個文件——`main.cpp`。 -* `test` - 此文件夾是您放置代碼單元測試的地方。 - -#### 文件 - -* `main.cpp` - `src` 文件夾中的此文件包含應用程序的入口點。打開此文件,您將看到以下代碼: - - ```cpp - #include - - void setup() { - // put your setup code here, to run once: - } - - void loop() { - // put your main code here, to run repeatedly: - } - ``` - - 當設備啟動時,Arduino 框架會運行一次 `setup` 函數,然後反覆運行 `loop` 函數,直到設備關閉。 - -* `.gitignore` - 此文件列出了在將代碼添加到 git 源代碼控制時需要忽略的文件和目錄,例如上傳到 GitHub 的倉庫。 - -* `platformio.ini` - 此文件包含設備和應用程序的配置。打開此文件,您將看到以下代碼: - - ```ini - [env:seeed_wio_terminal] - platform = atmelsam - board = seeed_wio_terminal - framework = arduino - ``` - - `[env:seeed_wio_terminal]` 部分包含 Wio Terminal 的配置。您可以有多個 `env` 部分,以便代碼可以為多個板子編譯。 - - 其他值與項目向導的配置相匹配: - - * `platform = atmelsam` 定義了 Wio Terminal 使用的硬件(基於 ATSAMD51 的微控制器) - * `board = seeed_wio_terminal` 定義了微控制器板的類型(Wio Terminal) - * `framework = arduino` 定義此項目使用 Arduino 框架。 - -### 編寫 Hello World 應用程序 - -現在您可以開始編寫 Hello World 應用程序。 - -#### 任務 - 編寫 Hello World 應用程序 - -編寫 Hello World 應用程序。 - -1. 在 VS Code 中打開 `main.cpp` 文件 - -1. 將代碼更改為以下內容: - - ```cpp - #include - - void setup() - { - Serial.begin(9600); - - while (!Serial) - ; // Wait for Serial to be ready - - delay(1000); - } - - void loop() - { - Serial.println("Hello World"); - delay(5000); - } - ``` - - `setup` 函數初始化與串口的連接——在此情況下,是用於連接 Wio Terminal 和電腦的 USB 端口。參數 `9600` 是 [波特率](https://wikipedia.org/wiki/Symbol_rate)(也稱為符號率),即通過串口每秒傳輸的數據速度(以位為單位)。此設置表示每秒傳輸 9,600 位(0 和 1)的數據。然後它等待串口準備好。 - - `loop` 函數將行 `Hello World!` 發送到串口,因此會發送 `Hello World!` 的字符以及一個換行符。然後它休眠 5,000 毫秒或 5 秒。`loop` 結束後,會再次運行,並反覆運行,直到微控制器斷電。 - -1. 將 Wio Terminal 設置為上傳模式。每次上傳新代碼到設備時,您都需要執行此操作: - - 1. 快速向下拉動電源開關兩次——每次都會彈回到開啟位置。 - - 1. 檢查 USB 端口右側的藍色狀態 LED。它應該在閃爍。 - - [![如何將 Wio Terminal 設置為上傳模式的視頻](https://img.youtube.com/vi/LeKU_7zLRrQ/0.jpg)](https://youtu.be/LeKU_7zLRrQ) - - 點擊上方圖片觀看操作視頻。 - -1. 編譯並上傳代碼到 Wio Terminal - - 1. 打開 VS Code 的命令面板 - - 1. 輸入 `PlatformIO Upload` 搜索上傳選項,並選擇 *PlatformIO: Upload* - - ![命令面板中的 PlatformIO 上傳選項](../../../../../translated_images/zh-HK/vscode-platformio-upload-command-palette.9e0f49cf80d1f1c3.webp) - - 如果需要,PlatformIO 會在上傳之前自動編譯代碼。 - - 1. 代碼將被編譯並上傳到 Wio Terminal - - > 💁 如果您使用 macOS,可能會出現 *DISK NOT EJECTED PROPERLY* 的通知。這是因為 Wio Terminal 在閃存過程中會被掛載為驅動器,並在編譯代碼寫入設備時斷開連接。您可以忽略此通知。 - - ⚠️ 如果出現上傳端口不可用的錯誤,首先確保 Wio Terminal 已連接到您的電腦,並通過屏幕左側的開關打開,並設置為上傳模式。底部的綠燈應該亮著,藍燈應該在閃爍。如果仍然出現錯誤,請再次快速向下拉動開關兩次,強制 Wio Terminal 進入上傳模式,然後重試上傳。 - -PlatformIO 有一個串口監視器,可以監視通過 USB 線纜從 Wio Terminal 發送的數據。這使您可以監視 `Serial.println("Hello World");` 命令發送的數據。 - -1. 打開 VS Code 的命令面板 - -1. 輸入 `PlatformIO Serial` 搜索串口監視器選項,並選擇 *PlatformIO: Serial Monitor* - - ![命令面板中的 PlatformIO 串口監視器選項](../../../../../translated_images/zh-HK/vscode-platformio-serial-monitor-command-palette.b348ec841b8a1c14.webp) - - 一個新終端將打開,通過串口發送的數據將流入此終端: - - ```output - > Executing task: platformio device monitor < - - --- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time - --- More details at http://bit.ly/pio-monitor-filters - --- Miniterm on /dev/cu.usbmodem101 9600,8,N,1 --- - --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- - Hello World - Hello World - ``` - - `Hello World` 每 5 秒會打印到串口監視器。 - -> 💁 您可以在 [code/wio-terminal](../../../../../1-getting-started/lessons/1-introduction-to-iot/code/wio-terminal) 文件夾中找到此代碼。 - -😀 您的 'Hello World' 程序成功了! - ---- - -**免責聲明**: -此文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議使用專業的人工作業翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解讀概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/2-deeper-dive/README.md b/translations/hk/1-getting-started/lessons/2-deeper-dive/README.md deleted file mode 100644 index 5d691ce19..000000000 --- a/translations/hk/1-getting-started/lessons/2-deeper-dive/README.md +++ /dev/null @@ -1,277 +0,0 @@ - -# 深入了解物聯網 - -![本課程的手繪筆記概覽](../../../../../translated_images/zh-HK/lesson-2.324b0580d620c25e0a24fb7fddfc0b29a846dd4b82c08e7a9466d580ee78ce51.jpg) - -> 手繪筆記由 [Nitya Narasimhan](https://github.com/nitya) 提供。點擊圖片查看更大版本。 - -本課程是 [Microsoft Reactor](https://developer.microsoft.com/reactor/?WT.mc_id=academic-17441-jabenn) 的 [Hello IoT 系列](https://youtube.com/playlist?list=PLmsFUfdnGr3xRts0TIwyaHyQuHaNQcb6-) 的一部分。課程分為兩段影片進行教學——一小時的課程以及一小時的辦公時間,深入探討課程的部分內容並回答問題。 - -[![課程 2:深入了解物聯網](https://img.youtube.com/vi/t0SySWw3z9M/0.jpg)](https://youtu.be/t0SySWw3z9M) - -[![課程 2:深入了解物聯網 - 辦公時間](https://img.youtube.com/vi/tTZYf9EST1E/0.jpg)](https://youtu.be/tTZYf9EST1E) - -> 🎥 點擊上方圖片觀看影片 - -## 課前測驗 - -[課前測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/3) - -## 簡介 - -本課程將更深入探討上一課中提到的一些概念。 - -在本課程中,我們將涵蓋以下內容: - -* [物聯網應用的組成部分](../../../../../1-getting-started/lessons/2-deeper-dive) -* [深入了解微控制器](../../../../../1-getting-started/lessons/2-deeper-dive) -* [深入了解單板電腦](../../../../../1-getting-started/lessons/2-deeper-dive) - -## 物聯網應用的組成部分 - -物聯網應用的兩個主要組成部分是 *網際網路* 和 *物件*。讓我們更詳細地了解這兩個部分。 - -### 物件 - -![Raspberry Pi 4](../../../../../translated_images/zh-HK/raspberry-pi-4.fd4590d308c3d456.webp) - -物聯網中的 **物件** 指的是能與物理世界互動的設備。這些設備通常是小型、價格低廉的電腦,運行速度較慢且耗電量低——例如,僅有幾千字節 RAM 的簡單微控制器(相比之下,PC 的 RAM 是幾 GB),運行速度僅為幾百 MHz(而 PC 是 GHz 級別),但耗電量極低,有時甚至可以用電池運行數週、數月甚至數年。 - -這些設備通過使用感測器從周圍環境收集數據,或通過控制輸出或執行器進行物理改變來與物理世界互動。典型的例子是一個智能恆溫器——一個具有溫度感測器、設置目標溫度的方式(如旋鈕或觸控螢幕),以及與加熱或冷卻系統連接的設備,當檢測到的溫度超出目標範圍時,系統會啟動。 - -![一個圖示顯示溫度和旋鈕作為物聯網設備的輸入,並控制加熱器作為輸出](../../../../../translated_images/zh-HK/basic-thermostat.a923217fd1f37e5a6f3390396a65c22a387419ea2dd17e518ec24315ba6ae9a8.png) - -可以作為物聯網設備的物件種類繁多,從專門感測某一項的硬體到通用設備,甚至包括你的智能手機!智能手機可以使用感測器檢測周圍環境,並使用執行器與世界互動——例如,使用 GPS 感測器檢測你的地點,並通過揚聲器提供導航指示。 - -✅ 想一想你周圍有哪些系統會從感測器讀取數據並據此做出決策。一個例子是烤箱的恆溫器。你能找到更多例子嗎? - -### 網際網路 - -物聯網應用中的 **網際網路** 部分包括物聯網設備可以連接以發送和接收數據的應用,以及可以處理來自物聯網設備的數據並幫助決策的其他應用,這些決策會發送請求給物聯網設備的執行器。 - -一個典型的設置是使用某種雲端服務,物聯網設備連接到該服務,該服務處理安全性、接收來自物聯網設備的消息,並將消息發送回設備。這個雲端服務還會連接到其他應用,這些應用可以處理或存儲感測器數據,或者將感測器數據與其他系統的數據結合起來進行決策。 - -設備並不總是通過 WiFi 或有線連接直接連接到網際網路。有些設備使用網狀網路技術(如藍牙)相互通信,通過具有網際網路連接的集線器設備進行連接。 - -以智能恆溫器為例,恆溫器通過家庭 WiFi 連接到雲端服務,將溫度數據發送到該服務,然後數據會被寫入某種數據庫,讓房主可以通過手機應用查看當前和過去的溫度。雲端中的另一個服務會知道房主想要的溫度,並通過雲端服務向物聯網設備發送消息,告訴加熱系統開啟或關閉。 - -![一個圖示顯示溫度和旋鈕作為物聯網設備的輸入,物聯網設備與雲端之間有雙向通信,雲端與手機之間也有雙向通信,並控制加熱器作為物聯網設備的輸出](../../../../../translated_images/zh-HK/mobile-controlled-thermostat.4a994010473d8d6a52ba68c67e5f02dc8928c717e93ca4b9bc55525aa75bbb60.png) - -更智能的版本可以使用雲端的 AI,結合其他物聯網設備(如檢測房間使用情況的佔用感測器)的數據,以及天氣數據甚至你的日曆,來智能地設置溫度。例如,如果從日曆中讀取到你正在度假,它可以關閉加熱;或者根據你使用的房間逐一關閉加熱,並隨著數據的積累變得越來越準確。 - -![一個圖示顯示多個溫度感測器和旋鈕作為物聯網設備的輸入,物聯網設備與雲端之間有雙向通信,雲端與手機、日曆和天氣服務之間也有雙向通信,並控制加熱器作為物聯網設備的輸出](../../../../../translated_images/zh-HK/smarter-thermostat.a75855f15d2d9e63.webp) - -✅ 還有哪些數據可以幫助讓一個連接網際網路的恆溫器變得更智能? - -### 邊緣上的物聯網 - -雖然物聯網中的 "I" 代表網際網路,但這些設備並不一定要連接到網際網路。在某些情況下,設備可以連接到 "邊緣" 設備——運行在本地網路上的閘道設備,這意味著你可以在不通過網際網路的情況下處理數據。當你有大量數據或網際網路連接速度較慢時,這樣做可以更快;它還允許你在無法連接網際網路的情況下運行,例如在船上或在人道主義危機的災區;此外,它還可以保護數據隱私。一些設備會包含使用雲端工具創建的處理代碼,並在本地運行以收集和響應數據,而無需通過網際網路進行決策。 - -一個例子是智能家居設備,例如 Apple HomePod、Amazon Alexa 或 Google Home,這些設備會使用在雲端訓練的 AI 模型來聆聽你的聲音,但實際運行在本地設備上。這些設備會在聽到某個詞或短語時 "喚醒",然後才將你的語音通過網際網路發送進行處理。設備會在檢測到語音中的停頓時停止發送語音。你在喚醒設備之前說的話,以及設備停止聆聽後說的話,都不會被發送到設備提供商的網際網路,因此是私密的。 - -✅ 想一想還有哪些情境中隱私很重要,因此數據處理最好在邊緣完成而不是在雲端完成。提示:想一想帶有攝像頭或其他影像設備的物聯網設備。 - -### 物聯網安全性 - -任何網際網路連接都需要考慮安全性。有一個老笑話說 "物聯網中的 S 代表安全性"——物聯網中沒有 "S",暗示它不安全。 - -物聯網設備連接到雲端服務,因此它的安全性取決於雲端服務的安全性——如果你的雲端服務允許任何設備連接,那麼惡意數據可能會被發送,或者可能發生病毒攻擊。由於物聯網設備與其他設備互動並進行控制,這可能會帶來非常現實的後果。例如,[Stuxnet 蠕蟲](https://wikipedia.org/wiki/Stuxnet) 操控離心機中的閥門以損壞它們。黑客還利用[安全性不足來訪問嬰兒監視器](https://www.npr.org/sections/thetwo-way/2018/06/05/617196788/s-c-mom-says-baby-monitor-was-hacked-experts-say-many-devices-are-vulnerable)和其他家庭監控設備。 - -> 💁 有時物聯網設備和邊緣設備會運行在完全與網際網路隔離的網路上,以保持數據的私密性和安全性。這被稱為 [空氣隔離](https://wikipedia.org/wiki/Air_gap_(networking))。 - -## 深入了解微控制器 - -在上一課中,我們介紹了微控制器。現在讓我們更深入地了解它們。 - -### CPU - -CPU 是微控制器的 "大腦"。它是運行你的代碼的處理器,並可以向任何連接的設備發送數據或接收數據。CPU 可以包含一個或多個核心——本質上是一個或多個可以協同工作來運行代碼的 CPU。 - -CPU 依賴於時鐘每秒數百萬或數十億次的滴答聲。每次滴答聲或週期,會同步 CPU 可以執行的操作。每次滴答聲,CPU 可以執行程序中的一條指令,例如從外部設備檢索數據或執行數學計算。這種規律的週期允許所有操作在處理下一條指令之前完成。 - -時鐘週期越快,每秒可以處理的指令越多,因此 CPU 的速度越快。CPU 的速度以 [赫茲 (Hz)](https://wikipedia.org/wiki/Hertz) 為單位測量,1 Hz 表示每秒一個週期或時鐘滴答。 - -> 🎓 CPU 的速度通常以 MHz 或 GHz 表示。1MHz 是 100 萬 Hz,1GHz 是 10 億 Hz。 - -> 💁 CPU 使用 [取指-解碼-執行週期](https://wikipedia.org/wiki/Instruction_cycle) 執行程序。每次時鐘滴答,CPU 會從記憶體中取出下一條指令,解碼它,然後執行它,例如使用算術邏輯單元 (ALU) 來加兩個數字。一些執行可能需要多個滴答聲才能完成,因此下一個週期會在指令完成後的下一次滴答聲運行。 - -![取指-解碼-執行週期,顯示從存儲在 RAM 中的程序中取指令,然後在 CPU 上解碼並執行](../../../../../translated_images/zh-HK/fetch-decode-execute.2fd6f150f6280392807f4475382319abd0cee0b90058e1735444d6baa6f2078c.png) - -微控制器的時鐘速度遠低於桌面或筆記本電腦,甚至大多數智能手機。例如,Wio Terminal 的 CPU 運行速度為 120MHz,即每秒 120,000,000 次週期。 - -✅ 一台普通的 PC 或 Mac 的 CPU 有多個核心,運行速度為數 GHz,這意味著時鐘每秒滴答數十億次。研究一下你的電腦的時鐘速度,並比較它比 Wio Terminal 快多少倍。 - -每次時鐘週期都會消耗電力並產生熱量。滴答聲越快,消耗的電力越多,產生的熱量也越多。PC 有散熱片和風扇來散熱,否則它們會在幾秒鐘內過熱並關機。微控制器通常沒有這些,因為它們運行得更冷,因此速度更慢。PC 通常使用主電源或大電池運行幾小時,而微控制器可以使用小電池運行數天、數月甚至數年。微控制器還可以有以不同速度運行的核心,當 CPU 的需求較低時切換到較慢的低功耗核心以減少功耗。 - -> 💁 一些 PC 和 Mac 也採用了相同的快速高功耗核心和較慢低功耗核心的組合,根據運行的任務在節省電池和提高速度之間切換。例如,最新 Apple 筆記本中的 M1 晶片可以在 4 個性能核心和 4 個效率核心之間切換,以根據任務優化電池壽命或速度。 - -✅ 做一些研究:閱讀 [Wikipedia CPU 文章](https://wikipedia.org/wiki/Central_processing_unit) 了解更多關於 CPU 的資訊。 - -#### 任務 - -調查 Wio Terminal。 - -如果你正在使用 Wio Terminal 進行這些課程,試著找到 CPU。在 [Wio Terminal 產品頁面](https://www.seeedstudio.com/Wio-Terminal-p-4509.html) 的 *硬體概覽* 部分找到內部結構的圖片,並試著通過背面的透明塑膠窗口找到 CPU。 - -### 記憶體 - -微控制器通常有兩種類型的記憶體——程序記憶體和隨機存取記憶體 (RAM)。 - -程序記憶體是非易失性的,這意味著無論是否有電力供應,寫入其中的內容都會保留。這是存儲程序代碼的記憶體。 - -RAM 是程序運行時使用的記憶體,包含程序分配的變數和從外設收集的數據。RAM 是易失性的,當電力中斷時,內容會丟失,程序會有效地重置。 -🎓 程式記憶體儲存你的程式碼,即使沒有電力也能保留。 -> 🎓 RAM 用於執行程式,當電源中斷時會被重置 - -與 CPU 一樣,微控制器上的記憶體比 PC 或 Mac 小得多。一台典型的 PC 可能有 8GB(8,000,000,000 字節)的 RAM,每個字節足以存儲一個字母或 0-255 之間的一個數字。而微控制器通常只有幾千字節(KB)的 RAM,一千字節等於 1,000 字節。上述提到的 Wio Terminal 擁有 192KB 的 RAM,即 192,000 字節,比一般 PC 少了超過 40,000 倍! - -下圖顯示了 192KB 和 8GB 之間的相對大小差異——中心的小點代表 192KB。 - -![192KB 和 8GB 的比較 - 超過 40,000 倍的差距](../../../../../translated_images/zh-HK/ram-comparison.6beb73541b42ac6f.webp) - -程式存儲空間也比 PC 小。一台典型的 PC 可能有 500GB 的硬碟用於程式存儲,而微控制器可能只有幾千字節或幾百萬字節(MB)的存儲空間(1MB 等於 1,000KB 或 1,000,000 字節)。Wio Terminal 擁有 4MB 的程式存儲空間。 - -✅ 做一些研究:你正在使用的電腦有多少 RAM 和存儲空間?與微控制器相比如何? - -### 輸入/輸出 - -微控制器需要輸入和輸出(I/O)連接來從感應器讀取數據並向執行器發送控制信號。它們通常包含多個通用輸入/輸出(GPIO)引腳。這些引腳可以通過軟體配置為輸入(接收信號)或輸出(發送信號)。 - -🧠⬅️ 輸入引腳用於從感應器讀取數值 - -🧠➡️ 輸出引腳向執行器發送指令 - -✅ 你將在後續課程中學到更多相關內容。 - -#### 任務 - -研究 Wio Terminal。 - -如果你正在使用 Wio Terminal 進行這些課程,找到 GPIO 引腳。在 [Wio Terminal 產品頁面](https://www.seeedstudio.com/Wio-Terminal-p-4509.html) 的 *Pinout diagram* 部分,了解每個引腳的功能。Wio Terminal 附帶了一張可以貼在背面的貼紙,上面標有引腳編號,如果還沒貼上,現在可以貼上。 - -### 實體大小 - -微控制器通常體積很小,其中最小的 [Freescale Kinetis KL03 MCU](https://www.edn.com/tiny-arm-cortex-m0-based-mcu-shrinks-package/) 小到可以放進高爾夫球的凹槽中。而 PC 的 CPU 僅處理器本身就可能有 40mm x 40mm 的大小,這還不包括為了防止過熱而需要的散熱片和風扇,這些使得 PC 的 CPU 比完整的微控制器大得多。Wio Terminal 開發套件包含微控制器、外殼、螢幕以及一系列連接和元件,其大小甚至不比裸露的 Intel i9 CPU 大多少,並且比帶有散熱片和風扇的 CPU 小得多! - -| 裝置 | 大小 | -| ----------------------------- | --------------------- | -| Freescale Kinetis KL03 | 1.6mm x 2mm x 1mm | -| Wio Terminal | 72mm x 57mm x 12mm | -| Intel i9 CPU、散熱片和風扇 | 136mm x 145mm x 103mm | - -### 框架與操作系統 - -由於速度和記憶體的限制,微控制器不會運行像桌面電腦那樣的操作系統(OS)。讓你的電腦運行的操作系統(Windows、Linux 或 macOS)需要大量的記憶體和處理能力來執行對微控制器來說完全不必要的任務。記住,微控制器通常被編程來執行一個或多個非常特定的任務,而不像 PC 或 Mac 這樣的通用電腦需要支持用戶界面、播放音樂或影片、提供文檔或程式編寫工具、玩遊戲或瀏覽互聯網。 - -要在沒有操作系統的情況下編程微控制器,你需要一些工具來構建你的程式碼,使其能夠在微控制器上運行,並使用 API 與任何外圍設備通信。每個微控制器都不同,因此製造商通常支持標準框架,這些框架允許你遵循標準的“配方”來構建程式碼,並使其能夠在支持該框架的任何微控制器上運行。 - -你也可以使用操作系統來編程微控制器——通常被稱為實時操作系統(RTOS),因為它們被設計用來實時處理與外圍設備的數據傳輸。這些操作系統非常輕量化,並提供以下功能: - -* 多執行緒,允許你的程式碼同時運行多個程式塊,無論是在多個核心上還是通過在單個核心上輪流執行 -* 網絡功能,允許安全地通過互聯網通信 -* 圖形用戶界面(GUI)元件,用於在帶有螢幕的設備上構建用戶界面(UI)。 - -✅ 閱讀一些不同的 RTOS:[Azure RTOS](https://azure.microsoft.com/services/rtos/?WT.mc_id=academic-17441-jabenn)、[FreeRTOS](https://www.freertos.org)、[Zephyr](https://www.zephyrproject.org) - -#### Arduino - -![Arduino 標誌](../../../../../images/arduino-logo.svg) - -[Arduino](https://www.arduino.cc) 可能是最受歡迎的微控制器框架,特別是在學生、愛好者和創客中。Arduino 是一個開源電子平台,結合了軟體和硬體。你可以從 Arduino 官方或其他製造商購買 Arduino 兼容的開發板,然後使用 Arduino 框架進行編程。 - -Arduino 開發板使用 C 或 C++ 編程。使用 C/C++ 可以使程式碼編譯得非常小且運行速度快,這對於像微控制器這樣受限的設備來說是必需的。Arduino 應用程式的核心被稱為 sketch,它是包含兩個函數的 C/C++ 程式碼——`setup` 和 `loop`。當開發板啟動時,Arduino 框架程式碼會執行一次 `setup` 函數,然後不斷重複執行 `loop` 函數,直到電源關閉。 - -你可以在 `setup` 函數中編寫初始化程式碼,例如連接 WiFi 和雲服務或初始化輸入和輸出引腳。而在 `loop` 函數中,你可以編寫處理程式碼,例如從感應器讀取數據並將其發送到雲端。通常你會在每次循環中加入延遲,例如,如果你只想每 10 秒發送一次感應器數據,你可以在循環結束時加入 10 秒的延遲,這樣微控制器可以進入休眠狀態以節省電力,然後在需要時 10 秒後再次運行循環。 - -![Arduino sketch 首先運行 setup,然後不斷重複運行 loop](../../../../../translated_images/zh-HK/arduino-sketch.79590cb837ff7a7c6a68d1afda6cab83fd53d3bb1bd9a8bf2eaf8d693a4d3ea6.png) - -✅ 這種程式架構被稱為 *事件循環* 或 *消息循環*。許多應用程式在底層使用這種架構,這也是大多數運行在 Windows、macOS 或 Linux 等操作系統上的桌面應用程式的標準。`loop` 會監聽來自用戶界面元件(如按鈕)或設備(如鍵盤)的消息,並對其作出響應。你可以在這篇 [事件循環的文章](https://wikipedia.org/wiki/Event_loop) 中閱讀更多內容。 - -Arduino 提供了與微控制器和 I/O 引腳交互的標準庫,這些庫在底層有不同的實現方式,以便在不同的微控制器上運行。例如,[`delay` 函數](https://www.arduino.cc/reference/en/language/functions/time/delay/) 會使程式暫停指定的時間,而 [`digitalRead` 函數](https://www.arduino.cc/reference/en/language/functions/digital-io/digitalread/) 會從指定的引腳讀取 `HIGH` 或 `LOW` 的值,無論程式碼在哪個開發板上運行。這些標準庫意味著為一塊開發板編寫的 Arduino 程式碼可以重新編譯後在任何其他 Arduino 開發板上運行,只要引腳相同且開發板支持相同的功能。 - -Arduino 還有一個龐大的第三方庫生態系統,這些庫可以為你的 Arduino 專案添加額外功能,例如使用感應器和執行器或連接到雲端 IoT 服務。 - -##### 任務 - -研究 Wio Terminal。 - -如果你正在使用 Wio Terminal 進行這些課程,重新閱讀你在上一課中編寫的程式碼。找到 `setup` 和 `loop` 函數。監控串列輸出,觀察 `loop` 函數被反覆調用。嘗試在 `setup` 函數中添加程式碼以寫入串列埠,並觀察這段程式碼每次重新啟動時只被調用一次。嘗試使用側邊的電源開關重新啟動設備,觀察這段程式碼在每次設備重新啟動時都會被調用。 - -## 更深入了解單板電腦 - -在上一課中,我們介紹了單板電腦。現在讓我們更深入地了解它們。 - -### 樹莓派 - -![樹莓派標誌](../../../../../translated_images/zh-HK/raspberry-pi-logo.4efaa16605cee054.webp) - -[樹莓派基金會](https://www.raspberrypi.org) 是一家來自英國的慈善機構,成立於 2009 年,旨在推廣計算機科學的學習,特別是在學校層面。作為這一使命的一部分,他們開發了一款單板電腦,稱為樹莓派。目前樹莓派有三種變體——全尺寸版本、較小的 Pi Zero,以及可以嵌入最終 IoT 設備中的計算模組。 - -![樹莓派 4](../../../../../translated_images/zh-HK/raspberry-pi-4.fd4590d308c3d456.webp) - -最新的全尺寸樹莓派是樹莓派 4B。它擁有一個四核心(4 核)1.5GHz 的 CPU,2GB、4GB 或 8GB 的 RAM,千兆以太網,WiFi,2 個支持 4K 螢幕的 HDMI 埠,一個音頻和複合視頻輸出埠,USB 埠(2 個 USB 2.0 和 2 個 USB 3.0),40 個 GPIO 引腳,一個樹莓派相機模組的相機連接埠,以及一個 SD 卡插槽。所有這些都集成在一塊 88mm x 58mm x 19.5mm 的電路板上,並由 3A 的 USB-C 電源供電。這些起價為 35 美元,比 PC 或 Mac 便宜得多。 - -> 💁 還有一款 Pi400 一體機電腦,將 Pi4 集成到鍵盤中。 - -![樹莓派 Zero](../../../../../translated_images/zh-HK/raspberry-pi-zero.f7a4133e1e7d54bb.webp) - -Pi Zero 更小,功耗更低。它擁有一個單核心 1GHz 的 CPU,512MB 的 RAM,WiFi(在 Zero W 型號中),一個 HDMI 埠,一個 micro-USB 埠,40 個 GPIO 引腳,一個樹莓派相機模組的相機連接埠,以及一個 SD 卡插槽。它的尺寸為 65mm x 30mm x 5mm,功耗非常低。Zero 售價 5 美元,帶 WiFi 的 W 版本售價 10 美元。 - -> 🎓 這兩款設備的 CPU 都是 ARM 處理器,而不是大多數 PC 和 Mac 中的 Intel/AMD x86 或 x64 處理器。這些處理器類似於一些微控制器中的 CPU,以及幾乎所有的手機、Microsoft Surface X 和新的 Apple Silicon Mac 中的 CPU。 - -所有樹莓派變體都運行一個基於 Debian Linux 的操作系統,稱為樹莓派 OS。這個系統有一個精簡版,沒有桌面,非常適合不需要螢幕的“無頭”專案,還有一個完整版本,帶有完整的桌面環境,包括網頁瀏覽器、辦公應用程式、編程工具和遊戲。由於該操作系統是 Debian Linux 的一個版本,你可以安裝任何在 Debian 上運行並為樹莓派內部的 ARM 處理器構建的應用程式或工具。 - -#### 任務 - -研究樹莓派。 - -如果你正在使用樹莓派進行這些課程,閱讀有關電路板上不同硬體元件的資料。 - -* 你可以在 [樹莓派硬體文檔頁面](https://www.raspberrypi.org/documentation/hardware/raspberrypi/) 上找到有關處理器的詳細資訊。閱讀你正在使用的樹莓派所使用的處理器。 -* 找到 GPIO 引腳。在 [樹莓派 GPIO 文檔](https://www.raspberrypi.org/documentation/hardware/raspberrypi/gpio/README.md) 上閱讀更多相關內容。使用 [GPIO 引腳使用指南](https://www.raspberrypi.org/documentation/usage/gpio/README.md) 來識別樹莓派上的不同引腳。 - -### 編程單板電腦 - -單板電腦是完整的電腦,運行完整的操作系統。這意味著你可以使用多種編程語言、框架和工具來編寫程式,而不像微控制器那樣依賴於 Arduino 等框架對開發板的支持。大多數編程語言都有庫可以訪問 GPIO 引腳,以便從感應器和執行器發送和接收數據。 - -✅ 你熟悉哪些編程語言?它們是否支持 Linux? - -在樹莓派上構建 IoT 應用程式最常用的編程語言是 Python。樹莓派有一個龐大的硬體生態系統,幾乎所有這些硬體都包括用於 Python 的相關程式庫。一些生態系統基於“帽子”(hats)——之所以這樣稱呼,是因為它們像帽子一樣坐在樹莓派的頂部,通過一個大插座連接到 40 個 GPIO 引腳上。這些帽子提供了額外的功能,例如螢幕、感應器、遙控車或適配器,允許你插入帶有標準化電纜的感應器。 -### 專業物聯網部署中的單板電腦使用 - -單板電腦不僅僅是開發套件,它們也被用於專業的物聯網部署。這些電腦能夠提供強大的功能來控制硬件,並執行如運行機器學習模型等複雜任務。例如,[Raspberry Pi 4 Compute Module](https://www.raspberrypi.org/blog/raspberry-pi-compute-module-4/) 提供了與 Raspberry Pi 4 相同的性能,但採用更緊湊且更便宜的形式,去掉了大部分的接口,專為嵌入定制硬件而設計。 - ---- - -## 🚀 挑戰 - -上一課的挑戰是列出你家中、學校或工作場所中所有你能想到的物聯網設備。對於這些設備中的每一個,你認為它們是基於微控制器、單板電腦,還是兩者的結合? - -## 課後測驗 - -[課後測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/4) - -## 回顧與自學 - -* 閱讀 [Arduino 入門指南](https://www.arduino.cc/en/Guide/Introduction),以更深入了解 Arduino 平台。 -* 閱讀 [Raspberry Pi 4 簡介](https://www.raspberrypi.org/products/raspberry-pi-4-model-b/),以了解更多關於 Raspberry Pi 的資訊。 -* 在 [Electrical Engineering Journal 的文章「What the FAQ are CPUs, MPUs, MCUs, and GPUs」](https://www.eejournal.com/article/what-the-faq-are-cpus-mpus-mcus-and-gpus/) 中學習更多相關概念和縮寫。 - -✅ 使用這些指南,結合 [硬件指南](../../../hardware.md) 中的鏈接所顯示的成本,決定你想使用哪種硬件平台,或者是否更願意使用虛擬設備。 - -## 作業 - -[比較與對比微控制器與單板電腦](assignment.md) - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/2-deeper-dive/assignment.md b/translations/hk/1-getting-started/lessons/2-deeper-dive/assignment.md deleted file mode 100644 index 5c999e368..000000000 --- a/translations/hk/1-getting-started/lessons/2-deeper-dive/assignment.md +++ /dev/null @@ -1,26 +0,0 @@ - -# 比較及對比微控制器與單板電腦 - -## 指引 - -本課程介紹了微控制器與單板電腦。請建立一個表格來比較及對比它們,並列出至少兩個使用微控制器而非單板電腦的原因,以及至少兩個使用單板電腦而非微控制器的原因。 - -## 評分標準 - -| 評分項目 | 優秀 | 合格 | 需要改進 | -| -------- | ---- | ---- | -------- | -| 建立一個表格比較微控制器與單板電腦 | 建立了一個包含多項正確比較及對比的列表 | 只建立了一個包含少量項目的列表 | 只能列出一項或未能列出任何比較及對比項目 | -| 使用其中一者的原因 | 能夠提供微控制器的兩個或以上原因,以及單板電腦的兩個或以上原因 | 只能提供微控制器的1-2個原因,以及單板電腦的1-2個原因 | 未能提供微控制器或單板電腦的1個或以上原因 | - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/README.md b/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/README.md deleted file mode 100644 index 8365a7d9b..000000000 --- a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/README.md +++ /dev/null @@ -1,230 +0,0 @@ - -# 使用感應器和致動器與物理世界互動 - -![本課程的手繪筆記概述](../../../../../translated_images/zh-HK/lesson-3.cc3b7b4cd646de598698cce043c0393fd62ef42bac2eaf60e61272cd844250f4.jpg) - -> 手繪筆記由 [Nitya Narasimhan](https://github.com/nitya) 提供。點擊圖片查看更大版本。 - -本課程是 [Microsoft Reactor](https://developer.microsoft.com/reactor/?WT.mc_id=academic-17441-jabenn) 的 [Hello IoT 系列](https://youtube.com/playlist?list=PLmsFUfdnGr3xRts0TIwyaHyQuHaNQcb6-) 的一部分。課程分為兩段影片進行教學——一段 1 小時的課程,以及一段 1 小時的辦公時間,深入探討課程內容並回答問題。 - -[![課程 3:使用感應器和致動器與物理世界互動](https://img.youtube.com/vi/Lqalu1v6aF4/0.jpg)](https://youtu.be/Lqalu1v6aF4) - -[![課程 3:使用感應器和致動器與物理世界互動 - 辦公時間](https://img.youtube.com/vi/qR3ekcMlLWA/0.jpg)](https://youtu.be/qR3ekcMlLWA) - -> 🎥 點擊上方圖片觀看影片 - -## 課前測驗 - -[課前測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/5) - -## 簡介 - -本課程介紹了 IoT 設備的兩個重要概念——感應器和致動器。您將親自操作這些設備,為您的 IoT 項目添加一個光感應器,然後添加一個由光線強度控制的 LED,實際上構建了一個夜燈。 - -在本課程中,我們將涵蓋: - -* [什麼是感應器?](../../../../../1-getting-started/lessons/3-sensors-and-actuators) -* [使用感應器](../../../../../1-getting-started/lessons/3-sensors-and-actuators) -* [感應器類型](../../../../../1-getting-started/lessons/3-sensors-and-actuators) -* [什麼是致動器?](../../../../../1-getting-started/lessons/3-sensors-and-actuators) -* [使用致動器](../../../../../1-getting-started/lessons/3-sensors-and-actuators) -* [致動器類型](../../../../../1-getting-started/lessons/3-sensors-and-actuators) - -## 什麼是感應器? - -感應器是感知物理世界的硬件設備——它們測量周圍的一個或多個屬性,並將信息傳送到 IoT 設備。感應器涵蓋了大量設備,因為可以測量的事物非常多,從自然屬性(如空氣溫度)到物理交互(如運動)。 - -一些常見的感應器包括: - -* 溫度感應器——這些感應器感知空氣溫度或它們所浸入物體的溫度。對於愛好者和開發者來說,這些感應器通常與氣壓和濕度結合在一個單一的感應器中。 -* 按鈕——這些感應器感知何時被按下。 -* 光感應器——這些感應器檢測光線強度,可以針對特定顏色、紫外線、紅外線或一般可見光。 -* 相機——這些感應器通過拍攝照片或流式視頻感知世界的視覺表示。 -* 加速度計——這些感應器感知多方向的運動。 -* 麥克風——這些感應器感知聲音,可以是一般的聲音強度或方向性聲音。 - -✅ 做一些研究。您的手機有哪些感應器? - -所有感應器都有一個共同點——它們將感知到的事物轉換為 IoT 設備可以解讀的電信號。如何解讀這些電信號取決於感應器以及用於與 IoT 設備通信的通信協議。 - -## 使用感應器 - -按照以下相關指南,為您的 IoT 設備添加感應器: - -* [Arduino - Wio Terminal](wio-terminal-sensor.md) -* [單板電腦 - Raspberry Pi](pi-sensor.md) -* [單板電腦 - 虛擬設備](virtual-device-sensor.md) - -## 感應器類型 - -感應器分為模擬感應器和數字感應器。 - -### 模擬感應器 - -一些最基本的感應器是模擬感應器。這些感應器接收來自 IoT 設備的電壓,感應器元件調整該電壓,然後測量從感應器返回的電壓以獲得感應器值。 - -> 🎓 電壓是衡量電流從一個地方移動到另一個地方的推動力,例如從電池的正極到負極。例如,一個標準的 AA 電池是 1.5V(V 是伏特的符號),可以以 1.5V 的推動力從正極推動電流到負極。不同的電子硬件需要不同的電壓才能工作,例如,LED 可以在 2-3V 之間發光,但 100W 的燈絲燈泡需要 240V。您可以在 [維基百科的電壓頁面](https://wikipedia.org/wiki/Voltage) 上了解更多關於電壓的知識。 - -一個例子是電位器。這是一個可以在兩個位置之間旋轉的旋鈕,感應器測量旋轉角度。 - -![一個電位器設置在中間位置,接收 5 伏特並返回 3.8 伏特](../../../../../translated_images/zh-HK/potentiometer.35a348b9ce22f6ec.webp) - -IoT 設備會向電位器發送一個電信號,電壓例如 5 伏特(5V)。當電位器被調整時,它會改變從另一端輸出的電壓。想像一下,您有一個標記為 0 到 [11](https://wikipedia.org/wiki/Up_to_eleven) 的電位器,例如放大器上的音量旋鈕。當電位器處於完全關閉位置(0)時,輸出為 0V(0 伏特)。當它處於完全開啟位置(11)時,輸出為 5V(5 伏特)。 - -> 🎓 這是一個過度簡化的描述,您可以在 [維基百科的電位器頁面](https://wikipedia.org/wiki/Potentiometer) 上了解更多關於電位器和可變電阻的知識。 - -感應器輸出的電壓隨後由 IoT 設備讀取,設備可以對其作出響應。根據感應器的不同,這個電壓可以是任意值,也可以映射到標準單位。例如,基於 [熱敏電阻](https://wikipedia.org/wiki/Thermistor) 的模擬溫度感應器會根據溫度改變其電阻。輸出電壓可以通過代碼中的計算轉換為開爾文溫度,並相應地轉換為 °C 或 °F。 - -✅ 您認為如果感應器返回的電壓高於發送的電壓(例如來自外部電源)會發生什麼?⛔️ 請勿測試此情況。 - -#### 模擬到數字轉換 - -IoT 設備是數字化的——它們無法處理模擬值,只能處理 0 和 1。這意味著模擬感應器的值需要在處理之前轉換為數字信號。許多 IoT 設備具有模擬到數字轉換器(ADCs),用於將模擬輸入轉換為其值的數字表示。感應器也可以通過連接板與 ADC 一起工作。例如,在使用 Raspberry Pi 的 Seeed Grove 生態系統中,模擬感應器連接到 Pi 上的特定端口,這些端口位於連接到 Pi 的 GPIO 引腳的 '帽子' 上,該帽子具有 ADC,可以將電壓轉換為可以通過 Pi 的 GPIO 引腳發送的數字信號。 - -想像一下,您有一個模擬光感應器連接到使用 3.3V 的 IoT 設備,並返回 1V 的值。這個 1V 在數字世界中沒有意義,因此需要進行轉換。電壓將根據設備和感應器使用的比例轉換為模擬值。一個例子是 Seeed Grove 光感應器,它輸出 0 到 1,023 的值。對於這個在 3.3V 下運行的感應器,1V 的輸出將是一個值 300。IoT 設備無法處理 300 作為模擬值,因此該值將被轉換為 `0000000100101100`,即 Grove '帽子' 的 300 的二進制表示。隨後由 IoT 設備處理。 - -✅ 如果您不懂二進制,請進行一些研究以了解數字如何用 0 和 1 表示。[BBC Bitesize 的二進制入門課程](https://www.bbc.co.uk/bitesize/guides/zwsbwmn/revision/1) 是一個很好的起點。 - -從編程的角度來看,所有這些通常由感應器附帶的庫處理,因此您不需要自己擔心這些轉換。對於 Grove 光感應器,您可以使用 Python 庫並調用 `light` 屬性,或者使用 Arduino 庫並調用 `analogRead` 來獲得 300 的值。 - -### 數字感應器 - -數字感應器與模擬感應器一樣,使用電壓變化來檢測周圍的世界。不同之處在於它們輸出數字信號,通過僅測量兩種狀態或使用內置 ADC 來完成。數字感應器越來越普遍,以避免需要在連接板或 IoT 設備本身使用 ADC。 - -最簡單的數字感應器是一個按鈕或開關。這是一個只有兩種狀態的感應器,開或關。 - -![一個按鈕接收 5 伏特。未按下時返回 0 伏特,按下時返回 5 伏特](../../../../../translated_images/zh-HK/button.eadb560b77ac45e56f523d9d8876e40444f63b419e33eb820082d461fa79490b.png) - -IoT 設備上的引腳(例如 GPIO 引腳)可以直接測量此信號作為 0 或 1。如果發送的電壓與返回的電壓相同,則讀取的值為 1,否則讀取的值為 0。無需轉換信號,它只能是 1 或 0。 - -> 💁 電壓從來不是完全精確的,尤其是感應器中的元件會有一些電阻,因此通常會有容差。例如,Raspberry Pi 的 GPIO 引腳工作在 3.3V,並將返回信號高於 1.8V 的讀取為 1,低於 1.8V 的讀取為 0。 - -* 3.3V 進入按鈕。按鈕未按下,因此輸出為 0V,值為 0 -* 3.3V 進入按鈕。按鈕按下,因此輸出為 3.3V,值為 1 - -更高級的數字感應器讀取模擬值,然後使用內置 ADC 將其轉換為數字信號。例如,數字溫度感應器仍然會像模擬感應器一樣使用熱電偶,並仍然測量由當前溫度下熱電偶的電阻引起的電壓變化。它不會返回模擬值,而是依靠設備或連接板將其轉換為數字信號,內置的 ADC 會將該值轉換為一系列 0 和 1,並將其發送到 IoT 設備。這些 0 和 1 的發送方式與按鈕的數字信號相同,其中 1 是全電壓,0 是 0V。 - -![一個數字溫度感應器將模擬讀數轉換為二進制數據,其中 0 是 0 伏特,1 是 5 伏特,然後將其發送到 IoT 設備](../../../../../translated_images/zh-HK/temperature-as-digital.85004491b977bae1.webp) - -發送數字數據使感應器變得更加複雜,可以發送更詳細的數據,甚至是加密數據以用於安全感應器。一個例子是相機。這是一個捕捉圖像並以包含該圖像的數字數據形式發送的感應器,通常以壓縮格式(如 JPEG)發送到 IoT 設備。它甚至可以通過捕捉圖像並逐幀發送完整圖像或壓縮視頻流來進行視頻流式傳輸。 - -## 什麼是致動器? - -致動器與感應器相反——它們將來自 IoT 設備的電信號轉換為與物理世界的交互,例如發光、發聲或驅動馬達。 - -一些常見的致動器包括: - -* LED——當打開時發出光 -* 揚聲器——根據發送的信號發出聲音,從基本的蜂鳴器到可以播放音樂的音頻揚聲器 -* 步進馬達——將信號轉換為一定量的旋轉,例如旋轉旋鈕 90° -* 繼電器——這些是可以通過電信號打開或關閉的開關。它們允許 IoT 設備的小電壓打開更大的電壓。 -* 屏幕——這些是更複雜的致動器,顯示多段顯示器上的信息。屏幕從簡單的 LED 顯示器到高分辨率視頻顯示器不等。 - -✅ 做一些研究。您的手機有哪些致動器? - -## 使用致動器 - -按照以下相關指南,為您的 IoT 設備添加致動器,並由感應器控制,以構建 IoT 夜燈。它將從光感應器收集光線強度,並使用 LED 作為致動器,在檢測到的光線強度過低時發出光。 - -![任務的流程圖,顯示光線強度的讀取和檢查,以及 LED 的控制](../../../../../translated_images/zh-HK/assignment-1-flow.7552a51acb1a5ec858dca6e855cdbb44206434006df8ba3799a25afcdab1665d.png) - -* [Arduino - Wio Terminal](wio-terminal-actuator.md) -* [單板電腦 - Raspberry Pi](pi-actuator.md) -* [單板電腦 - 虛擬設備](virtual-device-actuator.md) - -## 致動器類型 - -像感應器一樣,致動器分為模擬致動器和數字致動器。 - -### 模擬致動器 - -模擬致動器接收模擬信號並將其轉換為某種交互,交互根據提供的電壓而改變。 - -一個例子是可調光燈,例如您家中的燈。提供給燈的電壓量決定了它的亮度。 -![在低電壓下光線較暗,在高電壓下光線較亮](../../../../../translated_images/zh-HK/dimmable-light.9ceffeb195dec1a849da718b2d71b32c35171ff7dfea9c07bbf82646a67acf6b.png) - -就像感應器一樣,實際的物聯網裝置是基於數碼信號運作的,而不是模擬信號。這意味著,若要傳送模擬信號,物聯網裝置需要一個數碼轉模擬轉換器(DAC),這個轉換器可以直接內建於物聯網裝置中,或者安裝在連接板上。這樣可以將物聯網裝置的0和1轉換成致動器可以使用的模擬電壓。 - -✅ 如果物聯網裝置傳送的電壓高於致動器能承受的範圍,你認為會發生什麼事? -⛔️ 請勿嘗試測試這種情況。 - -#### 脈衝寬度調變 - -另一種將物聯網裝置的數碼信號轉換為模擬信號的方法是脈衝寬度調變(PWM)。這種方法涉及傳送大量短暫的數碼脈衝,模擬出類似模擬信號的效果。 - -例如,你可以使用PWM來控制馬達的速度。 - -假設你正在使用5V電源控制一個馬達。你向馬達傳送一個短脈衝,將電壓切換到高電平(5V)持續0.02秒。在這段時間內,馬達可以旋轉1/10圈,或36°。然後信號暫停0.02秒,傳送低電平信號(0V)。每個開啟和關閉的循環持續0.04秒,然後重複。 - -![以150 RPM旋轉的馬達的脈衝寬度調變](../../../../../translated_images/zh-HK/pwm-motor-150rpm.83347ac04ca38482.webp) - -這意味著在一秒內,你有25個持續0.02秒的5V脈衝,每個脈衝之後是0.02秒的0V暫停。每個脈衝使馬達旋轉1/10圈,這意味著馬達每秒完成2.5圈旋轉。你使用數碼信號使馬達以每秒2.5圈或每分鐘150轉(RPM)的速度旋轉(RPM是一種非標準的旋轉速度測量單位)。 - -```output -25 pulses per second x 0.1 rotations per pulse = 2.5 rotations per second -2.5 rotations per second x 60 seconds in a minute = 150rpm -``` - -> 🎓 當PWM信號開啟時間和關閉時間各佔一半時,稱為[50%占空比](https://wikipedia.org/wiki/Duty_cycle)。占空比是信號處於開啟狀態的時間百分比與關閉狀態的時間百分比的比較。 - -![以75 RPM旋轉的馬達的脈衝寬度調變](../../../../../translated_images/zh-HK/pwm-motor-75rpm.a5e4c939934b6e14.webp) - -你可以通過改變脈衝的大小來調整馬達的速度。例如,對於同一個馬達,你可以保持循環時間為0.04秒,但將開啟脈衝縮短一半至0.01秒,關閉脈衝延長至0.03秒。每秒的脈衝數量(25個)保持不變,但每個開啟脈衝的長度減半。一個減半的脈衝只會使馬達旋轉1/20圈,而每秒25個脈衝將完成1.25圈旋轉,即75 RPM。通過改變數碼信號的脈衝速度,你將模擬馬達的速度減半。 - -```output -25 pulses per second x 0.05 rotations per pulse = 1.25 rotations per second -1.25 rotations per second x 60 seconds in a minute = 75rpm -``` - -✅ 你認為如何保持馬達在低速時的旋轉平穩?你會選擇少量長脈衝和長暫停,還是大量非常短的脈衝和非常短的暫停? - -> 💁 一些感應器也使用PWM來將模擬信號轉換為數碼信號。 - -> 🎓 你可以在[Wikipedia的脈衝寬度調變頁面](https://wikipedia.org/wiki/Pulse-width_modulation)上閱讀更多關於脈衝寬度調變的內容。 - -### 數碼致動器 - -數碼致動器與數碼感應器類似,要麼只有兩種狀態(由高電壓或低電壓控制),要麼內建DAC,可以將數碼信號轉換為模擬信號。 - -一個簡單的數碼致動器是LED。當裝置傳送數碼信號1時,會傳送高電壓以點亮LED。當傳送數碼信號0時,電壓降至0V,LED熄滅。 - -![LED在0伏特時熄滅,在5伏特時點亮](../../../../../translated_images/zh-HK/led.ec6d94f66676a174ad06d9fa9ea49c2ee89beb18b312d5c6476467c66375b07f.png) - -✅ 你能想到其他簡單的兩狀態致動器嗎?一個例子是電磁閥,它是一種電磁鐵,可以被激活來執行例如移動門閂以鎖定/解鎖門的操作。 - -更高級的數碼致動器,例如屏幕,需要以特定格式傳送數碼數據。這些致動器通常附帶庫文件,使得傳送正確的數據來控制它們變得更容易。 - ---- - -## 🚀 挑戰 - -上一課的挑戰是列出你家中、學校或工作場所中的所有物聯網裝置,並判斷它們是基於微控制器還是單板電腦,或者是兩者的混合。 - -對於你列出的每個裝置,它們連接了哪些感應器和致動器?這些裝置連接的每個感應器和致動器的用途是什麼? - -## 課後測驗 - -[課後測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/6) - -## 回顧與自學 - -* 在[ThingLearn](http://thinglearn.jenlooper.com/curriculum/)上閱讀有關電力和電路的內容。 -* 在[Seeed Studios溫度感應器指南](https://www.seeedstudio.com/blog/2019/10/14/temperature-sensors-for-arduino-projects/)上閱讀有關不同類型溫度感應器的內容。 -* 在[Wikipedia的LED頁面](https://wikipedia.org/wiki/Light-emitting_diode)上閱讀有關LED的內容。 - -## 作業 - -[研究感應器和致動器](assignment.md) - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為具權威性的來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/assignment.md b/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/assignment.md deleted file mode 100644 index 55f9a68ac..000000000 --- a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/assignment.md +++ /dev/null @@ -1,31 +0,0 @@ - -# 研究感應器與致動器 - -## 指引 - -這一課涵蓋了感應器與致動器。請研究並描述一個可以與 IoT 開發套件搭配使用的感應器和致動器,包括以下內容: - -* 它的功能 -* 內部使用的電子元件/硬件 -* 它是模擬還是數碼 -* 它的單位及輸入或測量範圍 - -## 評分標準 - -| 評分標準 | 優異 | 合格 | 需要改進 | -| -------- | ---- | ---- | -------- | -| 描述感應器 | 描述了一個感應器,並詳細涵蓋了上述 4 個部分的內容。 | 描述了一個感應器,但僅能提供上述 2-3 個部分的內容。 | 描述了一個感應器,但僅能提供上述 1 個部分的內容。 | -| 描述致動器 | 描述了一個致動器,並詳細涵蓋了上述 4 個部分的內容。 | 描述了一個致動器,但僅能提供上述 2-3 個部分的內容。 | 描述了一個致動器,但僅能提供上述 1 個部分的內容。 | - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要信息,建議使用專業的人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/pi-actuator.md b/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/pi-actuator.md deleted file mode 100644 index 447ddd544..000000000 --- a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/pi-actuator.md +++ /dev/null @@ -1,130 +0,0 @@ - -# 建造夜燈 - Raspberry Pi - -在這部分課程中,你將在 Raspberry Pi 上添加一個 LED,並用它來製作一個夜燈。 - -## 硬件 - -夜燈現在需要一個執行器。 - -執行器是一個 **LED**,即[發光二極管](https://wikipedia.org/wiki/Light-emitting_diode),當電流通過時會發光。這是一個數字執行器,具有兩種狀態:開和關。發送值 1 會打開 LED,發送值 0 則會關閉 LED。LED 是一個外部 Grove 執行器,需要連接到 Raspberry Pi 上的 Grove Base hat。 - -夜燈邏輯的伪代码如下: - -```output -Check the light level. -If the light is less than 300 - Turn the LED on -Otherwise - Turn the LED off -``` - -### 連接 LED - -Grove LED 是一個模塊,包含多種顏色的 LED,讓你可以選擇喜歡的顏色。 - -#### 任務 - 連接 LED - -連接 LED。 - -![一個 Grove LED](../../../../../translated_images/zh-HK/grove-led.6c853be93f473cf2c439cfc74bb1064732b22251a83cedf66e62f783f9cc1a79.png) - -1. 選擇你喜歡的 LED,並將 LED 的腳插入 LED 模塊上的兩個孔中。 - - LED 是發光二極管,而二極管是一種只能單向導電的電子元件。這意味著 LED 必須以正確的方向連接,否則無法工作。 - - LED 的其中一隻腳是正極,另一隻是負極。LED 並不是完全圓形的,其中一側稍微平坦一些。稍微平坦的一側是負極。當你將 LED 連接到模塊時,確保圓形一側的腳連接到模塊外側標記為 **+** 的插座,而平坦一側的腳連接到模塊中間附近的插座。 - -1. LED 模塊有一個旋轉按鈕,可以用來控制亮度。首先用小型十字螺絲刀將其逆時針旋轉到最大亮度。 - -1. 將 Grove 電纜的一端插入 LED 模塊上的插座。電纜只能以一種方向插入。 - -1. 在 Raspberry Pi 關閉電源的情況下,將 Grove 電纜的另一端連接到 Grove Base hat 上標記為 **D5** 的數字插座。這個插座位於 GPIO 插針旁邊的一排插座中,從左數第二個。 - -![Grove LED 連接到 D5 插座](../../../../../translated_images/zh-HK/pi-led.97f1d474981dc35d1c7996c7b17de355d3d0a6bc9606d79fa5f89df933415122.png) - -## 編程夜燈 - -現在可以使用 Grove 光線感測器和 Grove LED 來編程夜燈。 - -### 任務 - 編程夜燈 - -編程夜燈。 - -1. 啟動 Raspberry Pi,並等待其完成啟動。 - -1. 在 VS Code 中打開你在上一部分作業中創建的夜燈項目,可以直接在 Raspberry Pi 上運行,也可以通過 Remote SSH 擴展連接。 - -1. 在 `app.py` 文件中添加以下代碼以導入所需的庫。這段代碼應該添加到其他 `import` 行的下方。 - - ```python - from grove.grove_led import GroveLed - ``` - - `from grove.grove_led import GroveLed` 語句從 Grove Python 庫中導入了 `GroveLed`。這個庫包含與 Grove LED 交互的代碼。 - -1. 在 `light_sensor` 聲明之後添加以下代碼,以創建管理 LED 的類的實例: - - ```python - led = GroveLed(5) - ``` - - `led = GroveLed(5)` 這一行創建了一個 `GroveLed` 類的實例,並連接到 **D5** 插針——即 LED 所連接的 Grove 數字插針。 - - > 💁 每個插座都有唯一的插針號碼。插針 0、2、4 和 6 是模擬插針,插針 5、16、18、22、24 和 26 是數字插針。 - -1. 在 `while` 循環內部,並在 `time.sleep` 之前添加一個檢查光線水平的代碼,用於控制 LED 的開啟或關閉: - - ```python - if light < 300: - led.on() - else: - led.off() - ``` - - 這段代碼檢查 `light` 值。如果該值小於 300,則調用 `GroveLed` 類的 `on` 方法,向 LED 發送數字值 1,打開 LED。如果光線值大於或等於 300,則調用 `off` 方法,向 LED 發送數字值 0,關閉 LED。 - - > 💁 這段代碼應該與 `print('Light level:', light)` 行保持相同的縮進級別,以確保它在 while 循環內部! - - > 💁 當向執行器發送數字值時,值 0 表示 0V,值 1 表示設備的最大電壓。對於 Raspberry Pi 使用的 Grove 感測器和執行器,值 1 的電壓為 3.3V。 - -1. 在 VS Code 的終端中運行以下命令以運行你的 Python 應用: - - ```sh - python3 app.py - ``` - - 光線值將輸出到控制台。 - - ```output - pi@raspberrypi:~/nightlight $ python3 app.py - Light level: 634 - Light level: 634 - Light level: 634 - Light level: 230 - Light level: 104 - Light level: 290 - ``` - -1. 遮住和移開光線感測器。注意當光線水平小於或等於 300 時,LED 會亮起;當光線水平大於 300 時,LED 會熄滅。 - - > 💁 如果 LED 沒有亮起,請確保它的連接方向正確,並且旋轉按鈕已設置為最大亮度。 - -![LED 連接到 Raspberry Pi,隨光線水平變化而亮起和熄滅](../../../../../images/pi-running-assignment-1-1.gif) - -> 💁 你可以在 [code-actuator/pi](../../../../../1-getting-started/lessons/3-sensors-and-actuators/code-actuator/pi) 文件夾中找到這段代碼。 - -😀 你的夜燈程式成功了! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/pi-sensor.md b/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/pi-sensor.md deleted file mode 100644 index 3bd7b47c3..000000000 --- a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/pi-sensor.md +++ /dev/null @@ -1,110 +0,0 @@ - -# 建造夜燈 - Raspberry Pi - -在這部分課程中,你將為 Raspberry Pi 添加一個光線感測器。 - -## 硬件 - -本課程使用的感測器是**光線感測器**,它利用[光電二極管](https://wikipedia.org/wiki/Photodiode)將光轉換為電信號。這是一個類比感測器,會傳送一個介於 0 到 1,000 的整數值,表示相對的光量,但不對應任何標準測量單位,例如 [lux](https://wikipedia.org/wiki/Lux)。 - -光線感測器是一個外部 Grove 感測器,需要連接到 Raspberry Pi 上的 Grove Base hat。 - -### 連接光線感測器 - -用於檢測光線水平的 Grove 光線感測器需要連接到 Raspberry Pi。 - -#### 任務 - 連接光線感測器 - -連接光線感測器 - -![Grove 光線感測器](../../../../../translated_images/zh-HK/grove-light-sensor.b8127b7c434e632d6bcdb57587a14e9ef69a268a22df95d08628f62b8fa5505c.png) - -1. 將 Grove 電纜的一端插入光線感測器模組上的插座。它只能以一種方式插入。 - -1. 在 Raspberry Pi 關機的情況下,將 Grove 電纜的另一端連接到 Grove Base hat 上標記為 **A0** 的類比插座。此插座位於 GPIO 插針旁邊插座排的右數第二個。 - -![Grove 光線感測器連接到 A0 插座](../../../../../translated_images/zh-HK/pi-light-sensor.66cc1e31fa48cd7d5f23400d4b2119aa41508275cb7c778053a7923b4e972d7e.png) - -## 程式設計光線感測器 - -現在可以使用 Grove 光線感測器進行程式設計。 - -### 任務 - 程式設計光線感測器 - -程式設計裝置。 - -1. 啟動 Raspberry Pi,等待其完成啟動。 - -1. 在 VS Code 中打開你在上一部分作業中建立的夜燈專案,可以直接在 Pi 上運行,也可以使用 Remote SSH 擴展連接。 - -1. 打開 `app.py` 文件,並刪除其中的所有程式碼。 - -1. 在 `app.py` 文件中添加以下程式碼以導入一些所需的庫: - - ```python - import time - from grove.grove_light_sensor_v1_2 import GroveLightSensor - ``` - - `import time` 語句導入了 `time` 模組,稍後在本作業中會使用。 - - `from grove.grove_light_sensor_v1_2 import GroveLightSensor` 語句從 Grove Python 庫中導入了 `GroveLightSensor`。此庫包含與 Grove 光線感測器交互的程式碼,並在 Pi 設置過程中已全域安裝。 - -1. 在上述程式碼之後添加以下程式碼,以建立管理光線感測器的類別實例: - - ```python - light_sensor = GroveLightSensor(0) - ``` - - `light_sensor = GroveLightSensor(0)` 這一行建立了一個 `GroveLightSensor` 類別的實例,連接到 **A0** 插針——光線感測器所連接的 Grove 類比插針。 - -1. 在上述程式碼之後添加一個無限迴圈,用於輪詢光線感測器的值並將其輸出到控制台: - - ```python - while True: - light = light_sensor.light - print('Light level:', light) - ``` - - 這將使用 `GroveLightSensor` 類別的 `light` 屬性讀取 0-1,023 範圍內的當前光線水平。此屬性從插針讀取類比值,然後將該值輸出到控制台。 - -1. 在迴圈的末尾添加一秒的短暫休眠,因為不需要連續檢查光線水平。休眠可以降低裝置的功耗。 - - ```python - time.sleep(1) - ``` - -1. 從 VS Code 的終端運行以下命令以運行你的 Python 應用程式: - - ```sh - python3 app.py - ``` - - 光線值將輸出到控制台。遮住或移開光線感測器,值將發生變化: - - ```output - pi@raspberrypi:~/nightlight $ python3 app.py - Light level: 634 - Light level: 634 - Light level: 634 - Light level: 230 - Light level: 104 - Light level: 290 - ``` - -> 💁 你可以在 [code-sensor/pi](../../../../../1-getting-started/lessons/3-sensors-and-actuators/code-sensor/pi) 資料夾中找到這段程式碼。 - -😀 為你的夜燈程式添加感測器成功了! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/virtual-device-actuator.md b/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/virtual-device-actuator.md deleted file mode 100644 index c866a6fe4..000000000 --- a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/virtual-device-actuator.md +++ /dev/null @@ -1,124 +0,0 @@ - -# 建造夜燈 - 虛擬物聯網硬件 - -在本課程的這部分,你將為虛擬物聯網設備添加一個 LED,並用它來創建一個夜燈。 - -## 虛擬硬件 - -夜燈需要一個執行器,通過 CounterFit 應用程序創建。 - -這個執行器是一個 **LED**。在實體物聯網設備中,它是一個[發光二極管](https://wikipedia.org/wiki/Light-emitting_diode),當電流通過時會發光。這是一個數字執行器,具有兩種狀態:開和關。發送值 1 會打開 LED,發送值 0 會關閉 LED。 - -夜燈邏輯的偽代碼如下: - -```output -Check the light level. -If the light is less than 300 - Turn the LED on -Otherwise - Turn the LED off -``` - -### 在 CounterFit 中添加執行器 - -要使用虛擬 LED,你需要將它添加到 CounterFit 應用程序中。 - -#### 任務 - 在 CounterFit 中添加執行器 - -將 LED 添加到 CounterFit 應用程序。 - -1. 確保 CounterFit 網頁應用程序正在運行(從本作業的上一部分開始)。如果未運行,請啟動它並重新添加光線感應器。 - -1. 創建一個 LED: - - 1. 在 *Actuator* 面板的 *Create actuator* 框中,打開 *Actuator type* 下拉框並選擇 *LED*。 - - 1. 將 *Pin* 設置為 *5*。 - - 1. 選擇 **Add** 按鈕,在 Pin 5 上創建 LED。 - - ![LED 設置](../../../../../translated_images/zh-HK/counterfit-create-led.ba9db1c9b8c622a635d6dfae5cdc4e70c2b250635bd4f0601c6cf0bd22b7ba46.png) - - LED 將被創建並顯示在執行器列表中。 - - ![已創建的 LED](../../../../../translated_images/zh-HK/counterfit-led.c0ab02de6d256ad84d9bad4d67a7faa709f0ea83e410cfe9b5561ef0cef30b1c.png) - - LED 創建後,你可以使用 *Color* 選擇器更改顏色。選擇顏色後,點擊 **Set** 按鈕即可更改顏色。 - -### 編程夜燈 - -現在可以使用 CounterFit 的光線感應器和 LED 來編程夜燈。 - -#### 任務 - 編程夜燈 - -編程夜燈。 - -1. 在 VS Code 中打開你在本作業上一部分創建的夜燈項目。如果需要,終止並重新創建終端以確保它使用虛擬環境運行。 - -1. 打開 `app.py` 文件。 - -1. 在 `app.py` 文件的頂部,其他 `import` 行之下,添加以下代碼以導入所需的庫: - - ```python - from counterfit_shims_grove.grove_led import GroveLed - ``` - - `from counterfit_shims_grove.grove_led import GroveLed` 語句從 CounterFit Grove shim Python 庫中導入 `GroveLed`。此庫包含與 CounterFit 應用程序中創建的 LED 交互的代碼。 - -1. 在 `light_sensor` 聲明之後添加以下代碼,以創建管理 LED 的類的實例: - - ```python - led = GroveLed(5) - ``` - - `led = GroveLed(5)` 行創建了一個 `GroveLed` 類的實例,連接到 Pin **5**——CounterFit Grove 的 LED 所連接的 Pin。 - -1. 在 `while` 循環內,`time.sleep` 之前添加檢查光線水平並打開或關閉 LED 的代碼: - - ```python - if light < 300: - led.on() - else: - led.off() - ``` - - 此代碼檢查 `light` 值。如果該值小於 300,則調用 `GroveLed` 類的 `on` 方法,向 LED 發送數字值 1,打開 LED。如果光線值大於或等於 300,則調用 `off` 方法,向 LED 發送數字值 0,關閉 LED。 - - > 💁 此代碼應與 `print('Light level:', light)` 行保持相同的縮進級別,以確保它在 while 循環內! - -1. 從 VS Code 終端運行以下命令以運行你的 Python 應用程序: - - ```sh - python3 app.py - ``` - - 光線值將輸出到控制台。 - - ```output - (.venv) ➜ GroveTest python3 app.py - Light level: 143 - Light level: 244 - Light level: 246 - Light level: 253 - ``` - -1. 更改 *Value* 或 *Random* 設置,使光線水平在 300 以上和以下變化。LED 將會打開和關閉。 - -![CounterFit 應用程序中的 LED 隨光線水平變化而打開和關閉](../../../../../images/virtual-device-running-assignment-1-1.gif) - -> 💁 你可以在 [code-actuator/virtual-device](../../../../../1-getting-started/lessons/3-sensors-and-actuators/code-actuator/virtual-device) 文件夾中找到此代碼。 - -😀 你的夜燈程序成功了! - ---- - -**免責聲明**: -此文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤詮釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/virtual-device-sensor.md b/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/virtual-device-sensor.md deleted file mode 100644 index efd0b2e5c..000000000 --- a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/virtual-device-sensor.md +++ /dev/null @@ -1,124 +0,0 @@ - -# 建立夜燈 - 虛擬物聯網硬件 - -在本課程的這部分,你將為虛擬物聯網設備添加一個光線感應器。 - -## 虛擬硬件 - -夜燈需要一個感應器,通過 CounterFit 應用程式創建。 - -這個感應器是一個 **光線感應器**。在實體物聯網設備中,它會是一個 [光電二極管](https://wikipedia.org/wiki/Photodiode),將光線轉換為電信號。光線感應器是一種模擬感應器,會傳送一個整數值,表示相對的光線量,但不會映射到任何標準測量單位,例如 [勒克斯](https://wikipedia.org/wiki/Lux)。 - -### 在 CounterFit 中添加感應器 - -要使用虛擬光線感應器,你需要將其添加到 CounterFit 應用程式中。 - -#### 任務 - 在 CounterFit 中添加感應器 - -將光線感應器添加到 CounterFit 應用程式中。 - -1. 確保 CounterFit 網頁應用程式正在運行,這是上一部分任務的要求。如果尚未運行,請啟動它。 - -1. 創建一個光線感應器: - - 1. 在 *Sensors* 面板的 *Create sensor* 框中,點擊 *Sensor type* 下拉框並選擇 *Light*。 - - 1. 將 *Units* 保持為 *NoUnits*。 - - 1. 確保 *Pin* 設置為 *0*。 - - 1. 點擊 **Add** 按鈕,在 Pin 0 上創建光線感應器。 - - ![光線感應器設置](../../../../../translated_images/zh-HK/counterfit-create-light-sensor.9f36a5e0d4458d8d554d54b34d2c806d56093d6e49fddcda2d20f6fef7f5cce1.png) - - 光線感應器將被創建並顯示在感應器列表中。 - - ![已創建的光線感應器](../../../../../translated_images/zh-HK/counterfit-light-sensor.5d0f5584df56b90f6b2561910d9cb20dfbd73eeff2177c238d38f4de54aefae1.png) - -## 編程光線感應器 - -現在可以編程設備以使用內建的光線感應器。 - -### 任務 - 編程光線感應器 - -編程設備。 - -1. 在 VS Code 中打開你在上一部分任務中創建的夜燈項目。如果需要,終止並重新創建終端以確保它正在使用虛擬環境。 - -1. 打開 `app.py` 文件。 - -1. 在 `app.py` 文件的頂部添加以下代碼,與其他 `import` 語句一起,用於導入所需的庫: - - ```python - import time - from counterfit_shims_grove.grove_light_sensor_v1_2 import GroveLightSensor - ``` - - `import time` 語句導入了 Python 的 `time` 模組,稍後在本任務中會使用。 - - `from counterfit_shims_grove.grove_light_sensor_v1_2 import GroveLightSensor` 語句從 CounterFit Grove shim Python 庫中導入了 `GroveLightSensor`。這個庫包含與 CounterFit 應用程式中創建的光線感應器交互的代碼。 - -1. 在文件底部添加以下代碼,用於創建管理光線感應器的類的實例: - - ```python - light_sensor = GroveLightSensor(0) - ``` - - `light_sensor = GroveLightSensor(0)` 這一行創建了一個 `GroveLightSensor` 類的實例,連接到 Pin **0**——CounterFit Grove 的光線感應器所連接的 Pin。 - -1. 在上述代碼之後添加一個無限循環,用於輪詢光線感應器的值並將其打印到控制台: - - ```python - while True: - light = light_sensor.light - print('Light level:', light) - ``` - - 這將使用 `GroveLightSensor` 類的 `light` 屬性讀取當前的光線水平。該屬性從 Pin 讀取模擬值,然後將該值打印到控制台。 - -1. 在 `while` 循環的末尾添加一秒的短暫休眠,因為光線水平不需要連續檢查。休眠可以減少設備的功耗。 - - ```python - time.sleep(1) - ``` - -1. 從 VS Code 的終端運行以下命令以運行你的 Python 應用程式: - - ```sh - python3 app.py - ``` - - 光線值將輸出到控制台。初始值將為 0。 - -1. 從 CounterFit 應用程式更改光線感應器的值,該值將被應用程式讀取。你可以通過以下兩種方式進行更改: - - * 在光線感應器的 *Value* 框中輸入一個數字,然後點擊 **Set** 按鈕。你輸入的數字將是感應器返回的值。 - - * 勾選 *Random* 選項框,並輸入 *Min* 和 *Max* 值,然後點擊 **Set** 按鈕。每次感應器讀取值時,它將讀取一個介於 *Min* 和 *Max* 之間的隨機數。 - - 你設置的值將輸出到控制台。更改 *Value* 或 *Random* 設置以使值發生變化。 - - ```output - (.venv) ➜ GroveTest python3 app.py - Light level: 143 - Light level: 244 - Light level: 246 - Light level: 253 - ``` - -> 💁 你可以在 [code-sensor/virtual-device](../../../../../1-getting-started/lessons/3-sensors-and-actuators/code-sensor/virtual-device) 文件夾中找到這段代碼。 - -😀 你的夜燈程式成功了! - ---- - -**免責聲明**: -此文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解讀概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/wio-terminal-actuator.md b/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/wio-terminal-actuator.md deleted file mode 100644 index c072efdb0..000000000 --- a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/wio-terminal-actuator.md +++ /dev/null @@ -1,124 +0,0 @@ - -# 製作夜燈 - Wio Terminal - -在這部分課程中,你將為 Wio Terminal 添加一個 LED,並用它來製作一個夜燈。 - -## 硬件 - -夜燈現在需要一個執行器。 - -這個執行器是一個 **LED**,即[發光二極管](https://wikipedia.org/wiki/Light-emitting_diode),當電流通過時會發光。這是一個數字執行器,只有兩種狀態:開和關。傳送值 1 會打開 LED,傳送值 0 會關閉它。這是一個外部的 Grove 執行器,需要連接到 Wio Terminal。 - -夜燈邏輯的偽代碼如下: - -```output -Check the light level. -If the light is less than 300 - Turn the LED on -Otherwise - Turn the LED off -``` - -### 連接 LED - -Grove LED 是一個模組,內含多種顏色的 LED,讓你可以選擇喜歡的顏色。 - -#### 任務 - 連接 LED - -連接 LED。 - -![一個 Grove LED](../../../../../translated_images/zh-HK/grove-led.6c853be93f473cf2c439cfc74bb1064732b22251a83cedf66e62f783f9cc1a79.png) - -1. 選擇你喜歡的 LED,將其引腳插入 LED 模組上的兩個孔中。 - - LED 是發光二極管,而二極管是一種只能單向導電的電子元件。這意味著 LED 必須以正確的方向連接,否則無法工作。 - - LED 的一個引腳是正極,另一個是負極。LED 並非完全圓形,其中一側稍微扁平一些。稍微扁平的一側是負極。當你將 LED 連接到模組時,確保圓形一側的引腳連接到模組外側標有 **+** 的插座,而扁平一側連接到模組中間較近的插座。 - -1. LED 模組上有一個旋鈕,可以用來調節亮度。使用小型十字螺絲刀將其逆時針旋轉到最亮的位置。 - -1. 將 Grove 電纜的一端插入 LED 模組上的插座。電纜只能以一種方向插入。 - -1. 在 Wio Terminal 與電腦或其他電源斷開連接的情況下,將 Grove 電纜的另一端連接到 Wio Terminal 上屏幕右側的 Grove 插座。這是距離電源按鈕最遠的插座。 - - > 💁 右側的 Grove 插座可用於模擬或數字傳感器和執行器。左側插座僅用於 I2C 和數字傳感器及執行器。 - -![Grove LED 連接到右側插座](../../../../../translated_images/zh-HK/wio-led.265a1897e72d7f21.webp) - -## 編程夜燈 - -現在可以使用內建的光線傳感器和 Grove LED 來編程夜燈。 - -### 任務 - 編程夜燈 - -編程夜燈。 - -1. 在 VS Code 中打開你在本次作業的上一部分中創建的夜燈項目。 - -1. 在 `setup` 函數的底部添加以下一行代碼: - - ```cpp - pinMode(D0, OUTPUT); - ``` - - 這行代碼配置了用於通過 Grove 端口與 LED 通信的引腳。 - - `D0` 引腳是右側 Grove 插座的數字引腳。這個引腳被設置為 `OUTPUT`,表示它連接到執行器,並將數據寫入該引腳。 - -1. 在 `loop` 函數中的 `delay` 之前立即添加以下代碼: - - ```cpp - if (light < 300) - { - digitalWrite(D0, HIGH); - } - else - { - digitalWrite(D0, LOW); - } - ``` - - 這段代碼檢查 `light` 值。如果該值小於 300,則向 `D0` 數字引腳發送 `HIGH` 值。`HIGH` 值為 1,會打開 LED。如果光線值大於或等於 300,則向引腳發送 `LOW` 值,`LOW` 值為 0,會關閉 LED。 - - > 💁 當向執行器發送數字值時,`LOW` 值為 0v,`HIGH` 值為設備的最大電壓。對於 Wio Terminal,`HIGH` 電壓為 3.3V。 - -1. 將 Wio Terminal 重新連接到你的電腦,並像之前一樣上傳新代碼。 - -1. 連接串行監視器。光線值將輸出到終端。 - - ```output - > Executing task: platformio device monitor < - - --- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time - --- More details at http://bit.ly/pio-monitor-filters - --- Miniterm on /dev/cu.usbmodem101 9600,8,N,1 --- - --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- - Light value: 4 - Light value: 5 - Light value: 4 - Light value: 158 - Light value: 343 - Light value: 348 - Light value: 344 - ``` - -1. 遮住和移開光線傳感器。注意當光線值為 300 或更低時,LED 會亮起;當光線值大於 300 時,LED 會熄滅。 - -![LED 連接到 Wio,隨著光線值變化亮起和熄滅](../../../../../images/wio-running-assignment-1-1.gif) - -> 💁 你可以在 [code-actuator/wio-terminal](../../../../../1-getting-started/lessons/3-sensors-and-actuators/code-actuator/wio-terminal) 文件夾中找到這段代碼。 - -😀 你的夜燈程序成功了! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/wio-terminal-sensor.md b/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/wio-terminal-sensor.md deleted file mode 100644 index ad8bfaab0..000000000 --- a/translations/hk/1-getting-started/lessons/3-sensors-and-actuators/wio-terminal-sensor.md +++ /dev/null @@ -1,87 +0,0 @@ - -# 添加感應器 - Wio Terminal - -在這部分課程中,你將使用 Wio Terminal 上的光感應器。 - -## 硬件 - -本課程使用的感應器是**光感應器**,它利用[光電二極管](https://wikipedia.org/wiki/Photodiode)將光轉換為電信號。這是一個模擬感應器,會傳送一個介於 0 到 1,023 的整數值,表示相對光量,但不對應任何標準測量單位,例如 [lux](https://wikipedia.org/wiki/Lux)。 - -光感應器內建於 Wio Terminal 中,透過背面的透明塑膠窗口可以看到。 - -![Wio Terminal 背面的光感應器](../../../../../translated_images/zh-HK/wio-light-sensor.b1f529f3c95f5165.webp) - -## 編程光感應器 - -現在可以編程設備以使用內建的光感應器。 - -### 任務 - -編程設備。 - -1. 在 VS Code 中打開你在前一部分作業中創建的夜燈項目。 - -1. 在 `setup` 函數的底部添加以下一行: - - ```cpp - pinMode(WIO_LIGHT, INPUT); - ``` - - 這一行配置了用於與感應器硬件通信的引腳。 - - `WIO_LIGHT` 引腳是連接到內建光感應器的 GPIO 引腳編號。該引腳被設置為 `INPUT`,表示它連接到感應器並將從引腳讀取數據。 - -1. 刪除 `loop` 函數的內容。 - -1. 在現在空的 `loop` 函數中添加以下代碼。 - - ```cpp - int light = analogRead(WIO_LIGHT); - Serial.print("Light value: "); - Serial.println(light); - ``` - - 此代碼從 `WIO_LIGHT` 引腳讀取模擬值。這會從內建光感應器讀取一個介於 0 到 1,023 的值。該值隨後被發送到串口,當此代碼運行時,你可以在串口監視器中讀取它。`Serial.print` 會寫入文本但不附加換行符,因此每行都會以 `Light value:` 開始,並以實際的光值結束。 - -1. 在 `loop` 的末尾添加一個一秒(1,000 毫秒)的短暫延遲,因為光線水平不需要被連續檢查。延遲可以減少設備的功耗。 - - ```cpp - delay(1000); - ``` - -1. 將 Wio Terminal 重新連接到你的電腦,並像之前一樣上傳新代碼。 - -1. 連接串口監視器。光值將輸出到終端。遮住或移開 Wio Terminal 背面的光感應器,值將會改變。 - - ```output - > Executing task: platformio device monitor < - - --- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time - --- More details at http://bit.ly/pio-monitor-filters - --- Miniterm on /dev/cu.usbmodem101 9600,8,N,1 --- - --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- - Light value: 4 - Light value: 5 - Light value: 4 - Light value: 158 - Light value: 343 - Light value: 348 - Light value: 344 - ``` - -> 💁 你可以在 [code-sensor/wio-terminal](../../../../../1-getting-started/lessons/3-sensors-and-actuators/code-sensor/wio-terminal) 文件夾中找到此代碼。 - -😀 成功將感應器添加到你的夜燈程序! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/4-connect-internet/README.md b/translations/hk/1-getting-started/lessons/4-connect-internet/README.md deleted file mode 100644 index 31a8b0cce..000000000 --- a/translations/hk/1-getting-started/lessons/4-connect-internet/README.md +++ /dev/null @@ -1,466 +0,0 @@ - -# 將你的設備連接到互聯網 - -![本課程的手繪筆記概述](../../../../../translated_images/zh-HK/lesson-4.7344e074ea68fa545fd320b12dce36d72dd62d28c3b4596cb26cf315f434b98f.jpg) - -> 手繪筆記由 [Nitya Narasimhan](https://github.com/nitya) 提供。點擊圖片查看更大的版本。 - -本課程是 [Microsoft Reactor](https://developer.microsoft.com/reactor/?WT.mc_id=academic-17441-jabenn) 的 [Hello IoT 系列](https://youtube.com/playlist?list=PLmsFUfdnGr3xRts0TIwyaHyQuHaNQcb6-) 的一部分。課程分為兩個視頻進行教學——一個1小時的課程和一個1小時的辦公時間,深入探討課程的部分內容並回答問題。 - -[![課程4:將你的設備連接到互聯網](https://img.youtube.com/vi/O4dd172mZhs/0.jpg)](https://youtu.be/O4dd172mZhs) - -[![課程4:將你的設備連接到互聯網 - 辦公時間](https://img.youtube.com/vi/j-cVCzRDE2Q/0.jpg)](https://youtu.be/j-cVCzRDE2Q) - -> 🎥 點擊上方圖片觀看視頻 - -## 課前測驗 - -[課前測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/7) - -## 簡介 - -IoT 中的 **I** 代表 **Internet**(互聯網)——雲端連接和服務使得 IoT 設備的許多功能得以實現,例如從連接到設備的傳感器收集測量數據,或發送消息控制執行器。IoT 設備通常使用標準通信協議連接到單一的雲端 IoT 服務,而該服務則與你的 IoT 應用的其他部分相連,例如 AI 服務用於基於數據做出智能決策,或網頁應用用於控制或報告。 - -> 🎓 從傳感器收集並發送到雲端的數據被稱為遙測數據。 - -IoT 設備可以接收來自雲端的消息。這些消息通常包含命令——即指示執行某些操作的指令,無論是內部操作(例如重啟或更新固件),還是使用執行器(例如打開燈光)。 - -本課程介紹了 IoT 設備用於連接雲端的一些通信協議,以及它們可能發送或接收的數據類型。你還將親自操作,為你的夜燈添加互聯網控制,將 LED 控制邏輯移至本地運行的“服務器”代碼。 - -在本課程中,我們將涵蓋: - -* [通信協議](../../../../../1-getting-started/lessons/4-connect-internet) -* [消息隊列遙測傳輸(MQTT)](../../../../../1-getting-started/lessons/4-connect-internet) -* [遙測數據](../../../../../1-getting-started/lessons/4-connect-internet) -* [命令](../../../../../1-getting-started/lessons/4-connect-internet) - -## 通信協議 - -IoT 設備用於與互聯網通信的流行通信協議有很多。其中最流行的是基於某種代理的發布/訂閱消息傳遞。IoT 設備連接到代理並發布遙測數據,同時訂閱命令。雲端服務也連接到代理,訂閱所有遙測消息並發布命令,這些命令可以針對特定設備或設備組。 - -![IoT 設備連接到代理並發布遙測數據,同時訂閱命令。雲端服務連接到代理,訂閱所有遙測數據並向特定設備發送命令。](../../../../../translated_images/zh-HK/pub-sub.7c7ed43fe9fd15d4.webp) - -MQTT 是 IoT 設備最流行的通信協議,本課程將介紹它。其他協議包括 AMQP 和 HTTP/HTTPS。 - -## 消息隊列遙測傳輸(MQTT) - -[MQTT](http://mqtt.org) 是一種輕量級的開放標準消息傳遞協議,可在設備之間傳遞消息。它於1999年設計用於監控石油管道,15年後由 IBM 釋出為開放標準。 - -MQTT 有一個單一代理和多個客戶端。所有客戶端都連接到代理,代理根據需要將消息路由到相關客戶端。消息是通過命名主題進行路由,而不是直接發送到個別客戶端。客戶端可以發布到某個主題,任何訂閱該主題的客戶端都會接收到消息。 - -![IoT 設備在 /telemetry 主題上發布遙測數據,雲端服務訂閱該主題](../../../../../translated_images/zh-HK/mqtt.cbf7f21d9adc3e17548b359444cc11bb4bf2010543e32ece9a47becf54438c23.png) - -✅ 做一些研究。如果你有大量 IoT 設備,如何確保你的 MQTT 代理能處理所有消息? - -### 將你的 IoT 設備連接到 MQTT - -為你的夜燈添加互聯網控制的第一步是將其連接到 MQTT 代理。 - -#### 任務 - -將你的設備連接到 MQTT 代理。 - -在本課程的這部分,你將把你的 IoT 夜燈連接到互聯網,以便遠程控制。稍後在本課程中,你的 IoT 設備將通過 MQTT 向公共 MQTT 代理發送一條包含光線水平的遙測消息,該消息將由你編寫的一些服務器代碼接收。該代碼將檢查光線水平並向設備發送一條命令消息,指示其打開或關閉 LED。 - -這種設置的實際應用場景可能是從多個光線傳感器收集數據後再決定是否打開燈光,例如在有大量燈光的地方,如體育場。這樣可以避免僅因一個傳感器被雲層或鳥遮擋而打開燈光,而其他傳感器檢測到足夠的光線。 - -✅ 還有哪些情況需要評估多個傳感器的數據後再發送命令? - -為了避免在本次作業中設置 MQTT 代理的複雜性,你可以使用一個運行 [Eclipse Mosquitto](https://www.mosquitto.org) 的公共測試服務器,這是一個開源的 MQTT 代理。該測試代理可在 [test.mosquitto.org](https://test.mosquitto.org) 公開使用,且不需要設置帳戶,是測試 MQTT 客戶端和服務器的絕佳工具。 - -> 💁 該測試代理是公開且不安全的。任何人都可以監聽你發布的內容,因此不應用於需要保密的數據。 - -![作業的流程圖,顯示光線水平被讀取和檢查,LED 被控制](../../../../../translated_images/zh-HK/assignment-1-internet-flow.3256feab5f052fd273bf4e331157c574c2c3fa42e479836fc9c3586f41db35a5.png) - -按照以下相關步驟將你的設備連接到 MQTT 代理: - -* [Arduino - Wio Terminal](wio-terminal-mqtt.md) -* [單板計算機 - Raspberry Pi/虛擬 IoT 設備](single-board-computer-mqtt.md) - -### 深入了解 MQTT - -主題可以有層次結構,客戶端可以使用通配符訂閱不同層次的主題。例如,你可以將溫度遙測消息發送到 `/telemetry/temperature` 主題,將濕度消息發送到 `/telemetry/humidity` 主題,然後在你的雲端應用中訂閱 `/telemetry/*` 主題以接收溫度和濕度的遙測消息。 - -消息可以設置服務質量(QoS),這決定了消息接收的保證程度。 - -* 最多一次 - 消息僅發送一次,客戶端和代理不採取額外步驟確認交付(即發送後不再關注)。 -* 至少一次 - 發送方多次重試消息直到收到確認(即確認交付)。 -* 恰好一次 - 發送方和接收方進行兩級握手以確保僅接收一份消息(即保證交付)。 - -✅ 哪些情況可能需要保證交付的消息而不是發送後不再關注的消息? - -雖然名字中有“消息隊列”(MQTT 的首字母縮寫),但它實際上並不支持消息隊列。這意味著如果客戶端斷開連接,然後重新連接,它不會接收到斷開期間發送的消息,除非這些消息已通過 QoS 過程開始處理。消息可以設置保留標誌。如果設置了該標誌,MQTT 代理將存儲最後一條帶有該標誌的消息,並將其發送給後來訂閱該主題的客戶端。這樣,客戶端將始終獲得最新消息。 - -MQTT 還支持保持連接功能,在消息之間的長時間間隔期間檢查連接是否仍然有效。 - -> 🦟 [Eclipse Foundation 的 Mosquitto](https://mosquitto.org) 提供了一個免費的 MQTT 代理,你可以自己運行以實驗 MQTT,還有一個公共 MQTT 代理可用於測試你的代碼,托管於 [test.mosquitto.org](https://test.mosquitto.org)。 - -MQTT 連接可以是公開和開放的,也可以通過使用用戶名和密碼或證書進行加密和安全保護。 - -> 💁 MQTT 通過 TCP/IP 進行通信,與 HTTP 使用相同的底層網絡協議,但使用不同的端口。你還可以通過 Websockets 使用 MQTT 與在瀏覽器中運行的網頁應用通信,或者在防火牆或其他網絡規則阻止標準 MQTT 連接的情況下使用。 - -## 遙測數據 - -遙測一詞源於希臘語,意為遠程測量。遙測是從傳感器收集數據並將其發送到雲端的行為。 - -> 💁 最早的遙測設備之一於1874年在法國發明,通過物理電線將實時天氣和雪深數據從勃朗峰傳送到巴黎。當時尚未有無線技術。 - -讓我們回顧一下課程1中的智能溫控器示例。 - -![使用多個房間傳感器的互聯網連接溫控器](../../../../../translated_images/zh-HK/telemetry.21e5d8b97649d2eb.webp) - -溫控器具有溫度傳感器以收集遙測數據。它很可能內置一個溫度傳感器,並可能通過無線協議(例如 [藍牙低功耗](https://wikipedia.org/wiki/Bluetooth_Low_Energy) (BLE))連接到多個外部溫度傳感器。 - -它可能發送的遙測數據示例如下: - -| 名稱 | 值 | 描述 | -| ---- | ----- | ----------- | -| `thermostat_temperature` | 18°C | 由溫控器內置溫度傳感器測量的溫度 | -| `livingroom_temperature` | 19°C | 由遠程溫度傳感器測量的溫度,該傳感器被命名為 `livingroom` 以標識其所在房間 | -| `bedroom_temperature` | 21°C | 由遠程溫度傳感器測量的溫度,該傳感器被命名為 `bedroom` 以標識其所在房間 | - -雲端服務可以使用這些遙測數據來決定發送哪些命令以控制供暖。 - -### 從你的 IoT 設備發送遙測數據 - -為你的夜燈添加互聯網控制的下一步是將光線水平遙測數據發送到 MQTT 代理的遙測主題。 - -#### 任務 - 從你的 IoT 設備發送遙測數據 - -將光線水平遙測數據發送到 MQTT 代理。 - -數據以 JSON 格式編碼——JSON 是 JavaScript Object Notation 的縮寫,是一種使用鍵/值對以文本形式編碼數據的標準。 - -✅ 如果你之前沒有接觸過 JSON,可以在 [JSON.org 文檔](https://www.json.org/) 中了解更多。 - -按照以下相關步驟將遙測數據從你的設備發送到 MQTT 代理: - -* [Arduino - Wio Terminal](wio-terminal-telemetry.md) -* [單板計算機 - Raspberry Pi/虛擬 IoT 設備](single-board-computer-telemetry.md) - -### 從 MQTT 代理接收遙測數據 - -如果沒有接收端,發送遙測數據就沒有意義。光線水平遙測數據需要有接收端來處理這些數據。該“服務器”代碼是你將部署到雲端服務的一部分,用於更大的 IoT 應用,但在這裡你將在本地計算機上運行該代碼(或者如果你直接在 Raspberry Pi 上編碼,也可以在 Pi 上運行)。服務器代碼由一個 Python 應用組成,該應用通過 MQTT 接收光線水平的遙測消息。稍後在本課程中,你將使其回復一條命令消息,指示 LED 打開或關閉。 - -✅ 做一些研究:如果沒有接收端,MQTT 消息會發生什麼? - -#### 安裝 Python 和 VS Code - -如果你本地沒有安裝 Python 和 VS Code,你需要安裝它們來編寫服務器代碼。如果你使用虛擬 IoT 設備,或者在 Raspberry Pi 上工作,可以跳過此步驟,因為你應該已經安裝並配置好。 - -##### 任務 - 安裝 Python 和 VS Code - -安裝 Python 和 VS Code。 - -1. 安裝 Python。請參考 [Python 下載頁面](https://www.python.org/downloads/) 了解如何安裝最新版本的 Python。 - -1. 安裝 Visual Studio Code (VS Code)。這是你將用來編寫 Python 虛擬設備代碼的編輯器。請參考 [VS Code 文檔](https://code.visualstudio.com?WT.mc_id=academic-17441-jabenn) 了解如何安裝 VS Code。 -💁 你可以自由選擇任何 Python IDE 或編輯器來進行這些課程,如果你有偏好的工具,但課程中的指示將以使用 VS Code 為基礎。 -1. 安裝 VS Code 的 Pylance 擴展。這是一個為 VS Code 提供 Python 語言支持的擴展。請參考 [Pylance 擴展文檔](https://marketplace.visualstudio.com/items?WT.mc_id=academic-17441-jabenn&itemName=ms-python.vscode-pylance) 了解如何在 VS Code 中安裝此擴展。 - -#### 配置 Python 虛擬環境 - -Python 的一個強大功能是能夠安裝 [pip 套件](https://pypi.org)——這些是由其他人編寫並發布到互聯網上的代碼包。你可以通過一條命令將 pip 套件安裝到你的電腦上,然後在代碼中使用該套件。接下來,你將使用 pip 安裝一個用於 MQTT 通信的套件。 - -默認情況下,當你安裝一個套件時,它會在你的電腦上全局可用,這可能會導致套件版本的問題——例如,一個應用程序依賴於某個版本的套件,而安裝另一個應用程序的新版本可能會導致該應用程序出現問題。為了解決這個問題,你可以使用 [Python 虛擬環境](https://docs.python.org/3/library/venv.html),它本質上是 Python 的一個副本,存放在一個專用文件夾中,當你安裝 pip 套件時,它們只會安裝到該文件夾中。 - -##### 任務 - 配置 Python 虛擬環境 - -配置 Python 虛擬環境並安裝 MQTT 的 pip 套件。 - -1. 在終端或命令行中,選擇一個位置運行以下命令以創建並進入一個新目錄: - - ```sh - mkdir nightlight-server - cd nightlight-server - ``` - -1. 現在運行以下命令,在 `.venv` 文件夾中創建一個虛擬環境: - - ```sh - python3 -m venv .venv - ``` - - > 💁 你需要明確調用 `python3` 來創建虛擬環境,以防你的系統同時安裝了 Python 2 和 Python 3(最新版本)。如果安裝了 Python 2,調用 `python` 可能會使用 Python 2 而不是 Python 3。 - -1. 激活虛擬環境: - - * 在 Windows 上: - * 如果你使用的是命令提示符或 Windows Terminal 中的命令提示符,運行: - - ```cmd - .venv\Scripts\activate.bat - ``` - - * 如果你使用的是 PowerShell,運行: - - ```powershell - .\.venv\Scripts\Activate.ps1 - ``` - - * 在 macOS 或 Linux 上,運行: - - ```cmd - source ./.venv/bin/activate - ``` - - > 💁 這些命令應該在你創建虛擬環境的同一位置運行。你永遠不需要進入 `.venv` 文件夾,應始終在創建虛擬環境的文件夾中運行激活命令以及任何安裝套件或運行代碼的命令。 - -1. 激活虛擬環境後,默認的 `python` 命令將運行用於創建虛擬環境的 Python 版本。運行以下命令以檢查版本: - - ```sh - python --version - ``` - - 輸出類似於以下內容: - - ```output - (.venv) ➜ nightlight-server python --version - Python 3.9.1 - ``` - - > 💁 你的 Python 版本可能不同——只要是 3.6 或更高版本即可。如果不是,請刪除該文件夾,安裝更新版本的 Python,然後重試。 - -1. 運行以下命令安裝 [Paho-MQTT](https://pypi.org/project/paho-mqtt/) 的 pip 套件,這是一個流行的 MQTT 庫。 - - ```sh - pip install paho-mqtt - ``` - - 此 pip 套件僅會安裝在虛擬環境中,無法在虛擬環境之外使用。 - -#### 編寫伺服器代碼 - -現在可以用 Python 編寫伺服器代碼。 - -##### 任務 - 編寫伺服器代碼 - -編寫伺服器代碼。 - -1. 在虛擬環境中,通過終端或命令行運行以下命令以創建一個名為 `app.py` 的 Python 文件: - - * 在 Windows 上運行: - - ```cmd - type nul > app.py - ``` - - * 在 macOS 或 Linux 上運行: - - ```cmd - touch app.py - ``` - -1. 在 VS Code 中打開當前文件夾: - - ```sh - code . - ``` - -1. 當 VS Code 啟動時,它會激活 Python 虛擬環境。這會顯示在底部狀態欄中: - - ![VS Code 顯示選擇的虛擬環境](../../../../../translated_images/zh-HK/vscode-virtual-env.8ba42e04c3d533cf.webp) - -1. 如果 VS Code 啟動時終端已在運行,則該終端不會激活虛擬環境。最簡單的方法是使用 **Kill the active terminal instance** 按鈕關閉終端: - - ![VS Code Kill the active terminal instance 按鈕](../../../../../translated_images/zh-HK/vscode-kill-terminal.1cc4de7c6f25ee08.webp) - -1. 通過選擇 *Terminal -> New Terminal* 或按下 `` CTRL+` `` 啟動新的 VS Code 終端。新終端將加載虛擬環境,並在終端中顯示激活命令。虛擬環境的名稱(`.venv`)也會顯示在提示符中: - - ```output - ➜ nightlight-server source .venv/bin/activate - (.venv) ➜ nightlight - ``` - -1. 從 VS Code 的文件資源管理器中打開 `app.py` 文件,並添加以下代碼: - - ```python - import json - import time - - import paho.mqtt.client as mqtt - - id = '' - - client_telemetry_topic = id + '/telemetry' - client_name = id + 'nightlight_server' - - mqtt_client = mqtt.Client(client_name) - mqtt_client.connect('test.mosquitto.org') - - mqtt_client.loop_start() - - def handle_telemetry(client, userdata, message): - payload = json.loads(message.payload.decode()) - print("Message received:", payload) - - mqtt_client.subscribe(client_telemetry_topic) - mqtt_client.on_message = handle_telemetry - - while True: - time.sleep(2) - ``` - - 將第 6 行的 `` 替換為你創建設備代碼時使用的唯一 ID。 - - ⚠️ 這 **必須** 是你在設備上使用的相同 ID,否則伺服器代碼將無法訂閱或發布到正確的主題。 - - 此代碼創建了一個具有唯一名稱的 MQTT 客戶端,並連接到 *test.mosquitto.org* broker。然後,它啟動了一個處理循環,該循環在後台線程中運行,監聽任何已訂閱主題的消息。 - - 客戶端隨後訂閱了遙測主題的消息,並定義了一個函數,當接收到消息時會調用該函數。當接收到遙測消息時,`handle_telemetry` 函數會被調用,並將接收到的消息打印到控制台。 - - 最後,一個無限循環保持應用程序運行。MQTT 客戶端在後台線程中監聽消息,並在主應用程序運行時始終保持運行。 - -1. 從 VS Code 終端運行以下命令以運行你的 Python 應用程序: - - ```sh - python app.py - ``` - - 應用程序將開始接收來自 IoT 設備的消息。 - -1. 確保你的設備正在運行並發送遙測消息。調整物理或虛擬設備檢測的光線水平。接收到的消息將打印到終端。 - - ```output - (.venv) ➜ nightlight-server python app.py - Message received: {'light': 0} - Message received: {'light': 400} - ``` - - nightlight 虛擬環境中的 app.py 文件必須在運行,才能讓 nightlight-server 虛擬環境中的 app.py 文件接收到發送的消息。 - -> 💁 你可以在 [code-server/server](../../../../../1-getting-started/lessons/4-connect-internet/code-server/server) 文件夾中找到此代碼。 - -### 遙測應該多久發送一次? - -遙測的一個重要考量是應該多久測量並發送一次數據?答案是——視情況而定。如果頻繁測量,你可以更快地響應測量的變化,但這會消耗更多的電力、更多的帶寬,生成更多的數據,並需要更多的雲資源來處理。你需要測量得足夠頻繁,但不能過於頻繁。 - -對於溫控器,每隔幾分鐘測量一次可能已經足夠,因為溫度不會頻繁變化。如果你一天只測量一次,那麼可能會在陽光明媚的白天因夜間溫度而加熱房屋;而如果你每秒測量一次,則會生成數千個不必要的重複溫度測量,這會影響用戶的網速和帶寬(對於有限帶寬計劃的用戶來說是個問題),並消耗更多電力,這對於像遠程傳感器這樣的電池供電設備可能是個問題,還會增加雲計算資源的成本。 - -如果你在監控工廠中某台機器的數據,而該機器如果故障可能會造成災難性損失和數百萬美元的收入損失,那麼可能需要每秒多次測量。浪費帶寬總比錯過表明機器需要停止並修復的遙測數據要好。 - -> 💁 在這種情況下,你可能需要考慮使用邊緣設備來先處理遙測數據,以減少對互聯網的依賴。 - -### 連接中斷 - -互聯網連接可能不穩定,斷線是常見的情況。在這種情況下,IoT 設備應該怎麼做——丟失數據,還是存儲數據直到連接恢復?答案同樣是視情況而定。 - -對於溫控器,數據可以在新的溫度測量完成後丟失。加熱系統不會在意 20 分鐘前的溫度是 20.5°C,如果現在的溫度是 19°C,那麼當前的溫度才決定加熱是否應該開啟或關閉。 - -對於機器,你可能需要保留數據,特別是如果這些數據用於趨勢分析。有些機器學習模型可以通過查看一段時間內的數據流(例如過去一小時)來檢測異常數據。這通常用於預測性維護,尋找可能即將發生故障的跡象,以便在故障發生前進行修理或更換。你可能希望機器的每一個遙測數據都被發送,以便在互聯網中斷後重新連接時能夠處理所有生成的遙測數據。 - -IoT 設備設計者還應考慮 IoT 設備在互聯網中斷或因位置導致信號丟失時是否仍能使用。一個智能溫控器應該能在無法向雲端發送遙測數據的情況下做出有限的決策來控制加熱。 - -[![這輛法拉利因為有人在地下無信號的地方嘗試升級而被鎖死](../../../../../translated_images/zh-HK/bricked-car.dc38f8efadc6c59d76211f981a521efb300939283dee468f79503aae3ec67615.png)](https://twitter.com/internetofshit/status/1315736960082808832) - -為了讓 MQTT 處理連接中斷,設備和伺服器代碼需要負責確保消息的交付(如果需要),例如要求所有發送的消息都通過回覆主題上的額外消息進行回覆,如果沒有回覆則手動排隊以便稍後重播。 - -## 命令 - -命令是由雲端發送到設備的消息,指示設備執行某些操作。大多數情況下,這涉及通過執行器輸出某些內容,但也可以是設備本身的指令,例如重啟或收集額外的遙測數據並將其作為命令的回應返回。 - -![一個連接到互聯網的溫控器接收到開啟加熱的命令](../../../../../translated_images/zh-HK/commands.d6c06bbbb3a02cce95f2831a1c331daf6dedd4e470c4aa2b0ae54f332016e504.png) - -例如,溫控器可以接收到來自雲端的命令以開啟加熱。根據所有傳感器的遙測數據,如果雲服務決定加熱應該開啟,它就會發送相關命令。 - -### 向 MQTT broker 發送命令 - -我們的互聯網控制夜燈的下一步是讓伺服器代碼根據檢測到的光線水平向 IoT 設備發送命令以控制燈光。 - -1. 在 VS Code 中打開伺服器代碼 - -1. 在 `client_telemetry_topic` 的聲明後添加以下行,定義要發送命令的主題: - - ```python - server_command_topic = id + '/commands' - ``` - -1. 在 `handle_telemetry` 函數的末尾添加以下代碼: - - ```python - command = { 'led_on' : payload['light'] < 300 } - print("Sending message:", command) - - client.publish(server_command_topic, json.dumps(command)) - ``` - - 這會向命令主題發送一個 JSON 消息,其中 `led_on` 的值根據光線是否小於 300 設置為 true 或 false。如果光線小於 300,則發送 true 指示設備開啟 LED。 - -1. 像之前一樣運行代碼 - -1. 調整物理或虛擬設備檢測的光線水平。接收到的消息和發送的命令將顯示在終端中: - - ```output - (.venv) ➜ nightlight-server python app.py - Message received: {'light': 0} - Sending message: {'led_on': True} - Message received: {'light': 400} - Sending message: {'led_on': False} - ``` - -> 💁 遙測和命令分別在單一主題上發送。這意味著來自多個設備的遙測數據將出現在同一遙測主題上,發送給多個設備的命令將出現在同一命令主題上。如果你想向特定設備發送命令,可以使用多個主題,命名為唯一的設備 ID,例如 `/commands/device1`,`/commands/device2`。這樣設備就可以只監聽針對該設備的消息。 - -> 💁 你可以在 [code-commands/server](../../../../../1-getting-started/lessons/4-connect-internet/code-commands/server) 文件夾中找到此代碼。 - -### 在 IoT 設備上處理命令 - -現在伺服器已經開始發送命令,你可以在 IoT 設備上添加代碼來處理命令並控制 LED。 - -按照以下相關步驟從 MQTT broker 監聽命令: - -* [Arduino - Wio Terminal](wio-terminal-commands.md) -* [單板電腦 - Raspberry Pi/虛擬 IoT 設備](single-board-computer-commands.md) - -一旦代碼編寫完成並運行,嘗試改變光線水平。觀察伺服器和設備的輸出,並在改變光線水平時觀察 LED 的反應。 - -### 連接中斷 - -如果雲服務需要向離線的 IoT 設備發送命令,應該怎麼做?答案仍然是視情況而定。 - -如果最新的命令覆蓋了早期的命令,那麼早期的命令可能可以忽略。如果雲服務發送了一個開啟加熱的命令,然後又發送了一個關閉加熱的命令,那麼可以忽略開啟命令並不重新發送。 - -如果命令需要按順序處理,例如移動機器人手臂向上,然後關閉抓手,那麼需要在連接恢復後按順序發送。 - -✅ 設備或伺服器代碼如何確保命令始終按順序通過 MQTT 發送並處理? - ---- - -## 🚀 挑戰 - -在過去三節課的挑戰是列出你家中、學校或工作場所中的所有 IoT 設備,並判斷它們是基於微控制器還是單板電腦,或者是兩者的混合,並思考它們使用了哪些傳感器和執行器。 -對於這些設備,想一想它們可能會發送或接收什麼信息。它們會發送什麼遙測數據?可能會接收什麼信息或指令?你認為它們的安全性如何? - -## 課後測驗 - -[課後測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/8) - -## 回顧與自學 - -在 [MQTT 的維基百科頁面](https://wikipedia.org/wiki/MQTT) 閱讀更多有關 MQTT 的內容。 - -嘗試使用 [Mosquitto](https://www.mosquitto.org) 自行運行一個 MQTT broker,並從你的 IoT 設備和伺服器代碼連接到它。 - -> 💁 提示 - Mosquitto 預設不允許匿名連接(即無需用戶名和密碼的連接),也不允許來自運行它的電腦以外的連接。 -> 你可以通過使用 [`mosquitto.conf` 配置文件](https://www.mosquitto.org/man/mosquitto-conf-5.html) 解決這個問題,內容如下: -> -> ```sh -> listener 1883 0.0.0.0 -> allow_anonymous true -> ``` - -## 作業 - -[比較和對比 MQTT 與其他通信協議](assignment.md) - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/4-connect-internet/assignment.md b/translations/hk/1-getting-started/lessons/4-connect-internet/assignment.md deleted file mode 100644 index 7c931b220..000000000 --- a/translations/hk/1-getting-started/lessons/4-connect-internet/assignment.md +++ /dev/null @@ -1,28 +0,0 @@ - -# 比較和對比 MQTT 與其他通訊協議 - -## 指引 - -本課程介紹了 MQTT 作為一種通訊協議。除此之外,還有其他協議,例如 AMQP 和 HTTP/HTTPS。 - -請研究這些協議,並將它們與 MQTT 進行比較和對比。考慮電力消耗、安全性,以及在連線中斷時的訊息持久性。 - -## 評分標準 - -| 評分標準 | 優秀 | 合格 | 需要改進 | -| -------- | ---- | ---- | -------- | -| 比較 AMQP 與 MQTT | 能夠比較和對比 AMQP 與 MQTT,並涵蓋電力消耗、安全性和訊息持久性。 | 能部分比較和對比 AMQP 與 MQTT,並涵蓋電力消耗、安全性和訊息持久性中的兩項。 | 能部分比較和對比 AMQP 與 MQTT,並涵蓋電力消耗、安全性和訊息持久性中的一項。 | -| 比較 HTTP/HTTPS 與 MQTT | 能夠比較和對比 HTTP/HTTPS 與 MQTT,並涵蓋電力消耗、安全性和訊息持久性。 | 能部分比較和對比 HTTP/HTTPS 與 MQTT,並涵蓋電力消耗、安全性和訊息持久性中的兩項。 | 能部分比較和對比 HTTP/HTTPS 與 MQTT,並涵蓋電力消耗、安全性和訊息持久性中的一項。 | - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/4-connect-internet/single-board-computer-commands.md b/translations/hk/1-getting-started/lessons/4-connect-internet/single-board-computer-commands.md deleted file mode 100644 index 13a0e16f1..000000000 --- a/translations/hk/1-getting-started/lessons/4-connect-internet/single-board-computer-commands.md +++ /dev/null @@ -1,67 +0,0 @@ - -# 通過互聯網控制你的夜燈 - 虛擬物聯網硬件與 Raspberry Pi - -在本課程的這部分,你將訂閱從 MQTT broker 發送到你的 Raspberry Pi 或虛擬物聯網設備的指令。 - -## 訂閱指令 - -下一步是訂閱從 MQTT broker 發送的指令並對其作出回應。 - -### 任務 - -訂閱指令。 - -1. 在 VS Code 中打開夜燈項目。 - -1. 如果你使用的是虛擬物聯網設備,請確保終端正在運行虛擬環境。如果你使用的是 Raspberry Pi,則不需要使用虛擬環境。 - -1. 在 `client_telemetry_topic` 定義之後添加以下代碼: - - ```python - server_command_topic = id + '/commands' - ``` - - `server_command_topic` 是設備訂閱以接收 LED 指令的 MQTT topic。 - -1. 在主循環的上方,`mqtt_client.loop_start()` 行之後添加以下代碼: - - ```python - def handle_command(client, userdata, message): - payload = json.loads(message.payload.decode()) - print("Message received:", payload) - - if payload['led_on']: - led.on() - else: - led.off() - - mqtt_client.subscribe(server_command_topic) - mqtt_client.on_message = handle_command - ``` - - 此代碼定義了一個函數 `handle_command`,該函數將消息作為 JSON 文檔讀取,並查找 `led_on` 屬性的值。如果設置為 `True`,則 LED 會被打開,否則會被關閉。 - - MQTT 客戶端訂閱服務器將發送消息的 topic,並設置 `handle_command` 函數在接收到消息時被調用。 - - > 💁 `on_message` 處理器會被所有訂閱的 topic 調用。如果你之後編寫的代碼需要監聽多個 topic,可以從傳遞給處理函數的 `message` 對象中獲取消息所屬的 topic。 - -1. 以與之前部分作業相同的方式運行代碼。如果你使用的是虛擬物聯網設備,請確保 CounterFit 應用正在運行,並且光傳感器和 LED 已在正確的引腳上創建。 - -1. 調整你的物理或虛擬設備檢測到的光線水平。接收到的消息和發送的指令將顯示在終端上。LED 也會根據光線水平開啟或關閉。 - -> 💁 你可以在 [code-commands/virtual-device](../../../../../1-getting-started/lessons/4-connect-internet/code-commands/virtual-device) 文件夾或 [code-commands/pi](../../../../../1-getting-started/lessons/4-connect-internet/code-commands/pi) 文件夾中找到此代碼。 - -😀 你已成功編寫代碼,使你的設備能夠響應來自 MQTT broker 的指令。 - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/4-connect-internet/single-board-computer-mqtt.md b/translations/hk/1-getting-started/lessons/4-connect-internet/single-board-computer-mqtt.md deleted file mode 100644 index 6e5d4e1c9..000000000 --- a/translations/hk/1-getting-started/lessons/4-connect-internet/single-board-computer-mqtt.md +++ /dev/null @@ -1,93 +0,0 @@ - -# 通過互聯網控制你的夜燈 - 虛擬物聯網硬件與樹莓派 - -物聯網設備需要編寫程式碼,通過 MQTT 與 *test.mosquitto.org* 通信,發送包含光感應器讀數的遙測值,並接收控制 LED 的指令。 - -在本課程的這部分,你將把你的樹莓派或虛擬物聯網設備連接到 MQTT broker。 - -## 安裝 MQTT 客戶端套件 - -為了與 MQTT broker 通信,你需要在樹莓派或虛擬設備的虛擬環境中安裝 MQTT 的 pip 套件。 - -### 任務 - -安裝 pip 套件 - -1. 在 VS Code 中打開夜燈項目。 - -1. 如果你使用的是虛擬物聯網設備,請確保終端正在運行虛擬環境。如果你使用的是樹莓派,則不需要使用虛擬環境。 - -1. 運行以下命令來安裝 MQTT 的 pip 套件: - - ```sh - pip3 install paho-mqtt - ``` - -## 編寫設備程式碼 - -設備已準備好編寫程式碼。 - -### 任務 - -編寫設備程式碼。 - -1. 在 `app.py` 文件的頂部添加以下導入: - - ```python - import paho.mqtt.client as mqtt - ``` - - `paho.mqtt.client` 庫允許你的應用通過 MQTT 進行通信。 - -1. 在光感應器和 LED 的定義之後添加以下程式碼: - - ```python - id = '' - - client_name = id + 'nightlight_client' - ``` - - 將 `` 替換為一個唯一的 ID,該 ID 將用作此設備客戶端的名稱,並在稍後用於此設備發布和訂閱的主題名稱。*test.mosquitto.org* broker 是公共的,許多人都在使用,包括其他完成此作業的學生。擁有唯一的 MQTT 客戶端名稱和主題名稱可以確保你的程式碼不會與其他人的程式碼衝突。在稍後創建伺服器程式碼時,你也需要使用此 ID。 - - > 💁 你可以使用像 [GUIDGen](https://www.guidgen.com) 這樣的網站來生成唯一的 ID。 - - `client_name` 是此 MQTT 客戶端在 broker 上的唯一名稱。 - -1. 在這段新程式碼的下方添加以下程式碼,以創建 MQTT 客戶端對象並連接到 MQTT broker: - - ```python - mqtt_client = mqtt.Client(client_name) - mqtt_client.connect('test.mosquitto.org') - - mqtt_client.loop_start() - - print("MQTT connected!") - ``` - - 此程式碼創建客戶端對象,連接到公共 MQTT broker,並啟動一個處理循環,該循環在後台執行緒中運行,監聽任何訂閱主題的消息。 - -1. 以與作業上一部分相同的方式運行程式碼。如果你使用的是虛擬物聯網設備,請確保 CounterFit 應用正在運行,並且光感應器和 LED 已在正確的引腳上創建。 - - ```output - (.venv) ➜ nightlight python app.py - MQTT connected! - Light level: 0 - Light level: 0 - ``` - -> 💁 你可以在 [code-mqtt/virtual-device](../../../../../1-getting-started/lessons/4-connect-internet/code-mqtt/virtual-device) 文件夾或 [code-mqtt/pi](../../../../../1-getting-started/lessons/4-connect-internet/code-mqtt/pi) 文件夾中找到此程式碼。 - -😀 你已成功將設備連接到 MQTT broker。 - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要信息,建議使用專業的人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/4-connect-internet/single-board-computer-telemetry.md b/translations/hk/1-getting-started/lessons/4-connect-internet/single-board-computer-telemetry.md deleted file mode 100644 index 6838febd9..000000000 --- a/translations/hk/1-getting-started/lessons/4-connect-internet/single-board-computer-telemetry.md +++ /dev/null @@ -1,74 +0,0 @@ - -# 通過互聯網控制你的夜燈 - 虛擬物聯網硬件和樹莓派 - -在本課程的這部分,你將從樹莓派或虛擬物聯網設備向 MQTT broker 發送光照水平的遙測數據。 - -## 發佈遙測數據 - -下一步是創建一個包含遙測數據的 JSON 文檔並將其發送到 MQTT broker。 - -### 任務 - -向 MQTT broker 發佈遙測數據。 - -1. 在 VS Code 中打開夜燈項目。 - -1. 如果你使用的是虛擬物聯網設備,請確保終端正在運行虛擬環境。如果你使用的是樹莓派,則不需要使用虛擬環境。 - -1. 在 `app.py` 文件的頂部添加以下導入: - - ```python - import json - ``` - - `json` 庫用於將遙測數據編碼為 JSON 文檔。 - -1. 在 `client_name` 聲明之後添加以下內容: - - ```python - client_telemetry_topic = id + '/telemetry' - ``` - - `client_telemetry_topic` 是設備將光照水平發佈到的 MQTT 主題。 - -1. 用以下內容替換文件末尾的 `while True:` 循環: - - ```python - while True: - light = light_sensor.light - telemetry = json.dumps({'light' : light}) - - print("Sending telemetry ", telemetry) - - mqtt_client.publish(client_telemetry_topic, telemetry) - - time.sleep(5) - ``` - - 此代碼將光照水平打包成 JSON 文檔並發佈到 MQTT broker。然後它會休眠以減少消息發送的頻率。 - -1. 以與完成上一部分作業相同的方式運行代碼。如果你使用的是虛擬物聯網設備,請確保 CounterFit 應用正在運行,並且光傳感器和 LED 已在正確的引腳上創建。 - - ```output - (.venv) ➜ nightlight python app.py - MQTT connected! - Sending telemetry {"light": 0} - Sending telemetry {"light": 0} - ``` - -> 💁 你可以在 [code-telemetry/virtual-device](../../../../../1-getting-started/lessons/4-connect-internet/code-telemetry/virtual-device) 文件夾或 [code-telemetry/pi](../../../../../1-getting-started/lessons/4-connect-internet/code-telemetry/pi) 文件夾中找到此代碼。 - -😀 你已成功從設備發送遙測數據。 - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業的人類翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/4-connect-internet/wio-terminal-commands.md b/translations/hk/1-getting-started/lessons/4-connect-internet/wio-terminal-commands.md deleted file mode 100644 index 07e6bd59c..000000000 --- a/translations/hk/1-getting-started/lessons/4-connect-internet/wio-terminal-commands.md +++ /dev/null @@ -1,93 +0,0 @@ - -# 通過互聯網控制你的夜燈 - Wio Terminal - -在本課程的這部分,你將訂閱來自 MQTT broker 發送到 Wio Terminal 的指令。 - -## 訂閱指令 - -下一步是訂閱來自 MQTT broker 的指令,並對其作出回應。 - -### 任務 - -訂閱指令。 - -1. 在 VS Code 中打開夜燈項目。 - -1. 在 `config.h` 文件的底部添加以下代碼,以定義指令的主題名稱: - - ```cpp - const string SERVER_COMMAND_TOPIC = ID + "/commands"; - ``` - - `SERVER_COMMAND_TOPIC` 是設備訂閱以接收 LED 指令的主題。 - -1. 在 `reconnectMQTTClient` 函數的末尾添加以下行,以便在 MQTT 客戶端重新連接時訂閱指令主題: - - ```cpp - client.subscribe(SERVER_COMMAND_TOPIC.c_str()); - ``` - -1. 在 `reconnectMQTTClient` 函數下方添加以下代碼。 - - ```cpp - void clientCallback(char *topic, uint8_t *payload, unsigned int length) - { - char buff[length + 1]; - for (int i = 0; i < length; i++) - { - buff[i] = (char)payload[i]; - } - buff[length] = '\0'; - - Serial.print("Message received:"); - Serial.println(buff); - - DynamicJsonDocument doc(1024); - deserializeJson(doc, buff); - JsonObject obj = doc.as(); - - bool led_on = obj["led_on"]; - - if (led_on) - digitalWrite(D0, HIGH); - else - digitalWrite(D0, LOW); - } - ``` - - 此函數將作為 MQTT 客戶端在接收到來自服務器的消息時調用的回調函數。 - - 消息以無符號 8 位整數數組的形式接收,因此需要轉換為字符數組才能作為文本處理。 - - 消息包含一個 JSON 文檔,並使用 ArduinoJson 庫進行解碼。JSON 文檔的 `led_on` 屬性被讀取,根據其值來控制 LED 的開啟或關閉。 - -1. 在 `createMQTTClient` 函數中添加以下代碼: - - ```cpp - client.setCallback(clientCallback); - ``` - - 此代碼將 `clientCallback` 設置為在接收到來自 MQTT broker 的消息時調用的回調函數。 - - > 💁 `clientCallback` 處理器會針對所有訂閱的主題被調用。如果你之後編寫的代碼需要監聽多個主題,可以通過回調函數中傳遞的 `topic` 參數獲取消息所屬的主題。 - -1. 將代碼上傳到你的 Wio Terminal,並使用串行監視器查看發送到 MQTT broker 的光線水平。 - -1. 調整你的物理或虛擬設備檢測到的光線水平。你將看到消息被接收以及指令在終端中被發送。根據光線水平,你還會看到 LED 被開啟或關閉。 - -> 💁 你可以在 [code-commands/wio-terminal](../../../../../1-getting-started/lessons/4-connect-internet/code-commands/wio-terminal) 文件夾中找到這段代碼。 - -😀 恭喜!你已成功編寫代碼,使你的設備能夠響應來自 MQTT broker 的指令。 - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/4-connect-internet/wio-terminal-mqtt.md b/translations/hk/1-getting-started/lessons/4-connect-internet/wio-terminal-mqtt.md deleted file mode 100644 index 8a823b21e..000000000 --- a/translations/hk/1-getting-started/lessons/4-connect-internet/wio-terminal-mqtt.md +++ /dev/null @@ -1,251 +0,0 @@ - -# 使用互聯網控制你的夜燈 - Wio Terminal - -物聯網設備需要編寫程式碼,通過 MQTT 與 *test.mosquitto.org* 通信,發送光感應器讀取的遙測值,並接收控制 LED 的指令。 - -在本課程的這部分,你將把 Wio Terminal 連接到 MQTT broker。 - -## 安裝 WiFi 和 MQTT Arduino 庫 - -為了與 MQTT broker 通信,你需要安裝一些 Arduino 庫來使用 Wio Terminal 的 WiFi 芯片,並與 MQTT 通信。在開發 Arduino 設備時,你可以使用大量的開源庫,這些庫實現了多種功能。Seeed 提供了 Wio Terminal 的 WiFi 通信庫。其他開發者也發布了與 MQTT broker 通信的庫,你將使用這些庫來操作你的設備。 - -這些庫以源代碼的形式提供,可以自動導入到 PlatformIO 並編譯到你的設備中。這樣,Arduino 庫可以在任何支持 Arduino 框架的設備上運行,前提是設備具有該庫所需的特定硬件。一些庫,例如 Seeed WiFi 庫,是針對特定硬件的。 - -庫可以全局安裝並在需要時編譯,也可以安裝到特定項目中。對於這次任務,庫將安裝到項目中。 - -✅ 你可以在 [PlatformIO 庫文檔](https://docs.platformio.org/en/latest/librarymanager/index.html) 中了解更多關於庫管理以及如何查找和安裝庫的信息。 - -### 任務 - 安裝 WiFi 和 MQTT Arduino 庫 - -安裝 Arduino 庫。 - -1. 在 VS Code 中打開夜燈項目。 - -1. 在 `platformio.ini` 文件的末尾添加以下內容: - - ```ini - lib_deps = - seeed-studio/Seeed Arduino rpcWiFi @ 1.0.5 - seeed-studio/Seeed Arduino FS @ 2.1.1 - seeed-studio/Seeed Arduino SFUD @ 2.0.2 - seeed-studio/Seeed Arduino rpcUnified @ 2.1.3 - seeed-studio/Seeed_Arduino_mbedtls @ 3.0.1 - ``` - - 這將導入 Seeed WiFi 庫。`@ ` 語法指的是該庫的特定版本號。 - - > 💁 你可以刪除 `@ ` 以始終使用最新版本的庫,但不能保證最新版本的庫能與下面的代碼正常運行。這裡的代碼已經使用該版本的庫進行測試。 - - 這就是添加庫所需的全部操作。下次 PlatformIO 編譯項目時,它將下載這些庫的源代碼並編譯到你的項目中。 - -1. 在 `lib_deps` 中添加以下內容: - - ```ini - knolleary/PubSubClient @ 2.8 - ``` - - 這將導入 [PubSubClient](https://github.com/knolleary/pubsubclient),一個 Arduino MQTT 客戶端。 - -## 連接到 WiFi - -現在可以將 Wio Terminal 連接到 WiFi。 - -### 任務 - 連接到 WiFi - -將 Wio Terminal 連接到 WiFi。 - -1. 在 `src` 文件夾中創建一個名為 `config.h` 的新文件。你可以通過選擇 `src` 文件夾或其中的 `main.cpp` 文件,然後在資源管理器中選擇 **新文件** 按鈕來完成。當你的光標位於資源管理器上時,該按鈕才會出現。 - - ![新文件按鈕](../../../../../translated_images/zh-HK/vscode-new-file-button.182702340fe6723c.webp) - -1. 在該文件中添加以下代碼以定義 WiFi 憑據的常量: - - ```cpp - #pragma once - - #include - - using namespace std; - - // WiFi credentials - const char *SSID = ""; - const char *PASSWORD = ""; - ``` - - 將 `` 替換為你的 WiFi SSID。將 `` 替換為你的 WiFi 密碼。 - -1. 打開 `main.cpp` 文件。 - -1. 在文件頂部添加以下 `#include` 指令: - - ```cpp - #include - #include - #include - - #include "config.h" - ``` - - 這將包含你之前添加的庫的頭文件以及配置頭文件。這些頭文件是必需的,用於告訴 PlatformIO 導入庫中的代碼。如果不顯式包含這些頭文件,某些代碼將不會被編譯,並且你會遇到編譯器錯誤。 - -1. 在 `setup` 函數上方添加以下代碼: - - ```cpp - void connectWiFi() - { - while (WiFi.status() != WL_CONNECTED) - { - Serial.println("Connecting to WiFi.."); - WiFi.begin(SSID, PASSWORD); - delay(500); - } - - Serial.println("Connected!"); - } - ``` - - 此代碼在設備未連接到 WiFi 時進行循環,並使用配置頭文件中的 SSID 和密碼嘗試連接。 - -1. 在 `setup` 函數底部,配置引腳後,調用此函數。 - - ```cpp - connectWiFi(); - ``` - -1. 將此代碼上傳到你的設備以檢查 WiFi 連接是否正常。你應該能在串行監視器中看到以下內容。 - - ```output - > Executing task: platformio device monitor < - - --- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time - --- More details at http://bit.ly/pio-monitor-filters - --- Miniterm on /dev/cu.usbmodem1101 9600,8,N,1 --- - --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- - Connecting to WiFi.. - Connected! - ``` - -## 連接到 MQTT - -Wio Terminal 連接到 WiFi 後,可以連接到 MQTT broker。 - -### 任務 - 連接到 MQTT - -連接到 MQTT broker。 - -1. 在 `config.h` 文件底部添加以下代碼以定義 MQTT broker 的連接詳細信息: - - ```cpp - // MQTT settings - const string ID = ""; - - const string BROKER = "test.mosquitto.org"; - const string CLIENT_NAME = ID + "nightlight_client"; - ``` - - 將 `` 替換為一個唯一的 ID,該 ID 將用作此設備客戶端的名稱,並在稍後用於此設備發布和訂閱的主題。*test.mosquitto.org* broker 是公共的,許多人都在使用,包括其他完成此任務的學生。擁有唯一的 MQTT 客戶端名稱和主題名稱可以確保你的代碼不會與其他人的代碼衝突。稍後在創建服務器代碼時,你也需要此 ID。 - - > 💁 你可以使用像 [GUIDGen](https://www.guidgen.com) 這樣的網站來生成唯一的 ID。 - - `BROKER` 是 MQTT broker 的 URL。 - - `CLIENT_NAME` 是此 MQTT 客戶端在 broker 上的唯一名稱。 - -1. 打開 `main.cpp` 文件,並在 `connectWiFi` 函數下方和 `setup` 函數上方添加以下代碼: - - ```cpp - WiFiClient wioClient; - PubSubClient client(wioClient); - ``` - - 此代碼使用 Wio Terminal WiFi 庫創建一個 WiFi 客戶端,並使用它創建一個 MQTT 客戶端。 - -1. 在此代碼下方添加以下內容: - - ```cpp - void reconnectMQTTClient() - { - while (!client.connected()) - { - Serial.print("Attempting MQTT connection..."); - - if (client.connect(CLIENT_NAME.c_str())) - { - Serial.println("connected"); - } - else - { - Serial.print("Retying in 5 seconds - failed, rc="); - Serial.println(client.state()); - - delay(5000); - } - } - } - ``` - - 此函數測試與 MQTT broker 的連接,如果未連接則重新連接。它會在未連接時進行循環,並使用配置頭文件中定義的唯一客戶端名稱嘗試連接。 - - 如果連接失敗,5 秒後重試。 - -1. 在 `reconnectMQTTClient` 函數下方添加以下代碼: - - ```cpp - void createMQTTClient() - { - client.setServer(BROKER.c_str(), 1883); - reconnectMQTTClient(); - } - ``` - - 此代碼為客戶端設置 MQTT broker,並設置接收到消息時的回調。然後嘗試連接到 broker。 - -1. 在 WiFi 連接後,在 `setup` 函數中調用 `createMQTTClient` 函數。 - -1. 用以下代碼替換整個 `loop` 函數: - - ```cpp - void loop() - { - reconnectMQTTClient(); - client.loop(); - - delay(2000); - } - ``` - - 此代碼首先重新連接到 MQTT broker。這些連接很容易中斷,因此定期檢查並在必要時重新連接是值得的。然後調用 MQTT 客戶端的 `loop` 方法來處理任何在訂閱的主題上接收到的消息。此應用程序是單線程的,因此消息不能在後台線程上接收,因此需要在主線程上分配時間來處理網絡連接上等待的消息。 - - 最後,2 秒的延遲確保光線水平不會發送得太頻繁,並減少設備的功耗。 - -1. 將代碼上傳到你的 Wio Terminal,並使用串行監視器查看設備連接到 WiFi 和 MQTT。 - - ```output - > Executing task: platformio device monitor < - - source /Users/jimbennett/GitHub/IoT-For-Beginners/1-getting-started/lessons/4-connect-internet/code-mqtt/wio-terminal/nightlight/.venv/bin/activate - --- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time - --- More details at http://bit.ly/pio-monitor-filters - --- Miniterm on /dev/cu.usbmodem1201 9600,8,N,1 --- - --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- - Connecting to WiFi.. - Connected! - Attempting MQTT connection...connected - ``` - -> 💁 你可以在 [code-mqtt/wio-terminal](../../../../../1-getting-started/lessons/4-connect-internet/code-mqtt/wio-terminal) 文件夾中找到此代碼。 - -😀 你已成功將設備連接到 MQTT broker。 - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/1-getting-started/lessons/4-connect-internet/wio-terminal-telemetry.md b/translations/hk/1-getting-started/lessons/4-connect-internet/wio-terminal-telemetry.md deleted file mode 100644 index dcaf8a9e8..000000000 --- a/translations/hk/1-getting-started/lessons/4-connect-internet/wio-terminal-telemetry.md +++ /dev/null @@ -1,93 +0,0 @@ - -# 通過互聯網控制你的夜燈 - Wio Terminal - -在這部分課程中,你將把 Wio Terminal 的光線水平數據作為遙測信息發送到 MQTT broker。 - -## 安裝 JSON Arduino 庫 - -使用 JSON 是通過 MQTT 發送消息的一種流行方式。有一個 Arduino 的 JSON 庫,可以讓讀取和寫入 JSON 文檔變得更簡單。 - -### 任務 - -安裝 Arduino JSON 庫。 - -1. 在 VS Code 中打開夜燈項目。 - -1. 在 `platformio.ini` 文件的 `lib_deps` 列表中添加以下額外的一行: - - ```ini - bblanchon/ArduinoJson @ 6.17.3 - ``` - - 這將導入 [ArduinoJson](https://arduinojson.org),一個 Arduino 的 JSON 庫。 - -## 發佈遙測信息 - -下一步是創建一個包含遙測信息的 JSON 文檔,並將其發送到 MQTT broker。 - -### 任務 - 發佈遙測信息 - -將遙測信息發佈到 MQTT broker。 - -1. 在 `config.h` 文件底部添加以下代碼,以定義 MQTT broker 的遙測主題名稱: - - ```cpp - const string CLIENT_TELEMETRY_TOPIC = ID + "/telemetry"; - ``` - - `CLIENT_TELEMETRY_TOPIC` 是設備將用來發佈光線水平的主題。 - -1. 打開 `main.cpp` 文件 - -1. 在文件頂部添加以下 `#include` 指令: - - ```cpp - #include - ``` - -1. 在 `loop` 函數內,`delay` 之前添加以下代碼: - - ```cpp - int light = analogRead(WIO_LIGHT); - - DynamicJsonDocument doc(1024); - doc["light"] = light; - - string telemetry; - serializeJson(doc, telemetry); - - Serial.print("Sending telemetry "); - Serial.println(telemetry.c_str()); - - client.publish(CLIENT_TELEMETRY_TOPIC.c_str(), telemetry.c_str()); - ``` - - 此代碼會讀取光線水平,並使用 ArduinoJson 創建一個包含該水平的 JSON 文檔。然後將其序列化為字符串,並通過 MQTT 客戶端發佈到遙測 MQTT 主題。 - -1. 將代碼上傳到你的 Wio Terminal,並使用串口監視器查看光線水平被發送到 MQTT broker 的情況。 - - ```output - Connecting to WiFi.. - Connected! - Attempting MQTT connection...connected - Sending telemetry {"light":652} - Sending telemetry {"light":612} - Sending telemetry {"light":583} - ``` - -> 💁 你可以在 [code-telemetry/wio-terminal](../../../../../1-getting-started/lessons/4-connect-internet/code-telemetry/wio-terminal) 文件夾中找到這段代碼。 - -😀 你已成功從設備發送遙測信息。 - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/README.md b/translations/hk/2-farm/README.md deleted file mode 100644 index da15a293c..000000000 --- a/translations/hk/2-farm/README.md +++ /dev/null @@ -1,34 +0,0 @@ - -# 使用物聯網進行農業 - -隨著人口增長,農業的需求也在增加。可用的土地面積不會改變,但氣候卻在變化——這給農民帶來了更多挑戰,尤其是那20億依靠自己種植的作物來維持生計的[自給農民](https://wikipedia.org/wiki/Subsistence_agriculture)。物聯網可以幫助農民更聰明地決定種植什麼以及何時收割,提升產量,減少手工勞動,並檢測和處理害蟲問題。 - -在這6節課中,你將學習如何應用物聯網來改善和自動化農業。 - -> 💁 這些課程將使用一些雲端資源。如果你未完成此專案中的所有課程,請確保[清理你的專案](../clean-up.md)。 - -## 主題 - -1. [使用物聯網預測植物生長](lessons/1-predict-plant-growth/README.md) -1. [檢測土壤濕度](lessons/2-detect-soil-moisture/README.md) -1. [自動化植物灌溉](lessons/3-automated-plant-watering/README.md) -1. [將你的植物遷移到雲端](lessons/4-migrate-your-plant-to-the-cloud/README.md) -1. [將你的應用邏輯遷移到雲端](lessons/5-migrate-application-to-the-cloud/README.md) -1. [保持你的植物安全](lessons/6-keep-your-plant-secure/README.md) - -## 致謝 - -所有課程由 [Jim Bennett](https://GitHub.com/JimBobBennett) 用 ♥️ 編寫 - ---- - -**免責聲明**: -此文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解讀概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/1-predict-plant-growth/README.md b/translations/hk/2-farm/lessons/1-predict-plant-growth/README.md deleted file mode 100644 index fb0888ce8..000000000 --- a/translations/hk/2-farm/lessons/1-predict-plant-growth/README.md +++ /dev/null @@ -1,284 +0,0 @@ - -# 使用物聯網預測植物生長 - -![本課程概述的手繪筆記](../../../../../translated_images/zh-HK/lesson-5.42b234299279d263143148b88ab4583861a32ddb03110c6c1120e41bb88b2592.jpg) - -> 手繪筆記由 [Nitya Narasimhan](https://github.com/nitya) 提供。點擊圖片查看更大版本。 - -## 課前測驗 - -[課前測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/9) - -## 簡介 - -植物需要某些條件才能生長——水、二氧化碳、養分、光和熱。在本課程中,您將學習如何通過測量空氣溫度來計算植物的生長和成熟速度。 - -本課程將涵蓋以下內容: - -* [數字農業](../../../../../2-farm/lessons/1-predict-plant-growth) -* [溫度在農業中的重要性](../../../../../2-farm/lessons/1-predict-plant-growth) -* [測量環境溫度](../../../../../2-farm/lessons/1-predict-plant-growth) -* [生長度日 (GDD)](../../../../../2-farm/lessons/1-predict-plant-growth) -* [使用溫度傳感器數據計算 GDD](../../../../../2-farm/lessons/1-predict-plant-growth) - -## 數字農業 - -數字農業正在改變我們的耕作方式,通過工具收集、存儲和分析農業數據。我們目前正處於世界經濟論壇所描述的“第四次工業革命”時期,而數字農業的興起被稱為“第四次農業革命”或“農業 4.0”。 - -> 🎓 “數字農業”這個術語還包括整個“農業價值鏈”,即從農場到餐桌的整個過程。它包括在食品運輸和加工過程中追蹤產品質量、倉庫和電子商務系統,甚至拖拉機租賃應用程序! - -這些變化使農民能夠提高產量,減少化肥和農藥的使用,並更有效地使用水資源。雖然主要在富裕國家使用,但傳感器和其他設備的價格正在逐漸下降,使其在發展中國家也越來越容易獲得。 - -數字農業支持的一些技術包括: - -* **溫度測量** - 測量溫度使農民能夠預測植物的生長和成熟。 -* **自動灌溉** - 測量土壤濕度並在土壤過於乾燥時啟動灌溉系統,而不是定時灌溉。定時灌溉可能導致在炎熱乾燥的時期作物缺水,或在下雨時過度灌溉。通過僅在土壤需要時灌溉,農民可以優化水的使用。 -* **害蟲控制** - 農民可以使用自動機器人或無人機上的攝像頭檢查害蟲,然後僅在需要的地方施用農藥,減少農藥的使用量並減少農藥流入當地水源。 - -✅ 做一些研究。還有哪些技術可以用來提高農業產量? - -> 🎓 “精準農業”這個術語用於描述基於每塊田地甚至田地的一部分進行觀察、測量和響應的方式。這包括測量水分、養分和害蟲水平並準確響應,例如僅灌溉田地的一小部分。 - -## 溫度在農業中的重要性 - -在學習植物時,大多數學生都會學到水、光、二氧化碳和養分的重要性。植物還需要溫暖才能生長——這就是為什麼植物在春天隨著溫度升高而開花,為什麼雪花蓮或水仙花可能因短暫的暖和天氣而提前萌芽,以及為什麼溫室和暖房能促進植物生長。 - -> 🎓 暖房和溫室的功能相似,但有一個重要區別。暖房是人工加熱的,能讓農民更精確地控制溫度;溫室則依賴陽光提供溫暖,通常只能通過窗戶或其他開口來排熱。 - -植物有基礎溫度、最佳溫度和最高溫度,這些都基於每日平均溫度。 - -* **基礎溫度** - 這是植物生長所需的最低每日平均溫度。 -* **最佳溫度** - 這是植物生長最快的最佳每日平均溫度。 -* **最高溫度** - 這是植物能承受的最高溫度。超過這個溫度,植物會停止生長以保存水分並保持存活。 - -> 💁 這些是平均溫度,基於白天和夜晚的溫度平均值。植物還需要白天和夜晚的不同溫度,以更有效地進行光合作用並在夜晚節省能量。 - -每種植物的基礎溫度、最佳溫度和最高溫度都不同。這就是為什麼有些植物在熱帶國家生長良好,而有些植物則適合寒冷的國家。 - -✅ 做一些研究。看看您花園、學校或當地公園中的植物,是否能找到它們的基礎溫度。 - -![一個顯示植物生長率隨溫度升高而增加,然後在溫度過高時下降的圖表](../../../../../translated_images/zh-HK/plant-growth-temp-graph.c6d69c9478e6ca83.webp) - -上圖顯示了一個生長率與溫度的示例圖表。在基礎溫度以下,植物不會生長。生長率在最佳溫度之前逐漸增加,然後在達到峰值後下降。在最高溫度時,生長停止。 - -這個圖表的形狀因植物種類而異。有些植物在最佳溫度以上的下降更陡峭,有些則從基礎溫度到最佳溫度的增長更緩慢。 - -> 💁 農民要想獲得最佳生長效果,需要了解植物的三個溫度值以及它們的圖表形狀。 - -如果農民能控制溫度,例如在商業暖房中,他們就能為植物進行最佳化。例如,商業暖房中種植番茄的溫度通常白天設置為約 25°C,夜間設置為約 20°C,以促進最快的生長。 - -> 🍅 結合這些溫度、人工光源、肥料和控制的二氧化碳水平,商業種植者可以全年種植和收穫。 - -## 測量環境溫度 - -溫度傳感器可以與物聯網設備一起使用來測量環境溫度。 - -### 任務 - 測量溫度 - -按照相關指南使用您的物聯網設備監測溫度: - -* [Arduino - Wio Terminal](wio-terminal-temp.md) -* [單板計算機 - Raspberry Pi](pi-temp.md) -* [單板計算機 - 虛擬設備](virtual-device-temp.md) - -## 生長度日 (GDD) - -生長度日(也稱為生長度單位)是一種基於溫度測量植物生長的方法。假設植物有足夠的水分、養分和二氧化碳,溫度決定了生長速度。 - -生長度日(GDD)每天計算,作為一天的平均溫度(以攝氏度計算)超過植物基礎溫度的部分。每種植物需要一定數量的 GDD 才能生長、開花或成熟並產出作物。每天的 GDD 越多,植物生長越快。 - -> 🇺🇸 對於美國人來說,生長度日也可以用華氏度計算。5 GDD(以攝氏度計算)相當於 9 GDD(以華氏度計算)。 - -完整的 GDD 計算公式有點複雜,但通常使用簡化公式作為良好的近似值: - -![GDD = T max + T min 除以 2,然後減去 T base](../../../../../translated_images/zh-HK/gdd-calculation.79b3660f9c5757aa92dc2dd2cdde75344e2d2c1565c4b3151640f7887edc0275.png) - -* **GDD** - 這是生長度日的數量 -* **T max** - 這是每日最高溫度(攝氏度) -* **T min** - 這是每日最低溫度(攝氏度) -* **T base** - 這是植物的基礎溫度(攝氏度) - -> 💁 有些變體處理 T max 超過 30°C 或 T min 低於 T base 的情況,但我們暫時忽略這些。 - -### 示例 - 玉米 🌽 - -根據品種不同,玉米(或稱為玉蜀黍)需要 800 到 2,700 GDD 才能成熟,其基礎溫度為 10°C。 - -在基礎溫度以上的第一天,測得以下溫度: - -| 測量項目 | 溫度 °C | -| :------- | :-----: | -| 最高溫度 | 16 | -| 最低溫度 | 12 | - -將這些數值代入公式: - -* T max = 16 -* T min = 12 -* T base = 10 - -計算結果為: - -![GDD = 16 + 12 除以 2,然後減去 10,結果為 4](../../../../../translated_images/zh-HK/gdd-calculation-corn.64a58b7a7afcd0dfd46ff733996d939f17f4f3feac9f0d1c632be3523e51ebd9.png) - -玉米在那一天獲得了 4 GDD。假設一種需要 800 GDD 才能成熟的玉米品種,它還需要 796 GDD 才能達到成熟。 - -✅ 做一些研究。看看您花園、學校或當地公園中的植物,是否能找到它們成熟或產出作物所需的 GDD 數量。 - -## 使用溫度傳感器數據計算 GDD - -植物的生長並不是固定日期的——例如,您不能種下一顆種子並確定植物會在 100 天後精確地結果。相反,農民可以大致了解植物的生長時間,然後每天檢查作物是否準備好。 - -這對於大型農場來說會帶來巨大的勞動力影響,並且農民可能會錯過意外提前成熟的作物。通過測量溫度,農民可以計算植物獲得的 GDD,從而只需在接近預期成熟時檢查。 - -通過使用物聯網設備收集溫度數據,農民可以在植物接近成熟時自動收到通知。一個典型的架構是物聯網設備測量溫度,然後使用像 MQTT 這樣的技術通過互聯網發布這些遙測數據。服務器代碼會監聽這些數據並將其保存到某個地方,例如數據庫。這樣,數據可以稍後進行分析,例如每天晚上計算當天的 GDD,累計每種作物的 GDD,並在植物接近成熟時發出警報。 - -![遙測數據被發送到服務器並保存到數據庫](../../../../../translated_images/zh-HK/save-telemetry-database.ddc9c6bea0c5ba39.webp) - -服務器代碼還可以增強數據,添加額外的信息。例如,物聯網設備可以發布一個標識符來指示是哪個設備,服務器代碼可以使用這個標識符查找設備的位置以及它正在監測的作物。它還可以添加基本數據,例如當前時間,因為某些物聯網設備沒有必要的硬件來準確跟蹤時間,或者需要額外的代碼通過互聯網讀取當前時間。 - -✅ 您認為為什麼不同的田地可能會有不同的溫度? - -### 任務 - 發布溫度信息 - -按照相關指南使用您的物聯網設備通過 MQTT 發布溫度數據,以便稍後進行分析: - -* [Arduino - Wio Terminal](wio-terminal-temp-publish.md) -* [單板計算機 - Raspberry Pi/虛擬物聯網設備](single-board-computer-temp-publish.md) - -### 任務 - 捕獲並存儲溫度信息 - -一旦物聯網設備開始發布遙測數據,服務器代碼可以編寫來訂閱這些數據並存儲它們。服務器代碼將數據保存到逗號分隔值 (CSV) 文件,而不是保存到數據庫。CSV 文件以文本形式存儲數據,每行是一條記錄,每個值由逗號分隔。它是一種方便、可讀性高且支持良好的文件存儲方式。 - -CSV 文件將有兩列——*日期* 和 *溫度*。*日期* 列設置為服務器接收到消息的當前日期和時間,*溫度* 來自遙測消息。 - -1. 重複第 4 課中的步驟,創建服務器代碼以訂閱遙測數據。您不需要添加發布命令的代碼。 - - 這些步驟包括: - - * 配置並激活 Python 虛擬環境 - - * 安裝 paho-mqtt pip 包 - - * 編寫代碼以監聽發布到遙測主題的 MQTT 消息 - - > ⚠️ 如果需要,您可以參考 [第 4 課中創建 Python 應用程序以接收遙測數據的說明](../../../1-getting-started/lessons/4-connect-internet/README.md#receive-telemetry-from-the-mqtt-broker)。 - - 為此項目命名文件夾 `temperature-sensor-server`。 - -1. 確保 `client_name` 反映此項目: - - ```cpp - client_name = id + 'temperature_sensor_server' - ``` - -1. 在文件的頂部添加以下導入,位於現有導入的下方: - - ```python - from os import path - import csv - from datetime import datetime - ``` - - 這導入了一個用於讀取文件的庫、一個與 CSV 文件交互的庫,以及一個幫助處理日期和時間的庫。 - -1. 在 `handle_telemetry` 函數之前添加以下代碼: - - ```python - temperature_file_name = 'temperature.csv' - fieldnames = ['date', 'temperature'] - - if not path.exists(temperature_file_name): - with open(temperature_file_name, mode='w') as csv_file: - writer = csv.DictWriter(csv_file, fieldnames=fieldnames) - writer.writeheader() - ``` - - 此代碼聲明了一些常量,用於指定要寫入的文件名以及 CSV 文件的列標題名稱。CSV 文件的第一行通常包含用逗號分隔的列標題。 - - 然後代碼檢查 CSV 文件是否已存在。如果不存在,則創建該文件並在第一行添加列標題。 - -1. 在 `handle_telemetry` 函數的末尾添加以下代碼: - - ```python - with open(temperature_file_name, mode='a') as temperature_file: - temperature_writer = csv.DictWriter(temperature_file, fieldnames=fieldnames) - temperature_writer.writerow({'date' : datetime.now().astimezone().replace(microsecond=0).isoformat(), 'temperature' : payload['temperature']}) - ``` -此程式碼會開啟 CSV 檔案,然後在檔案末尾新增一行資料。該行資料包含以人類可讀格式顯示的當前日期和時間,接著是從 IoT 裝置接收到的溫度數據。數據以 [ISO 8601 格式](https://wikipedia.org/wiki/ISO_8601) 儲存,包含時區資訊,但不包含微秒。 - -1. 像之前一樣執行此程式碼,確保您的 IoT 裝置正在傳送數據。一個名為 `temperature.csv` 的 CSV 檔案將會在相同的資料夾中建立。打開檔案後,您會看到日期/時間和溫度測量值: - - ```output - date,temperature - 2021-04-19T17:21:36-07:00,25 - 2021-04-19T17:31:36-07:00,24 - 2021-04-19T17:41:36-07:00,25 - ``` - -1. 執行此程式碼一段時間以收集數據。理想情況下,您應該執行一整天以收集足夠的數據來進行 GDD 計算。 - - -> 💁 如果您使用的是虛擬 IoT 裝置,請勾選隨機選項並設定範圍,以避免每次返回的溫度值都相同。 - ![勾選隨機選項並設定範圍](../../../../../translated_images/zh-HK/select-the-random-checkbox-and-set-a-range.32cf4bc7c12e797f.webp) - - > 💁 如果您想執行一整天,請確保執行伺服器程式碼的電腦不會進入睡眠模式,可以透過更改電源設定,或執行類似 [這個保持系統活躍的 Python 腳本](https://github.com/jaqsparow/keep-system-active) 來實現。 - -> 💁 您可以在 [code-server/temperature-sensor-server](../../../../../2-farm/lessons/1-predict-plant-growth/code-server/temperature-sensor-server) 資料夾中找到此程式碼。 - -### 任務 - 使用儲存的數據計算 GDD - -一旦伺服器捕獲了溫度數據,就可以計算植物的 GDD。 - -手動執行的步驟如下: - -1. 找到植物的基準溫度。例如,草莓的基準溫度是 10°C。 - -1. 從 `temperature.csv` 中找到當天的最高和最低溫度。 - -1. 使用之前提供的 GDD 計算公式來計算 GDD。 - -例如,如果當天的最高溫度是 25°C,最低溫度是 12°C: - -![GDD = 25 + 12 除以 2,然後從結果中減去 10,得到 8.5](../../../../../translated_images/zh-HK/gdd-calculation-strawberries.59f57db94b22adb8ff6efb951ace33af104a1c6ccca3ffb0f8169c14cb160c90.png) - -* 25 + 12 = 37 -* 37 / 2 = 18.5 -* 18.5 - 10 = 8.5 - -因此,草莓獲得了 **8.5** GDD。草莓需要大約 250 GDD 才能結果,因此還需要一段時間。 - ---- - -## 🚀 挑戰 - -植物的生長不僅需要熱量。還需要哪些其他條件? - -針對這些條件,找出是否有感測器可以測量它們。有沒有執行器可以控制這些條件?您會如何組合一個或多個 IoT 裝置來優化植物的生長? - -## 課後測驗 - -[課後測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/10) - -## 回顧與自學 - -* 在 [數位農業維基百科頁面](https://wikipedia.org/wiki/Digital_agriculture) 閱讀更多關於數位農業的資訊。同時也可以在 [精準農業維基百科頁面](https://wikipedia.org/wiki/Precision_agriculture) 閱讀更多關於精準農業的內容。 -* 完整的生長度日計算比這裡提供的簡化公式更為複雜。閱讀更多關於更複雜的公式以及如何處理低於基準溫度的情況,請參考 [生長度日維基百科頁面](https://wikipedia.org/wiki/Growing_degree-day)。 -* 如果我們仍然使用相同的耕作方法,未來可能會面臨糧食短缺的問題。了解更多關於高科技農業技術,請觀看這段 [未來高科技農場的 YouTube 影片](https://www.youtube.com/watch?v=KIEOuKD9KX8)。 - -## 作業 - -[使用 Jupyter Notebook 視覺化 GDD 數據](assignment.md) - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解讀概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/1-predict-plant-growth/assignment.md b/translations/hk/2-farm/lessons/1-predict-plant-growth/assignment.md deleted file mode 100644 index faf6e56f5..000000000 --- a/translations/hk/2-farm/lessons/1-predict-plant-growth/assignment.md +++ /dev/null @@ -1,57 +0,0 @@ - -# 使用 Jupyter Notebook 視覺化 GDD 數據 - -## 指引 - -在本課程中,你使用 IoT 感測器收集了 GDD 數據。要獲得良好的 GDD 數據,你需要收集多天的數據。為了幫助視覺化溫度數據並計算 GDD,你可以使用像 [Jupyter Notebooks](https://jupyter.org) 這樣的工具來分析數據。 - -首先,收集幾天的數據。你需要確保你的伺服器程式在 IoT 裝置運行期間一直運行,可以通過調整電源管理設定或運行類似 [這個保持系統活躍的 Python 腳本](https://github.com/jaqsparow/keep-system-active) 來實現。 - -一旦你有了溫度數據,你可以使用此 repo 中的 Jupyter Notebook 來視覺化數據並計算 GDD。Jupyter Notebook 將程式碼和指引混合在稱為 *cells* 的區塊中,通常是 Python 程式碼。你可以閱讀指引,然後逐塊執行每段程式碼。你也可以編輯程式碼。例如,在這個 Notebook 中,你可以編輯用於計算植物 GDD 的基準溫度。 - -1. 建立一個名為 `gdd-calculation` 的資料夾 - -1. 下載 [gdd.ipynb](./code-notebook/gdd.ipynb) 檔案並將其複製到 `gdd-calculation` 資料夾中。 - -1. 複製由 MQTT 伺服器建立的 `temperature.csv` 檔案 - -1. 在 `gdd-calculation` 資料夾中建立一個新的 Python 虛擬環境。 - -1. 安裝一些 Jupyter Notebook 所需的 pip 套件,以及管理和繪製數據所需的庫: - - ```sh - pip install --upgrade pip - pip install pandas - pip install matplotlib - pip install jupyter - ``` - -1. 在 Jupyter 中運行 Notebook: - - ```sh - jupyter notebook gdd.ipynb - ``` - - Jupyter 會啟動並在你的瀏覽器中打開 Notebook。按照 Notebook 中的指引逐步操作,視覺化測量的溫度並計算生長度日。 - - ![Jupyter Notebook](../../../../../translated_images/zh-HK/gdd-jupyter-notebook.c5b52cf21094f158a61f47f455490fd95f1729777ff90861a4521820bf354cdc.png) - -## 評分標準 - -| 評分項目 | 優秀 | 合格 | 需要改進 | -| -------- | ---- | ---- | -------- | -| 數據收集 | 收集至少 2 天完整的數據 | 收集至少 1 天完整的數據 | 收集部分數據 | -| 計算 GDD | 成功運行 Notebook 並計算 GDD | 成功運行 Notebook | 無法運行 Notebook | - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/1-predict-plant-growth/code-notebook/gdd.ipynb b/translations/hk/2-farm/lessons/1-predict-plant-growth/code-notebook/gdd.ipynb deleted file mode 100644 index dec031da1..000000000 --- a/translations/hk/2-farm/lessons/1-predict-plant-growth/code-notebook/gdd.ipynb +++ /dev/null @@ -1,167 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 生長度日\n", - "\n", - "此筆記本載入保存在 CSV 文件中的溫度數據,並進行分析。它會繪製溫度圖表,顯示每天的最高和最低溫度,並計算 GDD。\n", - "\n", - "使用此筆記本的方法:\n", - "\n", - "* 將 `temperature.csv` 文件複製到與此筆記本相同的文件夾中\n", - "* 使用上方的 **▶︎ Run** 按鈕運行所有單元格。這將運行選定的單元格,然後移至下一個單元格。\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在下面的單元格中,將 `base_temperature` 設置為植物的基礎溫度。\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "base_temperature = 10" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "現在需要使用 pandas 加載 CSV 文件\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "\n", - "# Read the temperature CSV file\n", - "df = pd.read_csv('temperature.csv')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(20, 10))\n", - "plt.plot(df['date'], df['temperature'])\n", - "plt.xticks(rotation='vertical');" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "一旦讀取了數據,就可以按 `date` 列進行分組,並提取每個日期的最低和最高溫度。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Convert datetimes to pure dates so we can group by the date\n", - "df['date'] = pd.to_datetime(df['date']).dt.date\n", - "\n", - "# Group the data by date so it can be analyzed by date\n", - "data_by_date = df.groupby('date')\n", - "\n", - "# Get the minimum and maximum temperatures for each date\n", - "min_by_date = data_by_date.min()\n", - "max_by_date = data_by_date.max()\n", - "\n", - "# Join the min and max temperatures into one dataframe and flatten it\n", - "min_max_by_date = min_by_date.join(max_by_date, on='date', lsuffix='_min', rsuffix='_max')\n", - "min_max_by_date = min_max_by_date.reset_index()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "可以使用標準GDD方程式計算GDD\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def calculate_gdd(row):\n", - " return ((row['temperature_max'] + row['temperature_min']) / 2) - base_temperature\n", - "\n", - "# Calculate the GDD for each row\n", - "min_max_by_date['gdd'] = min_max_by_date.apply (lambda row: calculate_gdd(row), axis=1)\n", - "\n", - "# Print the results\n", - "print(min_max_by_date[['date', 'gdd']].to_string(index=False))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.1" - }, - "metadata": { - "interpreter": { - "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49" - } - }, - "coopTranslator": { - "original_hash": "8fcf954f6042f0bf3601a2c836a09574", - "translation_date": "2025-08-26T15:58:15+00:00", - "source_file": "2-farm/lessons/1-predict-plant-growth/code-notebook/gdd.ipynb", - "language_code": "hk" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/1-predict-plant-growth/pi-temp.md b/translations/hk/2-farm/lessons/1-predict-plant-growth/pi-temp.md deleted file mode 100644 index 4b88a44e3..000000000 --- a/translations/hk/2-farm/lessons/1-predict-plant-growth/pi-temp.md +++ /dev/null @@ -1,125 +0,0 @@ - -# 測量溫度 - Raspberry Pi - -在本課程中,你將為 Raspberry Pi 添加一個溫度感應器。 - -## 硬件 - -你將使用的感應器是 [DHT11 濕度和溫度感應器](https://www.seeedstudio.com/Grove-Temperature-Humidity-Sensor-DHT11.html),它將兩個感應器結合在一個封裝中。這是一款相當流行的感應器,市面上有許多商業化的感應器結合了溫度、濕度,有時還包括大氣壓力。溫度感應器部分是一個負溫度係數(NTC)熱敏電阻,這是一種電阻隨溫度升高而減少的熱敏電阻。 - -這是一個數字感應器,因此它內置了一個 ADC(模數轉換器),用於生成包含溫度和濕度數據的數字信號,供微控制器讀取。 - -### 連接溫度感應器 - -Grove 溫度感應器可以連接到 Raspberry Pi。 - -#### 任務 - -連接溫度感應器 - -![Grove 溫度感應器](../../../../../translated_images/zh-HK/grove-dht11.07f8eafceee170043efbb53e1d15722bd4e00fbaa9ff74290b57e9f66eb82c17.png) - -1. 將 Grove 電纜的一端插入濕度和溫度感應器上的插座。它只能以一種方式插入。 - -1. 在 Raspberry Pi 關閉電源的情況下,將 Grove 電纜的另一端連接到 Pi 上 Grove Base hat 的數字插座 **D5**。此插座位於 GPIO 引腳旁邊的一排插座中,從左數第二個。 - -![Grove 溫度感應器連接到插座 A0](../../../../../translated_images/zh-HK/pi-temperature-sensor.3ff82fff672c8e565ef25a39d26d111de006b825a7e0867227ef4e7fbff8553c.png) - -## 編程溫度感應器 - -現在可以編程設備以使用已連接的溫度感應器。 - -### 任務 - -編程設備。 - -1. 啟動 Raspberry Pi,並等待其啟動。 - -1. 啟動 VS Code,可以直接在 Pi 上啟動,也可以通過 Remote SSH 擴展連接。 - - > ⚠️ 如果需要,可以參考 [第 1 課中設置和啟動 VS Code 的說明](../../../1-getting-started/lessons/1-introduction-to-iot/pi.md)。 - -1. 在終端中,為 `pi` 用戶的主目錄創建一個名為 `temperature-sensor` 的新文件夾。在此文件夾中創建一個名為 `app.py` 的文件: - - ```sh - mkdir temperature-sensor - cd temperature-sensor - touch app.py - ``` - -1. 在 VS Code 中打開此文件夾。 - -1. 要使用溫度和濕度感應器,需要安裝一個額外的 Pip 套件。在 VS Code 的終端中,運行以下命令以在 Pi 上安裝此 Pip 套件: - - ```sh - pip3 install seeed-python-dht - ``` - -1. 將以下代碼添加到 `app.py` 文件中以導入所需的庫: - - ```python - import time - from seeed_dht import DHT - ``` - - `from seeed_dht import DHT` 語句從 `seeed_dht` 模塊中導入 `DHT` 感應器類,用於與 Grove 溫度感應器交互。 - -1. 在上述代碼之後添加以下代碼,以創建管理溫度感應器的類的實例: - - ```python - sensor = DHT("11", 5) - ``` - - 這聲明了一個 `DHT` 類的實例,用於管理 **D**igital **H**umidity 和 **T**emperature 感應器。第一個參數告訴代碼使用的是 *DHT11* 感應器——你使用的庫支持此感應器的其他變體。第二個參數告訴代碼感應器連接到 Grove Base hat 的數字端口 `D5`。 - - > ✅ 記住,所有插座都有唯一的引腳編號。引腳 0、2、4 和 6 是模擬引腳,引腳 5、16、18、22、24 和 26 是數字引腳。 - -1. 在上述代碼之後添加一個無限循環,以輪詢溫度感應器的值並將其打印到控制台: - - ```python - while True: - _, temp = sensor.read() - print(f'Temperature {temp}°C') - ``` - - 調用 `sensor.read()` 返回一個包含濕度和溫度的元組。你只需要溫度值,因此濕度被忽略。然後將溫度值打印到控制台。 - -1. 在循環的末尾添加一個十秒的小延遲,因為不需要連續檢查溫度水平。延遲可以減少設備的功耗。 - - ```python - time.sleep(10) - ``` - -1. 從 VS Code 的終端運行以下命令以運行你的 Python 應用: - - ```sh - python3 app.py - ``` - - 你應該會看到溫度值輸出到控制台。使用某些物品加熱感應器,例如用拇指按壓感應器或使用風扇,觀察值的變化: - - ```output - pi@raspberrypi:~/temperature-sensor $ python3 app.py - Temperature 26°C - Temperature 26°C - Temperature 28°C - Temperature 30°C - Temperature 32°C - ``` - -> 💁 你可以在 [code-temperature/pi](../../../../../2-farm/lessons/1-predict-plant-growth/code-temperature/pi) 文件夾中找到此代碼。 - -😀 你的溫度感應器程式成功了! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/1-predict-plant-growth/single-board-computer-temp-publish.md b/translations/hk/2-farm/lessons/1-predict-plant-growth/single-board-computer-temp-publish.md deleted file mode 100644 index ad616dba0..000000000 --- a/translations/hk/2-farm/lessons/1-predict-plant-growth/single-board-computer-temp-publish.md +++ /dev/null @@ -1,71 +0,0 @@ - -# 發佈溫度數據 - 虛擬物聯網硬件與樹莓派 - -在本課程的這部分,你將通過 MQTT 發佈由樹莓派或虛擬物聯網設備檢測到的溫度值,以便稍後用於計算 GDD。 - -## 發佈溫度數據 - -一旦讀取到溫度數據,就可以通過 MQTT 發佈到某些「伺服器」代碼,該代碼將讀取這些數據並存儲起來,準備用於 GDD 計算。 - -### 任務 - 發佈溫度數據 - -編程設備以發佈溫度數據。 - -1. 如果尚未打開 `temperature-sensor` 應用項目,請將其打開。 - -1. 重複你在第 4 課中完成的步驟,連接到 MQTT 並發送遙測數據。你將使用相同的公共 Mosquitto broker。 - - 具體步驟如下: - - - 添加 MQTT 的 pip 套件 - - 添加連接到 MQTT broker 的代碼 - - 添加發佈遙測數據的代碼 - - > ⚠️ 如有需要,請參考[連接到 MQTT 的指引](../../../1-getting-started/lessons/4-connect-internet/single-board-computer-mqtt.md)和[發送遙測數據的指引](../../../1-getting-started/lessons/4-connect-internet/single-board-computer-telemetry.md)(第 4 課)。 - -1. 確保 `client_name` 反映此項目的名稱: - - ```python - client_name = id + 'temperature_sensor_client' - ``` - -1. 對於遙測數據,與其發送光線值,不如發送從 DHT 傳感器讀取的溫度值,並將其作為 JSON 文檔中的一個屬性,名為 `temperature`: - - ```python - _, temp = sensor.read() - telemetry = json.dumps({'temperature' : temp}) - ``` - -1. 溫度值不需要頻繁讀取——在短時間內它不會有太大變化,因此將 `time.sleep` 設置為 10 分鐘: - - ```cpp - time.sleep(10 * 60); - ``` - - > 💁 `sleep` 函數以秒為單位,因此為了更易於閱讀,這裡的值是通過計算得出的。1 分鐘有 60 秒,因此 10 x(每分鐘 60 秒)得出 10 分鐘的延遲。 - -1. 以與完成上一部分作業相同的方式運行代碼。如果你使用的是虛擬物聯網設備,請確保 CounterFit 應用正在運行,並且已在正確的引腳上創建了濕度和溫度傳感器。 - - ```output - pi@raspberrypi:~/temperature-sensor $ python3 app.py - MQTT connected! - Sending telemetry {"temperature": 25} - Sending telemetry {"temperature": 25} - ``` - -> 💁 你可以在 [code-publish-temperature/virtual-device](../../../../../2-farm/lessons/1-predict-plant-growth/code-publish-temperature/virtual-device) 文件夾或 [code-publish-temperature/pi](../../../../../2-farm/lessons/1-predict-plant-growth/code-publish-temperature/pi) 文件夾中找到此代碼。 - -😀 你已成功將設備的溫度數據作為遙測數據發佈出去。 - ---- - -**免責聲明**: -此文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議使用專業的人工作翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解讀概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/1-predict-plant-growth/virtual-device-temp.md b/translations/hk/2-farm/lessons/1-predict-plant-growth/virtual-device-temp.md deleted file mode 100644 index 9c7fdfbd3..000000000 --- a/translations/hk/2-farm/lessons/1-predict-plant-growth/virtual-device-temp.md +++ /dev/null @@ -1,154 +0,0 @@ - -# 測量溫度 - 虛擬物聯網硬件 - -在本課程中,你將為虛擬物聯網設備添加一個溫度感應器。 - -## 虛擬硬件 - -虛擬物聯網設備將使用模擬的 Grove 數字濕度和溫度感應器。這使得本實驗與使用 Raspberry Pi 和實體 Grove DHT11 感應器的操作保持一致。 - -該感應器結合了**溫度感應器**和**濕度感應器**,但在本實驗中,你只需關注溫度感應器部分。在實體物聯網設備中,溫度感應器通常是一個[熱敏電阻](https://wikipedia.org/wiki/Thermistor),通過感應溫度變化時的電阻變化來測量溫度。溫度感應器通常是數字感應器,內部會將測量到的電阻轉換為攝氏(或開氏、華氏)溫度。 - -### 添加感應器到 CounterFit - -要使用虛擬濕度和溫度感應器,你需要將這兩個感應器添加到 CounterFit 應用中。 - -#### 任務 - 添加感應器到 CounterFit - -將濕度和溫度感應器添加到 CounterFit 應用中。 - -1. 在你的電腦上創建一個名為 `temperature-sensor` 的新 Python 應用,包含一個名為 `app.py` 的文件和一個 Python 虛擬環境,並添加 CounterFit 的 pip 套件。 - - > ⚠️ 如果需要,你可以參考[第 1 課中創建和設置 CounterFit Python 項目的指導](../../../1-getting-started/lessons/1-introduction-to-iot/virtual-device.md)。 - -1. 安裝額外的 Pip 套件以安裝 DHT11 感應器的 CounterFit shim。確保你是在啟動虛擬環境的終端中進行安裝。 - - ```sh - pip install counterfit-shims-seeed-python-dht - ``` - -1. 確保 CounterFit 網頁應用正在運行。 - -1. 創建濕度感應器: - - 1. 在 *Sensors* 面板的 *Create sensor* 框中,展開 *Sensor type* 下拉框並選擇 *Humidity*。 - - 1. 保持 *Units* 設置為 *Percentage*。 - - 1. 確保 *Pin* 設置為 *5*。 - - 1. 點擊 **Add** 按鈕,在 Pin 5 上創建濕度感應器。 - - ![濕度感應器設置](../../../../../translated_images/zh-HK/counterfit-create-humidity-sensor.2750e27b6f30e09cf4e22101defd5252710717620816ab41ba688f91f757c49a.png) - - 濕度感應器將被創建並顯示在感應器列表中。 - - ![濕度感應器已創建](../../../../../translated_images/zh-HK/counterfit-humidity-sensor.7b12f7f339e430cb26c8211d2dba4ef75261b353a01da0932698b5bebd693f27.png) - -1. 創建溫度感應器: - - 1. 在 *Sensors* 面板的 *Create sensor* 框中,展開 *Sensor type* 下拉框並選擇 *Temperature*。 - - 1. 保持 *Units* 設置為 *Celsius*。 - - 1. 確保 *Pin* 設置為 *6*。 - - 1. 點擊 **Add** 按鈕,在 Pin 6 上創建溫度感應器。 - - ![溫度感應器設置](../../../../../translated_images/zh-HK/counterfit-create-temperature-sensor.199350ed34f7343d79dccbe95eaf6c11d2121f03d1c35ab9613b330c23f39b29.png) - - 溫度感應器將被創建並顯示在感應器列表中。 - - ![溫度感應器已創建](../../../../../translated_images/zh-HK/counterfit-temperature-sensor.f0560236c96a9016bafce7f6f792476fe3367bc6941a1f7d5811d144d4bcbfff.png) - -## 編寫溫度感應器應用 - -現在可以使用 CounterFit 感應器編寫溫度感應器應用。 - -### 任務 - 編寫溫度感應器應用 - -編寫溫度感應器應用。 - -1. 確保 `temperature-sensor` 應用已在 VS Code 中打開。 - -1. 打開 `app.py` 文件。 - -1. 在 `app.py` 文件的頂部添加以下代碼以連接應用到 CounterFit: - - ```python - from counterfit_connection import CounterFitConnection - CounterFitConnection.init('127.0.0.1', 5000) - ``` - -1. 在 `app.py` 文件中添加以下代碼以導入所需的庫: - - ```python - import time - from counterfit_shims_seeed_python_dht import DHT - ``` - - `from seeed_dht import DHT` 語句導入了 `DHT` 感應器類,用於通過 `counterfit_shims_seeed_python_dht` 模塊的 shim 與虛擬 Grove 溫度感應器交互。 - -1. 在上述代碼之後添加以下代碼以創建管理虛擬濕度和溫度感應器的類實例: - - ```python - sensor = DHT("11", 5) - ``` - - 這聲明了一個 `DHT` 類的實例,用於管理虛擬**數字濕度和溫度感應器**。第一個參數告訴代碼使用的是虛擬 *DHT11* 感應器。第二個參數告訴代碼感應器連接到端口 `5`。 - - > 💁 CounterFit 通過連接到兩個感應器來模擬這個濕度和溫度感應器,濕度感應器連接到創建 `DHT` 類時指定的引腳,而溫度感應器則運行在下一個引腳上。如果濕度感應器在引腳 5 上,shim 預期溫度感應器在引腳 6 上。 - -1. 在上述代碼之後添加一個無限循環以輪詢溫度感應器的值並將其打印到控制台: - - ```python - while True: - _, temp = sensor.read() - print(f'Temperature {temp}°C') - ``` - - 調用 `sensor.read()` 返回一個包含濕度和溫度的元組。你只需要溫度值,因此可以忽略濕度。然後將溫度值打印到控制台。 - -1. 在循環的末尾添加一個十秒的小延遲,因為不需要持續檢查溫度水平。延遲可以減少設備的功耗。 - - ```python - time.sleep(10) - ``` - -1. 在啟動虛擬環境的 VS Code 終端中運行以下命令以運行你的 Python 應用: - - ```sh - python app.py - ``` - -1. 在 CounterFit 應用中更改溫度感應器的值,該值將被應用讀取。你可以通過以下兩種方式進行更改: - - * 在溫度感應器的 *Value* 框中輸入一個數字,然後點擊 **Set** 按鈕。你輸入的數字將是感應器返回的值。 - - * 勾選 *Random* 選項框,並輸入 *Min* 和 *Max* 值,然後點擊 **Set** 按鈕。每次感應器讀取值時,它將讀取 *Min* 和 *Max* 之間的隨機數字。 - - 你應該能在控制台中看到你設置的值。更改 *Value* 或 *Random* 設置以查看值的變化。 - - ```output - (.venv) ➜ temperature-sensor python app.py - Temperature 28.25°C - Temperature 30.71°C - Temperature 25.17°C - ``` - -> 💁 你可以在 [code-temperature/virtual-device](../../../../../2-farm/lessons/1-predict-plant-growth/code-temperature/virtual-device) 文件夾中找到此代碼。 - -😀 你的溫度感應器程序成功了! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/1-predict-plant-growth/wio-terminal-temp-publish.md b/translations/hk/2-farm/lessons/1-predict-plant-growth/wio-terminal-temp-publish.md deleted file mode 100644 index 02dd6e91e..000000000 --- a/translations/hk/2-farm/lessons/1-predict-plant-growth/wio-terminal-temp-publish.md +++ /dev/null @@ -1,82 +0,0 @@ - -# 發佈溫度 - Wio Terminal - -在這部分課程中,你將透過 MQTT 發佈由 Wio Terminal 偵測到的溫度值,以便稍後用於計算 GDD。 - -## 發佈溫度 - -當溫度被讀取後,可以透過 MQTT 發佈到某些「伺服器」代碼,該代碼將讀取這些值並存儲起來,準備用於 GDD 計算。微控制器無法直接從互聯網讀取時間,也無法自帶實時時鐘來追蹤時間,設備需要被編程以執行此操作,前提是它具備必要的硬件。 - -為了簡化本課程,時間不會與感測器數據一起發送,而是可以在伺服器代碼接收到消息後再添加。 - -### 任務 - -編程設備以發佈溫度數據。 - -1. 打開 `temperature-sensor` Wio Terminal 專案 - -1. 重複你在第 4 課中所做的步驟,連接到 MQTT 並發送遙測數據,你將使用相同的公共 Mosquitto broker。 - - 這些步驟包括: - - - 在 `.ini` 文件中添加 Seeed WiFi 和 MQTT 庫 - - 添加配置文件和連接 WiFi 的代碼 - - 添加連接到 MQTT broker 的代碼 - - 添加發佈遙測數據的代碼 - - > ⚠️ 如有需要,請參考 [連接到 MQTT 的指引](../../../1-getting-started/lessons/4-connect-internet/wio-terminal-mqtt.md) 和 [發送遙測數據的指引](../../../1-getting-started/lessons/4-connect-internet/wio-terminal-telemetry.md)(第 4 課)。 - -1. 確保 `config.h` 標頭文件中的 `CLIENT_NAME` 反映此專案: - - ```cpp - const string CLIENT_NAME = ID + "temperature_sensor_client"; - ``` - -1. 對於遙測數據,將讀取自 DHT 感測器的溫度值作為 JSON 文件中的 `temperature` 屬性發送,而不是發送光線值,通過修改 `main.cpp` 中的 `loop` 函數: - - ```cpp - float temp_hum_val[2] = {0}; - dht.readTempAndHumidity(temp_hum_val); - - DynamicJsonDocument doc(1024); - doc["temperature"] = temp_hum_val[1]; - ``` - -1. 溫度值不需要頻繁讀取——在短時間內它不會有太大變化,因此將 `loop` 函數中的 `delay` 設置為 10 分鐘: - - ```cpp - delay(10 * 60 * 1000); - ``` - - > 💁 `delay` 函數以毫秒為單位,因此為了更容易閱讀,該值是通過計算得出的。1,000 毫秒等於 1 秒,60 秒等於 1 分鐘,因此 10 x (60 秒/分鐘) x (1000 毫秒/秒) 得出 10 分鐘的延遲。 - -1. 將此代碼上傳到你的 Wio Terminal,並使用串口監視器查看溫度是否已成功發送到 MQTT broker。 - - ```output - --- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time - --- More details at http://bit.ly/pio-monitor-filters - --- Miniterm on /dev/cu.usbmodem1201 9600,8,N,1 --- - --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- - Connecting to WiFi.. - Connected! - Attempting MQTT connection...connected - Sending telemetry {"temperature":25} - Sending telemetry {"temperature":25} - ``` - -> 💁 你可以在 [code-publish-temperature/wio-terminal](../../../../../2-farm/lessons/1-predict-plant-growth/code-publish-temperature/wio-terminal) 文件夾中找到此代碼。 - -😀 恭喜你成功將溫度作為遙測數據從你的設備發佈出去! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要資訊,建議使用專業的人類翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/1-predict-plant-growth/wio-terminal-temp.md b/translations/hk/2-farm/lessons/1-predict-plant-growth/wio-terminal-temp.md deleted file mode 100644 index 3864e48ca..000000000 --- a/translations/hk/2-farm/lessons/1-predict-plant-growth/wio-terminal-temp.md +++ /dev/null @@ -1,143 +0,0 @@ - -# 測量溫度 - Wio Terminal - -在本課程中,你將為 Wio Terminal 添加一個溫度感應器,並從中讀取溫度值。 - -## 硬件 - -Wio Terminal 需要一個溫度感應器。 - -你將使用的感應器是 [DHT11 濕度和溫度感應器](https://www.seeedstudio.com/Grove-Temperature-Humidity-Sensor-DHT11.html),它將兩個感應器結合在一個封裝中。這是一款相當流行的感應器,市面上有許多商業化的感應器結合了溫度、濕度,有時還包括大氣壓力。溫度感應器部分是一個負溫度係數(NTC)熱敏電阻,這是一種電阻隨溫度升高而減少的熱敏電阻。 - -這是一個數字感應器,因此內置了 ADC(模數轉換器),可以生成包含溫度和濕度數據的數字信號,供微控制器讀取。 - -### 連接溫度感應器 - -Grove 溫度感應器可以連接到 Wio Terminal 的數字端口。 - -#### 任務 - 連接溫度感應器 - -連接溫度感應器。 - -![Grove 溫度感應器](../../../../../translated_images/zh-HK/grove-dht11.07f8eafceee170043efbb53e1d15722bd4e00fbaa9ff74290b57e9f66eb82c17.png) - -1. 將 Grove 電纜的一端插入濕度和溫度感應器上的插座。它只能以一種方式插入。 - -1. 在 Wio Terminal 未連接到電腦或其他電源的情況下,將 Grove 電纜的另一端連接到 Wio Terminal 屏幕右側的 Grove 插座。這是距離電源按鈕最遠的插座。 - -![Grove 溫度感應器連接到右側插座](../../../../../translated_images/zh-HK/wio-temperature-sensor.2934928f38c7f79a.webp) - -## 編程溫度感應器 - -現在可以為 Wio Terminal 編程以使用已連接的溫度感應器。 - -### 任務 - 編程溫度感應器 - -編程設備。 - -1. 使用 PlatformIO 創建一個全新的 Wio Terminal 項目。將此項目命名為 `temperature-sensor`。在 `setup` 函數中添加代碼以配置串口。 - - > ⚠️ 如果需要,可以參考 [項目 1,課程 1 中創建 PlatformIO 項目的指導](../../../1-getting-started/lessons/1-introduction-to-iot/wio-terminal.md#create-a-platformio-project)。 - -1. 在項目的 `platformio.ini` 文件中添加 Seeed Grove 濕度和溫度感應器庫的依賴: - - ```ini - lib_deps = - seeed-studio/Grove Temperature And Humidity Sensor @ 1.0.1 - ``` - - > ⚠️ 如果需要,可以參考 [項目 1,課程 4 中向 PlatformIO 項目添加庫的指導](../../../1-getting-started/lessons/4-connect-internet/wio-terminal-mqtt.md#install-the-wifi-and-mqtt-arduino-libraries)。 - -1. 在文件頂部的現有 `#include ` 下添加以下 `#include` 指令: - - ```cpp - #include - #include - ``` - - 這些指令導入了與感應器交互所需的文件。`DHT.h` 標頭文件包含感應器本身的代碼,而添加 `SPI.h` 標頭確保在應用程序編譯時鏈接到與感應器通信所需的代碼。 - -1. 在 `setup` 函數之前,聲明 DHT 感應器: - - ```cpp - DHT dht(D0, DHT11); - ``` - - 這聲明了一個 `DHT` 類的實例,用於管理**數字濕度和溫度感應器**。它連接到 Wio Terminal 的 `D0` 端口,即右側的 Grove 插座。第二個參數告訴代碼使用的是 *DHT11* 感應器——你使用的庫支持此感應器的其他變體。 - -1. 在 `setup` 函數中添加代碼以設置串行連接: - - ```cpp - void setup() - { - Serial.begin(9600); - - while (!Serial) - ; // Wait for Serial to be ready - - delay(1000); - } - ``` - -1. 在 `setup` 函數的最後一個 `delay` 之後,添加啟動 DHT 感應器的調用: - - ```cpp - dht.begin(); - ``` - -1. 在 `loop` 函數中添加代碼以調用感應器並將溫度打印到串口: - - ```cpp - void loop() - { - float temp_hum_val[2] = {0}; - dht.readTempAndHumidity(temp_hum_val); - Serial.print("Temperature: "); - Serial.print(temp_hum_val[1]); - Serial.println ("°C"); - - delay(10000); - } - ``` - - 此代碼聲明了一個包含 2 個浮點數的空數組,並將其傳遞給 `DHT` 實例上的 `readTempAndHumidity` 調用。此調用將數組填充為 2 個值——濕度存儲在數組的第 0 個項目中(請記住,在 C++ 中,數組是從 0 開始的,因此第 0 個項目是數組的“第一”項目),溫度存儲在第 1 個項目中。 - - 溫度從數組的第 1 個項目中讀取,並打印到串口。 - - > 🇺🇸 溫度以攝氏度讀取。對於美國人,若要將攝氏度轉換為華氏度,請將讀取的攝氏值除以 5,然後乘以 9,再加上 32。例如,20°C 的溫度讀數轉換為 ((20/5)*9) + 32 = 68°F。 - -1. 編譯並上傳代碼到 Wio Terminal。 - - > ⚠️ 如果需要,可以參考 [項目 1,課程 1 中創建 PlatformIO 項目的指導](../../../1-getting-started/lessons/1-introduction-to-iot/wio-terminal.md#write-the-hello-world-app)。 - -1. 上傳完成後,可以使用串口監視器監控溫度: - - ```output - > Executing task: platformio device monitor < - - --- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time - --- More details at http://bit.ly/pio-monitor-filters - --- Miniterm on /dev/cu.usbmodem1201 9600,8,N,1 --- - --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- - Temperature: 25.00°C - Temperature: 25.00°C - Temperature: 25.00°C - Temperature: 24.00°C - ``` - -> 💁 你可以在 [code-temperature/wio-terminal](../../../../../2-farm/lessons/1-predict-plant-growth/code-temperature/wio-terminal) 文件夾中找到此代碼。 - -😀 你的溫度感應器程式成功了! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/2-detect-soil-moisture/README.md b/translations/hk/2-farm/lessons/2-detect-soil-moisture/README.md deleted file mode 100644 index b65045163..000000000 --- a/translations/hk/2-farm/lessons/2-detect-soil-moisture/README.md +++ /dev/null @@ -1,152 +0,0 @@ - -C,讀作 *I-squared-C*,是一種多控制器、多外設的通訊協議,任何連接的設備都可以作為控制器或外設,通過 I - -2C 有速度限制,並且有三種不同模式以固定速度運行。最快的模式是高速模式,最大速度為 3.4Mbps(每秒百萬位元),但支持該速度的設備非常少。例如,Raspberry Pi 的速度限制在快速模式下為 400Kbps(每秒千位元)。標準模式的速度為 100Kbps。 - -💁 如果你使用 Raspberry Pi 和 Grove Base hat 作為你的 IoT 硬件,你會看到板上有多個 I2C 插座,可以用來與 I2C 傳感器通信。模擬 Grove 傳感器也使用 I2C 和 ADC(模擬數字轉換器)將模擬值作為數字數據發送,因此你使用的光傳感器模擬了一個模擬引腳,並通過 I2C 發送值,因為 Raspberry Pi 只支持數字引腳。 - -### 通用異步接收器-發射器 (UART) - -UART 涉及物理電路,允許兩個設備進行通信。每個設備都有兩個通信引腳——發射 (Tx) 和接收 (Rx),第一個設備的 Tx 引腳連接到第二個設備的 Rx 引腳,第二個設備的 Tx 引腳連接到第一個設備的 Rx 引腳。這使得數據可以雙向傳輸。 - -* 設備 1 從其 Tx 引腳發送數據,設備 2 在其 Rx 引腳接收數據 -* 設備 1 在其 Rx 引腳接收由設備 2 從其 Tx 引腳發送的數據 - -![UART 的 Tx 引腳連接到另一個芯片的 Rx 引腳,反之亦然](../../../../../translated_images/zh-HK/uart.d0dbd3fb9e3728c6.webp) - -🎓 數據是一次發送一位,這被稱為*串行*通信。大多數操作系統和微控制器都有*串行端口*,即可以向你的代碼提供串行數據的連接。 - -UART 設備有一個 [波特率](https://wikipedia.org/wiki/Symbol_rate)(也稱為符號率),即數據以每秒位數發送和接收的速度。一個常見的波特率是 9,600,意味著每秒發送 9,600 位(0 和 1)的數據。 - -UART 使用起始位和停止位——即它發送一個起始位來表示即將發送一個字節(8 位)數據,然後在發送完 8 位後發送一個停止位。 - -UART 的速度取決於硬件,但即使是最快的實現也不超過 6.5 Mbps(每秒百萬位元,或每秒發送的 0 或 1 數據)。 - -你可以通過 GPIO 引腳使用 UART——你可以設置一個引腳為 Tx,另一個引腳為 Rx,然後將它們連接到另一個設備。 - -💁 如果你使用 Raspberry Pi 和 Grove Base hat 作為你的 IoT 硬件,你會看到板上有一個 UART 插座,可以用來與使用 UART 協議的傳感器通信。 - -### 串行外設接口 (SPI) - -SPI 是為短距離通信設計的,例如在微控制器上與存儲設備(如閃存)通信。它基於控制器/外設模型,單個控制器(通常是 IoT 設備的處理器)與多個外設交互。控制器通過選擇一個外設並發送或請求數據來控制所有操作。 - -💁 與 I2C 一樣,控制器和外設這些術語是最近的改變,因此你可能仍然會看到使用舊術語。 - -SPI 控制器使用 3 根線,外加每個外設額外的一根線。外設使用 4 根線。這些線包括: - -| 線 | 名稱 | 描述 | -| ---- | --------- | ----------- | -| COPI | 控制器輸出,外設輸入 | 這根線用於將數據從控制器發送到外設。 | -| CIPO | 控制器輸入,外設輸出 | 這根線用於將數據從外設發送到控制器。 | -| SCLK | 串行時鐘 | 這根線以控制器設置的速率發送時鐘信號。 | -| CS | 芯片選擇 | 控制器有多根線,每個外設一根,每根線連接到相應外設的 CS 線。 | - -![一個控制器和兩個外設的 SPI](../../../../../translated_images/zh-HK/spi.297431d6f98b386b.webp) - -CS 線用於一次激活一個外設,通過 COPI 和 CIPO 線進行通信。當控制器需要更換外設時,它會停用連接到當前激活外設的 CS 線,然後激活連接到下一個外設的線。 - -SPI 是*全雙工*的,這意味著控制器可以同時從同一外設發送和接收數據,使用 COPI 和 CIPO 線。SPI 使用 SCLK 線上的時鐘信號來保持設備同步,因此不像直接通過 UART 發送那樣需要起始位和停止位。 - -SPI 沒有定義的速度限制,實現通常能夠每秒傳輸多兆字節的數據。 - -IoT 開發套件通常支持通過一些 GPIO 引腳使用 SPI。例如,在 Raspberry Pi 上,你可以使用 GPIO 引腳 19、21、23、24 和 26 進行 SPI。 - -### 無線 - -一些傳感器可以通過標準無線協議進行通信,例如藍牙(主要是藍牙低功耗,或 BLE)、LoRaWAN(一種**Lo**ng **Ra**nge 低功耗網絡協議)或 WiFi。這些允許遠程傳感器不需要物理連接到 IoT 設備。 - -一個例子是商業土壤濕度傳感器。這些傳感器會測量田地中的土壤濕度,然後通過 LoRaWAN 將數據發送到集線設備,該設備會處理數據或通過互聯網發送。這使得傳感器可以遠離管理數據的 IoT 設備,減少功耗以及對大型 WiFi 網絡或長電纜的需求。 - -BLE 在高級傳感器中很受歡迎,例如用於手腕上的健身追蹤器。這些設備結合了多個傳感器,並通過 BLE 將傳感器數據發送到你的手機等 IoT 設備。 - -✅ 你身上、家裡或學校裡是否有任何藍牙傳感器?這些可能包括溫度傳感器、佔用傳感器、設備追蹤器和健身設備。 - -商業設備的一種流行連接方式是 Zigbee。Zigbee 使用 WiFi 在設備之間形成網狀網絡,每個設備盡可能多地連接到附近的設備,形成像蜘蛛網一樣的大量連接。當一個設備想要向互聯網發送消息時,它可以將消息發送到最近的設備,然後由這些設備轉發給其他附近的設備,如此類推,直到到達協調器並可以發送到互聯網。 - -🐝 Zigbee 的名字來源於蜜蜂返回蜂巢後的搖擺舞。 - -## 測量土壤中的濕度水平 - -你可以使用土壤濕度傳感器、IoT 設備和一盆室內植物或附近的一片土壤來測量土壤中的濕度水平。 - -### 任務 - 測量土壤濕度 - -按照相關指南使用你的 IoT 設備測量土壤濕度: - -* [Arduino - Wio Terminal](wio-terminal-soil-moisture.md) -* [單板計算機 - Raspberry Pi](pi-soil-moisture.md) -* [單板計算機 - 虛擬設備](virtual-device-soil-moisture.md) - -## 傳感器校準 - -傳感器依賴於測量電氣特性,例如電阻或電容。 - -🎓 電阻,以歐姆 (Ω) 為單位,表示電流通過某物時的阻力有多大。當電壓施加到材料上時,通過它的電流量取決於材料的電阻。你可以在 [Wikipedia 的電阻頁面](https://wikipedia.org/wiki/Electrical_resistance_and_conductance) 上了解更多。 - -🎓 電容,以法拉 (F) 為單位,表示元件或電路收集和存儲電能的能力。你可以在 [Wikipedia 的電容頁面](https://wikipedia.org/wiki/Capacitance) 上了解更多。 - -這些測量值並不總是有用——想像一下,一個溫度傳感器給你一個 22.5KΩ 的測量值!因此,測量值需要通過校準轉換為有用的單位——即將測量值與測量的量匹配,以便新測量值可以轉換為正確的單位。 - -一些傳感器是預先校準的。例如,你在上一課中使用的溫度傳感器已經校準,可以返回以 °C 為單位的溫度測量值。在工廠中,第一個傳感器會暴露於一系列已知溫度,並測量電阻。然後,這些數據會用於建立一個計算公式,可以將測量值從 Ω(電阻的單位)轉換為 °C。 - -💁 計算電阻與溫度的公式稱為 [Steinhart–Hart 方程](https://wikipedia.org/wiki/Steinhart–Hart_equation)。 - -### 土壤濕度傳感器校準 - -土壤濕度是通過重力或體積含水量測量的。 - -* 重力含水量是測量每單位重量土壤中的水重量,單位為每公斤干土中的公斤水 -* 體積含水量是測量每單位體積土壤中的水體積,單位為每立方米干土中的立方米水 - -🇺🇸 對於美國人,由於單位的一致性,這些可以用磅代替公斤或立方英尺代替立方米進行測量。 - -土壤濕度傳感器測量電阻或電容——這不僅因土壤濕度而異,還因土壤類型而異,因為土壤中的成分會改變其電氣特性。理想情況下,傳感器應進行校準——即從傳感器獲取讀數並與使用更科學方法獲得的測量值進行比較。例如,實驗室可以使用特定田地的樣本幾次一年計算重力土壤濕度,並使用這些數據校準傳感器,將傳感器讀數與重力土壤濕度匹配。 - -![電壓與土壤濕度含量的圖表](../../../../../translated_images/zh-HK/soil-moisture-to-voltage.df86d80cda158700.webp) - -上圖顯示了如何校準傳感器。電壓是通過土壤樣本捕獲的,然後通過實驗室測量濕重與干重進行測量(通過測量濕重,然後在烤箱中烘干並測量干重)。一旦獲得了一些讀數,就可以將它們繪製在圖表上並擬合一條線。這條線可以用來將 IoT 設備的土壤濕度傳感器讀數轉換為實際的土壤濕度測量值。 - -💁 對於電阻式土壤濕度傳感器,隨著土壤濕度的增加,電壓會增加。對於電容式土壤濕度傳感器,隨著土壤濕度的增加,電壓會減少,因此這些圖表的斜率會向下而不是向上。 - -![從圖表中插值的土壤濕度值](../../../../../translated_images/zh-HK/soil-moisture-to-voltage-with-reading.681cb3e1f8b68caf.webp) - -上圖顯示了土壤濕度傳感器的電壓讀數,通過將其追蹤到圖表上的線,可以計算出實際的土壤濕度。 - -這種方法意味著農民只需要為田地獲得一些實驗室測量值,然後他們可以使用 IoT 設備測量土壤濕度——大大加快了測量的速度。 - ---- - -## 🚀 挑戰 - -電阻式和電容式土壤濕度傳感器有許多差異。這些差異是什麼?哪種類型(如果有的話)最適合農民使用?這個答案是否會因發展中國家和已開發國家而改變? - -## 課後測驗 - -[課後測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/12) - -## 回顧與自學 - -閱讀有關傳感器和執行器使用的硬件和協議: - -* [GPIO Wikipedia 頁面](https://wikipedia.org/wiki/General-purpose_input/output) -* [UART Wikipedia 頁面](https://wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter) -* [SPI Wikipedia 頁面](https://wikipedia.org/wiki/Serial_Peripheral_Interface) -* [I2C Wikipedia 頁面](https://wikipedia.org/wiki/I²C) -* [Zigbee Wikipedia 頁面](https://wikipedia.org/wiki/Zigbee) - -## 作業 - -[校準你的傳感器](assignment.md) - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/2-detect-soil-moisture/assignment.md b/translations/hk/2-farm/lessons/2-detect-soil-moisture/assignment.md deleted file mode 100644 index 2134fafbf..000000000 --- a/translations/hk/2-farm/lessons/2-detect-soil-moisture/assignment.md +++ /dev/null @@ -1,61 +0,0 @@ - -# 校準你的感測器 - -## 指引 - -在這節課中,你收集了土壤濕度感測器的讀數,範圍為0-1023。為了將這些數值轉換為實際的土壤濕度讀數,你需要校準你的感測器。你可以通過測量土壤樣本並計算這些樣本的重力土壤濕度含量來完成校準。 - -你需要重複以下步驟多次,每次使用不同濕度的土壤來獲得所需的讀數。 - -1. 使用土壤濕度感測器進行一次土壤濕度讀數,並記錄下這個讀數。 - -1. 取一份土壤樣本並稱重,記錄下這個重量。 - -1. 將土壤烘乾——最好的方法是使用110°C(230°F)的溫暖烤箱烘乾幾個小時。你也可以在陽光下烘乾,或者將土壤放置在溫暖、乾燥的地方,直到土壤完全乾燥。乾燥後的土壤應該是粉狀且鬆散的。 - - > 💁 在實驗室中,為了獲得最準確的結果,你可以在烤箱中烘乾48-72小時。如果你的學校有烘乾烤箱,看看是否可以使用這些烤箱進行更長時間的烘乾。烘乾時間越長,樣本越乾燥,結果越準確。 - -1. 再次稱重土壤。 - - > 🔥 如果你是在烤箱中烘乾的,請確保土壤已經冷卻! - -重力土壤濕度的計算公式為: - -![土壤濕度百分比等於濕土重量減去乾土重量,除以乾土重量,再乘以100](../../../../../translated_images/zh-HK/gsm-calculation.6da38c6201eec14e7573bb2647aa18892883193553d23c9d77e5dc681522dfb2.png) - -* W - 濕土的重量 -* W - 乾土的重量 - -例如,假設你有一份土壤樣本,濕重為212克,乾重為197克。 - -![填入計算公式的例子](../../../../../translated_images/zh-HK/gsm-calculation-example.99f9803b4f29e97668e7c15412136c0c399ab12dbba0b89596fdae9d8aedb6fb.png) - -* W = 212克 -* W = 197克 -* 212 - 197 = 15 -* 15 / 197 = 0.076 -* 0.076 * 100 = 7.6% - -在這個例子中,土壤的重力土壤濕度為7.6%。 - -當你至少有3個樣本的讀數後,繪製一個土壤濕度百分比與土壤濕度感測器讀數的圖表,並添加一條最佳擬合線。然後,你可以通過從這條線上讀取值來計算給定感測器讀數的重力土壤濕度含量。 - -## 評分標準 - -| 評分標準 | 優秀 | 合格 | 需要改進 | -| -------- | ---- | ---- | -------- | -| 收集校準數據 | 收集至少3個校準樣本 | 收集至少2個校準樣本 | 收集至少1個校準樣本 | -| 進行校準讀數 | 成功繪製校準圖表並從感測器讀數中轉換為重力土壤濕度含量 | 成功繪製校準圖表 | 無法繪製圖表 | - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/2-detect-soil-moisture/pi-soil-moisture.md b/translations/hk/2-farm/lessons/2-detect-soil-moisture/pi-soil-moisture.md deleted file mode 100644 index 8ebe62b70..000000000 --- a/translations/hk/2-farm/lessons/2-detect-soil-moisture/pi-soil-moisture.md +++ /dev/null @@ -1,108 +0,0 @@ - -# 測量土壤濕度 - Raspberry Pi - -在本課程中,你將為 Raspberry Pi 添加一個電容式土壤濕度傳感器,並從中讀取數值。 - -## 硬件 - -Raspberry Pi 需要一個電容式土壤濕度傳感器。 - -你將使用的傳感器是 [電容式土壤濕度傳感器](https://www.seeedstudio.com/Grove-Capacitive-Moisture-Sensor-Corrosion-Resistant.html),它通過檢測土壤的電容來測量濕度,這是一種隨著土壤濕度變化而改變的特性。當土壤濕度增加時,電壓會下降。 - -這是一個模擬傳感器,因此使用模擬引腳,並通過 Raspberry Pi 上的 Grove Base Hat 的 10 位 ADC 將電壓轉換為 1-1,023 的數字信號。然後通過 Pi 的 GPIO 引腳以 I²C 的方式傳輸。 - -### 連接土壤濕度傳感器 - -Grove 土壤濕度傳感器可以連接到 Raspberry Pi。 - -#### 任務 - 連接土壤濕度傳感器 - -連接土壤濕度傳感器。 - -![Grove 土壤濕度傳感器](../../../../../translated_images/zh-HK/grove-capacitive-soil-moisture-sensor.e7f0776cce30e78be5cc5a07839385fd6718857f31b5bf5ad3d0c73c83b2f0ef.png) - -1. 將 Grove 電纜的一端插入土壤濕度傳感器上的插座。它只能以一種方式插入。 - -1. 在 Raspberry Pi 關機的情況下,將 Grove 電纜的另一端連接到 Pi 上 Grove Base Hat 的模擬插座 **A0**。該插座位於 GPIO 引腳旁邊的一排插座中,從右數第二個。 - -![Grove 土壤濕度傳感器連接到 A0 插座](../../../../../translated_images/zh-HK/pi-soil-moisture-sensor.fdd7eb2393792cf6739cacf1985d9f55beda16d372f30d0b5a51d586f978a870.png) - -1. 將土壤濕度傳感器插入土壤中。傳感器上有一條“最高位置線”——一條白線。將傳感器插入土壤,直到但不超過這條線。 - -![Grove 土壤濕度傳感器插入土壤中](../../../../../translated_images/zh-HK/soil-moisture-sensor-in-soil.bfad91002bda5e96.webp) - -## 編程土壤濕度傳感器 - -現在可以編程 Raspberry Pi 以使用連接的土壤濕度傳感器。 - -### 任務 - 編程土壤濕度傳感器 - -編程設備。 - -1. 啟動 Raspberry Pi,並等待其完成啟動。 - -1. 啟動 VS Code,可以直接在 Pi 上啟動,也可以通過 Remote SSH 擴展連接。 - - > ⚠️ 如果需要,可以參考 [夜燈課程第 1 課中設置和啟動 VS Code 的指導](../../../1-getting-started/lessons/1-introduction-to-iot/pi.md)。 - -1. 在終端中,於 `pi` 用戶的主目錄中創建一個名為 `soil-moisture-sensor` 的新文件夾。在此文件夾中創建一個名為 `app.py` 的文件。 - -1. 在 VS Code 中打開此文件夾。 - -1. 在 `app.py` 文件中添加以下代碼以導入所需的庫: - - ```python - import time - from grove.adc import ADC - ``` - - `import time` 語句導入了 `time` 模塊,稍後將在此任務中使用。 - - `from grove.adc import ADC` 語句從 Grove Python 庫中導入了 `ADC`。該庫包含與 Pi Base Hat 上的模擬到數字轉換器交互並從模擬傳感器讀取電壓的代碼。 - -1. 在此代碼下方添加以下代碼以創建 `ADC` 類的實例: - - ```python - adc = ADC() - ``` - -1. 添加一個無限循環,從 A0 引腳上的 ADC 讀取數據,並將結果寫入控制台。該循環可以在每次讀取之間休眠 10 秒。 - - ```python - while True: - soil_moisture = adc.read(0) - print("Soil moisture:", soil_moisture) - - time.sleep(10) - ``` - -1. 運行 Python 應用程序。你將看到土壤濕度測量值顯示在控制台上。向土壤中添加一些水,或者將傳感器從土壤中移除,觀察數值的變化。 - - ```output - pi@raspberrypi:~/soil-moisture-sensor $ python3 app.py - Soil moisture: 615 - Soil moisture: 612 - Soil moisture: 498 - Soil moisture: 493 - Soil moisture: 490 - Soil Moisture: 388 - ``` - - 在上述示例輸出中,你可以看到隨著水的添加,電壓下降。 - -> 💁 你可以在 [code/pi](../../../../../2-farm/lessons/2-detect-soil-moisture/code/pi) 文件夾中找到此代碼。 - -😀 你的土壤濕度傳感器程序成功了! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/2-detect-soil-moisture/virtual-device-soil-moisture.md b/translations/hk/2-farm/lessons/2-detect-soil-moisture/virtual-device-soil-moisture.md deleted file mode 100644 index bb82bccf6..000000000 --- a/translations/hk/2-farm/lessons/2-detect-soil-moisture/virtual-device-soil-moisture.md +++ /dev/null @@ -1,123 +0,0 @@ - -# 測量土壤濕度 - 虛擬物聯網硬件 - -在本課程中,您將為虛擬物聯網設備添加一個電容式土壤濕度傳感器,並從中讀取數值。 - -## 虛擬硬件 - -虛擬物聯網設備將使用模擬的 Grove 電容式土壤濕度傳感器。這使得本實驗與使用 Raspberry Pi 和實體 Grove 電容式土壤濕度傳感器的操作保持一致。 - -在實體物聯網設備中,土壤濕度傳感器是一種電容式傳感器,通過檢測土壤的電容來測量土壤濕度。當土壤濕度改變時,電容也會隨之改變。隨著土壤濕度增加,電壓會下降。 - -這是一個模擬傳感器,因此使用模擬的 10 位 ADC(模擬到數字轉換器)來報告 1-1,023 的數值。 - -### 在 CounterFit 中添加土壤濕度傳感器 - -要使用虛擬土壤濕度傳感器,您需要將其添加到 CounterFit 應用中。 - -#### 任務 - 在 CounterFit 中添加土壤濕度傳感器 - -將土壤濕度傳感器添加到 CounterFit 應用中。 - -1. 在您的電腦上創建一個名為 `soil-moisture-sensor` 的文件夾,並在其中創建一個名為 `app.py` 的單一文件和一個 Python 虛擬環境,然後添加 CounterFit 的 pip 套件。 - - > ⚠️ 如果需要,您可以參考[第 1 課中創建和設置 CounterFit Python 項目的指導](../../../1-getting-started/lessons/1-introduction-to-iot/virtual-device.md)。 - -1. 確保 CounterFit 網頁應用正在運行。 - -1. 創建一個土壤濕度傳感器: - - 1. 在 *Sensors* 面板的 *Create sensor* 框中,展開 *Sensor type* 下拉框並選擇 *Soil Moisture*。 - - 1. 將 *Units* 保持為 *NoUnits*。 - - 1. 確保 *Pin* 設置為 *0*。 - - 1. 點擊 **Add** 按鈕,在 Pin 0 上創建 *Soil Moisture* 傳感器。 - - ![土壤濕度傳感器設置](../../../../../translated_images/zh-HK/counterfit-create-soil-moisture-sensor.35266135a5e0ae68b29a684d7db0d2933a8098b2307d197f7c71577b724603aa.png) - - 土壤濕度傳感器將被創建並顯示在傳感器列表中。 - - ![已創建的土壤濕度傳感器](../../../../../translated_images/zh-HK/counterfit-soil-moisture-sensor.81742b2de0e9de60a3b3b9a2ff8ecc686d428eb6d71820f27a693be26e5aceee.png) - -## 編程土壤濕度傳感器應用 - -現在可以使用 CounterFit 傳感器編程土壤濕度傳感器應用。 - -### 任務 - 編程土壤濕度傳感器應用 - -編程土壤濕度傳感器應用。 - -1. 確保 `soil-moisture-sensor` 應用已在 VS Code 中打開。 - -1. 打開 `app.py` 文件。 - -1. 在 `app.py` 文件的頂部添加以下代碼,以連接應用到 CounterFit: - - ```python - from counterfit_connection import CounterFitConnection - CounterFitConnection.init('127.0.0.1', 5000) - ``` - -1. 在 `app.py` 文件中添加以下代碼以導入一些所需的庫: - - ```python - import time - from counterfit_shims_grove.adc import ADC - ``` - - `import time` 語句導入了 `time` 模塊,稍後將在此任務中使用。 - - `from counterfit_shims_grove.adc import ADC` 語句導入了 `ADC` 類,用於與虛擬模擬到數字轉換器交互,該轉換器可以連接到 CounterFit 傳感器。 - -1. 在此代碼下方添加以下代碼以創建 `ADC` 類的實例: - - ```python - adc = ADC() - ``` - -1. 添加一個無限循環,從 Pin 0 的 ADC 讀取數值並將結果寫入控制台。此循環可以在每次讀取之間休眠 10 秒。 - - ```python - while True: - soil_moisture = adc.read(0) - print("Soil moisture:", soil_moisture) - - time.sleep(10) - ``` - -1. 從 CounterFit 應用中更改土壤濕度傳感器的值,該值將被應用讀取。您可以通過以下兩種方式進行更改: - - * 在土壤濕度傳感器的 *Value* 框中輸入一個數字,然後點擊 **Set** 按鈕。您輸入的數字將是傳感器返回的值。 - - * 勾選 *Random* 選項框,並輸入 *Min* 和 *Max* 值,然後點擊 **Set** 按鈕。每次傳感器讀取值時,它將讀取 *Min* 和 *Max* 之間的隨機數字。 - -1. 運行 Python 應用。您將看到土壤濕度測量值寫入控制台。更改 *Value* 或 *Random* 設置以查看值的變化。 - - ```output - (.venv) ➜ soil-moisture-sensor $ python app.py - Soil moisture: 615 - Soil moisture: 612 - Soil moisture: 498 - Soil moisture: 493 - Soil moisture: 490 - Soil Moisture: 388 - ``` - -> 💁 您可以在 [code/virtual-device](../../../../../2-farm/lessons/2-detect-soil-moisture/code/virtual-device) 文件夾中找到此代碼。 - -😀 您的土壤濕度傳感器程序成功了! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/2-detect-soil-moisture/wio-terminal-soil-moisture.md b/translations/hk/2-farm/lessons/2-detect-soil-moisture/wio-terminal-soil-moisture.md deleted file mode 100644 index 4ff390d0a..000000000 --- a/translations/hk/2-farm/lessons/2-detect-soil-moisture/wio-terminal-soil-moisture.md +++ /dev/null @@ -1,117 +0,0 @@ - -# 測量土壤濕度 - Wio Terminal - -在本課程中,你將為 Wio Terminal 添加一個電容式土壤濕度傳感器,並從中讀取數據。 - -## 硬件 - -Wio Terminal 需要一個電容式土壤濕度傳感器。 - -你將使用的傳感器是 [電容式土壤濕度傳感器](https://www.seeedstudio.com/Grove-Capacitive-Moisture-Sensor-Corrosion-Resistant.html),它通過檢測土壤的電容來測量土壤濕度,這是一個會隨著土壤濕度變化而改變的特性。當土壤濕度增加時,電壓會下降。 - -這是一個模擬傳感器,因此需要連接到 Wio Terminal 的模擬引腳,並使用內置的 ADC(模數轉換器)生成 0-1,023 的數值。 - -### 連接土壤濕度傳感器 - -Grove 土壤濕度傳感器可以連接到 Wio Terminal 的可配置模擬/數字端口。 - -#### 任務 - 連接土壤濕度傳感器 - -連接土壤濕度傳感器。 - -![Grove 土壤濕度傳感器](../../../../../translated_images/zh-HK/grove-capacitive-soil-moisture-sensor.e7f0776cce30e78be5cc5a07839385fd6718857f31b5bf5ad3d0c73c83b2f0ef.png) - -1. 將 Grove 電纜的一端插入土壤濕度傳感器的插座中。電纜只能以一種方式插入。 - -1. 在 Wio Terminal 未連接到電腦或其他電源的情況下,將 Grove 電纜的另一端連接到 Wio Terminal 屏幕右側的 Grove 插座。這是距離電源按鈕最遠的插座。 - -![Grove 土壤濕度傳感器連接到右側插座](../../../../../translated_images/zh-HK/wio-soil-moisture-sensor.46919b61c3f6cb74.webp) - -1. 將土壤濕度傳感器插入土壤中。傳感器上有一條“最高位置線”——一條白線。將傳感器插入到該線以下,但不要超過該線。 - -![Grove 土壤濕度傳感器插入土壤中](../../../../../translated_images/zh-HK/soil-moisture-sensor-in-soil.bfad91002bda5e96.webp) - -1. 現在可以將 Wio Terminal 連接到電腦。 - -## 編程土壤濕度傳感器 - -現在可以為 Wio Terminal 編程以使用連接的土壤濕度傳感器。 - -### 任務 - 編程土壤濕度傳感器 - -為設備編程。 - -1. 使用 PlatformIO 創建一個全新的 Wio Terminal 項目。將此項目命名為 `soil-moisture-sensor`。在 `setup` 函數中添加代碼以配置串口。 - - > ⚠️ 如果需要,可以參考 [項目 1,第 1 課中創建 PlatformIO 項目的說明](../../../1-getting-started/lessons/1-introduction-to-iot/wio-terminal.md#create-a-platformio-project)。 - -1. 該傳感器沒有專用的庫,但你可以使用內置的 Arduino [`analogRead`](https://www.arduino.cc/reference/en/language/functions/analog-io/analogread/) 函數從模擬引腳讀取數據。首先,在 `setup` 函數中配置模擬引腳為輸入,以便可以從中讀取數據,添加以下代碼: - - ```cpp - pinMode(A0, INPUT); - ``` - - 這將 `A0` 引腳(模擬/數字組合引腳)設置為輸入引腳,以便可以讀取電壓。 - -1. 在 `loop` 函數中添加以下代碼以從該引腳讀取電壓: - - ```cpp - int soil_moisture = analogRead(A0); - ``` - -1. 在此代碼下方,添加以下代碼將數值打印到串口: - - ```cpp - Serial.print("Soil Moisture: "); - Serial.println(soil_moisture); - ``` - -1. 最後,在末尾添加一個 10 秒的延遲: - - ```cpp - delay(10000); - ``` - -1. 編譯並將代碼上傳到 Wio Terminal。 - - > ⚠️ 如果需要,可以參考 [項目 1,第 1 課中創建 PlatformIO 項目的說明](../../../1-getting-started/lessons/1-introduction-to-iot/wio-terminal.md#write-the-hello-world-app)。 - -1. 上傳完成後,你可以使用串口監視器監控土壤濕度。向土壤中添加一些水,或者將傳感器從土壤中取出,觀察數值的變化。 - - ```output - > Executing task: platformio device monitor < - - --- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time - --- More details at http://bit.ly/pio-monitor-filters - --- Miniterm on /dev/cu.usbmodem1201 9600,8,N,1 --- - --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- - Soil Moisture: 526 - Soil Moisture: 529 - Soil Moisture: 521 - Soil Moisture: 494 - Soil Moisture: 454 - Soil Moisture: 456 - Soil Moisture: 395 - Soil Moisture: 388 - Soil Moisture: 394 - Soil Moisture: 391 - ``` - - 在上面的示例輸出中,你可以看到隨著水的添加,電壓下降。 - -> 💁 你可以在 [code/wio-terminal](../../../../../2-farm/lessons/2-detect-soil-moisture/code/wio-terminal) 文件夾中找到此代碼。 - -😀 你的土壤濕度傳感器程序成功了! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/3-automated-plant-watering/README.md b/translations/hk/2-farm/lessons/3-automated-plant-watering/README.md deleted file mode 100644 index b068ebee7..000000000 --- a/translations/hk/2-farm/lessons/3-automated-plant-watering/README.md +++ /dev/null @@ -1,314 +0,0 @@ - -# 自動化植物灌溉 - -![本課程的手繪筆記概覽](../../../../../translated_images/zh-HK/lesson-7.30b5f577d3cb8e031238751475cb519c7d6dbaea261b5df4643d086ffb2a03bb.jpg) - -> 手繪筆記由 [Nitya Narasimhan](https://github.com/nitya) 提供。點擊圖片查看更大版本。 - -這節課是 [IoT for Beginners Project 2 - Digital Agriculture 系列](https://youtube.com/playlist?list=PLmsFUfdnGr3yCutmcVg6eAUEfsGiFXgcx) 的一部分,由 [Microsoft Reactor](https://developer.microsoft.com/reactor/?WT.mc_id=academic-17441-jabenn) 提供。 - -[![IoT 驅動的自動植物灌溉](https://img.youtube.com/vi/g9FfZwv9R58/0.jpg)](https://youtu.be/g9FfZwv9R58) - -## 課前測驗 - -[課前測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/13) - -## 簡介 - -在上一節課中,你學會了如何監測土壤濕度。在這節課中,你將學習如何構建一個自動灌溉系統的核心組件,該系統能根據土壤濕度進行反應。你還將學習有關時間控制的知識——例如感測器可能需要一些時間來響應變化,而執行器可能需要時間來改變感測器測量的屬性。 - -在這節課中,我們將涵蓋以下內容: - -* [從低功率 IoT 設備控制高功率設備](../../../../../2-farm/lessons/3-automated-plant-watering) -* [控制繼電器](../../../../../2-farm/lessons/3-automated-plant-watering) -* [通過 MQTT 控制你的植物](../../../../../2-farm/lessons/3-automated-plant-watering) -* [感測器與執行器的時間控制](../../../../../2-farm/lessons/3-automated-plant-watering) -* [為植物控制伺服器添加時間控制](../../../../../2-farm/lessons/3-automated-plant-watering) - -## 從低功率 IoT 設備控制高功率設備 - -IoT 設備使用低電壓。雖然這足以驅動感測器和像 LED 這樣的低功率執行器,但對於控制更大的硬體(例如用於灌溉的水泵)來說,這樣的電壓就太低了。即使是用於家居植物的小型水泵,其電流需求也超過了 IoT 開發板的承受範圍,可能會燒毀電路板。 - -> 🎓 電流以安培(A)為單位測量,表示電路中流動的電量。電壓提供推動力,而電流表示被推動的量。你可以在 [維基百科的電流頁面](https://wikipedia.org/wiki/Electric_current) 上了解更多。 - -解決方案是將水泵連接到外部電源,並使用執行器來開啟或關閉水泵,就像你用手指打開燈一樣。用手指翻動開關只需要很少的能量(來自你的身體),而這個動作會將燈連接到 110V/240V 的市電。 - -![一個燈開關打開燈的電源](../../../../../translated_images/zh-HK/light-switch.760317ad6ab8bd6d611da5352dfe9c73a94a0822ccec7df3c8bae35da18e1658.png) - -> 🎓 [市電](https://wikipedia.org/wiki/Mains_electricity) 是指通過國家基礎設施輸送到家庭和企業的電力,在世界許多地方都很常見。 - -✅ IoT 設備通常能提供 3.3V 或 5V,電流小於 1 安培(1A)。相比之下,市電通常為 230V(北美為 120V,日本為 100V),能為需要 30A 電流的設備提供電力。 - -有許多執行器可以完成這項工作,包括可以模仿手指動作的機械設備,最常見的是繼電器。 - -### 繼電器 - -繼電器是一種電磁開關,能將電信號轉換為機械運動來打開或關閉開關。繼電器的核心是一個電磁鐵。 - -> 🎓 [電磁鐵](https://wikipedia.org/wiki/Electromagnet) 是通過電流流過線圈產生磁場的磁鐵。當電流通過時,線圈會被磁化;當電流關閉時,線圈失去磁性。 - -![當電磁鐵通電時,產生磁場,打開輸出電路的開關](../../../../../translated_images/zh-HK/relay-on.4db16a0fd6b66926.webp) - -在繼電器中,控制電路為電磁鐵供電。當電磁鐵通電時,它會拉動一個槓桿,移動開關,閉合一對觸點,從而完成輸出電路。 - -![當電磁鐵斷電時,磁場消失,關閉輸出電路的開關](../../../../../translated_images/zh-HK/relay-off.c34a178a2960fecd.webp) - -當控制電路斷電時,電磁鐵關閉,釋放槓桿並打開觸點,從而關閉輸出電路。繼電器是一種數位執行器——高信號打開繼電器,低信號關閉繼電器。 - -輸出電路可以用來為額外的硬體供電,例如灌溉系統。IoT 設備可以打開繼電器,完成灌溉系統的輸出電路,植物就會被澆水。然後 IoT 設備可以關閉繼電器,切斷灌溉系統的電源,停止供水。 - -![一個繼電器打開,啟動水泵向植物供水](../../../../../images/strawberry-pump.gif) - -在上面的影片中,繼電器被打開。繼電器上的 LED 亮起,表示它已打開(某些繼電器板上有 LED 指示繼電器的開關狀態),電力被送到水泵,啟動水泵向植物供水。 - -> 💁 繼電器還可以用來在兩個輸出電路之間切換,而不是簡單地打開或關閉一個電路。當槓桿移動時,它會將開關從完成一個輸出電路切換到完成另一個輸出電路,通常共用一個電源連接或接地連接。 - -✅ 做一些研究:繼電器有多種類型,差異包括控制電路在通電時是打開還是關閉繼電器,或者是否有多個輸出電路。了解這些不同類型的繼電器。 - -當槓桿移動時,你通常可以聽到它與電磁鐵接觸時發出的清脆的“咔嗒”聲。 - -> 💁 繼電器可以被接線設計成在連接時實際上切斷繼電器的電源,從而關閉繼電器,然後再次向繼電器供電,如此反覆。這樣繼電器會快速地“咔嗒”作響,發出嗡嗡聲。這就是一些早期電鈴中的蜂鳴器的工作原理。 - -### 繼電器的功率 - -電磁鐵啟動並拉動槓桿所需的功率很小,可以使用 IoT 開發板的 3.3V 或 5V 輸出來控制。輸出電路則可以承載更多的功率,具體取決於繼電器,包括市電電壓甚至更高的工業用電壓。這樣,IoT 開發板可以控制灌溉系統,從單株植物的小型水泵到整個商業農場的大型工業系統。 - -![一個 Grove 繼電器,標註了控制電路、輸出電路和繼電器](../../../../../translated_images/zh-HK/grove-relay-labelled.293e068f5c3c2a199bd7892f2661fdc9e10c920b535cfed317fbd6d1d4ae1168.png) - -上圖顯示了一個 Grove 繼電器。控制電路連接到 IoT 設備,使用 3.3V 或 5V 打開或關閉繼電器。輸出電路有兩個端子,任一端都可以是電源或接地。輸出電路可以處理高達 250V、10A 的電流,足以驅動一系列市電設備。你還可以找到能處理更高功率的繼電器。 - -![通過繼電器連接的水泵](../../../../../translated_images/zh-HK/pump-wired-to-relay.66c5cfc0d8918990.webp) - -在上圖中,水泵通過繼電器供電。一條紅線將 USB 電源的 +5V 端子連接到繼電器的輸出電路的一個端子,另一條紅線將輸出電路的另一個端子連接到水泵。一條黑線將水泵連接到 USB 電源的接地端子。當繼電器打開時,它完成了電路,將 5V 電壓送到水泵,啟動水泵。 - -## 控制繼電器 - -你可以使用 IoT 開發板來控制繼電器。 - -### 任務 - 控制繼電器 - -按照相關指南,使用你的 IoT 設備控制繼電器: - -* [Arduino - Wio Terminal](wio-terminal-relay.md) -* [單板電腦 - Raspberry Pi](pi-relay.md) -* [單板電腦 - 虛擬設備](virtual-device-relay.md) - -## 通過 MQTT 控制你的植物 - -目前,你的繼電器是由 IoT 設備直接根據單一的土壤濕度讀數控制的。在商業灌溉系統中,控制邏輯通常是集中化的,這樣可以根據多個感測器的數據進行澆水決策,並且可以在一個地方更改所有配置。為了模擬這一點,你可以通過 MQTT 控制繼電器。 - -### 任務 - 通過 MQTT 控制繼電器 - -1. 在你的 `soil-moisture-sensor` 專案中添加相關的 MQTT 庫/pip 套件和代碼以連接到 MQTT。將客戶端 ID 命名為 `soilmoisturesensor_client`,並在前面加上你的 ID。 - - > ⚠️ 如果需要,可以參考 [專案 1,第 4 課中連接到 MQTT 的說明](../../../1-getting-started/lessons/4-connect-internet/README.md#connect-your-iot-device-to-mqtt)。 - -1. 添加相關設備代碼,使用 `soil_moisture` 作為屬性名稱,發送帶有土壤濕度設置的遙測數據。 - - > ⚠️ 如果需要,可以參考 [專案 1,第 4 課中發送遙測數據到 MQTT 的說明](../../../1-getting-started/lessons/4-connect-internet/README.md#send-telemetry-from-your-iot-device)。 - -1. 在名為 `soil-moisture-sensor-server` 的資料夾中創建一些本地伺服器代碼,訂閱遙測數據並發送命令來控制繼電器。將命令消息中的屬性命名為 `relay_on`,並將客戶端 ID 命名為 `soilmoisturesensor_server`,在前面加上你的 ID。保持與你在專案 1,第 4 課中編寫的伺服器代碼相同的結構,因為你將在本課程的後續部分中添加到此代碼。 - - > ⚠️ 如果需要,可以參考 [專案 1,第 4 課中發送遙測數據到 MQTT 的說明](../../../1-getting-started/lessons/4-connect-internet/README.md#write-the-server-code) 和 [通過 MQTT 發送命令的說明](../../../1-getting-started/lessons/4-connect-internet/README.md#send-commands-to-the-mqtt-broker)。 - -1. 添加相關設備代碼,根據接收到的命令控制繼電器,使用消息中的 `relay_on` 屬性。如果 `soil_moisture` 大於 450,則發送 true 給 `relay_on`,否則發送 false,邏輯與之前添加到 IoT 設備中的邏輯相同。 - - > ⚠️ 如果需要,可以參考 [專案 1,第 4 課中處理來自 MQTT 的命令的說明](../../../1-getting-started/lessons/4-connect-internet/README.md#handle-commands-on-the-iot-device)。 - -> 💁 你可以在 [code-mqtt](../../../../../2-farm/lessons/3-automated-plant-watering/code-mqtt) 資料夾中找到這段代碼。 - -確保代碼在你的設備和本地伺服器上運行,並通過改變土壤濕度水平進行測試,可以通過更改虛擬感測器發送的值,或者通過向土壤中添加水或將感測器從土壤中移除來改變濕度。 - -## 感測器與執行器的時間控制 - -回到第 3 課,你構建了一個夜燈——當光感測器檢測到低光水平時,LED 會立即亮起。光感測器能夠即時檢測光線變化,設備也能快速響應,僅受 `loop` 函數或 `while True:` 循環中的延遲時間限制。作為 IoT 開發者,你不能總是依賴這樣快速的反饋迴路。 - -### 土壤濕度的時間控制 - -如果你在上一課中使用了實體感測器來測量土壤濕度,你可能會注意到,在你給植物澆水後,土壤濕度讀數需要幾秒鐘才會下降。這並不是因為感測器反應慢,而是因為水滲透到土壤中需要時間。 -💁 如果你在感應器附近澆水過多,你可能會看到讀數迅速下降,然後又回升——這是因為感應器附近的水分向周圍土壤擴散,導致感應器附近的土壤濕度降低所引起的。 -![土壤濕度測量值為 658,在澆水時並未立即改變,只有在水滲透到土壤後才下降至 320](../../../../../translated_images/zh-HK/soil-moisture-travel.a0e31af222cf1438.webp) - -在上圖中,土壤濕度讀數顯示為 658。植物被澆水,但這個讀數並未立即改變,因為水尚未到達感測器。甚至在水到達感測器之前,澆水可能已經完成,然後讀數才會下降以反映新的濕度水平。 - -如果你正在撰寫基於土壤濕度水平通過繼電器控制灌溉系統的程式碼,你需要考慮這種延遲,並在你的 IoT 裝置中加入更智能的時間控制。 - -✅ 花點時間思考如何解決這個問題。 - -### 控制感測器和執行器的時間 - -假設你被指派建造一個農場的灌溉系統。根據土壤類型,理想的植物土壤濕度水平已被確定為對應於 400-450 的類比電壓讀數。 - -你可以像夜燈一樣編程裝置——只要感測器讀數高於 450,就啟動繼電器以啟動水泵。問題是水需要一段時間才能從水泵通過土壤到達感測器。感測器在檢測到 450 的水平時會停止供水,但由於泵送的水繼續滲透到土壤中,濕度水平會繼續下降。最終結果是浪費水,並可能損害植物根部。 - -✅ 記住——過多的水對植物的危害可能和過少的水一樣,並且浪費了珍貴的資源。 - -更好的解決方案是理解執行器啟動與感測器讀取的屬性改變之間存在延遲。這意味著感測器不僅需要等待一段時間再測量值,執行器也需要在下一次感測器測量之前關閉一段時間。 - -每次繼電器應該開啟多久?最好謹慎行事,只讓繼電器開啟短時間,然後等待水滲透,再重新檢查濕度水平。畢竟,你可以隨時再次開啟水泵添加更多水,但無法從土壤中移除水。 - -> 💁 這種時間控制非常特定於你正在建造的 IoT 裝置、測量的屬性以及使用的感測器和執行器。 - -![一株草莓植物通過水泵連接到水源,水泵通過繼電器控制。繼電器和植物中的土壤濕度感測器都連接到 Raspberry Pi](../../../../../translated_images/zh-HK/strawberry-with-pump.b410fc72ac6aabad.webp) - -例如,我有一株草莓植物,配備了一個土壤濕度感測器和一個由繼電器控制的水泵。我觀察到當我添加水時,土壤濕度讀數需要大約 20 秒才能穩定。這意味著我需要關閉繼電器並等待 20 秒再檢查濕度水平。我寧願水少一點也不願水太多——我可以隨時再次開啟水泵,但無法從植物中移除水。 - -![步驟 1,測量濕度。步驟 2,添加水。步驟 3,等待水滲透到土壤。步驟 4,重新測量濕度](../../../../../translated_images/zh-HK/soil-moisture-delay.865f3fae206db01d.webp) - -這意味著最佳的澆水流程可能如下: - -* 啟動水泵 5 秒 -* 等待 20 秒 -* 檢查土壤濕度 -* 如果濕度水平仍然高於需求,重複上述步驟 - -5 秒可能對水泵來說太長,特別是當濕度水平僅略高於所需水平時。最佳的時間設定方式是嘗試後根據感測器數據進行調整,形成一個持續的反饋循環。這甚至可以導致更精細的時間控制,例如每超過所需濕度 100,啟動水泵 1 秒,而不是固定的 5 秒。 - -✅ 做些研究:是否有其他時間考量?是否可以在任何土壤濕度過低的時候澆水,或者是否有特定的時間適合或不適合澆水? - -> 💁 天氣預測也可以納入考量,用於控制戶外種植的自動灌溉系統。如果預測有雨,澆水可以暫停,直到雨停後再進行。此時土壤可能已經足夠濕潤,不需要澆水,比在雨前澆水更有效率。 - -## 為植物控制伺服器添加時間控制 - -伺服器程式碼可以修改以添加澆水流程的時間控制,並等待土壤濕度水平的改變。控制繼電器時間的伺服器邏輯如下: - -1. 接收到遙測訊息 -1. 檢查土壤濕度水平 -1. 如果濕度水平正常,則不執行任何操作。如果讀數過高(表示土壤濕度過低),則: - 1. 發送指令啟動繼電器 - 1. 等待 5 秒 - 1. 發送指令關閉繼電器 - 1. 等待 20 秒讓土壤濕度水平穩定 - -澆水流程,即從接收到遙測訊息到準備再次處理土壤濕度水平的過程,大約需要 25 秒。我們每 10 秒發送一次土壤濕度水平,因此在伺服器等待土壤濕度水平穩定期間,可能會接收到新的訊息,這可能會啟動另一個澆水流程。 - -有兩種解決方法: - -* 更改 IoT 裝置程式碼,使其每分鐘只發送一次遙測訊息,這樣澆水流程可以在下一次訊息發送之前完成 -* 在澆水流程期間取消訂閱遙測訊息 - -第一種選擇對於大型農場來說並不總是好的解決方案。農民可能希望在澆水期間捕捉土壤濕度水平,以便日後分析,例如了解農場不同區域的水流情況,以指導更有針對性的澆水。第二種選擇更好——程式碼只是忽略無法使用的遙測訊息,但遙測訊息仍然存在,供其他可能訂閱的服務使用。 - -> 💁 IoT 數據並非僅由一個裝置發送到一個服務,而是許多裝置可以將數據發送到一個代理,許多服務可以從代理中接收數據。例如,一個服務可以接收土壤濕度數據並將其存儲到資料庫中以供日後分析。另一個服務也可以接收相同的遙測訊息來控制灌溉系統。 - -### 任務 - 為植物控制伺服器添加時間控制 - -更新你的伺服器程式碼,使繼電器運行 5 秒,然後等待 20 秒。 - -1. 如果尚未打開,請在 VS Code 中打開 `soil-moisture-sensor-server` 資料夾。確保虛擬環境已啟動。 - -1. 打開 `app.py` 文件 - -1. 在現有的導入語句下方添加以下程式碼: - - ```python - import threading - ``` - - 此語句從 Python 庫中導入 `threading`,threading 允許 Python 在等待期間執行其他程式碼。 - -1. 在處理遙測訊息的 `handle_telemetry` 函數之前添加以下程式碼: - - ```python - water_time = 5 - wait_time = 20 - ``` - - 這定義了繼電器運行的時間(`water_time`)以及之後檢查土壤濕度的等待時間(`wait_time`)。 - -1. 在此程式碼下方添加以下內容: - - ```python - def send_relay_command(client, state): - command = { 'relay_on' : state } - print("Sending message:", command) - client.publish(server_command_topic, json.dumps(command)) - ``` - - 此程式碼定義了一個名為 `send_relay_command` 的函數,用於通過 MQTT 發送指令來控制繼電器。遙測訊息被創建為字典,然後轉換為 JSON 字串。傳遞到 `state` 的值決定繼電器應該開啟還是關閉。 - -1. 在 `send_relay_code` 函數之後添加以下程式碼: - - ```python - def control_relay(client): - print("Unsubscribing from telemetry") - mqtt_client.unsubscribe(client_telemetry_topic) - - send_relay_command(client, True) - time.sleep(water_time) - send_relay_command(client, False) - - time.sleep(wait_time) - - print("Subscribing to telemetry") - mqtt_client.subscribe(client_telemetry_topic) - ``` - - 此程式碼定義了一個函數,用於基於所需的時間控制繼電器。它首先取消訂閱遙測訊息,以便在澆水期間不處理土壤濕度訊息。接著它發送指令啟動繼電器,等待 `water_time` 後發送指令關閉繼電器。最後它等待 `wait_time` 秒讓土壤濕度水平穩定,然後重新訂閱遙測訊息。 - -1. 將 `handle_telemetry` 函數更改為以下內容: - - ```python - def handle_telemetry(client, userdata, message): - payload = json.loads(message.payload.decode()) - print("Message received:", payload) - - if payload['soil_moisture'] > 450: - threading.Thread(target=control_relay, args=(client,)).start() - ``` - - 此程式碼檢查土壤濕度水平。如果濕度水平大於 450,則土壤需要澆水,並調用 `control_relay` 函數。此函數在單獨的執行緒中運行,作為背景執行。 - -1. 確保你的 IoT 裝置正在運行,然後運行此程式碼。更改土壤濕度水平並觀察繼電器的行為——它應該運行 5 秒,然後至少保持關閉 20 秒,只有在土壤濕度水平不足時才會再次啟動。 - - ```output - (.venv) ➜ soil-moisture-sensor-server ✗ python app.py - Message received: {'soil_moisture': 457} - Unsubscribing from telemetry - Sending message: {'relay_on': True} - Sending message: {'relay_on': False} - Subscribing to telemetry - Message received: {'soil_moisture': 302} - ``` - - 在模擬灌溉系統中測試此功能的一個好方法是使用乾燥的土壤,然後在繼電器開啟時手動倒水,當繼電器關閉時停止倒水。 - -> 💁 你可以在 [code-timing](../../../../../2-farm/lessons/3-automated-plant-watering/code-timing) 資料夾中找到此程式碼。 - -> 💁 如果你想使用水泵建造一個真實的灌溉系統,你可以使用 [6V 水泵](https://www.seeedstudio.com/6V-Mini-Water-Pump-p-1945.html) 和 [USB 終端電源供應](https://www.adafruit.com/product/3628)。確保水泵的電源或輸出通過繼電器連接。 - ---- - -## 🚀 挑戰 - -你能想到其他 IoT 或電氣裝置也有類似的問題嗎?即執行器的結果需要一段時間才能到達感測器。你家裡或學校可能就有幾個。 - -* 它們測量什麼屬性? -* 屬性在執行器使用後需要多久才能改變? -* 屬性超過所需值是否可以接受? -* 如果需要,如何將屬性返回到所需值? - -## 課後測驗 - -[課後測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/14) - -## 回顧與自學 - -* 閱讀更多關於繼電器的內容,包括它們在電話交換機中的歷史用途:[繼電器 Wikipedia 頁面](https://wikipedia.org/wiki/Relay)。 - -## 作業 - -[建造更高效的澆水流程](assignment.md) - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/3-automated-plant-watering/assignment.md b/translations/hk/2-farm/lessons/3-automated-plant-watering/assignment.md deleted file mode 100644 index e97be8e8c..000000000 --- a/translations/hk/2-farm/lessons/3-automated-plant-watering/assignment.md +++ /dev/null @@ -1,54 +0,0 @@ - -# 建立更高效的灌溉循環 - -## 指引 - -這節課講解了如何通過感應器數據控制繼電器,而該繼電器可以進一步控制灌溉系統的水泵。對於一塊特定的土壤,運行水泵固定的時間應該對土壤濕度產生相同的影響。這意味著你可以了解灌溉幾秒鐘會對應土壤濕度讀數的某個下降幅度。利用這些數據,你可以構建一個更可控的灌溉系統。 - -在這次作業中,你需要計算水泵應該運行多長時間才能達到特定的土壤濕度上升。 - -> ⚠️ 如果你使用的是虛擬 IoT 硬件,你可以完成這個過程,但需要通過在繼電器開啟時每秒手動增加固定的土壤濕度讀數來模擬結果。 - -1. 從乾燥的土壤開始。測量土壤濕度。 - -1. 添加固定量的水,可以通過運行水泵 1 秒鐘或倒入固定量的水來完成。 - - > 水泵應始終以恆定的速率運行,因此每秒運行時應提供相同量的水。 - -1. 等待土壤濕度水平穩定後進行讀數。 - -1. 重複多次,並創建一個結果表。以下是該表的一個示例。 - - | 總水泵運行時間 | 土壤濕度 | 減少量 | - | --- | --: | -: | - | 乾燥 | 643 | 0 | - | 1秒 | 621 | 22 | - | 2秒 | 601 | 20 | - | 3秒 | 579 | 22 | - | 4秒 | 560 | 19 | - | 5秒 | 539 | 21 | - | 6秒 | 521 | 18 | - -1. 計算每秒灌溉水量對土壤濕度的平均影響。在上述示例中,每秒灌溉水量使讀數平均減少 20.3。 - -1. 使用這些數據改進你的伺服器代碼,讓水泵運行所需的時間以達到所需的土壤濕度水平。 - -## 評分標準 - -| 評估標準 | 優秀 | 合格 | 需要改進 | -| -------- | ---- | ---- | -------- | -| 捕捉土壤濕度數據 | 能夠在添加固定水量後捕捉多次讀數 | 能夠在添加固定水量後捕捉部分讀數 | 只能捕捉一兩次讀數,或無法使用固定水量 | -| 校準伺服器代碼 | 能夠計算土壤濕度讀數的平均減少量,並更新伺服器代碼以使用該數據 | 能夠計算平均減少量,但無法更新伺服器代碼,或無法正確計算平均值,但能正確更新伺服器代碼 | 無法計算平均值,或無法更新伺服器代碼 | - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/3-automated-plant-watering/pi-relay.md b/translations/hk/2-farm/lessons/3-automated-plant-watering/pi-relay.md deleted file mode 100644 index 48f6cb0c6..000000000 --- a/translations/hk/2-farm/lessons/3-automated-plant-watering/pi-relay.md +++ /dev/null @@ -1,123 +0,0 @@ - -# 控制繼電器 - Raspberry Pi - -在本課程中,您將在 Raspberry Pi 上添加一個繼電器,除了土壤濕度感測器外,還可以根據土壤濕度水平來控制它。 - -## 硬件 - -Raspberry Pi 需要一個繼電器。 - -您將使用的繼電器是 [Grove 繼電器](https://www.seeedstudio.com/Grove-Relay.html),這是一個常開型繼電器(意味著當沒有信號傳送到繼電器時,輸出電路是開路或斷開的),它可以處理最高 250V 和 10A 的輸出電路。 - -這是一個數字執行器,因此需要連接到 Grove Base Hat 的數字插腳。 - -### 連接繼電器 - -Grove 繼電器可以連接到 Raspberry Pi。 - -#### 任務 - -連接繼電器。 - -![Grove 繼電器](../../../../../translated_images/zh-HK/grove-relay.d426958ca210fbd0fb7983d7edc069d46c73a8b0a099d94797bd756f7b6bb6be.png) - -1. 將 Grove 電纜的一端插入繼電器上的插座。它只能以一種方式插入。 - -1. 在 Raspberry Pi 關機的情況下,將 Grove 電纜的另一端連接到 Pi 上 Grove Base Hat 的數字插座 **D5**。此插座位於 GPIO 插腳旁邊插座行的第二個位置。保持土壤濕度感測器連接到 **A0** 插座。 - -![Grove 繼電器連接到 D5 插座,土壤濕度感測器連接到 A0 插座](../../../../../translated_images/zh-HK/pi-relay-and-soil-moisture-sensor.02f3198975b8c53e69ec716cd2719ce117700bd1fc933eaf93476c103c57939b.png) - -1. 如果土壤濕度感測器尚未插入土壤,請將其插入土壤中(從上一課程中)。 - -## 編程繼電器 - -現在可以編程 Raspberry Pi 以使用連接的繼電器。 - -### 任務 - -編程設備。 - -1. 啟動 Raspberry Pi,並等待其啟動。 - -1. 在 VS Code 中打開上一課程的 `soil-moisture-sensor` 專案(如果尚未打開)。您將在此專案中進行添加。 - -1. 在現有的導入語句下,將以下代碼添加到 `app.py` 文件中: - - ```python - from grove.grove_relay import GroveRelay - ``` - - 此語句從 Grove Python 庫中導入 `GroveRelay`,以與 Grove 繼電器進行互動。 - -1. 在 `ADC` 類的聲明下方添加以下代碼,以創建一個 `GroveRelay` 實例: - - ```python - relay = GroveRelay(5) - ``` - - 這會使用插腳 **D5** 創建一個繼電器,該插腳是您連接繼電器的數字插腳。 - -1. 為了測試繼電器是否正常工作,將以下代碼添加到 `while True:` 循環中: - - ```python - relay.on() - time.sleep(.5) - relay.off() - ``` - - 此代碼會打開繼電器,等待 0.5 秒,然後關閉繼電器。 - -1. 運行 Python 應用程式。繼電器將每 10 秒打開和關閉一次,打開和關閉之間有半秒的延遲。您會聽到繼電器點擊聲,然後熄滅。當繼電器打開時,Grove 板上的 LED 會亮起,關閉時則熄滅。 - - ![繼電器打開和關閉](../../../../../images/relay-turn-on-off.gif) - -## 根據土壤濕度控制繼電器 - -現在繼電器已正常工作,可以根據土壤濕度讀數進行控制。 - -### 任務 - -控制繼電器。 - -1. 刪除您添加的用於測試繼電器的 3 行代碼。用以下代碼替換它們: - - ```python - if soil_moisture > 450: - print("Soil Moisture is too low, turning relay on.") - relay.on() - else: - print("Soil Moisture is ok, turning relay off.") - relay.off() - ``` - - 此代碼會檢查土壤濕度感測器的土壤濕度水平。如果濕度高於 450,則打開繼電器;如果低於 450,則關閉繼電器。 - - > 💁 請記住,電容式土壤濕度感測器的讀數越低,土壤中的濕度越高,反之亦然。 - -1. 運行 Python 應用程式。您會看到繼電器根據土壤濕度水平打開或關閉。嘗試在乾燥的土壤中測試,然後添加水。 - - ```output - Soil Moisture: 638 - Soil Moisture is too low, turning relay on. - Soil Moisture: 452 - Soil Moisture is too low, turning relay on. - Soil Moisture: 347 - Soil Moisture is ok, turning relay off. - ``` - -> 💁 您可以在 [code-relay/pi](../../../../../2-farm/lessons/3-automated-plant-watering/code-relay/pi) 文件夾中找到此代碼。 - -😀 恭喜!您的土壤濕度感測器成功控制了繼電器! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業的人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/3-automated-plant-watering/virtual-device-relay.md b/translations/hk/2-farm/lessons/3-automated-plant-watering/virtual-device-relay.md deleted file mode 100644 index e8fd25a49..000000000 --- a/translations/hk/2-farm/lessons/3-automated-plant-watering/virtual-device-relay.md +++ /dev/null @@ -1,127 +0,0 @@ - -# 控制繼電器 - 虛擬物聯網硬件 - -在這部分課程中,你將在虛擬物聯網設備中添加一個繼電器,除了土壤濕度傳感器外,還可以根據土壤濕度水平來控制它。 - -## 虛擬硬件 - -虛擬物聯網設備將使用模擬的 Grove 繼電器。這樣的設置與使用 Raspberry Pi 和實體 Grove 繼電器的操作方式相同。 - -在實體物聯網設備中,繼電器通常是常開型繼電器(即當沒有信號發送到繼電器時,輸出電路是斷開的)。這類繼電器可以處理高達 250V 和 10A 的輸出電路。 - -### 將繼電器添加到 CounterFit - -要使用虛擬繼電器,你需要將它添加到 CounterFit 應用中。 - -#### 任務 - -將繼電器添加到 CounterFit 應用中。 - -1. 如果尚未打開,請在 VS Code 中打開上一課的 `soil-moisture-sensor` 項目。你將在此基礎上進行添加。 - -1. 確保 CounterFit 網頁應用正在運行。 - -1. 創建一個繼電器: - - 1. 在 *Actuators* 面板的 *Create actuator* 框中,展開 *Actuator type* 下拉框,選擇 *Relay*。 - - 1. 將 *Pin* 設置為 *5*。 - - 1. 點擊 **Add** 按鈕,在 Pin 5 上創建繼電器。 - - ![繼電器設置](../../../../../translated_images/zh-HK/counterfit-create-relay.fa7c40fd0f2f6afc33b35ea94fcb235085be4861e14e3fe6b9b7bcfc82d1c888.png) - - 繼電器將被創建並顯示在 Actuators 列表中。 - - ![已創建的繼電器](../../../../../translated_images/zh-HK/counterfit-relay.bbf74c1dbdc8b9acd983367fcbd06703a402aefef6af54ddb28e11307ba8a12c.png) - -## 編程繼電器 - -現在可以編程土壤濕度傳感器應用以使用虛擬繼電器。 - -### 任務 - -編程虛擬設備。 - -1. 如果尚未打開,請在 VS Code 中打開上一課的 `soil-moisture-sensor` 項目。你將在此基礎上進行添加。 - -1. 在 `app.py` 文件的現有導入語句下方添加以下代碼: - - ```python - from counterfit_shims_grove.grove_relay import GroveRelay - ``` - - 這段語句從 Grove Python shim 庫中導入 `GroveRelay`,以便與虛擬 Grove 繼電器交互。 - -1. 在 `ADC` 類的聲明下方添加以下代碼,創建一個 `GroveRelay` 實例: - - ```python - relay = GroveRelay(5) - ``` - - 這段代碼使用 Pin **5** 創建了一個繼電器,這是你之前連接繼電器的引腳。 - -1. 為了測試繼電器是否正常工作,在 `while True:` 循環中添加以下代碼: - - ```python - relay.on() - time.sleep(.5) - relay.off() - ``` - - 這段代碼會打開繼電器,等待 0.5 秒,然後關閉繼電器。 - -1. 運行 Python 應用。繼電器將每 10 秒打開和關閉一次,打開和關閉之間有 0.5 秒的延遲。你將看到 CounterFit 應用中的虛擬繼電器隨著打開和關閉而閉合和斷開。 - - ![虛擬繼電器打開和關閉](../../../../../images/virtual-relay-turn-on-off.gif) - -## 根據土壤濕度控制繼電器 - -現在繼電器已經可以正常工作,接下來可以根據土壤濕度讀數來控制它。 - -### 任務 - -控制繼電器。 - -1. 刪除你添加的測試繼電器的 3 行代碼,並用以下代碼替換: - - ```python - if soil_moisture > 450: - print("Soil Moisture is too low, turning relay on.") - relay.on() - else: - print("Soil Moisture is ok, turning relay off.") - relay.off() - ``` - - 這段代碼會檢查來自土壤濕度傳感器的濕度水平。如果濕度值高於 450,則打開繼電器;如果低於 450,則關閉繼電器。 - - > 💁 請記住,電容式土壤濕度傳感器的讀數越低,表示土壤中的濕度越高,反之亦然。 - -1. 運行 Python 應用。你將看到繼電器根據土壤濕度水平打開或關閉。更改土壤濕度傳感器的 *Value* 或 *Random* 設置以查看值的變化。 - - ```output - Soil Moisture: 638 - Soil Moisture is too low, turning relay on. - Soil Moisture: 452 - Soil Moisture is too low, turning relay on. - Soil Moisture: 347 - Soil Moisture is ok, turning relay off. - ``` - -> 💁 你可以在 [code-relay/virtual-device](../../../../../2-farm/lessons/3-automated-plant-watering/code-relay/virtual-device) 文件夾中找到這段代碼。 - -😀 你的虛擬土壤濕度傳感器控制繼電器程序成功完成! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/3-automated-plant-watering/wio-terminal-relay.md b/translations/hk/2-farm/lessons/3-automated-plant-watering/wio-terminal-relay.md deleted file mode 100644 index ad16d4480..000000000 --- a/translations/hk/2-farm/lessons/3-automated-plant-watering/wio-terminal-relay.md +++ /dev/null @@ -1,121 +0,0 @@ - -# 控制繼電器 - Wio Terminal - -在這部分課程中,您將在 Wio Terminal 上添加一個繼電器,除了土壤濕度傳感器外,還根據土壤濕度水平來控制它。 - -## 硬件 - -Wio Terminal 需要一個繼電器。 - -您將使用的繼電器是 [Grove 繼電器](https://www.seeedstudio.com/Grove-Relay.html),這是一個常開型繼電器(意味著當沒有信號發送到繼電器時,輸出電路是開路或斷開的),它可以處理最高 250V 和 10A 的輸出電路。 - -這是一個數字執行器,因此需要連接到 Wio Terminal 的數字引腳。模擬/數字端口已經被土壤濕度傳感器使用,因此繼電器需要插入另一個端口,該端口是一個結合 I2C 和數字端口。 - -### 連接繼電器 - -Grove 繼電器可以連接到 Wio Terminal 的數字端口。 - -#### 任務 - -連接繼電器。 - -![Grove 繼電器](../../../../../translated_images/zh-HK/grove-relay.d426958ca210fbd0fb7983d7edc069d46c73a8b0a099d94797bd756f7b6bb6be.png) - -1. 將 Grove 電纜的一端插入繼電器上的插座。它只能以一種方式插入。 - -1. 在 Wio Terminal 未連接到電腦或其他電源的情況下,將 Grove 電纜的另一端連接到 Wio Terminal 屏幕左側的 Grove 插座。保持土壤濕度傳感器連接到右側插座。 - -![Grove 繼電器連接到左側插座,土壤濕度傳感器連接到右側插座](../../../../../translated_images/zh-HK/wio-relay-and-soil-moisture-sensor.ed722202d42babe0.webp) - -1. 如果土壤濕度傳感器尚未插入土壤,請將其插入。 - -## 編程繼電器 - -現在可以編程 Wio Terminal 以使用連接的繼電器。 - -### 任務 - -編程設備。 - -1. 在 VS Code 中打開上一課的 `soil-moisture-sensor` 項目(如果尚未打開)。您將在此項目中進行添加。 - -2. 這個執行器沒有專用的庫——它是一個通過高或低信號控制的數字執行器。要打開它,您需要向引腳發送高信號(3.3V);要關閉它,則發送低信號(0V)。您可以使用內置的 Arduino [`digitalWrite`](https://www.arduino.cc/reference/en/language/functions/digital-io/digitalwrite/) 函數來完成此操作。首先,在 `setup` 函數的底部添加以下代碼,以將結合 I2C/數字端口設置為輸出引腳,向繼電器發送電壓: - - ```cpp - pinMode(PIN_WIRE_SCL, OUTPUT); - ``` - - `PIN_WIRE_SCL` 是結合 I2C/數字端口的端口號。 - -1. 為了測試繼電器是否正常工作,將以下代碼添加到 `loop` 函數中,放在最後的 `delay` 之後: - - ```cpp - digitalWrite(PIN_WIRE_SCL, HIGH); - delay(500); - digitalWrite(PIN_WIRE_SCL, LOW); - ``` - - 此代碼向連接繼電器的引腳寫入高信號以打開繼電器,等待 500 毫秒(半秒),然後寫入低信號以關閉繼電器。 - -1. 編譯並上傳代碼到 Wio Terminal。 - -1. 上傳完成後,繼電器將每 10 秒打開和關閉一次,打開和關閉之間有半秒的延遲。您會聽到繼電器點擊聲,LED 在繼電器打開時亮起,關閉時熄滅。 - - ![繼電器打開和關閉](../../../../../images/relay-turn-on-off.gif) - -## 根據土壤濕度控制繼電器 - -現在繼電器已正常工作,可以根據土壤濕度讀數進行控制。 - -### 任務 - -控制繼電器。 - -1. 刪除您添加的用於測試繼電器的 3 行代碼。用以下代碼替換: - - ```cpp - if (soil_moisture > 450) - { - Serial.println("Soil Moisture is too low, turning relay on."); - digitalWrite(PIN_WIRE_SCL, HIGH); - } - else - { - Serial.println("Soil Moisture is ok, turning relay off."); - digitalWrite(PIN_WIRE_SCL, LOW); - } - ``` - - 此代碼檢查來自土壤濕度傳感器的土壤濕度水平。如果濕度值高於 450,則打開繼電器;如果低於 450,則關閉繼電器。 - - > 💁 記住,電容式土壤濕度傳感器的讀數越低,土壤中的濕度越高,反之亦然。 - -1. 編譯並上傳代碼到 Wio Terminal。 - -1. 通過串行監視器監控設備。您將看到繼電器根據土壤濕度水平打開或關閉。嘗試在乾燥的土壤中測試,然後添加水。 - - ```output - Soil Moisture: 638 - Soil Moisture is too low, turning relay on. - Soil Moisture: 452 - Soil Moisture is too low, turning relay on. - Soil Moisture: 347 - Soil Moisture is ok, turning relay off. - ``` - -> 💁 您可以在 [code-relay/wio-terminal](../../../../../2-farm/lessons/3-automated-plant-watering/code-relay/wio-terminal) 文件夾中找到此代碼。 - -😀 恭喜!您的土壤濕度傳感器成功控制了繼電器! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/4-migrate-your-plant-to-the-cloud/README.md b/translations/hk/2-farm/lessons/4-migrate-your-plant-to-the-cloud/README.md deleted file mode 100644 index ad7d857c0..000000000 --- a/translations/hk/2-farm/lessons/4-migrate-your-plant-to-the-cloud/README.md +++ /dev/null @@ -1,451 +0,0 @@ - -# 將你的植物遷移到雲端 - -![本課程的手繪筆記概覽](../../../../../translated_images/zh-HK/lesson-8.3f21f3c11159e6a0a376351973ea5724d5de68fa23b4288853a174bed9ac48c3.jpg) - -> 手繪筆記由 [Nitya Narasimhan](https://github.com/nitya) 提供。點擊圖片查看更大版本。 - -本課程是 [Microsoft Reactor](https://developer.microsoft.com/reactor/?WT.mc_id=academic-17441-jabenn) 的 [IoT for Beginners Project 2 - Digital Agriculture 系列](https://youtube.com/playlist?list=PLmsFUfdnGr3yCutmcVg6eAUEfsGiFXgcx) 的一部分。 - -[![使用 Azure IoT Hub 將設備連接到雲端](https://img.youtube.com/vi/bNxjopXkhvk/0.jpg)](https://youtu.be/bNxjopXkhvk) - -## 課前測驗 - -[課前測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/15) - -## 簡介 - -在上一課中,你學會了如何將植物連接到 MQTT broker,並通過本地運行的伺服器代碼控制繼電器。這構成了從家中單株植物到商業農場所使用的互聯網自動灌溉系統的核心。 - -IoT 設備通過公共 MQTT broker 進行通信,這是一種展示原理的方法,但並不是最可靠或安全的方式。在本課中,你將學習雲端以及公共雲服務提供的 IoT 功能,並學習如何將你的植物從公共 MQTT broker 遷移到這些雲服務之一。 - -在本課中,我們將涵蓋: - -* [什麼是雲端?](../../../../../2-farm/lessons/4-migrate-your-plant-to-the-cloud) -* [創建雲端訂閱](../../../../../2-farm/lessons/4-migrate-your-plant-to-the-cloud) -* [雲端 IoT 服務](../../../../../2-farm/lessons/4-migrate-your-plant-to-the-cloud) -* [在雲端創建 IoT 服務](../../../../../2-farm/lessons/4-migrate-your-plant-to-the-cloud) -* [與 IoT Hub 通信](../../../../../2-farm/lessons/4-migrate-your-plant-to-the-cloud) -* [將設備連接到 IoT 服務](../../../../../2-farm/lessons/4-migrate-your-plant-to-the-cloud) - -## 什麼是雲端? - -在雲端出現之前,當公司想為員工(例如數據庫或文件存儲)或公眾(例如網站)提供服務時,他們需要建立並運營一個數據中心。這可能是一個裝有少量電腦的房間,也可能是一棟裝有大量電腦的大樓。公司需要管理所有事情,包括: - -* 購買電腦 -* 硬件維護 -* 電力和冷卻 -* 網絡連接 -* 安全性,包括建築物的安全和電腦軟件的安全 -* 軟件安裝和更新 - -這樣做成本非常高,還需要各種專業技能的員工,並且在需要變更時速度非常慢。例如,如果一家在線商店需要為繁忙的假期季節做準備,他們需要提前幾個月計劃購買更多硬件,進行配置,安裝軟件以運行銷售流程。假期結束後,銷售量下降,他們就會留下閒置的電腦,直到下一個繁忙季節。 - -✅ 你認為這樣的方式能讓公司快速應對變化嗎?如果一家在線服裝零售商因為某位名人穿著他們的衣服而突然走紅,他們能否快速增加計算能力以支持突然湧入的訂單? - -### 別人的電腦 - -雲端經常被戲稱為「別人的電腦」。最初的想法很簡單——與其購買電腦,不如租用別人的電腦。雲計算提供商會管理大型數據中心,負責購買和安裝硬件,管理電力和冷卻、網絡連接、建築安全、硬件和軟件更新等所有事情。作為客戶,你只需租用所需的電腦,需求增加時租用更多,需求減少時減少租用數量。這些雲端數據中心遍布全球。 - -![一個 Microsoft 雲端數據中心](../../../../../translated_images/zh-HK/azure-region-existing.73f704604f2aa6cb9b5a49ed40e93d4fd81ae3f4e6af4a8ca504023902832f56.png) -![一個 Microsoft 雲端數據中心的擴建計劃](../../../../../translated_images/zh-HK/azure-region-planned-expansion.a5074a1e8af74f156a73552d502429e5b126ea5019274d767ecb4b9afdad442b.png) - -這些數據中心的面積可以達到數平方公里。上圖是幾年前拍攝的 Microsoft 雲端數據中心,展示了初始規模以及擴建計劃。擴建所清理的區域超過 5 平方公里。 - -> 💁 這些數據中心需要消耗大量電力,因此有些數據中心甚至擁有自己的發電站。由於其規模和雲提供商的投資,這些數據中心通常非常環保。它們比大量小型數據中心更高效,主要使用可再生能源運行,雲提供商還致力於減少浪費、降低用水量,並重新種植森林以補償為建設數據中心而砍伐的樹木。你可以在 [Azure 可持續性網站](https://azure.microsoft.com/global-infrastructure/sustainability/?WT.mc_id=academic-17441-jabenn) 上了解更多關於雲提供商如何推動可持續發展的信息。 - -✅ 做一些研究:查閱主要的雲服務提供商,例如 [Microsoft 的 Azure](https://azure.microsoft.com/?WT.mc_id=academic-17441-jabenn) 或 [Google 的 GCP](https://cloud.google.com)。他們有多少個數據中心?這些數據中心分佈在哪些地方? - -使用雲端可以幫助公司降低成本,讓他們專注於自己的核心業務,將雲計算的專業知識交給提供商。公司不再需要租用或購買數據中心空間,向不同的供應商支付連接和電力費用,或僱用專家。相反,他們只需向雲提供商支付一張月費賬單,所有事情都由雲提供商處理。 - -雲提供商則可以利用規模經濟降低成本,例如批量購買電腦以獲得更低價格,投資工具以減少維護工作量,甚至設計和製造自己的硬件以改進雲服務。 - -### Microsoft Azure - -Azure 是 Microsoft 的開發者雲端,這是你在本課中將使用的雲端。以下視頻簡要介紹了 Azure: - -[![Azure 概覽視頻](../../../../../translated_images/zh-HK/what-is-azure-video-thumbnail.20174db09e03bbb8.webp)](https://www.microsoft.com/videoplayer/embed/RE4Ibng?WT.mc_id=academic-17441-jabenn) - -## 創建雲端訂閱 - -要使用雲端服務,你需要向雲提供商註冊一個訂閱。在本課中,你將註冊一個 Microsoft Azure 訂閱。如果你已經有 Azure 訂閱,可以跳過此任務。以下描述的訂閱詳情在撰寫本文時是準確的,但可能會有所變更。 - -> 💁 如果你是通過學校學習本課程,你可能已經擁有一個 Azure 訂閱。請向你的老師確認。 - -Azure 提供兩種類型的免費訂閱: - -* **Azure for Students** - 這是為 18 歲以上學生設計的訂閱。註冊時不需要信用卡,你可以使用學校的電子郵件地址驗證你的學生身份。註冊後,你將獲得 100 美元的雲資源使用額,以及包括 IoT 服務免費版本在內的免費服務。此訂閱有效期為 12 個月,每年可續訂一次,只要你仍是學生。 - -* **Azure 免費訂閱** - 這是為非學生設計的訂閱。註冊時需要提供信用卡,但不會扣款,僅用於驗證你是真人而非機器人。你將獲得 200 美元的信用額度,可在前 30 天內用於任何服務,此外還有 Azure 服務的免費層級。一旦信用額度用完,除非你轉為按需付費訂閱,否則不會向你的信用卡收費。 - -> 💁 Microsoft 還為 18 歲以下學生提供 Azure for Students Starter 訂閱,但在撰寫本文時,該訂閱不支持任何 IoT 服務。 - -### 任務 - 註冊免費雲端訂閱 - -如果你是 18 歲以上的學生,可以註冊 Azure for Students 訂閱。你需要使用學校的電子郵件地址進行驗證。你可以通過以下兩種方式之一完成: - -* 在 [education.github.com/pack](https://education.github.com/pack) 註冊 GitHub 學生開發者包。這將為你提供一系列工具和優惠,包括 GitHub 和 Microsoft Azure。註冊開發者包後,你可以啟用 Azure for Students 優惠。 - -* 直接在 [azure.microsoft.com/free/students](https://azure.microsoft.com/free/students/?WT.mc_id=academic-17441-jabenn) 註冊 Azure for Students 帳戶。 - -> ⚠️ 如果你的學校電子郵件地址未被識別,請在 [此 repo 提交問題](https://github.com/Microsoft/IoT-For-Beginners/issues),我們將查看是否可以將其添加到 Azure for Students 的允許列表中。 - -如果你不是學生,或者沒有有效的學校電子郵件地址,可以註冊 Azure 免費訂閱。 - -* 在 [azure.microsoft.com/free](https://azure.microsoft.com/free/?WT.mc_id=academic-17441-jabenn) 註冊 Azure 免費訂閱。 - -## 雲端 IoT 服務 - -你之前使用的公共測試 MQTT broker 是一個很好的學習工具,但作為商業用途的工具存在一些缺點: - -* 可靠性 - 它是一個免費服務,沒有任何保證,隨時可能關閉 -* 安全性 - 它是公共的,因此任何人都可以監聽你的數據或發送命令控制你的硬件 -* 性能 - 它設計用於少量測試消息,無法處理大量消息的傳輸 -* 發現性 - 無法知道有哪些設備已連接 - -雲端 IoT 服務解決了這些問題。這些服務由大型雲提供商維護,他們在可靠性方面投入巨大,並隨時解決可能出現的問題。這些服務內建安全性,防止黑客讀取你的數據或發送惡意命令。它們還具有高性能,能夠每天處理數百萬條消息,並利用雲端按需擴展。 - -> 💁 雖然這些優勢需要每月支付費用,但大多數雲提供商提供免費版本的 IoT 服務,限制每日消息數量或可連接的設備數量。這些免費版本通常足夠開發者學習使用。在本課中,你將使用免費版本。 - -IoT 設備可以通過設備 SDK(提供與服務功能交互的代碼庫)或直接使用通信協議(如 MQTT 或 HTTP)連接到雲服務。設備 SDK 通常是最簡單的路徑,因為它處理了所有細節,例如知道要發布或訂閱的主題,以及如何處理安全性。 - -![設備通過設備 SDK 連接到服務。伺服器代碼也通過 SDK 連接到服務](../../../../../translated_images/zh-HK/iot-service-connectivity.7e873847921a5d6fd60d0ba3a943210194518cee0d4e362476624316443275c3.png) - -你的設備然後通過該服務與應用程序的其他部分通信——類似於你之前通過 MQTT 發送遙測數據和接收命令的方式。這通常使用服務 SDK 或類似的庫。消息從設備發送到服務,應用程序的其他組件可以讀取這些消息,並將消息發送回設備。 - -![沒有有效密鑰的設備無法連接到 IoT 服務](../../../../../translated_images/zh-HK/iot-service-allowed-denied-connection.818b0063ac213fb84204a7229303764d9b467ca430fb822b4ac2fca267d56726.png) - -這些服務通過了解所有可以連接並發送數據的設備來實現安全性,這可以通過預先註冊設備或為設備提供密鑰或證書來完成,設備在首次連接時使用這些密鑰或證書進行註冊。未知設備無法連接,如果嘗試連接,服務會拒絕並忽略其發送的消息。 - -✅ 做一些研究:開放式 IoT 服務允許任何設備或代碼連接會有哪些缺點?你能找到黑客利用這種情況的具體例子嗎? - -應用程序的其他組件可以連接到 IoT 服務,了解所有已連接或已註冊的設備,並直接與它們進行單個或批量通信。 -💁 物聯網服務亦提供額外功能,雲端供應商亦有額外的服務和應用程式可以連接到該服務。例如,如果你想將所有設備發送的遙測訊息存儲到資料庫中,通常只需在雲端供應商的配置工具中點擊幾下,就可以將服務連接到資料庫並將數據流入。 -## 在雲端建立 IoT 服務 - -現在你已擁有 Azure 訂閱,可以註冊一個 IoT 服務。Microsoft 的 IoT 服務叫做 Azure IoT Hub。 - -![Azure IoT Hub 標誌](../../../../../translated_images/zh-HK/azure-iot-hub-logo.28a19de76d0a1932464d858f7558712bcdace3e5ec69c434d482ed7ce41c3a26.png) - -以下影片簡要介紹了 Azure IoT Hub: - -[![Azure IoT Hub 概述影片](https://img.youtube.com/vi/smuZaZZXKsU/0.jpg)](https://www.youtube.com/watch?v=smuZaZZXKsU) - -> 🎥 點擊上方圖片觀看影片 - -✅ 花點時間進行研究,閱讀 [Microsoft IoT Hub 文件](https://docs.microsoft.com/azure/iot-hub/about-iot-hub?WT.mc_id=academic-17441-jabenn) 中的 IoT Hub 概述。 - -Azure 提供的雲端服務可以透過網頁式入口網站或命令列介面 (CLI) 進行配置。這次任務中,你將使用 CLI。 - -### 任務 - 安裝 Azure CLI - -要使用 Azure CLI,首先需要在你的 PC 或 Mac 上安裝它。 - -1. 按照 [Azure CLI 文件](https://docs.microsoft.com/cli/azure/install-azure-cli?WT.mc_id=academic-17441-jabenn) 中的指示安裝 CLI。 - -1. Azure CLI 支援多種擴展功能,這些功能可用於管理各種 Azure 服務。透過命令列或終端執行以下指令來安裝 IoT 擴展功能: - - ```sh - az extension add --name azure-iot - ``` - -1. 從命令列或終端執行以下指令,使用 Azure CLI 登錄你的 Azure 訂閱。 - - ```sh - az login - ``` - - 預設瀏覽器將開啟一個網頁。使用你註冊 Azure 訂閱的帳戶登錄。登錄完成後,可以關閉瀏覽器標籤。 - -1. 如果你有多個 Azure 訂閱,例如學校提供的訂閱和自己的 Azure for Students 訂閱,你需要選擇要使用的訂閱。執行以下指令列出你有權訪問的所有訂閱: - - ```sh - az account list --output table - ``` - - 在輸出中,你將看到每個訂閱的名稱及其 `SubscriptionId`。 - - ```output - ➜ ~ az account list --output table - Name CloudName SubscriptionId State IsDefault - ---------------------- ----------- ------------------------------------ ------- ----------- - School-subscription AzureCloud cb30cde9-814a-42f0-a111-754cb788e4e1 Enabled True - Azure for Students AzureCloud fa51c31b-162c-4599-add6-781def2e1fbf Enabled False - ``` - - 使用以下指令選擇你要使用的訂閱: - - ```sh - az account set --subscription - ``` - - 將 `` 替換為你想使用的訂閱 ID。執行此指令後,重新執行列出帳戶的指令。你會看到 `IsDefault` 列被標記為 `True`,表示你剛剛設置的訂閱。 - -### 任務 - 建立資源群組 - -Azure 服務,例如 IoT Hub 實例、虛擬機器、資料庫或 AI 服務,被稱為 **資源**。每個資源都必須屬於一個 **資源群組**,資源群組是一個或多個資源的邏輯分組。 - -> 💁 使用資源群組可以一次管理多個服務。例如,完成此專案的所有課程後,你可以刪除資源群組,所有資源將自動刪除。 - -1. Azure 在全球有多個數據中心,分布於不同的地區。當你建立 Azure 資源或資源群組時,必須指定其建立位置。執行以下指令以獲取位置列表: - - ```sh - az account list-locations --output table - ``` - - 你將看到一個位置列表。這個列表會很長。 - - > 💁 截至撰寫本文時,你可以部署到 65 個位置。 - - ```output - ➜ ~ az account list-locations --output table - DisplayName Name RegionalDisplayName - ------------------------ ------------------- ------------------------------------- - East US eastus (US) East US - East US 2 eastus2 (US) East US 2 - South Central US southcentralus (US) South Central US - ... - ``` - - 記下距離你最近的地區的 `Name` 列值。你可以在 [Azure 地理位置頁面](https://azure.microsoft.com/global-infrastructure/geographies/?WT.mc_id=academic-17441-jabenn) 上找到地區的地圖。 - -1. 執行以下指令建立名為 `soil-moisture-sensor` 的資源群組。資源群組名稱在你的訂閱中必須是唯一的。 - - ```sh - az group create --name soil-moisture-sensor \ - --location - ``` - - 將 `` 替換為你在上一步選擇的位置。 - -### 任務 - 建立 IoT Hub - -現在你可以在資源群組中建立 IoT Hub 資源。 - -1. 使用以下指令建立你的 IoT Hub 資源: - - ```sh - az iot hub create --resource-group soil-moisture-sensor \ - --sku F1 \ - --partition-count 2 \ - --name - ``` - - 將 `` 替換為你的 Hub 名稱。此名稱必須是全球唯一的——即任何人建立的 IoT Hub 都不能有相同的名稱。此名稱將用於指向 Hub 的 URL,因此需要唯一。使用類似 `soil-moisture-sensor-` 的名稱,並在末尾添加唯一標識符,例如隨機字或你的名字。 - - `--sku F1` 選項表示使用免費層。免費層每天支援 8,000 條消息,並提供大部分付費層的功能。 - - > 🎓 Azure 服務的不同定價級別被稱為層。每個層都有不同的成本,並提供不同的功能或數據量。 - - > 💁 如果你想了解更多定價資訊,可以查看 [Azure IoT Hub 定價指南](https://azure.microsoft.com/pricing/details/iot-hub/?WT.mc_id=academic-17441-jabenn)。 - - `--partition-count 2` 選項定義 IoT Hub 支援的數據流數量。更多分區可以減少多個設備讀寫 IoT Hub 時的數據阻塞。分區超出了這些課程的範疇,但此值需要設置以建立免費層 IoT Hub。 - - > 💁 每個訂閱只能有一個免費層 IoT Hub。 - -IoT Hub 將被建立。這可能需要一分鐘左右完成。 - -## 與 IoT Hub 通訊 - -在上一課中,你使用 MQTT 並在不同的主題上來回發送消息,每個主題有不同的用途。與其通過不同的主題發送消息,IoT Hub 提供了多種定義的方式供設備與 Hub 通訊,或 Hub 與設備通訊。 - -> 💁 在底層,IoT Hub 與設備之間的通訊可以使用 MQTT、HTTPS 或 AMQP。 - -* 設備到雲端 (D2C) 消息 - 這些是從設備發送到 IoT Hub 的消息,例如遙測數據。應用程式代碼可以從 IoT Hub 讀取這些消息。 - - > 🎓 在底層,IoT Hub 使用一個 Azure 服務叫做 [Event Hubs](https://docs.microsoft.com/azure/event-hubs/?WT.mc_id=academic-17441-jabenn)。當你編寫代碼來讀取發送到 Hub 的消息時,這些通常被稱為事件。 - -* 雲端到設備 (C2D) 消息 - 這些是從應用程式代碼通過 IoT Hub 發送到 IoT 設備的消息。 - -* 直接方法請求 - 這些是從應用程式代碼通過 IoT Hub 發送到 IoT 設備的消息,用於請求設備執行某些操作,例如控制執行器。這些消息需要回應,以便應用程式代碼知道是否成功處理。 - -* 設備雙胞胎 - 這些是 JSON 文件,保持設備與 IoT Hub 之間的同步,用於存儲設備報告的設置或其他屬性,或 IoT Hub 希望在設備上設置的屬性(稱為期望值)。 - -IoT Hub 可以存儲消息和直接方法請求一段可配置的時間(默認為一天),因此如果設備或應用程式代碼失去連接,重新連接後仍然可以檢索離線期間發送的消息。設備雙胞胎永久保存在 IoT Hub 中,因此設備可以隨時重新連接並獲取最新的設備雙胞胎。 - -✅ 進行研究:閱讀 IoT Hub 文件中的 [設備到雲端通訊指南](https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-d2c-guidance?WT.mc_id=academic-17441-jabenn) 和 [雲端到設備通訊指南](https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-c2d-guidance?WT.mc_id=academic-17441-jabenn)。 - -## 連接你的設備到 IoT 服務 - -Hub 建立後,你的 IoT 設備可以連接到它。只有註冊的設備才能連接到服務,因此你需要先註冊設備。註冊後,你可以獲得一個連接字串,設備可以使用它來連接。此連接字串是設備專屬的,包含 IoT Hub、設備和允許設備連接的密鑰資訊。 - -> 🎓 連接字串是一個通用術語,指包含連接詳細資訊的一段文字。它們在連接 IoT Hub、資料庫和許多其他服務時使用。通常包括服務的識別符,例如 URL,以及安全資訊如密鑰。這些字串會傳遞給 SDK 用於連接服務。 - -> ⚠️ 連接字串應保持安全!安全性將在未來課程中詳細介紹。 - -### 任務 - 註冊你的 IoT 設備 - -可以使用 Azure CLI 將 IoT 設備註冊到你的 IoT Hub。 - -1. 執行以下指令註冊設備: - - ```sh - az iot hub device-identity create --device-id soil-moisture-sensor \ - --hub-name - ``` - - 將 `` 替換為你用於 IoT Hub 的名稱。 - - 這將建立一個 ID 為 `soil-moisture-sensor` 的設備。 - -1. 當你的 IoT 設備使用 SDK 連接到 IoT Hub 時,需要使用一個連接字串,該字串提供 Hub 的 URL 和密鑰。執行以下指令以獲取連接字串: - - ```sh - az iot hub device-identity connection-string show --device-id soil-moisture-sensor \ - --output table \ - --hub-name - ``` - - 將 `` 替換為你用於 IoT Hub 的名稱。 - -1. 保存輸出中顯示的連接字串,稍後你將需要它。 - -### 任務 - 將你的 IoT 設備連接到雲端 - -按照相關指南將你的 IoT 設備連接到雲端: - -* [Arduino - Wio Terminal](wio-terminal-connect-hub.md) -* [單板電腦 - Raspberry Pi/虛擬 IoT 設備](single-board-computer-connect-hub.md) - -### 任務 - 監控事件 - -目前,你不需要更新伺服器代碼。你可以使用 Azure CLI 監控來自 IoT 設備的事件。 - -1. 確保你的 IoT 設備正在運行並發送土壤濕度遙測值。 - -1. 在命令提示符或終端中執行以下指令以監控發送到 IoT Hub 的消息: - - ```sh - az iot hub monitor-events --hub-name - ``` - - 將 `` 替換為你用於 IoT Hub 的名稱。 - - 你將在控制台輸出中看到消息,這些消息是由你的 IoT 設備發送的。 - - ```output - Starting event monitor, use ctrl-c to stop... - { - "event": { - "origin": "soil-moisture-sensor", - "module": "", - "interface": "", - "component": "", - "payload": "{\"soil_moisture\": 376}" - } - }, - { - "event": { - "origin": "soil-moisture-sensor", - "module": "", - "interface": "", - "component": "", - "payload": "{\"soil_moisture\": 381}" - } - } - ``` - - `payload` 的內容將與你的 IoT 設備發送的消息相匹配。 - - > 截至撰寫本文時,`az iot` 擴展在 Apple Silicon 上尚未完全運作。如果你使用 Apple Silicon 設備,則需要使用其他方式監控消息,例如 [Visual Studio Code 的 Azure IoT 工具](https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-vscode-iot-toolkit-cloud-device-messaging)。 - -1. 這些消息自動附加了一些屬性,例如發送的時間戳。這些屬性被稱為 *註解*。要查看所有消息註解,使用以下指令: - - ```sh - az iot hub monitor-events --properties anno --hub-name - ``` - - 將 `` 替換為你用於 IoT Hub 的名稱。 - - 你將在控制台輸出中看到消息,這些消息是由你的 IoT 設備發送的。 - - ```output - Starting event monitor, use ctrl-c to stop... - { - "event": { - "origin": "soil-moisture-sensor", - "module": "", - "interface": "", - "component": "", - "properties": {}, - "annotations": { - "iothub-connection-device-id": "soil-moisture-sensor", - "iothub-connection-auth-method": "{\"scope\":\"device\",\"type\":\"sas\",\"issuer\":\"iothub\",\"acceptingIpFilterRule\":null}", - "iothub-connection-auth-generation-id": "637553997165220462", - "iothub-enqueuedtime": 1619976150288, - "iothub-message-source": "Telemetry", - "x-opt-sequence-number": 1379, - "x-opt-offset": "550576", - "x-opt-enqueued-time": 1619976150277 - }, - "payload": "{\"soil_moisture\": 381}" - } - } - ``` - - 註解中的時間值是 [UNIX 時間](https://wikipedia.org/wiki/Unix_time),表示自 1970 年 1 月 1 日午夜以來的秒數。 - - 完成後退出事件監控。 - -### 任務 - 控制你的 IoT 設備 - -你也可以使用 Azure CLI 對你的 IoT 設備調用直接方法。 - -1. 在命令提示符或終端中執行以下指令以調用 IoT 設備上的 `relay_on` 方法: - - ```sh - az iot hub invoke-device-method --device-id soil-moisture-sensor \ - --method-name relay_on \ - --method-payload '{}' \ - --hub-name - ``` - - 將 ` - -` 使用你為 IoT Hub 設定的名稱。 - -這會發送一個直接方法請求,請求的方法由 `method-name` 指定。直接方法可以包含方法所需的數據作為負載,這些數據可以在 `method-payload` 參數中以 JSON 格式指定。 - -你會看到繼電器打開,並且你的 IoT 裝置會顯示相應的輸出: - -```output - Direct method received - relay_on - ``` - -1. 重複上述步驟,但將 `--method-name` 設定為 `relay_off`。你會看到繼電器關閉,並且 IoT 裝置會顯示相應的輸出。 - ---- - -## 🚀 挑戰 - -IoT Hub 的免費層每天允許 8,000 條消息。你所編寫的程式每 10 秒發送一次遙測消息。每天每 10 秒發送一條消息會產生多少條消息? - -思考一下土壤濕度測量應該多頻繁發送?如何修改你的程式以保持在免費層的限制內,同時能夠按需檢查但不過於頻繁?如果你想添加第二個裝置又該怎麼辦? - -## 課後測驗 - -[課後測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/16) - -## 回顧與自學 - -IoT Hub SDK 是開源的,適用於 Arduino 和 Python。在 GitHub 的程式碼倉庫中,有許多範例展示如何使用不同的 IoT Hub 功能。 - -* 如果你使用的是 Wio Terminal,請查看 [GitHub 上的 Arduino 範例](https://github.com/Azure/azure-iot-pal-arduino/tree/master/pal/samples) -* 如果你使用的是 Raspberry Pi 或虛擬裝置,請查看 [GitHub 上的 Python 範例](https://github.com/Azure/azure-iot-sdk-python/tree/master/azure-iot-hub/samples) - -## 作業 - -[了解雲端服務](assignment.md) - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/4-migrate-your-plant-to-the-cloud/assignment.md b/translations/hk/2-farm/lessons/4-migrate-your-plant-to-the-cloud/assignment.md deleted file mode 100644 index 6370c9610..000000000 --- a/translations/hk/2-farm/lessons/4-migrate-your-plant-to-the-cloud/assignment.md +++ /dev/null @@ -1,33 +0,0 @@ - -# 認識雲端服務 - -## 指引 - -雲端服務,例如 Microsoft 的 Azure,不僅僅是提供租用的計算資源。主要的雲端服務類型包括: - -* 基礎設施即服務 (IaaS) -* 平台即服務 (PaaS) -* 無伺服器架構 -* 軟件即服務 (SaaS) - -了解這些不同類型的服務,並解釋它們是什麼以及它們之間的差異。說明哪些服務與物聯網 (IoT) 開發者相關。 - -## 評分標準 - -| 評分標準 | 優秀 | 合格 | 需要改進 | -| -------- | ---- | ---- | -------- | -| 解釋不同的雲端服務 | 清楚解釋了所有 4 種服務類型 | 能夠解釋 3 種服務類型 | 只能解釋 1 或 2 種服務類型 | -| 解釋哪些服務與物聯網相關 | 描述了哪些服務與物聯網開發者相關並解釋了原因 | 描述了哪些服務與物聯網開發者相關但未解釋原因 | 無法描述哪些服務與物聯網開發者相關 | - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/4-migrate-your-plant-to-the-cloud/single-board-computer-connect-hub.md b/translations/hk/2-farm/lessons/4-migrate-your-plant-to-the-cloud/single-board-computer-connect-hub.md deleted file mode 100644 index feb763991..000000000 --- a/translations/hk/2-farm/lessons/4-migrate-your-plant-to-the-cloud/single-board-computer-connect-hub.md +++ /dev/null @@ -1,130 +0,0 @@ - -# 將您的 IoT 裝置連接到雲端 - 虛擬 IoT 硬件和 Raspberry Pi - -在本課程的這部分,您將把虛擬 IoT 裝置或 Raspberry Pi 連接到您的 IoT Hub,以傳送遙測數據並接收指令。 - -## 將您的裝置連接到 IoT Hub - -下一步是將您的裝置連接到 IoT Hub。 - -### 任務 - 連接到 IoT Hub - -1. 在 VS Code 中打開 `soil-moisture-sensor` 資料夾。如果您使用的是虛擬 IoT 裝置,請確保虛擬環境已在終端中運行。 - -1. 安裝一些額外的 Pip 套件: - - ```sh - pip3 install azure-iot-device - ``` - - `azure-iot-device` 是一個用於與 IoT Hub 通訊的庫。 - -1. 在 `app.py` 文件的頂部,現有的導入語句下方添加以下導入: - - ```python - from azure.iot.device import IoTHubDeviceClient, Message, MethodResponse - ``` - - 此代碼導入了與 IoT Hub 通訊的 SDK。 - -1. 刪除 `import paho.mqtt.client as mqtt` 這行代碼,因為不再需要此庫。刪除所有 MQTT 相關代碼,包括主題名稱、所有使用 `mqtt_client` 的代碼以及 `handle_command`。保留 `while True:` 循環,只需刪除此循環中的 `mqtt_client.publish` 行。 - -1. 在導入語句下方添加以下代碼: - - ```python - connection_string = "" - ``` - - 將 `` 替換為您在本課程早些時候為裝置檢索的連接字串。 - - > 💁 這並不是最佳做法。連接字串不應存儲在源代碼中,因為它可能被檢入源代碼控制並被任何人發現。我們在這裡這樣做是為了簡化操作。理想情況下,您應該使用類似環境變數的方式以及像 [`python-dotenv`](https://pypi.org/project/python-dotenv/) 這樣的工具。您將在即將到來的課程中學到更多相關內容。 - -1. 在此代碼下方添加以下代碼,以創建一個可以與 IoT Hub 通訊的裝置客戶端對象並連接它: - - ```python - device_client = IoTHubDeviceClient.create_from_connection_string(connection_string) - - print('Connecting') - device_client.connect() - print('Connected') - ``` - -1. 運行此代碼。您將看到您的裝置已連接。 - - ```output - pi@raspberrypi:~/soil-moisture-sensor $ python3 app.py - Connecting - Connected - Soil moisture: 379 - ``` - -## 傳送遙測數據 - -現在您的裝置已連接,您可以將遙測數據傳送到 IoT Hub,而不是 MQTT broker。 - -### 任務 - 傳送遙測數據 - -1. 在 `while True` 循環內,睡眠之前添加以下代碼: - - ```python - message = Message(json.dumps({ 'soil_moisture': soil_moisture })) - device_client.send_message(message) - ``` - - 此代碼創建了一個包含土壤濕度讀數的 JSON 字串的 IoT Hub `Message`,然後將其作為裝置到雲端的消息傳送到 IoT Hub。 - -## 處理指令 - -您的裝置需要處理來自伺服器代碼的指令,以控制繼電器。這是通過直接方法請求發送的。 - -## 任務 - 處理直接方法請求 - -1. 在 `while True` 循環之前添加以下代碼: - - ```python - def handle_method_request(request): - print("Direct method received - ", request.name) - - if request.name == "relay_on": - relay.on() - elif request.name == "relay_off": - relay.off() - ``` - - 此代碼定義了一個方法 `handle_method_request`,當 IoT Hub 調用直接方法時會被調用。每個直接方法都有一個名稱,此代碼期望名為 `relay_on` 的方法用於打開繼電器,以及名為 `relay_off` 的方法用於關閉繼電器。 - - > 💁 這也可以通過單一的直接方法請求來實現,將繼電器的期望狀態作為有效負載傳遞,該有效負載可以通過方法請求傳遞並從 `request` 對象中獲取。 - -1. 直接方法需要一個響應來告訴調用代碼它們已被處理。在 `handle_method_request` 函數的末尾添加以下代碼,以創建對請求的響應: - - ```python - method_response = MethodResponse.create_from_method_request(request, 200) - device_client.send_method_response(method_response) - ``` - - 此代碼向直接方法請求發送一個 HTTP 狀態碼為 200 的響應,並將其返回到 IoT Hub。 - -1. 在此函數定義下方添加以下代碼: - - ```python - device_client.on_method_request_received = handle_method_request - ``` - - 此代碼告訴 IoT Hub 客戶端在調用直接方法時調用 `handle_method_request` 函數。 - -> 💁 您可以在 [code/pi](../../../../../2-farm/lessons/4-migrate-your-plant-to-the-cloud/code/pi) 或 [code/virtual-device](../../../../../2-farm/lessons/4-migrate-your-plant-to-the-cloud/code/virtual-device) 資料夾中找到此代碼。 - -😀 您的土壤濕度感測器程式已連接到您的 IoT Hub! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言版本的文件應被視為具權威性的來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/4-migrate-your-plant-to-the-cloud/wio-terminal-connect-hub.md b/translations/hk/2-farm/lessons/4-migrate-your-plant-to-the-cloud/wio-terminal-connect-hub.md deleted file mode 100644 index 2b2a5b99f..000000000 --- a/translations/hk/2-farm/lessons/4-migrate-your-plant-to-the-cloud/wio-terminal-connect-hub.md +++ /dev/null @@ -1,306 +0,0 @@ - -# 將您的 IoT 裝置連接到雲端 - Wio Terminal - -在這部分課程中,您將把 Wio Terminal 連接到 IoT Hub,以傳送遙測數據並接收指令。 - -## 將裝置連接到 IoT Hub - -下一步是將您的裝置連接到 IoT Hub。 - -### 任務 - 連接到 IoT Hub - -1. 在 VS Code 中打開 `soil-moisture-sensor` 專案。 - -1. 打開 `platformio.ini` 檔案。移除 `knolleary/PubSubClient` 函式庫的依賴。這個函式庫是用來連接到公共 MQTT broker 的,但連接到 IoT Hub 時不需要。 - -1. 添加以下函式庫依賴: - - ```ini - seeed-studio/Seeed Arduino RTC @ 2.0.0 - arduino-libraries/AzureIoTHub @ 1.6.0 - azure/AzureIoTUtility @ 1.6.1 - azure/AzureIoTProtocol_MQTT @ 1.6.0 - azure/AzureIoTProtocol_HTTP @ 1.6.0 - azure/AzureIoTSocket_WiFi @ 1.0.2 - ``` - - `Seeed Arduino RTC` 函式庫提供與 Wio Terminal 的實時時鐘互動的程式碼,用於追蹤時間。其餘的函式庫則讓您的 IoT 裝置能夠連接到 IoT Hub。 - -1. 在 `platformio.ini` 檔案的底部添加以下內容: - - ```ini - build_flags = - -DDONT_USE_UPLOADTOBLOB - ``` - - 這會設置一個編譯器標誌,在編譯 Arduino IoT Hub 程式碼時需要用到。 - -1. 打開 `config.h` 標頭檔案。移除所有 MQTT 設定,並添加以下裝置連接字串的常數: - - ```cpp - // IoT Hub settings - const char *CONNECTION_STRING = ""; - ``` - - 將 `` 替換為您之前複製的裝置連接字串。 - -1. 連接到 IoT Hub 使用的是基於時間的權杖。這意味著 IoT 裝置需要知道當前時間。與 Windows、macOS 或 Linux 等操作系統不同,微控制器不會自動通過網際網路同步當前時間。因此,您需要添加程式碼從 [NTP](https://wikipedia.org/wiki/Network_Time_Protocol) 伺服器獲取當前時間。一旦獲取時間,它可以存儲在 Wio Terminal 的實時時鐘中,這樣即使裝置未斷電,也能在稍後請求正確的時間。新增一個名為 `ntp.h` 的檔案,並添加以下程式碼: - - ```cpp - #pragma once - - #include "DateTime.h" - #include - #include "samd/NTPClientAz.h" - #include - - static void initTime() - { - WiFiUDP _udp; - time_t epochTime = (time_t)-1; - NTPClientAz ntpClient; - - ntpClient.begin(); - - while (true) - { - epochTime = ntpClient.getEpochTime("0.pool.ntp.org"); - - if (epochTime == (time_t)-1) - { - Serial.println("Fetching NTP epoch time failed! Waiting 2 seconds to retry."); - delay(2000); - } - else - { - Serial.print("Fetched NTP epoch time is: "); - - char buff[32]; - sprintf(buff, "%.f", difftime(epochTime, (time_t)0)); - Serial.println(buff); - break; - } - } - - ntpClient.end(); - - struct timeval tv; - tv.tv_sec = epochTime; - tv.tv_usec = 0; - - settimeofday(&tv, NULL); - } - ``` - - 這段程式碼的細節超出了本課程的範圍。它定義了一個名為 `initTime` 的函式,用於從 NTP 伺服器獲取當前時間,並用它來設置 Wio Terminal 的時鐘。 - -1. 打開 `main.cpp` 檔案,移除所有 MQTT 程式碼,包括 `PubSubClient.h` 標頭檔案、`PubSubClient` 變數的宣告、`reconnectMQTTClient` 和 `createMQTTClient` 方法,以及對這些變數和方法的任何調用。這個檔案應該只包含連接 WiFi、獲取土壤濕度並用它創建 JSON 文件的程式碼。 - -1. 在 `main.cpp` 檔案的頂部添加以下 `#include` 指令,以包含 IoT Hub 函式庫和設置時間的標頭檔案: - - ```cpp - #include - #include - #include - #include "ntp.h" - ``` - -1. 在 `setup` 函式的末尾添加以下調用來設置當前時間: - - ```cpp - initTime(); - ``` - -1. 在檔案頂部的 `#include` 指令下方添加以下變數宣告: - - ```cpp - IOTHUB_DEVICE_CLIENT_LL_HANDLE _device_ll_handle; - ``` - - 這宣告了一個 `IOTHUB_DEVICE_CLIENT_LL_HANDLE`,即 IoT Hub 連接的句柄。 - -1. 在這之下添加以下程式碼: - - ```cpp - static void connectionStatusCallback(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void *user_context) - { - if (result == IOTHUB_CLIENT_CONNECTION_AUTHENTICATED) - { - Serial.println("The device client is connected to iothub"); - } - else - { - Serial.println("The device client has been disconnected"); - } - } - ``` - - 這定義了一個回調函式,當連接到 IoT Hub 的狀態發生變化(例如連接或斷開)時會被調用。狀態會被發送到序列埠。 - -1. 在這之下添加一個函式來連接到 IoT Hub: - - ```cpp - void connectIoTHub() - { - IoTHub_Init(); - - _device_ll_handle = IoTHubDeviceClient_LL_CreateFromConnectionString(CONNECTION_STRING, MQTT_Protocol); - - if (_device_ll_handle == NULL) - { - Serial.println("Failure creating Iothub device. Hint: Check your connection string."); - return; - } - - IoTHubDeviceClient_LL_SetConnectionStatusCallback(_device_ll_handle, connectionStatusCallback, NULL); - } - ``` - - 這段程式碼初始化 IoT Hub 函式庫程式碼,然後使用 `config.h` 標頭檔案中的連接字串創建連接。這個連接基於 MQTT。如果連接失敗,錯誤會被發送到序列埠——如果您在輸出中看到這個錯誤,請檢查連接字串。最後,設置連接狀態回調。 - -1. 在 `setup` 函式中,在調用 `initTime` 的下方調用這個函式: - - ```cpp - connectIoTHub(); - ``` - -1. 與 MQTT 客戶端一樣,這段程式碼運行在單線程上,因此需要時間來處理由 Hub 發送和接收的消息。在 `loop` 函式的頂部添加以下內容來實現: - - ```cpp - IoTHubDeviceClient_LL_DoWork(_device_ll_handle); - ``` - -1. 編譯並上傳這段程式碼。您會在序列監視器中看到連接: - - ```output - Connecting to WiFi.. - Connected! - Fetched NTP epoch time is: 1619983687 - Sending telemetry {"soil_moisture":391} - The device client is connected to iothub - ``` - - 在輸出中,您可以看到 NTP 時間被獲取,然後裝置客戶端連接。連接可能需要幾秒鐘,因此在裝置連接時,您可能會在輸出中看到土壤濕度。 - - > 💁 您可以使用像 [unixtimestamp.com](https://www.unixtimestamp.com) 這樣的網站將 NTP 的 UNIX 時間轉換為更易讀的格式。 - -## 傳送遙測數據 - -現在您的裝置已連接,您可以將遙測數據傳送到 IoT Hub,而不是 MQTT broker。 - -### 任務 - 傳送遙測數據 - -1. 在 `setup` 函式上方添加以下函式: - - ```cpp - void sendTelemetry(const char *telemetry) - { - IOTHUB_MESSAGE_HANDLE message_handle = IoTHubMessage_CreateFromString(telemetry); - IoTHubDeviceClient_LL_SendEventAsync(_device_ll_handle, message_handle, NULL, NULL); - IoTHubMessage_Destroy(message_handle); - } - ``` - - 這段程式碼從作為參數傳遞的字串創建一個 IoT Hub 消息,將其發送到 Hub,然後清理消息物件。 - -1. 在 `loop` 函式中,在將遙測數據發送到序列埠的那一行之後調用這段程式碼: - - ```cpp - sendTelemetry(telemetry.c_str()); - ``` - -## 處理指令 - -您的裝置需要處理來自伺服器程式碼的指令,以控制繼電器。這是通過直接方法請求發送的。 - -### 任務 - 處理直接方法請求 - -1. 在 `connectIoTHub` 函式之前添加以下程式碼: - - ```cpp - int directMethodCallback(const char *method_name, const unsigned char *payload, size_t size, unsigned char **response, size_t *response_size, void *userContextCallback) - { - Serial.printf("Direct method received %s\r\n", method_name); - - if (strcmp(method_name, "relay_on") == 0) - { - digitalWrite(PIN_WIRE_SCL, HIGH); - } - else if (strcmp(method_name, "relay_off") == 0) - { - digitalWrite(PIN_WIRE_SCL, LOW); - } - } - ``` - - 這段程式碼定義了一個回調方法,當 IoT Hub 函式庫收到直接方法請求時會調用。請求的方法名稱會通過 `method_name` 參數傳遞。這個函式會將調用的方法名稱打印到序列埠,然後根據方法名稱打開或關閉繼電器。 - - > 💁 這也可以通過單一的直接方法請求來實現,將繼電器的期望狀態作為有效負載傳遞,該有效負載可以通過 `payload` 參數獲取。 - -1. 在 `directMethodCallback` 函式的末尾添加以下程式碼: - - ```cpp - char resultBuff[16]; - sprintf(resultBuff, "{\"Result\":\"\"}"); - *response_size = strlen(resultBuff); - *response = (unsigned char *)malloc(*response_size); - memcpy(*response, resultBuff, *response_size); - - return IOTHUB_CLIENT_OK; - ``` - - 直接方法請求需要一個回應,回應分為兩部分——作為文字的回應和返回碼。這段程式碼會創建以下 JSON 文件作為結果: - - ```JSON - { - "Result": "" - } - ``` - - 然後將其複製到 `response` 參數中,並在 `response_size` 參數中設置這個回應的大小。這段程式碼最後返回 `IOTHUB_CLIENT_OK`,表示方法已正確處理。 - -1. 在 `connectIoTHub` 函式的末尾添加以下程式碼來連接回調: - - ```cpp - IoTHubClient_LL_SetDeviceMethodCallback(_device_ll_handle, directMethodCallback, NULL); - ``` - -1. `loop` 函式會調用 `IoTHubDeviceClient_LL_DoWork` 函式來處理 IoT Hub 發送的事件。由於 `delay` 的原因,這個函式每 10 秒才會被調用一次,這意味著直接方法每 10 秒才會被處理一次。為了提高效率,可以將 10 秒的延遲分解為多個較短的延遲,每次調用 `IoTHubDeviceClient_LL_DoWork`。為此,在 `loop` 函式之前添加以下程式碼: - - ```cpp - void work_delay(int delay_time) - { - int current = 0; - do - { - IoTHubDeviceClient_LL_DoWork(_device_ll_handle); - delay(100); - current += 100; - } while (current < delay_time); - } - ``` - - 這段程式碼會重複循環,調用 `IoTHubDeviceClient_LL_DoWork`,每次延遲 100 毫秒。它會根據 `delay_time` 參數的值執行多次,這意味著裝置最多等待 100 毫秒來處理直接方法請求。 - -1. 在 `loop` 函式中,移除對 `IoTHubDeviceClient_LL_DoWork` 的調用,並將 `delay(10000)` 替換為以下程式碼來調用這個新函式: - - ```cpp - work_delay(10000); - ``` - -> 💁 您可以在 [code/wio-terminal](../../../../../2-farm/lessons/4-migrate-your-plant-to-the-cloud/code/wio-terminal) 資料夾中找到這段程式碼。 - -😀 您的土壤濕度感測器程式已成功連接到 IoT Hub! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/5-migrate-application-to-the-cloud/README.md b/translations/hk/2-farm/lessons/5-migrate-application-to-the-cloud/README.md deleted file mode 100644 index e0a2b7e05..000000000 --- a/translations/hk/2-farm/lessons/5-migrate-application-to-the-cloud/README.md +++ /dev/null @@ -1,656 +0,0 @@ - -# 將應用程式邏輯遷移到雲端 - -![本課程的手繪筆記概覽](../../../../../translated_images/zh-HK/lesson-9.dfe99c8e891f48e179724520da9f5794392cf9a625079281ccdcbf09bd85e1b6.jpg) - -> 手繪筆記由 [Nitya Narasimhan](https://github.com/nitya) 提供。點擊圖片查看更大版本。 - -本課程是 [IoT for Beginners Project 2 - Digital Agriculture 系列](https://youtube.com/playlist?list=PLmsFUfdnGr3yCutmcVg6eAUEfsGiFXgcx) 的一部分,由 [Microsoft Reactor](https://developer.microsoft.com/reactor/?WT.mc_id=academic-17441-jabenn) 提供。 - -[![使用無伺服器代碼控制您的 IoT 裝置](https://img.youtube.com/vi/VVZDcs5u1_I/0.jpg)](https://youtu.be/VVZDcs5u1_I) - -## 課前測驗 - -[課前測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/17) - -## 簡介 - -在上一課中,您學會了如何將植物土壤濕度監測和繼電器控制連接到基於雲端的 IoT 服務。下一步是將控制繼電器定時的伺服器代碼遷移到雲端。在本課中,您將學習如何使用無伺服器函數來完成這一過程。 - -本課將涵蓋以下內容: - -* [什麼是無伺服器?](../../../../../2-farm/lessons/5-migrate-application-to-the-cloud) -* [建立無伺服器應用程式](../../../../../2-farm/lessons/5-migrate-application-to-the-cloud) -* [建立 IoT Hub 事件觸發器](../../../../../2-farm/lessons/5-migrate-application-to-the-cloud) -* [從無伺服器代碼發送直接方法請求](../../../../../2-farm/lessons/5-migrate-application-to-the-cloud) -* [將無伺服器代碼部署到雲端](../../../../../2-farm/lessons/5-migrate-application-to-the-cloud) - -## 什麼是無伺服器? - -無伺服器(或無伺服器計算)是指創建小型代碼塊,這些代碼塊會在雲端中根據不同類型的事件執行。當事件發生時,您的代碼會被執行,並接收有關該事件的數據。這些事件可以來自多種來源,包括網頁請求、放入佇列的消息、數據庫中數據的更改,或者 IoT 裝置發送到 IoT 服務的消息。 - -![事件從 IoT 服務發送到無伺服器服務,所有事件同時由多個函數處理](../../../../../translated_images/zh-HK/iot-messages-to-serverless.0194da1cc0732bb7d0f823aed3fce54735c6b1ad3bf36089804d8aaefc0a774f.png) - -> 💁 如果您之前使用過數據庫觸發器,可以將其視為類似的概念,即代碼因事件(如插入一行)而觸發。 - -![當許多事件同時發生時,無伺服器服務會擴展以同時處理所有事件](../../../../../translated_images/zh-HK/serverless-scaling.f8c769adf0413fd1.webp) - -您的代碼僅在事件發生時執行,其他時間並不會保持活躍。事件發生時,您的代碼會被加載並執行。這使得無伺服器具有很高的可擴展性——如果許多事件同時發生,雲端提供商可以根據需要同時運行多個函數,利用其可用的伺服器資源。這種模式的缺點是,如果需要在事件之間共享信息,則需要將其存儲在數據庫等地方,而不是存儲在記憶體中。 - -您的代碼以函數的形式編寫,並將事件的詳細信息作為參數傳遞。您可以使用多種程式語言來編寫這些無伺服器函數。 - -> 🎓 無伺服器也被稱為「函數即服務」(Functions as a Service,FaaS),因為每個事件觸發器都以代碼中的函數形式實現。 - -儘管名稱是「無伺服器」,但實際上仍然使用伺服器。這個名稱的由來是因為作為開發者,您不需要關心運行代碼所需的伺服器,您只需關心代碼是否能響應事件執行。雲端提供商有一個無伺服器*運行時*,負責管理伺服器分配、網絡、存儲、CPU、記憶體以及運行代碼所需的一切。這種模式意味著您無法按伺服器付費,因為並不存在具體的伺服器。相反,您按代碼運行的時間和使用的記憶體量付費。 - -> 💰 無伺服器是運行雲端代碼最便宜的方式之一。例如,在撰寫本文時,一家雲端提供商允許所有無伺服器函數每月執行總計 1,000,000 次之前免費,超過後每 1,000,000 次執行收費 0.20 美元。當您的代碼未運行時,您不需要支付任何費用。 - -作為 IoT 開發者,無伺服器模型非常理想。您可以編寫一個函數,當任何連接到雲端 IoT 服務的 IoT 裝置發送消息時,該函數會被調用。您的代碼將處理所有發送的消息,但僅在需要時運行。 - -✅ 回顧您之前編寫的伺服器代碼,該代碼通過 MQTT 接收消息。這段代碼如何在雲端使用無伺服器運行?您認為需要對代碼進行哪些更改以支持無伺服器計算? - -> 💁 無伺服器模型正在擴展到其他雲端服務,不僅僅是運行代碼。例如,雲端中還有無伺服器數據庫,這些數據庫使用無伺服器定價模型,按對數據庫的請求次數(如查詢或插入)付費,通常基於完成請求所需的工作量定價。例如,根據主鍵選擇一行的操作成本低於執行多表聯接並返回數千行的複雜操作。 - -## 建立無伺服器應用程式 - -Microsoft 的無伺服器計算服務稱為 Azure Functions。 - -![Azure Functions 標誌](../../../../../translated_images/zh-HK/azure-functions-logo.1cfc8e3204c9c44aaf80fcf406fc8544d80d7f00f8d3e8ed6fed764563e17564.png) - -以下的短視頻提供了 Azure Functions 的概覽: - -[![Azure Functions 概覽視頻](https://img.youtube.com/vi/8-jz5f_JyEQ/0.jpg)](https://www.youtube.com/watch?v=8-jz5f_JyEQ) - -> 🎥 點擊上方圖片觀看視頻 - -✅ 花點時間進行一些研究,閱讀 [Microsoft Azure Functions 文檔](https://docs.microsoft.com/azure/azure-functions/functions-overview?WT.mc_id=academic-17441-jabenn) 中的 Azure Functions 概覽。 - -要編寫 Azure Functions,您需要從選擇的程式語言開始創建 Azure Functions 應用程式。Azure Functions 原生支持 Python、JavaScript、TypeScript、C#、F#、Java 和 Powershell。在本課中,您將學習如何使用 Python 編寫 Azure Functions 應用程式。 - -> 💁 Azure Functions 還支持自定義處理程序,因此您可以使用任何支持 HTTP 請求的語言編寫函數,包括像 COBOL 這樣的舊語言。 - -Functions 應用程式由一個或多個*觸發器*組成——這些觸發器是響應事件的函數。您可以在一個應用程式中包含多個觸發器,並共享通用配置。例如,您的 Functions 應用程式的配置文件中可以包含 IoT Hub 的連接詳細信息,應用程式中的所有函數都可以使用這些信息來連接並監聽事件。 - -### 任務 - 安裝 Azure Functions 工具 - -> 在撰寫本文時,Azure Functions 的代碼工具在 Apple Silicon 上對 Python 項目尚未完全支持。您需要使用基於 Intel 的 Mac、Windows PC 或 Linux PC。 - -Azure Functions 的一大特點是您可以在本地運行它們。與雲端中使用的運行時相同的運行時可以在您的電腦上運行,這使您能夠編寫響應 IoT 消息的代碼並在本地運行。您甚至可以在處理事件時調試代碼。一旦您對代碼感到滿意,就可以將其部署到雲端。 - -Azure Functions 工具可作為 CLI 使用,稱為 Azure Functions Core Tools。 - -1. 按照 [Azure Functions Core Tools 文檔](https://docs.microsoft.com/azure/azure-functions/functions-run-local?WT.mc_id=academic-17441-jabenn) 中的說明安裝 Azure Functions Core Tools。 - -1. 安裝 VS Code 的 Azure Functions 擴展。此擴展提供創建、調試和部署 Azure Functions 的支持。請參考 [Azure Functions 擴展文檔](https://marketplace.visualstudio.com/items?WT.mc_id=academic-17441-jabenn&itemName=ms-azuretools.vscode-azurefunctions) 了解如何在 VS Code 中安裝此擴展。 - -當您將 Azure Functions 應用程式部署到雲端時,它需要使用少量雲端存儲來存儲應用程式文件和日誌文件等內容。當您在本地運行 Functions 應用程式時,仍然需要連接到雲端存儲,但可以使用名為 [Azurite](https://github.com/Azure/Azurite) 的存儲模擬器。這個模擬器在本地運行,但表現得像雲端存儲。 - -> 🎓 在 Azure 中,Azure Functions 使用的存儲是 Azure Storage Account。這些帳戶可以存儲文件、Blob、表中的數據或佇列中的數據。您可以在多個應用程式之間共享一個存儲帳戶,例如 Functions 應用程式和 Web 應用程式。 - -1. Azurite 是一個 Node.js 應用程式,因此您需要安裝 Node.js。您可以在 [Node.js 官網](https://nodejs.org/) 上找到下載和安裝說明。如果您使用的是 Mac,也可以通過 [Homebrew](https://formulae.brew.sh/formula/node) 安裝。 - -1. 使用以下命令安裝 Azurite(`npm` 是安裝 Node.js 時附帶的工具): - - ```sh - npm install -g azurite - ``` - -1. 創建一個名為 `azurite` 的資料夾,供 Azurite 用於存儲數據: - - ```sh - mkdir azurite - ``` - -1. 運行 Azurite,並將此新資料夾作為參數傳遞: - - ```sh - azurite --location azurite - ``` - - Azurite 存儲模擬器將啟動,並準備好供本地 Functions 運行時連接。 - - ```output - ➜ ~ azurite --location azurite - Azurite Blob service is starting at http://127.0.0.1:10000 - Azurite Blob service is successfully listening at http://127.0.0.1:10000 - Azurite Queue service is starting at http://127.0.0.1:10001 - Azurite Queue service is successfully listening at http://127.0.0.1:10001 - Azurite Table service is starting at http://127.0.0.1:10002 - Azurite Table service is successfully listening at http://127.0.0.1:10002 - ``` - -### 任務 - 創建 Azure Functions 項目 - -Azure Functions CLI 可用於創建新的 Functions 應用程式。 - -1. 為您的 Functions 應用程式創建一個資料夾並進入該資料夾。將其命名為 `soil-moisture-trigger`: - - ```sh - mkdir soil-moisture-trigger - cd soil-moisture-trigger - ``` - -1. 在此資料夾中創建一個 Python 虛擬環境: - - ```sh - python3 -m venv .venv - ``` - -1. 啟用虛擬環境: - - * 在 Windows 上: - * 如果您使用的是命令提示符或 Windows Terminal 中的命令提示符,運行: - - ```cmd - .venv\Scripts\activate.bat - ``` - - * 如果您使用的是 PowerShell,運行: - - ```powershell - .\.venv\Scripts\Activate.ps1 - ``` - - * 在 macOS 或 Linux 上,運行: - - ```cmd - source ./.venv/bin/activate - ``` - - > 💁 這些命令應從您創建虛擬環境的相同位置運行。您永遠不需要進入 `.venv` 資料夾,應始終從創建虛擬環境時所在的資料夾運行啟用命令以及任何安裝包或運行代碼的命令。 - -1. 運行以下命令以在此資料夾中創建 Functions 應用程式: - - ```sh - func init --worker-runtime python soil-moisture-trigger - ``` - - 這將在當前資料夾中創建三個文件: - - * `host.json` - 此 JSON 文件包含您的 Functions 應用程式的設置。您無需修改這些設置。 - * `local.settings.json` - 此 JSON 文件包含應用程式在本地運行時使用的設置,例如 IoT Hub 的連接字符串。這些設置僅限於本地使用,不應添加到源代碼控制中。當應用程式部署到雲端時,這些設置不會被部署,而是從應用程式設置中加載。這將在本課稍後介紹。 - * `requirements.txt` - 這是一個 [Pip requirements 文件](https://pip.pypa.io/en/stable/user_guide/#requirements-files),其中包含運行 Functions 應用程式所需的 Pip 包。 - -1. `local.settings.json` 文件中有一個設置是 Functions 應用程式將使用的存儲帳戶。默認為空設置,因此需要設置為以下值以連接到 Azurite 本地存儲模擬器: - - ```json - "AzureWebJobsStorage": "UseDevelopmentStorage=true", - ``` - -1. 使用 requirements 文件安裝必要的 Pip 包: - - ```sh - pip install -r requirements.txt - ``` - - > 💁 必需的 Pip 包需要在此文件中,以便當 Functions 應用程式部署到雲端時,運行時可以確保安裝正確的包。 - -1. 為測試一切是否正常運行,您可以啟動 Functions 運行時。運行以下命令啟動: - - ```sh - func start - ``` - - 您將看到運行時啟動並報告未找到任何作業函數(觸發器)。 - - ```output - (.venv) ➜ soil-moisture-trigger func start - Found Python version 3.9.1 (python3). - - Azure Functions Core Tools - Core Tools Version: 3.0.3442 Commit hash: 6bfab24b2743f8421475d996402c398d2fe4a9e0 (64-bit) - Function Runtime Version: 3.0.15417.0 - - [2021-05-05T01:24:46.795Z] No job functions found. - ``` -> ⚠️ 如果收到防火牆通知,請授予訪問權限,因為 `func` 應用程式需要能夠讀寫您的網絡。 -> ⚠️ 如果你正在使用 macOS,輸出可能會出現警告: -> -> ```output - > (.venv) ➜ soil-moisture-trigger func start - > Found Python version 3.9.1 (python3). - > - > Azure Functions Core Tools - > Core Tools Version: 3.0.3442 Commit hash: 6bfab24b2743f8421475d996402c398d2fe4a9e0 (64-bit) - > Function Runtime Version: 3.0.15417.0 - > - > [2021-06-16T08:18:28.315Z] Cannot create directory for shared memory usage: /dev/shm/AzureFunctions - > [2021-06-16T08:18:28.316Z] System.IO.FileSystem: Access to the path '/dev/shm/AzureFunctions' is denied. Operation not permitted. - > [2021-06-16T08:18:30.361Z] No job functions found. - > ``` -> -> 只要 Functions 應用程式能正確啟動並列出正在運行的函數,你可以忽略這些警告。如 [Microsoft Docs Q&A 上的這個問題](https://docs.microsoft.com/answers/questions/396617/azure-functions-core-tools-error-osx-devshmazurefu.html?WT.mc_id=academic-17441-jabenn) 所述,這些警告可以忽略。 - -1. 按下 `ctrl+c` 停止 Functions 應用程式。 - -1. 在 VS Code 中打開當前資料夾,可以直接打開 VS Code 然後選擇此資料夾,或者執行以下命令: - - ```sh - code . - ``` - - VS Code 會檢測到你的 Functions 專案並顯示一個通知: - - ```output - Detected an Azure Functions Project in folder "soil-moisture-trigger" that may have been created outside of - VS Code. Initialize for optimal use with VS Code? - ``` - - ![通知](../../../../../translated_images/zh-HK/vscode-azure-functions-init-notification.bd19b49229963edb.webp) - - 在通知中選擇 **Yes**。 - -1. 確保 Python 虛擬環境在 VS Code 的終端中正在運行。如果需要,終止並重新啟動它。 - -## 建立 IoT Hub 事件觸發器 - -Functions 應用程式是你的無伺服器代碼的外殼。為了響應 IoT Hub 的事件,你可以在此應用程式中新增一個 IoT Hub 觸發器。此觸發器需要連接到發送到 IoT Hub 的消息流並對其作出響應。要獲取這些消息流,你的觸發器需要連接到 IoT Hub 的 *事件中心兼容端點*。 - -IoT Hub 是基於另一個 Azure 服務 Azure Event Hubs 的。Event Hubs 是一個允許發送和接收消息的服務,而 IoT Hub 擴展了它以增加 IoT 設備的功能。連接到 IoT Hub 以讀取消息的方式與使用 Event Hubs 是相同的。 - -✅ 做一些研究:閱讀 [Azure Event Hubs 文件](https://docs.microsoft.com/azure/event-hubs/event-hubs-about?WT.mc_id=academic-17441-jabenn) 中的概述。基本功能與 IoT Hub 有何不同? - -要讓 IoT 設備連接到 IoT Hub,它必須使用一個密鑰來確保只有允許的設備可以連接。同樣,當連接以讀取消息時,你的代碼需要一個包含密鑰的連接字串以及 IoT Hub 的詳細信息。 - -> 💁 預設的連接字串具有 **iothubowner** 權限,這使得任何使用它的代碼都擁有 IoT Hub 的全部權限。理想情況下,你應該使用最低必要權限進行連接。這將在下一課中介紹。 - -一旦你的觸發器已連接,函數中的代碼將會在每次消息發送到 IoT Hub 時被調用,無論是哪個設備發送的。觸發器會將消息作為參數傳遞。 - -### 任務 - 獲取事件中心兼容端點的連接字串 - -1. 從 VS Code 的終端執行以下命令以獲取 IoT Hub 的事件中心兼容端點的連接字串: - - ```sh - az iot hub connection-string show --default-eventhub \ - --output table \ - --hub-name - ``` - - 將 `` 替換為你用於 IoT Hub 的名稱。 - -1. 在 VS Code 中打開 `local.settings.json` 文件。在 `Values` 部分內新增以下值: - - ```json - "IOT_HUB_CONNECTION_STRING": "" - ``` - - 將 `` 替換為上一步中的值。你需要在上一行後添加逗號以使其成為有效的 JSON。 - -### 任務 - 建立事件觸發器 - -現在你可以建立事件觸發器了。 - -1. 從 VS Code 的終端,在 `soil-moisture-trigger` 資料夾內執行以下命令: - - ```sh - func new --name iot-hub-trigger --template "Azure Event Hub trigger" - ``` - - 這將建立一個名為 `iot-hub-trigger` 的新函數。觸發器將連接到 IoT Hub 的事件中心兼容端點,因此你可以使用事件中心觸發器。沒有特定的 IoT Hub 觸發器。 - -這將在 `soil-moisture-trigger` 資料夾內建立一個名為 `iot-hub-trigger` 的資料夾,其中包含此函數。此資料夾內將包含以下文件: - -* `__init__.py` - 這是包含觸發器的 Python 代碼文件,使用標準的 Python 文件命名規則將此資料夾轉換為 Python 模組。 - - 此文件將包含以下代碼: - - ```python - import logging - - import azure.functions as func - - - def main(event: func.EventHubEvent): - logging.info('Python EventHub trigger processed an event: %s', - event.get_body().decode('utf-8')) - ``` - - 觸發器的核心是 `main` 函數。每次 IoT Hub 發送消息時,該函數都會被調用,並將消息作為 `event` 傳遞,還有一些屬性與你在上一課中看到的註解相同。 - - 此函數的核心是記錄事件。 - -* `function.json` - 此文件包含觸發器的配置。主要配置在名為 `bindings` 的部分中。綁定是 Azure Functions 與其他 Azure 服務之間的連接術語。此函數有一個輸入綁定到事件中心 - 它連接到事件中心並接收數據。 - - > 💁 你也可以有輸出綁定,函數的輸出可以發送到另一個服務。例如,你可以新增一個輸出綁定到資料庫,並將 IoT Hub 的事件從函數返回,這樣它就會自動插入到資料庫中。 - - ✅ 做一些研究:閱讀 [Azure Functions 觸發器和綁定概念文件](https://docs.microsoft.com/azure/azure-functions/functions-triggers-bindings?WT.mc_id=academic-17441-jabenn&tabs=python) 中的綁定相關內容。 - - `bindings` 部分包括綁定的配置。以下是一些重要的值: - - * `"type": "eventHubTrigger"` - 指定函數需要監聽來自事件中心的事件 - * `"name": "events"` - 事件中心事件的參數名稱。這與 Python 代碼中 `main` 函數的參數名稱一致。 - * `"direction": "in"` - 這是一個輸入綁定,事件中心的數據進入函數 - * `"connection": ""` - 定義要從設置中讀取連接字串的名稱。在本地運行時,這將從 `local.settings.json` 文件中讀取此設置。 - - > 💁 連接字串不能存儲在 `function.json` 文件中,必須從設置中讀取。這是為了防止意外暴露你的連接字串。 - -1. 由於 [Azure Functions 模板中的一個錯誤](https://github.com/Azure/azure-functions-templates/issues/1250),`function.json` 中的 `cardinality` 字段值不正確。將此字段的值從 `many` 更新為 `one`: - - ```json - "cardinality": "one", - ``` - -1. 更新 `function.json` 文件中 `"connection"` 的值,使其指向你在 `local.settings.json` 文件中新增的值: - - ```json - "connection": "IOT_HUB_CONNECTION_STRING", - ``` - - > 💁 記住 - 這需要指向設置,而不是包含實際的連接字串。 - -1. 連接字串包含 `eventHubName` 值,因此 `function.json` 文件中的此值需要清空。將此值更新為空字串: - - ```json - "eventHubName": "", - ``` - -### 任務 - 運行事件觸發器 - -1. 確保你沒有運行 IoT Hub 事件監視器。如果此監視器與 Functions 應用程式同時運行,Functions 應用程式將無法連接並消耗事件。 - - > 💁 多個應用程式可以使用不同的 *消費者組* 連接到 IoT Hub 的端點。這些內容將在後續課程中介紹。 - -1. 要運行 Functions 應用程式,從 VS Code 的終端執行以下命令: - - ```sh - func start - ``` - - Functions 應用程式將啟動,並發現 `iot-hub-trigger` 函數。它將處理過去一天內已發送到 IoT Hub 的任何事件。 - - ```output - (.venv) ➜ soil-moisture-trigger func start - Found Python version 3.9.1 (python3). - - Azure Functions Core Tools - Core Tools Version: 3.0.3442 Commit hash: 6bfab24b2743f8421475d996402c398d2fe4a9e0 (64-bit) - Function Runtime Version: 3.0.15417.0 - - Functions: - - iot-hub-trigger: eventHubTrigger - - For detailed output, run func with --verbose flag. - [2021-05-05T02:44:07.517Z] Worker process started and initialized. - [2021-05-05T02:44:09.202Z] Executing 'Functions.iot-hub-trigger' (Reason='(null)', Id=802803a5-eae9-4401-a1f4-176631456ce4) - [2021-05-05T02:44:09.205Z] Trigger Details: PartitionId: 0, Offset: 1011240-1011632, EnqueueTimeUtc: 2021-05-04T19:04:04.2030000Z-2021-05-04T19:04:04.3900000Z, SequenceNumber: 2546-2547, Count: 2 - [2021-05-05T02:44:09.352Z] Python EventHub trigger processed an event: {"soil_moisture":628} - [2021-05-05T02:44:09.354Z] Python EventHub trigger processed an event: {"soil_moisture":624} - [2021-05-05T02:44:09.395Z] Executed 'Functions.iot-hub-trigger' (Succeeded, Id=802803a5-eae9-4401-a1f4-176631456ce4, Duration=245ms) - ``` - - 每次函數調用的輸出都會被 `Executing 'Functions.iot-hub-trigger'`/`Executed 'Functions.iot-hub-trigger'` 塊包圍,因此你可以看到每次函數調用處理了多少消息。 - -1. 確保你的 IoT 設備正在運行,你將看到新的土壤濕度消息出現在 Functions 應用程式中。 - -1. 停止並重新啟動 Functions 應用程式。你會看到它不會再次處理之前的消息,只會處理新的消息。 - -> 💁 VS Code 也支持調試你的 Functions。你可以通過點擊代碼行開始處的邊框設置斷點,或者將光標放在代碼行上並選擇 *Run -> Toggle breakpoint*,或者按下 `F9`。你可以通過選擇 *Run -> Start debugging*,按下 `F5`,或者選擇 *Run and debug* 面板並點擊 **Start debugging** 按鈕啟動調試器。通過這樣做,你可以查看正在處理的事件的詳細信息。 - -#### 疑難排解 - -* 如果出現以下錯誤: - - ```output - The listener for function 'Functions.iot-hub-trigger' was unable to start. Microsoft.WindowsAzure.Storage: Connection refused. System.Net.Http: Connection refused. System.Private.CoreLib: Connection refused. - ``` - - 檢查 Azurite 是否正在運行,並且你已在 `local.settings.json` 文件中將 `AzureWebJobsStorage` 設置為 `UseDevelopmentStorage=true`。 - -* 如果出現以下錯誤: - - ```output - System.Private.CoreLib: Exception while executing function: Functions.iot-hub-trigger. System.Private.CoreLib: Result: Failure Exception: AttributeError: 'list' object has no attribute 'get_body' - ``` - - 檢查你是否已將 `function.json` 文件中的 `cardinality` 設置為 `one`。 - -* 如果出現以下錯誤: - - ```output - Azure.Messaging.EventHubs: The path to an Event Hub may be specified as part of the connection string or as a separate value, but not both. Please verify that your connection string does not have the `EntityPath` token if you are passing an explicit Event Hub name. (Parameter 'connectionString'). - ``` - - 檢查你是否已將 `function.json` 文件中的 `eventHubName` 設置為空字串。 - -## 從無伺服器代碼發送直接方法請求 - -到目前為止,你的 Functions 應用程式正在使用事件中心兼容端點監聽來自 IoT Hub 的消息。現在你需要向 IoT 設備發送命令。這是通過使用 *Registry Manager* 連接到 IoT Hub 來完成的。Registry Manager 是一個工具,允許你查看哪些設備已註冊到 IoT Hub,並通過發送雲到設備消息、直接方法請求或更新設備雙向通信。你也可以使用它來註冊、更新或刪除 IoT Hub 中的設備。 - -要連接到 Registry Manager,你需要一個連接字串。 - -### 任務 - 獲取 Registry Manager 連接字串 - -1. 要獲取連接字串,執行以下命令: - - ```sh - az iot hub connection-string show --policy-name service \ - --output table \ - --hub-name - ``` - - 將 `` 替換為你用於 IoT Hub 的名稱。 - - 連接字串是為 *ServiceConnect* 策略請求的,使用 `--policy-name service` 參數。當你請求連接字串時,可以指定該連接字串允許的權限。ServiceConnect 策略允許你的代碼連接並向 IoT 設備發送消息。 - - ✅ 做一些研究:閱讀 [IoT Hub 權限文件](https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-security#iot-hub-permissions?WT.mc_id=academic-17441-jabenn) 中的不同策略。 - -1. 在 VS Code 中打開 `local.settings.json` 文件。在 `Values` 部分內新增以下值: - - ```json - "REGISTRY_MANAGER_CONNECTION_STRING": "" - ``` - - 將 `` 替換為上一步中的值。你需要在上一行後添加逗號以使其成為有效的 JSON。 - -### 任務 - 向設備發送直接方法請求 - -1. Registry Manager 的 SDK 可通過 Pip 套件獲得。在 `requirements.txt` 文件中新增以下行以添加此套件的依賴: - - ```sh - azure-iot-hub - ``` - -1. 確保 VS Code 終端已激活虛擬環境,並執行以下命令以安裝 Pip 套件: - - ```sh - pip install -r requirements.txt - ``` - -1. 在 `__init__.py` 文件中新增以下導入: - - ```python - import json - import os - from azure.iot.hub import IoTHubRegistryManager - from azure.iot.hub.models import CloudToDeviceMethod - ``` - - 這導入了一些系統庫,以及與 Registry Manager 交互並發送直接方法請求的庫。 - -1. 移除 `main` 方法內的代碼,但保留該方法本身。 - -1. 在 `main` 方法內新增以下代碼: - - ```python - body = json.loads(event.get_body().decode('utf-8')) - device_id = event.iothub_metadata['connection-device-id'] - - logging.info(f'Received message: {body} from {device_id}') - ``` - - 此代碼提取事件的主體,其中包含 IoT 設備發送的 JSON 消息。 - - 然後從消息的註解中獲取設備 ID。事件的主體包含作為遙測發送的消息,`iothub_metadata` 字典包含 IoT Hub 設置的屬性,例如發送者的設備 ID 和消息發送的時間。 - - 此信息隨後被記錄。當你在本地運行 Functions 應用程式時,你會在終端中看到這些日誌。 - -1. 在此代碼下方新增以下代碼: - - ```python - soil_moisture = body['soil_moisture'] - - if soil_moisture > 450: - direct_method = CloudToDeviceMethod(method_name='relay_on', payload='{}') - else: - direct_method = CloudToDeviceMethod(method_name='relay_off', payload='{}') - ``` - - 此代碼從消息中獲取土壤濕度。然後檢查土壤濕度,根據其值,為 `relay_on` 或 `relay_off` 直接方法創建一個輔助類。方法請求不需要有效載荷,因此發送一個空的 JSON 文檔。 - -1. 在此代碼下方新增以下代碼: - - ```python - logging.info(f'Sending direct method request for {direct_method.method_name} for device {device_id}') - - registry_manager_connection_string = os.environ['REGISTRY_MANAGER_CONNECTION_STRING'] - registry_manager = IoTHubRegistryManager(registry_manager_connection_string) - ``` -此程式碼從 `local.settings.json` 檔案中載入 `REGISTRY_MANAGER_CONNECTION_STRING`。該檔案中的值會作為環境變數提供,並可透過 `os.environ` 函數讀取,該函數會返回所有環境變數的字典。 - -> 💁 當此程式碼部署到雲端時,`local.settings.json` 檔案中的值將被設置為 *應用程式設定*,並可從環境變數中讀取。 - -接著,程式碼使用連接字串建立一個 Registry Manager 輔助類別的實例。 - -1. 在下方新增以下程式碼: - - ```python - registry_manager.invoke_device_method(device_id, direct_method) - - logging.info('Direct method request sent!') - ``` - - 此程式碼指示 Registry Manager 將直接方法請求發送到傳送遙測數據的裝置。 - - > 💁 在您之前使用 MQTT 創建的應用程式版本中,繼電器控制命令是發送給所有裝置的。程式碼假設您只有一個裝置。此版本的程式碼將方法請求發送給單一裝置,因此即使您有多個濕度感測器和繼電器的設置,也能將正確的直接方法請求發送到正確的裝置。 - -1. 執行 Functions 應用程式,並確保您的 IoT 裝置正在傳送數據。您將看到訊息被處理,並且直接方法請求被發送。將土壤濕度感測器插入或移出土壤,觀察數值變化以及繼電器的開啟與關閉。 - -> 💁 您可以在 [code/functions](../../../../../2-farm/lessons/5-migrate-application-to-the-cloud/code/functions) 資料夾中找到此程式碼。 - -## 將無伺服器程式碼部署到雲端 - -您的程式碼現在已在本地運行,下一步是將 Functions 應用程式部署到雲端。 - -### 任務 - 建立雲端資源 - -您的 Functions 應用程式需要部署到 Azure 中的 Functions App 資源,該資源位於您為 IoT Hub 創建的資源群組內。您還需要在 Azure 中建立一個儲存帳戶,以取代您本地運行的模擬儲存。 - -1. 執行以下命令以建立儲存帳戶: - - ```sh - az storage account create --resource-group soil-moisture-sensor \ - --sku Standard_LRS \ - --name - ``` - - 將 `` 替換為您的儲存帳戶名稱。此名稱需要在全球範圍內唯一,因為它是用於訪問儲存帳戶的 URL 的一部分。名稱只能使用小寫字母和數字,不能包含其他字符,且限制為 24 個字符。可以使用類似 `sms` 的名稱,並在末尾添加唯一標識符,例如隨機單詞或您的名字。 - - `--sku Standard_LRS` 選擇定價層,選擇最低成本的通用帳戶。儲存沒有免費層,您需要為使用的部分付費。成本相對較低,最昂貴的儲存每月每 GB 不到 0.05 美元。 - - ✅ 在 [Azure 儲存帳戶定價頁面](https://azure.microsoft.com/pricing/details/storage/?WT.mc_id=academic-17441-jabenn) 上了解定價。 - -1. 執行以下命令以建立 Functions App: - - ```sh - az functionapp create --resource-group soil-moisture-sensor \ - --runtime python \ - --functions-version 3 \ - --os-type Linux \ - --consumption-plan-location \ - --storage-account \ - --name - ``` - - 將 `` 替換為您在上一課程中創建資源群組時使用的位置。 - - 將 `` 替換為您在上一步中創建的儲存帳戶名稱。 - - 將 `` 替換為您的 Functions App 的唯一名稱。此名稱需要在全球範圍內唯一,因為它是用於訪問 Functions App 的 URL 的一部分。可以使用類似 `soil-moisture-sensor-` 的名稱,並在末尾添加唯一標識符,例如隨機單詞或您的名字。 - - `--functions-version 3` 選項設置要使用的 Azure Functions 版本。版本 3 是最新版本。 - - `--os-type Linux` 告訴 Functions 執行時使用 Linux 作為操作系統來託管這些功能。Functions 可以根據使用的程式語言託管在 Linux 或 Windows 上。Python 應用程式僅支援 Linux。 - -### 任務 - 上傳您的應用程式設定 - -在開發 Functions App 時,您將一些設定存儲在 `local.settings.json` 檔案中,用於 IoT Hub 的連接字串。這些設定需要寫入 Azure 中的 Functions App 的應用程式設定,以便程式碼可以使用。 - -> 🎓 `local.settings.json` 檔案僅用於本地開發設定,這些設定不應提交到版本控制系統(如 GitHub)。當部署到雲端時,應用程式設定會被使用。應用程式設定是託管在雲端的鍵/值對,可以在程式碼中或執行時通過環境變數讀取。 - -1. 執行以下命令以在 Functions App 的應用程式設定中設置 `IOT_HUB_CONNECTION_STRING`: - - ```sh - az functionapp config appsettings set --resource-group soil-moisture-sensor \ - --name \ - --settings "IOT_HUB_CONNECTION_STRING=" - ``` - - 將 `` 替換為您為 Functions App 使用的名稱。 - - 將 `` 替換為 `local.settings.json` 檔案中 `IOT_HUB_CONNECTION_STRING` 的值。 - -1. 重複上述步驟,但將 `REGISTRY_MANAGER_CONNECTION_STRING` 的值設置為 `local.settings.json` 檔案中對應的值。 - -執行這些命令時,還會輸出 Functions App 的所有應用程式設定列表。您可以使用此列表檢查值是否正確設置。 - -> 💁 您將看到一個已設置的 `AzureWebJobsStorage` 值。在您的 `local.settings.json` 檔案中,此值被設置為使用本地儲存模擬器。當您創建 Functions App 時,您將儲存帳戶作為參數傳遞,並且此值會自動設置。 - -### 任務 - 將 Functions App 部署到雲端 - -現在 Functions App 已準備好,您的程式碼可以部署。 - -1. 從 VS Code 的終端執行以下命令以發佈 Functions App: - - ```sh - func azure functionapp publish - ``` - - 將 `` 替換為您為 Functions App 使用的名稱。 - -程式碼將被打包並發送到 Functions App,然後部署並啟動。會有大量的控制台輸出,最後會確認部署完成並列出已部署的功能。在此情況下,列表中只會包含觸發器。 - -```output -Deployment successful. -Remote build succeeded! -Syncing triggers... -Functions in soil-moisture-sensor: - iot-hub-trigger - [eventHubTrigger] -``` - -確保您的 IoT 裝置正在運行。通過調整土壤濕度或將感測器插入或移出土壤來改變濕度水平。您將看到繼電器隨著土壤濕度的變化而開啟和關閉。 - ---- - -## 🚀 挑戰 - -在上一課程中,您通過在繼電器開啟時取消訂閱 MQTT 訊息,以及在其關閉後短時間內取消訂閱,來管理繼電器的計時。在這裡,您無法使用此方法——您無法取消訂閱 IoT Hub 觸發器。 - -思考在 Functions App 中處理此問題的不同方法。 - -## 課後測驗 - -[課後測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/18) - -## 回顧與自學 - -* 在 [Wikipedia 的無伺服器計算頁面](https://wikipedia.org/wiki/Serverless_computing) 上閱讀有關無伺服器計算的內容。 -* 閱讀 [Azure 部落格文章](https://azure.microsoft.com/blog/go-serverless-for-your-iot-needs/?WT.mc_id=academic-17441-jabenn),了解在 Azure 中使用無伺服器的更多範例。 -* 在 [Azure Functions YouTube 頻道](https://www.youtube.com/c/AzureFunctions) 上學習更多有關 Azure Functions 的知識。 - -## 作業 - -[新增手動繼電器控制](assignment.md) - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/5-migrate-application-to-the-cloud/assignment.md b/translations/hk/2-farm/lessons/5-migrate-application-to-the-cloud/assignment.md deleted file mode 100644 index 03d9f2a19..000000000 --- a/translations/hk/2-farm/lessons/5-migrate-application-to-the-cloud/assignment.md +++ /dev/null @@ -1,70 +0,0 @@ - -# 添加手動繼電器控制 - -## 指示 - -無伺服器代碼可以由多種方式觸發,包括 HTTP 請求。你可以使用 HTTP 觸發器為你的繼電器控制添加手動覆蓋功能,讓使用者可以通過網絡請求開啟或關閉繼電器。 - -在這個任務中,你需要為你的 Functions App 添加兩個 HTTP 觸發器,用於開啟和關閉繼電器,並重用你在本課中學到的知識來向設備發送指令。 - -一些提示: - -* 你可以使用以下命令為現有的 Functions App 添加 HTTP 觸發器: - - ```sh - func new --name --template "HTTP trigger" - ``` - - 將 `` 替換為你的 HTTP 觸發器的名稱。可以使用類似 `relay_on` 和 `relay_off` 的名稱。 - -* HTTP 觸發器可以設置訪問控制。默認情況下,它們需要在 URL 中傳遞特定於函數的 API 密鑰才能運行。對於這個任務,你可以移除這個限制,讓任何人都可以運行該函數。為此,更新 HTTP 觸發器的 `function.json` 文件中的 `authLevel` 設置為以下內容: - - ```json - "authLevel": "anonymous" - ``` - - > 💁 你可以在 [Function access keys documentation](https://docs.microsoft.com/azure/azure-functions/functions-bindings-http-webhook-trigger?WT.mc_id=academic-17441-jabenn#authorization-keys) 中閱讀更多關於訪問控制的內容。 - -* HTTP 觸發器默認支持 GET 和 POST 請求。這意味著你可以使用網絡瀏覽器調用它們——網絡瀏覽器會發送 GET 請求。 - - 當你在本地運行你的 Functions App 時,你會看到觸發器的 URL: - - ```output - Functions: - - relay_off: [GET,POST] http://localhost:7071/api/relay_off - - relay_on: [GET,POST] http://localhost:7071/api/relay_on - - iot-hub-trigger: eventHubTrigger - ``` - - 將 URL 粘貼到瀏覽器中並按 `return`,或者在 VS Code 的終端窗口中 `Ctrl+點擊`(macOS 上為 `Cmd+點擊`)鏈接以在默認瀏覽器中打開。這將運行觸發器。 - - > 💁 注意 URL 中包含 `/api`——HTTP 觸發器默認位於 `api` 子域中。 - -* 當你部署 Functions App 時,HTTP 觸發器的 URL 將是: - - `https://.azurewebsites.net/api/` - - 其中 `` 是你的 Functions App 的名稱,`` 是你的觸發器的名稱。 - -## 評分標準 - -| 評分標準 | 優秀 | 合格 | 需要改進 | -| -------- | --------- | -------- | ----------------- | -| 創建 HTTP 觸發器 | 創建了兩個觸發器來開啟和關閉繼電器,並使用了適當的名稱 | 創建了一個具有適當名稱的觸發器 | 未能創建任何觸發器 | -| 從 HTTP 觸發器控制繼電器 | 能夠將兩個觸發器連接到 IoT Hub 並適當地控制繼電器 | 能夠將一個觸發器連接到 IoT Hub 並適當地控制繼電器 | 未能將觸發器連接到 IoT Hub | - ---- - -**免責聲明**: -此文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解讀概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/6-keep-your-plant-secure/README.md b/translations/hk/2-farm/lessons/6-keep-your-plant-secure/README.md deleted file mode 100644 index b6e483e55..000000000 --- a/translations/hk/2-farm/lessons/6-keep-your-plant-secure/README.md +++ /dev/null @@ -1,245 +0,0 @@ - -# 保護你的植物安全 - -![本課程的手繪筆記概述](../../../../../translated_images/zh-HK/lesson-10.829c86b80b9403bb770929ee553a1d293afe50dc23121aaf9be144673ae012cc.jpg) - -> 手繪筆記由 [Nitya Narasimhan](https://github.com/nitya) 提供。點擊圖片查看更大的版本。 - -## 課前測驗 - -[課前測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/19) - -## 簡介 - -在過去幾節課中,你已經創建了一個土壤監測物聯網設備並將其連接到雲端。但如果競爭農民的黑客成功控制了你的物聯網設備會怎樣?如果他們發送高土壤濕度讀數,導致你的植物永遠得不到澆水,或者讓你的灌溉系統一直運行,導致植物因過度澆水而死亡,並讓你在水費上損失一大筆錢? - -在本課程中,你將學習如何保護物聯網設備的安全。作為本項目的最後一課,你還將學習如何清理雲端資源,以減少潛在的成本。 - -本課程將涵蓋以下內容: - -* [為什麼需要保護物聯網設備的安全?](../../../../../2-farm/lessons/6-keep-your-plant-secure) -* [加密技術](../../../../../2-farm/lessons/6-keep-your-plant-secure) -* [保護你的物聯網設備](../../../../../2-farm/lessons/6-keep-your-plant-secure) -* [生成並使用 X.509 證書](../../../../../2-farm/lessons/6-keep-your-plant-secure) - -> 🗑 這是本項目的最後一課,因此在完成本課程和作業後,別忘了清理你的雲端服務。你需要這些服務來完成作業,因此請確保先完成作業。 -> -> 如果需要,請參考[清理項目指南](../../../clean-up.md)以獲取相關指導。 - -## 為什麼需要保護物聯網設備的安全? - -物聯網安全涉及確保只有預期的設備能夠連接到你的雲端物聯網服務並發送遙測數據,並且只有你的雲端服務能夠向你的設備發送指令。物聯網數據可能包含個人信息,包括醫療或隱私數據,因此整個應用程序需要考慮安全性,以防止這些數據洩漏。 - -如果你的物聯網應用程序不安全,可能會面臨以下風險: - -* 假冒設備可能發送錯誤數據,導致你的應用程序做出錯誤反應。例如,它們可能發送持續的高土壤濕度讀數,導致你的灌溉系統永遠不啟動,植物因缺水而死亡。 -* 未授權的用戶可能會讀取物聯網設備的數據,包括個人或商業關鍵數據。 -* 黑客可能會發送指令以控制設備,可能會損壞設備或連接的硬件。 -* 通過連接到物聯網設備,黑客可能利用此途徑訪問其他網絡,進而進入私人系統。 -* 惡意用戶可能訪問個人數據並利用這些數據進行勒索。 - -這些是真實世界中的情景,並且經常發生。一些例子在之前的課程中已經提到,但以下是更多例子: - -* 2018年,黑客利用魚缸溫控器上的開放WiFi接入點,成功侵入了一家賭場的網絡並竊取數據。[The Hacker News - Casino Gets Hacked Through Its Internet-Connected Fish Tank Thermometer](https://thehackernews.com/2018/04/iot-hacking-thermometer.html) -* 2016年,Mirai Botnet發起了一次針對Dyn(互聯網服務提供商)的拒絕服務攻擊,導致大部分互聯網癱瘓。該Botnet使用惡意軟件連接到使用默認用戶名和密碼的物聯網設備(如DVR和攝像頭),並從那裡發起攻擊。[The Guardian - DDoS attack that disrupted internet was largest of its kind in history, experts say](https://www.theguardian.com/technology/2016/oct/26/ddos-attack-dyn-mirai-botnet) -* Spiral Toys的CloudPets連接玩具用戶數據庫在互聯網上公開可用。[Troy Hunt - Data from connected CloudPets teddy bears leaked and ransomed, exposing kids' voice messages](https://www.troyhunt.com/data-from-connected-cloudpets-teddy-bears-leaked-and-ransomed-exposing-kids-voice-messages/). -* Strava標記了你跑過的跑者並顯示了他們的路線,讓陌生人能有效地看到你住在哪裡。[Kim Komndo - Fitness app could lead a stranger right to your home — change this setting](https://www.komando.com/security-privacy/strava-fitness-app-privacy/755349/). - -✅ 做一些研究:搜索更多物聯網攻擊和數據洩漏的例子,特別是與個人物品(如互聯網連接的牙刷或體重秤)相關的案例。思考這些攻擊可能對受害者或客戶造成的影響。 - -> 💁 安全是一個非常大的話題,本課程只會涉及一些基本內容,例如如何將設備連接到雲端。其他未涵蓋的主題包括監控數據在傳輸中的變化、直接攻擊設備或更改設備配置。物聯網攻擊是一個如此大的威脅,以至於像 [Azure Defender for IoT](https://azure.microsoft.com/services/azure-defender-for-iot/?WT.mc_id=academic-17441-jabenn) 這樣的工具已被開發出來。這些工具類似於你電腦上的防病毒和安全工具,但專為小型、低功耗的物聯網設備設計。 - -## 加密技術 - -當設備連接到物聯網服務時,它會使用一個ID來識別自己。問題是這個ID可能會被克隆——黑客可以設置一個惡意設備,使用與真實設備相同的ID,但發送虛假數據。 - -![有效設備和惡意設備可能使用相同的ID發送遙測數據](../../../../../translated_images/zh-HK/iot-device-and-hacked-device-connecting.e0671675df74d6d99eb1dedb5a670e606f698efa6202b1ad4c8ae548db299cc6.png) - -解決方法是將發送的數據轉換為一種加密格式,使用設備和雲端都知道的一個值來加密數據。這個過程稱為*加密*,用於加密數據的值稱為*加密密鑰*。 - -![如果使用加密,則只有加密的消息會被接受,其他消息會被拒絕](../../../../../translated_images/zh-HK/iot-device-and-hacked-device-connecting-encryption.5941aff601fc978f979e46f2849b573564eeb4a4dc5b52f669f62745397492fb.png) - -雲端服務可以使用一個過程將數據轉換回可讀格式,這個過程稱為*解密*,使用相同的加密密鑰或一個*解密密鑰*。如果加密的消息無法通過密鑰解密,則表明設備已被攻擊,消息會被拒絕。 - -進行加密和解密的技術稱為*密碼學*。 - -### 早期的密碼學 - -最早的密碼學類型是替換密碼,最早可追溯到3500年前。替換密碼涉及用一個字母替換另一個字母。例如,[凱撒密碼](https://wikipedia.org/wiki/Caesar_cipher)涉及按定義的數量移動字母表,只有加密消息的發送者和預期接收者知道要移動多少個字母。 - -[維吉尼亞密碼](https://wikipedia.org/wiki/Vigenère_cipher)進一步發展,使用單詞來加密文本,使原始文本中的每個字母都按不同的數量移動,而不是始終按相同的字母數移動。 - -密碼學被用於廣泛的用途,例如在古代美索不達米亞保護陶工的釉料配方、在印度寫秘密情書或在古埃及保密魔法咒語。 - -### 現代密碼學 - -現代密碼學更加先進,比早期方法更難破解。現代密碼學使用複雜的數學方法來加密數據,擁有太多可能的密鑰使得暴力破解攻擊幾乎不可能。 - -密碼學在安全通信中有很多用途。如果你正在GitHub上閱讀此頁面,你可能會注意到網站地址以*HTTPS*開頭,這意味著你的瀏覽器和GitHub的網絡服務器之間的通信是加密的。如果有人能夠讀取你的瀏覽器和GitHub之間的互聯網流量,他們將無法讀取數據,因為數據是加密的。你的電腦甚至可能加密硬盤上的所有數據,因此如果有人偷走它,他們在沒有你的密碼的情況下無法讀取任何數據。 - -> 🎓 HTTPS代表超文本傳輸協議**安全** - -不幸的是,並非所有事物都是安全的。有些設備沒有安全性,有些設備使用容易破解的密鑰,有些甚至所有同類型的設備都使用相同的密鑰。有報導稱一些非常個人的物聯網設備都使用相同的密碼來通過WiFi或藍牙連接。如果你能連接到自己的設備,你也能連接到別人的。一旦連接,你可能訪問一些非常私密的數據,或者控制他們的設備。 - -> 💁 儘管現代密碼學的複雜性以及破解加密可能需要數十億年的說法,量子計算的興起使得在非常短的時間內破解所有已知加密成為可能! - -### 對稱密鑰和非對稱密鑰 - -加密有兩種類型——對稱和非對稱。 - -**對稱**加密使用相同的密鑰來加密和解密數據。發送者和接收者都需要知道相同的密鑰。這是最不安全的類型,因為密鑰需要以某種方式共享。為了讓發送者向接收者發送加密消息,發送者可能首先需要向接收者發送密鑰。 - -![對稱密鑰加密使用相同的密鑰加密和解密消息](../../../../../translated_images/zh-HK/send-message-symmetric-key.a2e8ad0d495896ff.webp) - -如果密鑰在傳輸過程中被竊取,或者發送者或接收者被黑客攻擊並且密鑰被找到,加密就可能被破解。 - -![對稱密鑰加密只有在黑客未獲得密鑰的情況下才安全——如果密鑰被竊取,他們可以攔截並解密消息](../../../../../translated_images/zh-HK/send-message-symmetric-key-hacker.e7cb53db1707adfb.webp) - -**非對稱**加密使用兩個密鑰——加密密鑰和解密密鑰,稱為公鑰/私鑰對。公鑰用於加密消息,但不能用於解密;私鑰用於解密消息,但不能用於加密。 - -![非對稱加密使用不同的密鑰加密和解密。加密密鑰會發送給任何消息發送者,以便他們在向擁有密鑰的接收者發送消息之前加密消息](../../../../../translated_images/zh-HK/send-message-asymmetric.7abe327c62615b8c.webp) - -接收者分享他們的公鑰,發送者使用這個公鑰加密消息。一旦消息被發送,接收者使用他們的私鑰解密消息。非對稱加密更安全,因為私鑰由接收者保密,從不共享。任何人都可以擁有公鑰,因為它只能用於加密消息。 - -對稱加密比非對稱加密更快,非對稱加密更安全。一些系統會同時使用兩者——使用非對稱加密來加密和共享對稱密鑰,然後使用對稱密鑰加密所有數據。這使得在發送者和接收者之間共享對稱密鑰更加安全,並且在加密和解密數據時更快。 - -## 保護你的物聯網設備 - -物聯網設備可以使用對稱或非對稱加密進行保護。對稱加密更簡單,但安全性較低。 - -### 對稱密鑰 - -當你設置物聯網設備與IoT Hub交互時,你使用了一個連接字符串。一個示例連接字符串如下: - -```output -HostName=soil-moisture-sensor.azure-devices.net;DeviceId=soil-moisture-sensor;SharedAccessKey=Bhry+ind7kKEIDxubK61RiEHHRTrPl7HUow8cEm/mU0= -``` - -這個連接字符串由三部分組成,使用分號分隔,每部分是一個鍵和值: - -| 鍵 | 值 | 描述 | -| --- | ----- | ----------- | -| HostName | `soil-moisture-sensor.azure-devices.net` | IoT Hub的URL | -| DeviceId | `soil-moisture-sensor` | 設備的唯一ID | -| SharedAccessKey | `Bhry+ind7kKEIDxubK61RiEHHRTrPl7HUow8cEm/mU0=` | 設備和IoT Hub都知道的對稱密鑰 | - -連接字符串的最後一部分`SharedAccessKey`是設備和IoT Hub都知道的對稱密鑰。這個密鑰不會從設備發送到雲端,也不會從雲端發送到設備。它用於加密發送或接收的數據。 - -✅ 做一個實驗:如果你在連接物聯網設備時更改連接字符串中的`SharedAccessKey`部分,會發生什麼?試試看。 - -當設備首次嘗試連接時,它會發送一個共享訪問簽名(SAS)令牌,該令牌由IoT Hub的URL、一個訪問簽名的到期時間戳(通常是當前時間起的一天)和一個簽名組成。這個簽名由URL和到期時間組成,並使用連接字符串中的共享訪問密鑰加密。 - -IoT Hub使用共享訪問密鑰解密這個簽名,如果解密後的值與URL和到期時間匹配,設備就被允許連接。它還會驗證當前時間是否在到期時間之前,以防止惡意設備捕獲真實設備的SAS令牌並使用它。 - -這是一種優雅的方式來驗證發送者是否是正確的設備。通過以解密和加密形式發送一些已知數據,服務器可以通過解密加密數據並確保結果與發送的解密版本匹配來驗證設備。如果匹配,則表明發送者和接收者擁有相同的對稱加密密鑰。 -> 💁 因為有過期時間,你的 IoT 裝置需要知道準確的時間,通常是從 [NTP](https://wikipedia.org/wiki/Network_Time_Protocol) 伺服器讀取。如果時間不準確,連線將會失敗。 -連接後,所有從設備發送到 IoT Hub 或從 IoT Hub 發送到設備的數據都將使用共享訪問密鑰進行加密。 - -✅ 你認為如果多個設備共享相同的連接字串會發生什麼? - -> 💁 將此密鑰存儲在代碼中是一個糟糕的安全做法。如果黑客獲得你的源代碼,他們就能獲得你的密鑰。此外,在發布代碼時也會更加困難,因為你需要為每個設備重新編譯以更新密鑰。更好的方法是從硬件安全模組(HSM)加載此密鑰——這是一個存儲加密值的 IoT 設備芯片,代碼可以讀取這些值。 -> -> 在學習 IoT 時,通常更容易將密鑰放入代碼中,就像你在之前的課程中所做的那樣,但你必須確保此密鑰未被提交到公共源代碼控制中。 - -設備有兩個密鑰以及兩個對應的連接字串。這使你可以輪換密鑰——即如果第一個密鑰被泄露,可以切換到另一個密鑰並重新生成第一個密鑰。 - -### X.509 證書 - -當你使用公鑰/私鑰對進行非對稱加密時,你需要向任何想要向你發送數據的人提供你的公鑰。問題是,接收者如何確定這確實是你的公鑰,而不是其他人冒充你?與其直接提供密鑰,你可以將公鑰放入由可信第三方驗證的證書中,這被稱為 X.509 證書。 - -X.509 證書是包含公鑰部分的數字文件。它們通常由一些被稱為[證書授權機構](https://wikipedia.org/wiki/Certificate_authority) (CAs) 的可信組織簽發,並由 CA 進行數字簽名以表明密鑰是有效的並且來自你。你信任證書以及公鑰的來源,因為你信任 CA,就像你信任護照或駕駛執照是由可信的國家簽發的一樣。證書需要付費,因此你也可以“自簽名”,即自己創建並簽署證書,用於測試目的。 - -> 💁 你絕不應該在生產環境中使用自簽名證書。 - -這些證書包含多個字段,包括公鑰的來源、簽發證書的 CA 的詳細信息、證書的有效期以及公鑰本身。在使用證書之前,最好通過檢查其是否由原始 CA 簽名來驗證它。 - -✅ 你可以在 [Microsoft Understanding X.509 Public Key Certificates tutorial](https://docs.microsoft.com/azure/iot-hub/tutorial-x509-certificates?WT.mc_id=academic-17441-jabenn#certificate-fields) 中查看證書字段的完整列表。 - -使用 X.509 證書時,發送方和接收方都會擁有自己的公鑰和私鑰,以及包含公鑰的 X.509 證書。然後他們以某種方式交換 X.509 證書,使用彼此的公鑰加密發送的數據,並使用自己的私鑰解密接收到的數據。 - -![與其共享公鑰,你可以共享證書。證書的使用者可以通過檢查簽署證書的授權機構來驗證它是否來自你。](../../../../../translated_images/zh-HK/send-message-certificate.9cc576ac1e46b76e.webp) - -使用 X.509 證書的一大優勢是它們可以在設備之間共享。你可以創建一個證書,將其上傳到 IoT Hub,並將其用於所有設備。每個設備只需要知道私鑰即可解密從 IoT Hub 接收到的消息。 - -設備用於加密發送到 IoT Hub 的消息的證書由 Microsoft 發布。這是許多 Azure 服務使用的相同證書,有時已內置於 SDK 中。 - -> 💁 記住,公鑰就是公鑰。Azure 公鑰只能用於加密發送到 Azure 的數據,不能用於解密,因此可以隨處共享,包括在源代碼中。例如,你可以在 [Azure IoT C SDK 源代碼](https://github.com/Azure/azure-iot-sdk-c/blob/master/certs/certs.c) 中看到它。 - -✅ X.509 證書有很多術語。你可以在 [The layman’s guide to X.509 certificate jargon](https://techcommunity.microsoft.com/t5/internet-of-things/the-layman-s-guide-to-x-509-certificate-jargon/ba-p/2203540?WT.mc_id=academic-17441-jabenn) 中閱讀一些可能遇到的術語的定義。 - -## 生成並使用 X.509 證書 - -生成 X.509 證書的步驟如下: - -1. 創建公鑰/私鑰對。最廣泛使用的生成公鑰/私鑰對的算法之一是 [Rivest–Shamir–Adleman](https://wikipedia.org/wiki/RSA_(cryptosystem))(RSA)。 - -1. 提交公鑰及相關數據進行簽名,可以由 CA 簽名,也可以自簽名。 - -Azure CLI 提供了命令來在 IoT Hub 中創建新的設備身份,並自動生成公鑰/私鑰對以及自簽名證書。 - -> 💁 如果你想查看詳細步驟,而不是使用 Azure CLI,你可以在 [Microsoft IoT Hub 文檔中的使用 OpenSSL 創建自簽名證書教程](https://docs.microsoft.com/azure/iot-hub/tutorial-x509-self-sign?WT.mc_id=academic-17441-jabenn) 中找到。 - -### 任務 - 使用 X.509 證書創建設備身份 - -1. 運行以下命令以註冊新的設備身份,並自動生成密鑰和證書: - - ```sh - az iot hub device-identity create --device-id soil-moisture-sensor-x509 \ - --am x509_thumbprint \ - --output-dir . \ - --hub-name - ``` - - 將 `` 替換為你用於 IoT Hub 的名稱。 - - 這將創建一個 ID 為 `soil-moisture-sensor-x509` 的設備,以區分你在上一課中創建的設備身份。此命令還會在當前目錄中創建兩個文件: - - * `soil-moisture-sensor-x509-key.pem` - 此文件包含設備的私鑰。 - * `soil-moisture-sensor-x509-cert.pem` - 此文件是設備的 X.509 證書。 - - 請妥善保管這些文件!私鑰文件不應提交到公共源代碼控制中。 - -### 任務 - 在設備代碼中使用 X.509 證書 - -按照相關指南,使用 X.509 證書將你的 IoT 設備連接到雲端: - -* [Arduino - Wio Terminal](wio-terminal-x509.md) -* [單板計算機 - Raspberry Pi/虛擬 IoT 設備](single-board-computer-x509.md) - ---- - -## 🚀 挑戰 - -創建、管理和刪除 Azure 服務(如資源組和 IoT Hub)有多種方法。一種方法是使用 [Azure Portal](https://portal.azure.com?WT.mc_id=academic-17441-jabenn)——一個基於網頁的界面,提供 GUI 來管理你的 Azure 服務。 - -前往 [portal.azure.com](https://portal.azure.com?WT.mc_id=academic-17441-jabenn) 並探索該入口。看看你是否能使用該入口創建一個 IoT Hub,然後將其刪除。 - -**提示** - 通過入口創建服務時,你不需要提前創建資源組,可以在創建服務時創建資源組。完成後請確保刪除它! - -你可以在 [Azure portal documentation](https://docs.microsoft.com/azure/azure-portal/?WT.mc_id=academic-17441-jabenn) 中找到大量文檔、教程和指南。 - -## 課後測驗 - -[課後測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/20) - -## 回顧與自學 - -* 在 [Wikipedia 的密碼學歷史頁面](https://wikipedia.org/wiki/History_of_cryptography) 上閱讀密碼學的歷史。 -* 在 [Wikipedia 的 X.509 頁面](https://wikipedia.org/wiki/X.509) 上閱讀 X.509 證書的相關內容。 - -## 作業 - -[構建一個新的 IoT 設備](assignment.md) - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/6-keep-your-plant-secure/assignment.md b/translations/hk/2-farm/lessons/6-keep-your-plant-secure/assignment.md deleted file mode 100644 index 13fca96a9..000000000 --- a/translations/hk/2-farm/lessons/6-keep-your-plant-secure/assignment.md +++ /dev/null @@ -1,29 +0,0 @@ - -# 建立一個新的物聯網裝置 - -## 指引 - -在過去的六堂課中,你已經學習了數位農業的相關知識,以及如何使用物聯網裝置收集數據來預測植物生長,並根據土壤濕度讀數自動灌溉。 - -運用你所學到的知識,使用一個感測器和一個致動器來建立一個新的物聯網裝置。將遙測數據傳送到 IoT Hub,並使用無伺服器程式碼來控制致動器。你可以使用在本課程或之前的專案中已經使用過的感測器和致動器,或者如果你有其他硬體,可以嘗試一些新的組合。 - -## 評分標準 - -| 評分項目 | 優秀 | 合格 | 需要改進 | -| -------- | ---- | ---- | -------- | -| 為物聯網裝置編寫程式以使用感測器和致動器 | 成功編寫了一個能與感測器和致動器配合運作的物聯網裝置 | 成功編寫了一個能與感測器或致動器配合運作的物聯網裝置 | 未能編寫一個能使用感測器或致動器的物聯網裝置 | -| 將物聯網裝置連接到 IoT Hub | 成功部署了一個 IoT Hub,並能傳送遙測數據到 IoT Hub,且能接收來自 IoT Hub 的指令 | 成功部署了一個 IoT Hub,並能傳送遙測數據或接收指令 | 未能部署 IoT Hub,或無法與物聯網裝置進行通訊 | -| 使用無伺服器程式碼控制致動器 | 成功部署了一個 Azure Function,並能根據遙測事件觸發來控制裝置 | 成功部署了一個 Azure Function,能根據遙測事件觸發,但無法控制致動器 | 未能部署 Azure Function | - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業的人類翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/6-keep-your-plant-secure/single-board-computer-x509.md b/translations/hk/2-farm/lessons/6-keep-your-plant-secure/single-board-computer-x509.md deleted file mode 100644 index 972d664ea..000000000 --- a/translations/hk/2-farm/lessons/6-keep-your-plant-secure/single-board-computer-x509.md +++ /dev/null @@ -1,71 +0,0 @@ - -# 在您的設備代碼中使用 X.509 憑證 - 虛擬 IoT 硬件和 Raspberry Pi - -在本課程的這部分,您將使用 X.509 憑證將虛擬 IoT 設備或 Raspberry Pi 連接到您的 IoT Hub。 - -## 將設備連接到 IoT Hub - -下一步是使用 X.509 憑證將您的設備連接到 IoT Hub。 - -### 任務 - 連接到 IoT Hub - -1. 將密鑰和憑證文件複製到包含您 IoT 設備代碼的文件夾中。如果您通過 VS Code Remote SSH 使用 Raspberry Pi,並且在您的 PC 或 Mac 上創建了密鑰,您可以將文件拖放到 VS Code 的資源管理器中以完成複製。 - -1. 打開 `app.py` 文件 - -1. 要使用 X.509 憑證進行連接,您需要 IoT Hub 的主機名稱和 X.509 憑證。首先,在創建設備客戶端之前,添加以下代碼來創建一個包含主機名稱的變量: - - ```python - host_name = "" - ``` - - 將 `` 替換為您的 IoT Hub 的主機名稱。您可以從 `connection_string` 的 `HostName` 部分獲取此名稱。它將是您的 IoT Hub 的名稱,並以 `.azure-devices.net` 結尾。 - -1. 在此代碼下方,聲明一個包含設備 ID 的變量: - - ```python - device_id = "soil-moisture-sensor-x509" - ``` - -1. 您需要一個包含 X.509 文件的 `X509` 類實例。將 `X509` 添加到從 `azure.iot.device` 模組導入的類列表中: - - ```python - from azure.iot.device import IoTHubDeviceClient, Message, MethodResponse, X509 - ``` - -1. 使用您的憑證和密鑰文件創建一個 `X509` 類實例,將以下代碼添加到 `host_name` 聲明的下方: - - ```python - x509 = X509("./soil-moisture-sensor-x509-cert.pem", "./soil-moisture-sensor-x509-key.pem") - ``` - - 這將使用之前創建的 `soil-moisture-sensor-x509-cert.pem` 和 `soil-moisture-sensor-x509-key.pem` 文件來創建 `X509` 類。 - -1. 用以下代碼替換從連接字串創建 `device_client` 的那一行代碼: - - ```python - device_client = IoTHubDeviceClient.create_from_x509_certificate(x509, host_name, device_id) - ``` - - 這將使用 X.509 憑證而不是連接字串進行連接。 - -1. 刪除包含 `connection_string` 變量的那一行代碼。 - -1. 運行您的代碼。監控發送到 IoT Hub 的消息,並像之前一樣發送直接方法請求。您將看到設備連接並發送土壤濕度讀數,同時接收直接方法請求。 - -> 💁 您可以在 [code/pi](../../../../../2-farm/lessons/6-keep-your-plant-secure/code/pi) 或 [code/virtual-device](../../../../../2-farm/lessons/6-keep-your-plant-secure/code/virtual-device) 文件夾中找到此代碼。 - -😀 您的土壤濕度傳感器程序已使用 X.509 憑證成功連接到您的 IoT Hub! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文檔的母語版本作為權威來源。對於關鍵資訊,建議尋求專業的人類翻譯服務。我們對因使用此翻譯而引起的任何誤解或錯誤詮釋概不負責。 \ No newline at end of file diff --git a/translations/hk/2-farm/lessons/6-keep-your-plant-secure/wio-terminal-x509.md b/translations/hk/2-farm/lessons/6-keep-your-plant-secure/wio-terminal-x509.md deleted file mode 100644 index 04ee079ff..000000000 --- a/translations/hk/2-farm/lessons/6-keep-your-plant-secure/wio-terminal-x509.md +++ /dev/null @@ -1,17 +0,0 @@ - -# 在您的設備代碼中使用 X.509 證書 - Wio Terminal - -截至撰寫本文時,Azure Arduino SDK 尚未支援 X.509 證書。如果您想嘗試使用 X.509 證書,可以參考[使用 Python SDK 的虛擬 IoT 設備指引](single-board-computer-x509.md) - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/3-transport/README.md b/translations/hk/3-transport/README.md deleted file mode 100644 index 8f26a45c6..000000000 --- a/translations/hk/3-transport/README.md +++ /dev/null @@ -1,38 +0,0 @@ - -# 從農場到工廠的運輸 - 使用物聯網追蹤食品運送 - -許多農民種植作物是為了銷售——他們可能是商業農民,出售他們種植的所有作物,或者是自給農民,出售多餘的農產品來購買生活必需品。不管怎樣,食品必須從農場運送到消費者手中,這通常依賴於從農場到集散中心或加工廠,再到商店的大宗運輸。例如,一位種植番茄的農民會收割番茄,將它們裝箱,然後將箱子裝上卡車,運送到加工廠。番茄會在那裡被分類,然後以加工食品、零售銷售或餐廳消費的形式送到消費者手中。 - -物聯網可以通過追蹤運輸中的食品來幫助這個供應鏈——確保司機按照規定的路線行駛,監控車輛位置,並在車輛到達時發出警報,讓食品可以盡快卸貨並準備加工。 - -> 🎓 *供應鏈* 是指製造和交付某物品的一系列活動。例如,在番茄種植中,它涵蓋了種子、土壤、肥料和水的供應,種植番茄,將番茄運送到集散中心,運輸到超市的本地中心,再運送到個別超市,陳列出售,然後被消費者購買並帶回家食用。每一步就像鏈條中的一個環節。 - -> 🎓 供應鏈中的運輸部分被稱為 *物流*。 - -在這四節課中,你將學習如何應用物聯網來改善供應鏈,通過監控食品被裝載到(虛擬)卡車上,並追蹤其移動到目的地的過程。你將學習GPS追蹤、如何存儲和可視化GPS數據,以及如何在卡車到達目的地時接收警報。 - -> 💁 這些課程將使用一些雲端資源。如果你未完成此專案中的所有課程,請確保[清理你的專案](../clean-up.md)。 - -## 主題 - -1. [位置追蹤](lessons/1-location-tracking/README.md) -1. [存儲位置數據](lessons/2-store-location-data/README.md) -1. [可視化位置數據](lessons/3-visualize-location-data/README.md) -1. [地理圍欄](lessons/4-geofences/README.md) - -## 鳴謝 - -所有課程由 [Jen Looper](https://github.com/jlooper) 和 [Jim Bennett](https://GitHub.com/JimBobBennett) 用 ♥️ 編寫。 - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/3-transport/lessons/1-location-tracking/README.md b/translations/hk/3-transport/lessons/1-location-tracking/README.md deleted file mode 100644 index f0bbeb76b..000000000 --- a/translations/hk/3-transport/lessons/1-location-tracking/README.md +++ /dev/null @@ -1,216 +0,0 @@ - -# 位置追蹤 - -![本課程的手繪筆記概覽](../../../../../translated_images/zh-HK/lesson-11.9fddbac4b664c6d50ab7ac9bb32f1fc3f945f03760e72f7f43938073762fb017.jpg) - -> 手繪筆記由 [Nitya Narasimhan](https://github.com/nitya) 提供。點擊圖片查看更大版本。 - -## 課前測驗 - -[課前測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/21) - -## 簡介 - -將食物從農場送到消費者手中的主要過程包括將農產品裝箱後,運輸到卡車、船隻、飛機或其他商業運輸工具上,然後將食物送到某個地方——可能是直接送到客戶手中,或者送到一個中央樞紐或倉庫進行處理。從農場到消費者的整個端到端過程被稱為*供應鏈*。以下是亞利桑那州立大學 W. P. Carey 商學院的一段影片,詳細介紹了供應鏈的概念及其管理方式。 - -[![什麼是供應鏈管理?來自亞利桑那州立大學 W. P. Carey 商學院的影片](https://img.youtube.com/vi/Mi1QBxVjZAw/0.jpg)](https://www.youtube.com/watch?v=Mi1QBxVjZAw) - -> 🎥 點擊上方圖片觀看影片 - -添加物聯網(IoT)設備可以大幅改善供應鏈管理,讓您能更好地掌握物品的位置,規劃運輸和貨物處理,並更快速地應對問題。 - -當管理一隊車輛(如卡車)時,知道每輛車在某一時間的位置是非常有幫助的。車輛可以安裝 GPS 感應器,將其位置發送到物聯網系統,讓車主能精確定位車輛的位置,查看其行駛路線,並知道它們何時會到達目的地。大多數車輛在 WiFi 覆蓋範圍之外運行,因此它們使用行動網絡來傳送這類數據。有時,GPS 感應器內建於更複雜的物聯網設備中,例如電子日誌。這些設備可以追蹤卡車的行駛時間,以確保駕駛員遵守當地的工作時數法律。 - -在本課程中,您將學習如何使用全球定位系統(GPS)感應器追蹤車輛的位置。 - -本課程將涵蓋以下內容: - -* [連接車輛](../../../../../3-transport/lessons/1-location-tracking) -* [地理空間座標](../../../../../3-transport/lessons/1-location-tracking) -* [全球定位系統(GPS)](../../../../../3-transport/lessons/1-location-tracking) -* [讀取 GPS 感應器數據](../../../../../3-transport/lessons/1-location-tracking) -* [NMEA GPS 數據](../../../../../3-transport/lessons/1-location-tracking) -* [解碼 GPS 感應器數據](../../../../../3-transport/lessons/1-location-tracking) - -## 連接車輛 - -物聯網正在改變貨物運輸的方式,通過創建一隊*連接車輛*。這些車輛與中央 IT 系統相連,報告其位置和其他感應器數據。擁有一隊連接車輛有多種好處: - -* 位置追蹤 - 您可以隨時精確定位車輛的位置,這樣可以: - - * 當車輛即將到達目的地時收到警報,為卸貨工作做好準備 - * 定位被盜車輛 - * 將位置和路線數據與交通問題結合,讓您在行程中重新規劃車輛路線 - * 符合法律規定。例如,一些國家會根據車輛在公共道路上行駛的里程數徵稅(如[紐西蘭的道路使用費](https://www.nzta.govt.nz/vehicles/licensing-rego/road-user-charges/)),因此知道車輛何時在公共道路上行駛 vs 私人道路上行駛,可以更容易計算應繳稅款。 - * 在車輛故障時知道派遣維修人員的位置 - -* 駕駛員數據 - 確保駕駛員遵守速度限制、以適當的速度轉彎、提前且有效地剎車,並安全駕駛。連接車輛還可以配備攝像頭記錄事故。這可以與保險掛鉤,為良好駕駛員提供更低的保費。 - -* 駕駛時數合規 - 確保駕駛員僅在法律允許的時數內駕駛,根據他們啟動和關閉引擎的時間進行追蹤。 - -這些好處可以結合使用——例如,將駕駛時數合規與位置追蹤結合,重新規劃駕駛員的路線,如果他們無法在允許的駕駛時數內到達目的地。這些還可以與其他車輛特定的數據結合,例如冷藏卡車的溫度數據,讓車輛在當前路線無法保持貨物溫度時重新規劃路線。 - -> 🎓 物流是將貨物從一個地方運送到另一個地方的過程,例如從農場經過一個或多個倉庫運送到超市。一位農夫將西紅柿裝箱,裝上卡車,運送到中央倉庫,然後裝上另一輛可能載有不同類型農產品的卡車,最終送到超市。 - -車輛追蹤的核心組件是 GPS——能夠在地球上任何地方精確定位的感應器。在本課程中,您將學習如何使用 GPS 感應器,首先從學習如何定義地球上的位置開始。 - -## 地理空間座標 - -地理空間座標用於定義地球表面上的點,類似於如何使用座標在電腦螢幕上繪製像素或在十字繡中定位針腳。對於單個點,您需要一對座標。例如,美國華盛頓州雷德蒙市的微軟總部位於 47.6423109, -122.1390293。 - -### 緯度和經度 - -地球是一個球體——一個三維的圓。因此,點的位置是通過將其分為 360 度來定義的,這與圓的幾何學相同。緯度測量從北到南的度數,經度測量從東到西的度數。 - -> 💁 沒有人真正知道為什麼圓被分為 360 度。[維基百科上的角度頁面](https://wikipedia.org/wiki/Degree_(angle))介紹了一些可能的原因。 - -![緯度線從北極的 90°,到赤道的 0°,再到南極的 -90°](../../../../../translated_images/zh-HK/latitude-lines.11d8d91dfb2014a57437272d7db7fd6607243098e8685f06e0c5f1ec984cb7eb.png) - -緯度是通過圍繞地球並與赤道平行的線來測量的,將北半球和南半球分為各 90°。赤道為 0°,北極為 90°,也稱為北緯 90°,南極為 -90°,或南緯 90°。 - -經度是測量東西方向的度數。經度的 0° 起點稱為*本初子午線*,於 1884 年被定義為一條從北極到南極的線,通過[英國格林威治皇家天文台](https://wikipedia.org/wiki/Royal_Observatory,_Greenwich)。 - -![經度線從本初子午線以西的 -180°,到本初子午線的 0°,再到以東的 180°](../../../../../translated_images/zh-HK/longitude-meridians.ab4ef1c91c064586b0185a3c8d39e585903696c6a7d28c098a93a629cddb5d20.png) - -> 🎓 子午線是一條從北極到南極的假想直線,形成一個半圓。 - -要測量某一點的經度,您需要測量從本初子午線到通過該點的子午線沿赤道的度數。經度範圍從 -180°(即西經 180°),經過本初子午線的 0°,到 180°(即東經 180°)。180° 和 -180° 指的是同一點,即對蹠子午線或第 180 條子午線。這是一條位於地球另一側的子午線,與本初子午線相對。 - -> 💁 對蹠子午線不要與國際日期變更線混淆,雖然它們大致位於相同位置,但國際日期變更線並不是一條直線,會根據地緣政治邊界進行調整。 - -✅ 做一些研究:試著找出您目前位置的緯度和經度。 - -### 度、分和秒 vs 十進制度數 - -傳統上,緯度和經度的度數測量使用六十進制(sexagesimal)數字系統,這是一種由古巴比倫人使用的數字系統,他們最早測量並記錄了時間和距離。您可能每天都在使用六十進制而不自知——例如將小時分為 60 分鐘,將分鐘分為 60 秒。 - -緯度和經度以度、分和秒為單位測量,其中 1 分為 1/60 度,1 秒為 1/60 分。 - -例如,在赤道上: - -* 1° 緯度約為 **111.3 公里** -* 1 分緯度為 111.3/60 = **1.855 公里** -* 1 秒緯度為 1.855/60 = **0.031 公里** - -分的符號為單引號,秒的符號為雙引號。例如,2 度 17 分 43 秒可以寫作 2°17'43"。秒的小數部分以小數表示,例如半秒為 0°0'0.5"。 - -電腦不使用六十進制,因此在大多數電腦系統中,這些座標以十進制度數表示。例如,2°17'43" 為 2.295277。通常省略度數符號。 - -一個點的座標總是以 `緯度, 經度` 的格式給出,因此前面提到的微軟總部 47.6423109,-122.117198 的座標為: - -* 緯度為 47.6423109(赤道以北 47.6423109 度) -* 經度為 -122.1390293(本初子午線以西 122.1390293 度)。 - -![微軟總部位於 47.6423109,-122.117198](../../../../../translated_images/zh-HK/microsoft-gps-location-world.a321d481b010f6adfcca139b2ba0adc53b79f58a540495b8e2ce7f779ea64bfe.png) - -## 全球定位系統(GPS) - -GPS 系統使用多顆環繞地球運行的衛星來定位您的位置。您可能在不知不覺中使用過 GPS 系統——例如在手機上的地圖應用(如 Apple 地圖或 Google 地圖)中查看您的位置,或者在叫車應用(如 Uber 或 Lyft)中查看您的車輛位置,或者在車內使用衛星導航(sat-nav)。 - -> 🎓 衛星導航中的“衛星”指的就是 GPS 衛星! - -GPS 系統的工作原理是多顆衛星發送信號,包含每顆衛星的當前位置和精確的時間戳。這些信號通過無線電波傳送,並由 GPS 感應器中的天線接收。GPS 感應器檢測到這些信號後,使用當前時間測量信號從衛星到達感應器所需的時間。由於無線電波的速度是恆定的,GPS 感應器可以使用發送的時間戳計算感應器與衛星的距離。通過結合至少 3 顆衛星的數據和發送的位置,GPS 感應器可以精確定位其在地球上的位置。 - -> 💁 GPS 感應器需要天線來檢測無線電波。內建於卡車和汽車的 GPS 天線通常安裝在擋風玻璃或車頂,以獲得良好的信號。如果您使用的是單獨的 GPS 系統,例如智能手機或物聯網設備,則需要確保內建於 GPS 系統或手機中的天線能夠清晰地看到天空,例如安裝在擋風玻璃上。 - -![通過知道感應器與多顆衛星的距離,可以計算出位置](../../../../../translated_images/zh-HK/gps-satellites.04acf1148fe25fbf1586bc2e8ba698e8d79b79a50c36824b38417dd13372b90f.png) - -GPS 衛星環繞地球運行,並非固定在感應器上方,因此位置數據包括海拔高度(相對於海平面)以及緯度和經度。 - -過去,GPS 的精確度受到美國軍方的限制,精度約為 5 米。這一限制於 2000 年被取消,現在的精度可達 30 厘米。但由於信號干擾,並非總能達到這一精度。 - -✅ 如果您有智能手機,打開地圖應用,看看您的位置有多精確。可能需要一段時間讓手機檢測到多顆衛星以獲得更精確的位置。 -💁 衛星內部配備了極其精準的原子鐘,但由於愛因斯坦的特殊相對論和廣義相對論所預測的速度增加導致時間減慢的現象,衛星每天相比地球上的原子鐘會漂移38微秒(0.0000038秒)。這是因為衛星的運行速度比地球自轉速度更快。這種漂移現象已被用來證明特殊相對論和廣義相對論的預測,並且在設計GPS系統時必須進行調整。從字面上看,GPS衛星上的時間運行得更慢。 -GPS 系統已由多個國家和政治聯盟開發並部署,包括美國、俄羅斯、日本、印度、歐盟和中國。現代 GPS 感應器可以連接到大多數這些系統,以獲得更快速和更準確的定位。 - -> 🎓 每個部署中的衛星群組被稱為星座(constellations)。 - -## 讀取 GPS 感應器數據 - -大多數 GPS 感應器通過 UART 傳輸 GPS 數據。 - -> ⚠️ UART 已在 [項目 2,第 2 課](../../../2-farm/lessons/2-detect-soil-moisture/README.md#universal-asynchronous-receiver-transmitter-uart) 中介紹。如有需要,請回顧該課程。 - -您可以在 IoT 設備上使用 GPS 感應器來獲取 GPS 數據。 - -### 任務 - 連接 GPS 感應器並讀取 GPS 數據 - -按照相關指南,使用您的 IoT 設備讀取 GPS 數據: - -* [Arduino - Wio Terminal](wio-terminal-gps-sensor.md) -* [單板電腦 - Raspberry Pi](pi-gps-sensor.md) -* [單板電腦 - 虛擬設備](virtual-device-gps-sensor.md) - -## NMEA GPS 數據 - -當您運行代碼時,可能會在輸出中看到一些看似亂碼的內容。這其實是標準的 GPS 數據,每一部分都有其意義。 - -GPS 感應器使用 NMEA 消息輸出數據,遵循 NMEA 0183 標準。NMEA 是 [National Marine Electronics Association](https://www.nmea.org)(美國國家海洋電子協會)的縮寫,這是一個美國的貿易組織,負責制定海洋電子設備之間的通信標準。 - -> 💁 該標準是專有的,售價至少為 2,000 美元,但由於有足夠的信息已進入公共領域,大部分標準已被逆向工程,可用於開源和其他非商業代碼。 - -這些消息是基於文本的。每條消息由一個以 `$` 字符開頭的 *句子* 組成,接著是兩個字符表示消息來源(例如,GP 表示美國 GPS 系統,GN 表示 GLONASS,俄羅斯的 GPS 系統),以及三個字符表示消息類型。消息的其餘部分是用逗號分隔的字段,最後以換行符結束。 - -一些可以接收到的消息類型包括: - -| 類型 | 描述 | -| ---- | ----------- | -| GGA | GPS 定位數據,包括 GPS 感應器的緯度、經度和海拔,以及用於計算該定位的可見衛星數量。 | -| ZDA | 當前日期和時間,包括本地時區。 | -| GSV | 可見衛星的詳細信息 - 定義為 GPS 感應器可以檢測到信號的衛星。 | - -> 💁 GPS 數據包括時間戳,因此您的 IoT 設備可以從 GPS 感應器獲取時間,而無需依賴 NTP 服務器或內部實時時鐘。 - -GGA 消息包括當前位置,使用 `(dd)dmm.mmmm` 格式,並附帶一個單字符表示方向。格式中的 `d` 表示度數,`m` 表示分,秒則以分的小數表示。例如,2°17'43" 會表示為 217.716666667 - 即 2 度,17.716666667 分。 - -方向字符可以是 `N` 或 `S` 表示緯度的北或南,`E` 或 `W` 表示經度的東或西。例如,緯度 2°17'43" 的方向字符為 `N`,而 -2°17'43" 的方向字符為 `S`。 - -例如 - NMEA 句子 `$GNGGA,020604.001,4738.538654,N,12208.341758,W,1,3,,164.7,M,-17.1,M,,*67` - -* 緯度部分是 `4738.538654,N`,轉換為十進制度數為 47.6423109。`4738.538654` 是 47.6423109,方向為 `N`(北),因此是正緯度。 - -* 經度部分是 `12208.341758,W`,轉換為十進制度數為 -122.1390293。`12208.341758` 是 122.1390293°,方向為 `W`(西),因此是負經度。 - -## 解碼 GPS 感應器數據 - -與其使用原始的 NMEA 數據,不如將其解碼為更有用的格式。您可以使用多個開源庫來幫助從原始 NMEA 消息中提取有用的數據。 - -### 任務 - 解碼 GPS 感應器數據 - -按照相關指南,使用您的 IoT 設備解碼 GPS 感應器數據: - -* [Arduino - Wio Terminal](wio-terminal-gps-decode.md) -* [單板電腦 - Raspberry Pi/虛擬 IoT 設備](single-board-computer-gps-decode.md) - ---- - -## 🚀 挑戰 - -編寫您自己的 NMEA 解碼器!與其依賴第三方庫來解碼 NMEA 句子,您能否編寫自己的解碼器來從 NMEA 句子中提取緯度和經度? - -## 課後測驗 - -[課後測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/22) - -## 回顧與自學 - -* 在 [維基百科的地理坐標系統頁面](https://wikipedia.org/wiki/Geographic_coordinate_system) 閱讀更多關於地理坐標的信息。 -* 在 [維基百科的本初子午線頁面](https://wikipedia.org/wiki/Prime_meridian#Prime_meridian_on_other_planetary_bodies) 閱讀地球以外其他天體的本初子午線。 -* 研究來自歐盟、日本、俄羅斯、印度和美國等不同國家和政治聯盟的各種 GPS 系統。 - -## 作業 - -[調查其他 GPS 數據](assignment.md) - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/3-transport/lessons/1-location-tracking/assignment.md b/translations/hk/3-transport/lessons/1-location-tracking/assignment.md deleted file mode 100644 index 0bb15a6d7..000000000 --- a/translations/hk/3-transport/lessons/1-location-tracking/assignment.md +++ /dev/null @@ -1,29 +0,0 @@ - -# 探索其他 GPS 數據 - -## 指引 - -從您的 GPS 感測器接收到的 NMEA 訊息除了位置資訊外,還包含其他數據。探索這些額外的數據,並在您的 IoT 裝置中加以利用。 - -例如 - 您能否獲取當前的日期和時間?如果您使用的是微控制器,能否像在上一個項目中使用 NTP 信號設置時鐘一樣,使用 GPS 數據來設置時鐘?您能否獲取海拔高度(您距離海平面的高度)或當前速度? - -如果您使用的是虛擬 IoT 裝置,則可以通過使用工具 [nmeagen.org](https://www.nmeagen.org) 生成的 NMEA 訊息來獲取部分數據。 - -## 評分標準 - -| 評分標準 | 卓越 | 合格 | 需要改進 | -| -------- | ----- | ---- | -------- | -| 獲取更多 GPS 數據 | 能夠獲取並使用更多 GPS 數據,作為遙測數據或用於設置 IoT 裝置 | 能夠獲取更多 GPS 數據,但無法使用它 | 無法獲取更多 GPS 數據 | - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業的人工作翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解讀概不負責。 \ No newline at end of file diff --git a/translations/hk/3-transport/lessons/1-location-tracking/pi-gps-sensor.md b/translations/hk/3-transport/lessons/1-location-tracking/pi-gps-sensor.md deleted file mode 100644 index 29c237f87..000000000 --- a/translations/hk/3-transport/lessons/1-location-tracking/pi-gps-sensor.md +++ /dev/null @@ -1,193 +0,0 @@ - -# 讀取 GPS 數據 - Raspberry Pi - -在這部分課程中,你將為 Raspberry Pi 添加一個 GPS 感測器,並從中讀取數據。 - -## 硬件 - -Raspberry Pi 需要一個 GPS 感測器。 - -你將使用的感測器是 [Grove GPS Air530 感測器](https://www.seeedstudio.com/Grove-GPS-Air530-p-4584.html)。這款感測器可以連接多個 GPS 系統,提供快速且準確的定位。感測器由兩部分組成——感測器的核心電子元件,以及通過細線連接的外部天線,用於接收衛星的電波。 - -這是一個 UART 感測器,因此它通過 UART 傳輸 GPS 數據。 - -## 連接 GPS 感測器 - -Grove GPS 感測器可以連接到 Raspberry Pi。 - -### 任務 - 連接 GPS 感測器 - -連接 GPS 感測器。 - -![Grove GPS 感測器](../../../../../translated_images/zh-HK/grove-gps-sensor.247943bf69b03f0d1820ef6ed10c587f9b650e8db55b936851c92412180bd3e2.png) - -1. 將 Grove 電纜的一端插入 GPS 感測器上的插座。它只能以一種方向插入。 - -1. 在 Raspberry Pi 關機的情況下,將 Grove 電纜的另一端連接到 Pi 上的 Grove Base Hat 的 UART 插座,該插座標記為 **UART**。此插座位於中間排,靠近 SD 卡插槽的一側,遠離 USB 端口和以太網插座。 - - ![Grove GPS 感測器連接到 UART 插座](../../../../../translated_images/zh-HK/pi-gps-sensor.1f99ee2b2f6528915047ec78967bd362e0e4ee0ed594368a3837b9cf9cdaca64.png) - -1. 將 GPS 感測器放置在天線能夠看到天空的位置——理想情況下靠近窗戶或室外。天線周圍沒有障礙物時,信號會更清晰。 - -## 編程 GPS 感測器 - -現在可以編程 Raspberry Pi 以使用連接的 GPS 感測器。 - -### 任務 - 編程 GPS 感測器 - -編程設備。 - -1. 啟動 Raspberry Pi,並等待其完成啟動。 - -1. GPS 感測器有兩個 LED——藍色 LED 在傳輸數據時閃爍,綠色 LED 在接收衛星數據時每秒閃爍一次。啟動 Raspberry Pi 時,確保藍色 LED 閃爍。幾分鐘後,綠色 LED 會開始閃爍——如果沒有,可能需要重新調整天線的位置。 - -1. 啟動 VS Code,可以直接在 Raspberry Pi 上啟動,也可以通過 Remote SSH 擴展連接。 - - > ⚠️ 如果需要,可以參考 [課程 1 中設置和啟動 VS Code 的指導](../../../1-getting-started/lessons/1-introduction-to-iot/pi.md)。 - -1. 對於支持藍牙的新版本 Raspberry Pi,藍牙使用的串口與 Grove UART 端口使用的串口存在衝突。為了解決這個問題,請執行以下操作: - - 1. 在 VS Code 的終端中,使用以下命令通過 `nano` 編輯 `/boot/config.txt` 文件,`nano` 是內置的終端文本編輯器: - - ```sh - sudo nano /boot/config.txt - ``` - - > 由於需要使用 `sudo` 權限(提升的權限)來編輯此文件,因此無法直接通過 VS Code 編輯。 - - 1. 使用光標鍵移動到文件末尾,然後將以下代碼複製並粘貼到文件末尾: - - ```ini - dtoverlay=pi3-miniuart-bt - dtoverlay=pi3-disable-bt - enable_uart=1 - ``` - - 你可以使用設備的常規鍵盤快捷鍵粘貼(Windows、Linux 或 Raspberry Pi OS 上使用 `Ctrl+v`,macOS 上使用 `Cmd+v`)。 - - 1. 按 `Ctrl+x` 保存文件並退出 nano。當被詢問是否保存修改的緩衝區時,按 `y`,然後按 `Enter` 確認覆蓋 `/boot/config.txt`。 - - > 如果出現錯誤,可以選擇不保存退出,然後重複這些步驟。 - - 1. 使用以下命令在 nano 中編輯 `/boot/cmdline.txt` 文件: - - ```sh - sudo nano /boot/cmdline.txt - ``` - - 1. 此文件包含多個用空格分隔的鍵值對。刪除任何鍵為 `console` 的鍵值對。它們可能看起來像這樣: - - ```output - console=serial0,115200 console=tty1 - ``` - - 你可以使用光標鍵移動到這些條目,然後使用常規的 `del` 或 `backspace` 鍵刪除。 - - 例如,如果原始文件如下所示: - - ```output - console=serial0,115200 console=tty1 root=PARTUUID=058e2867-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait - ``` - - 新版本將如下所示: - - ```output - root=PARTUUID=058e2867-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait - ``` - - 1. 按上述步驟保存文件並退出 nano。 - - 1. 重啟 Raspberry Pi,然後在 Pi 重啟後重新連接到 VS Code。 - -1. 在終端中,為 `pi` 用戶的主目錄創建一個名為 `gps-sensor` 的新文件夾。在此文件夾中創建一個名為 `app.py` 的文件。 - -1. 在 VS Code 中打開此文件夾。 - -1. GPS 模塊通過串口傳輸 UART 數據。安裝 `pyserial` Pip 包以便從 Python 代碼與串口通信: - - ```sh - pip3 install pyserial - ``` - -1. 將以下代碼添加到你的 `app.py` 文件中: - - ```python - import time - import serial - - serial = serial.Serial('/dev/ttyAMA0', 9600, timeout=1) - serial.reset_input_buffer() - serial.flush() - - def print_gps_data(line): - print(line.rstrip()) - - while True: - line = serial.readline().decode('utf-8') - - while len(line) > 0: - print_gps_data(line) - line = serial.readline().decode('utf-8') - - time.sleep(1) - ``` - - 此代碼從 `pyserial` Pip 包中導入 `serial` 模塊。然後它連接到 `/dev/ttyAMA0` 串口——這是 Grove Pi Base Hat 使用的 UART 端口的地址。接著,它清除此串口連接中的任何現有數據。 - - 接下來定義了一個名為 `print_gps_data` 的函數,用於將傳入的行打印到控制台。 - - 然後代碼進行無限循環,每次循環從串口讀取多行文本。它對每行調用 `print_gps_data` 函數。 - - 在所有數據讀取完成後,循環會休眠 1 秒,然後再次嘗試。 - -1. 運行此代碼。你將看到來自 GPS 感測器的原始輸出,類似如下: - - ```output - $GNGGA,020604.001,4738.538654,N,12208.341758,W,1,3,,164.7,M,-17.1,M,,*67 - $GPGSA,A,1,,,,,,,,,,,,,,,*1E - $BDGSA,A,1,,,,,,,,,,,,,,,*0F - $GPGSV,1,1,00*79 - $BDGSV,1,1,00*68 - ``` - - > 如果在停止並重新啟動代碼時遇到以下錯誤之一,請在你的 while 循環中添加 `try - except` 塊。 - - ```output - UnicodeDecodeError: 'utf-8' codec can't decode byte 0x93 in position 0: invalid start byte - UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf1 in position 0: invalid continuation byte - ``` - - ```python - while True: - try: - line = serial.readline().decode('utf-8') - - while len(line) > 0: - print_gps_data() - line = serial.readline().decode('utf-8') - - # There's a random chance the first byte being read is part way through a character. - # Read another full line and continue. - - except UnicodeDecodeError: - line = serial.readline().decode('utf-8') - - time.sleep(1) - ``` - -> 💁 你可以在 [code-gps/pi](../../../../../3-transport/lessons/1-location-tracking/code-gps/pi) 文件夾中找到此代碼。 - -😀 恭喜!你的 GPS 感測器程式成功運行! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/3-transport/lessons/1-location-tracking/single-board-computer-gps-decode.md b/translations/hk/3-transport/lessons/1-location-tracking/single-board-computer-gps-decode.md deleted file mode 100644 index 0a5ba14e8..000000000 --- a/translations/hk/3-transport/lessons/1-location-tracking/single-board-computer-gps-decode.md +++ /dev/null @@ -1,75 +0,0 @@ - -# 解碼 GPS 數據 - 虛擬 IoT 硬件及 Raspberry Pi - -在本課程中,你將解碼由 Raspberry Pi 或虛擬 IoT 設備從 GPS 感測器讀取的 NMEA 訊息,並提取緯度和經度。 - -## 解碼 GPS 數據 - -當從串口讀取到原始 NMEA 數據後,可以使用開源 NMEA 庫進行解碼。 - -### 任務 - 解碼 GPS 數據 - -編寫設備程式以解碼 GPS 數據。 - -1. 如果尚未開啟 `gps-sensor` 應用程式專案,請開啟它。 - -1. 安裝 `pynmea2` Pip 套件。此套件包含解碼 NMEA 訊息的程式碼。 - - ```sh - pip3 install pynmea2 - ``` - -1. 在 `app.py` 文件的導入部分新增以下程式碼以導入 `pynmea2` 模組: - - ```python - import pynmea2 - ``` - -1. 用以下內容替換 `print_gps_data` 函數的內容: - - ```python - msg = pynmea2.parse(line) - if msg.sentence_type == 'GGA': - lat = pynmea2.dm_to_sd(msg.lat) - lon = pynmea2.dm_to_sd(msg.lon) - - if msg.lat_dir == 'S': - lat = lat * -1 - - if msg.lon_dir == 'W': - lon = lon * -1 - - print(f'{lat},{lon} - from {msg.num_sats} satellites') - ``` - - 此程式碼將使用 `pynmea2` 庫解析從 UART 串口讀取的行。 - - 如果訊息的句子類型是 `GGA`,則這是一條位置固定訊息,並進行處理。緯度和經度值將從訊息中讀取,並從 NMEA `(d)ddmm.mmmm` 格式轉換為十進制度數。`dm_to_sd` 函數負責進行此轉換。 - - 接著檢查緯度的方向,如果緯度是南方,則將其值轉換為負數。同樣地,如果經度是西方,也將其轉換為負數。 - - 最後,座標將與用於定位的衛星數量一起打印到控制台。 - -1. 執行程式碼。如果你使用的是虛擬 IoT 設備,請確保 CounterFit 應用程式正在運行並且 GPS 數據正在發送。 - - ```output - pi@raspberrypi:~/gps-sensor $ python3 app.py - 47.6423109,-122.1390293 - from 3 satellites - ``` - -> 💁 你可以在 [code-gps-decode/virtual-device](../../../../../3-transport/lessons/1-location-tracking/code-gps-decode/virtual-device) 資料夾或 [code-gps-decode/pi](../../../../../3-transport/lessons/1-location-tracking/code-gps-decode/pi) 資料夾中找到此程式碼。 - -😀 你的 GPS 感測器程式成功解碼數據! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/3-transport/lessons/1-location-tracking/virtual-device-gps-sensor.md b/translations/hk/3-transport/lessons/1-location-tracking/virtual-device-gps-sensor.md deleted file mode 100644 index d6e558603..000000000 --- a/translations/hk/3-transport/lessons/1-location-tracking/virtual-device-gps-sensor.md +++ /dev/null @@ -1,144 +0,0 @@ - -# 讀取 GPS 數據 - 虛擬物聯網硬件 - -在本課程中,你將為虛擬物聯網設備添加一個 GPS 感測器,並從中讀取數據。 - -## 虛擬硬件 - -虛擬物聯網設備將使用一個模擬的 GPS 感測器,該感測器可通過 UART 串口訪問。 - -實體的 GPS 感測器會有一個天線,用來接收來自 GPS 衛星的電波,並將 GPS 信號轉換為 GPS 數據。虛擬版本則模擬這一過程,允許你設置緯度和經度、發送原始 NMEA 句子,或者上傳包含多個位置的 GPX 文件,這些位置可以依次返回。 - -> 🎓 NMEA 句子將在本課程後續部分進行講解 - -### 在 CounterFit 中添加感測器 - -要使用虛擬 GPS 感測器,你需要在 CounterFit 應用中添加一個。 - -#### 任務 - 在 CounterFit 中添加感測器 - -在 CounterFit 應用中添加 GPS 感測器。 - -1. 在你的電腦上創建一個名為 `gps-sensor` 的新 Python 應用,並在其中建立一個名為 `app.py` 的文件和一個 Python 虛擬環境,然後添加 CounterFit 的 pip 套件。 - - > ⚠️ 如果需要,你可以參考[第 1 課中創建和設置 CounterFit Python 項目的指導](../../../1-getting-started/lessons/1-introduction-to-iot/virtual-device.md)。 - -1. 安裝額外的 Pip 套件,以便安裝一個 CounterFit shim,該 shim 可以通過串口與基於 UART 的感測器通信。確保你是在啟動了虛擬環境的終端中進行安裝。 - - ```sh - pip install counterfit-shims-serial - ``` - -1. 確保 CounterFit 網頁應用正在運行。 - -1. 創建一個 GPS 感測器: - - 1. 在 *Sensors* 面板的 *Create sensor* 框中,展開 *Sensor type* 下拉框並選擇 *UART GPS*。 - - 1. 保持 *Port* 設置為 */dev/ttyAMA0*。 - - 1. 點擊 **Add** 按鈕,在 `/dev/ttyAMA0` 端口上創建 GPS 感測器。 - - ![GPS 感測器設置](../../../../../translated_images/zh-HK/counterfit-create-gps-sensor.6385dc9357d85ad1d47b4abb2525e7651fd498917d25eefc5a72feab09eedc70.png) - - GPS 感測器將被創建並顯示在感測器列表中。 - - ![已創建的 GPS 感測器](../../../../../translated_images/zh-HK/counterfit-gps-sensor.3fbb15af0a5367566f2f11324ef5a6f30861cdf2b497071a5e002b7aa473550e.png) - -## 編程 GPS 感測器 - -現在可以為虛擬物聯網設備編程以使用虛擬 GPS 感測器。 - -### 任務 - 編程 GPS 感測器 - -編程 GPS 感測器應用。 - -1. 確保 `gps-sensor` 應用已在 VS Code 中打開。 - -1. 打開 `app.py` 文件。 - -1. 在 `app.py` 文件的頂部添加以下代碼,以連接應用到 CounterFit: - - ```python - from counterfit_connection import CounterFitConnection - CounterFitConnection.init('127.0.0.1', 5000) - ``` - -1. 在其下方添加以下代碼以導入一些需要的庫,包括 CounterFit 串口庫: - - ```python - import time - import counterfit_shims_serial - - serial = counterfit_shims_serial.Serial('/dev/ttyAMA0') - ``` - - 此代碼從 `counterfit_shims_serial` Pip 套件中導入 `serial` 模塊。然後連接到 `/dev/ttyAMA0` 串口——這是虛擬 GPS 感測器用於其 UART 端口的地址。 - -1. 在其下方添加以下代碼以從串口讀取數據並將值打印到控制台: - - ```python - def print_gps_data(line): - print(line.rstrip()) - - while True: - line = serial.readline().decode('utf-8') - - while len(line) > 0: - print_gps_data(line) - line = serial.readline().decode('utf-8') - - time.sleep(1) - ``` - - 定義了一個名為 `print_gps_data` 的函數,用於將傳入的行打印到控制台。 - - 接著代碼進行無限循環,在每次循環中從串口讀取多行文本。它會為每行調用 `print_gps_data` 函數。 - - 在所有數據讀取完成後,循環會休眠 1 秒,然後再次嘗試。 - -1. 運行此代碼,確保使用與 CounterFit 應用運行的終端不同的終端,以便 CounterFit 應用保持運行。 - -1. 從 CounterFit 應用中更改 GPS 感測器的值。你可以通過以下方式之一進行操作: - - * 將 **Source** 設置為 `Lat/Lon`,並設置明確的緯度、經度以及用於獲取 GPS 定位的衛星數量。此值僅會發送一次,因此勾選 **Repeat** 框以使數據每秒重複發送。 - - ![選擇緯度和經度的 GPS 感測器](../../../../../translated_images/zh-HK/counterfit-gps-sensor-latlon.008c867d75464fbe7f84107cc57040df565ac07cb57d2f21db37d087d470197d.png) - - * 將 **Source** 設置為 `NMEA`,並在文本框中添加一些 NMEA 句子。所有這些值都會被發送,每個新的 GGA(位置修正)句子可以在 1 秒延遲後被讀取。 - - ![設置 NMEA 句子的 GPS 感測器](../../../../../translated_images/zh-HK/counterfit-gps-sensor-nmea.c62eea442171e17e19528b051b104cfcecdc9cd18db7bc72920f29821ae63f73.png) - - 你可以使用像 [nmeagen.org](https://www.nmeagen.org) 這樣的工具通過在地圖上繪製來生成這些句子。這些值僅會發送一次,因此勾選 **Repeat** 框以使數據在全部發送後每秒重複一次。 - - * 將 **Source** 設置為 GPX 文件,並上傳一個包含軌跡位置的 GPX 文件。你可以從一些流行的地圖和徒步網站(如 [AllTrails](https://www.alltrails.com/))下載 GPX 文件。這些文件包含多個 GPS 位置作為軌跡,GPS 感測器將以 1 秒間隔返回每個新位置。 - - ![設置 GPX 文件的 GPS 感測器](../../../../../translated_images/zh-HK/counterfit-gps-sensor-gpxfile.8310b063ce8a425ccc8ebeec8306aeac5e8e55207f007d52c6e1194432a70cd9.png) - - 這些值僅會發送一次,因此勾選 **Repeat** 框以使數據在全部發送後每秒重複一次。 - - 配置好 GPS 設置後,選擇 **Set** 按鈕以將這些值提交到感測器。 - -1. 你將看到來自 GPS 感測器的原始輸出,類似以下內容: - - ```output - $GNGGA,020604.001,4738.538654,N,12208.341758,W,1,3,,164.7,M,-17.1,M,,*67 - $GNGGA,020604.001,4738.538654,N,12208.341758,W,1,3,,164.7,M,-17.1,M,,*67 - ``` - -> 💁 你可以在 [code-gps/virtual-device](../../../../../3-transport/lessons/1-location-tracking/code-gps/virtual-device) 文件夾中找到此代碼。 - -😀 你的 GPS 感測器程式成功了! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/3-transport/lessons/1-location-tracking/wio-terminal-gps-decode.md b/translations/hk/3-transport/lessons/1-location-tracking/wio-terminal-gps-decode.md deleted file mode 100644 index cae8825c2..000000000 --- a/translations/hk/3-transport/lessons/1-location-tracking/wio-terminal-gps-decode.md +++ /dev/null @@ -1,83 +0,0 @@ - -# 解碼 GPS 數據 - Wio Terminal - -在這部分課程中,你將解碼從 Wio Terminal 的 GPS 感測器讀取的 NMEA 訊息,並提取緯度和經度。 - -## 解碼 GPS 數據 - -一旦從序列埠讀取到原始的 NMEA 數據,就可以使用開源的 NMEA 函式庫進行解碼。 - -### 任務 - 解碼 GPS 數據 - -編寫程式讓裝置解碼 GPS 數據。 - -1. 如果尚未開啟,請打開 `gps-sensor` 應用程式專案。 - -1. 在專案的 `platformio.ini` 檔案中,新增 [TinyGPSPlus](https://github.com/mikalhart/TinyGPSPlus) 函式庫的依賴項目。這個函式庫包含解碼 NMEA 數據的程式碼。 - - ```ini - lib_deps = - mikalhart/TinyGPSPlus @ 1.0.2 - ``` - -1. 在 `main.cpp` 中,新增一行包含 TinyGPSPlus 函式庫的指令: - - ```cpp - #include - ``` - -1. 在 `Serial3` 宣告的下方,宣告一個 TinyGPSPlus 物件來處理 NMEA 訊息: - - ```cpp - TinyGPSPlus gps; - ``` - -1. 將 `printGPSData` 函式的內容更改為以下內容: - - ```cpp - if (gps.encode(Serial3.read())) - { - if (gps.location.isValid()) - { - Serial.print(gps.location.lat(), 6); - Serial.print(F(",")); - Serial.print(gps.location.lng(), 6); - Serial.print(" - from "); - Serial.print(gps.satellites.value()); - Serial.println(" satellites"); - } - } - ``` - - 這段程式碼會從 UART 序列埠讀取下一個字元並傳入 `gps` NMEA 解碼器。每讀取一個字元後,它會檢查解碼器是否已讀取到有效的訊息,然後檢查是否已讀取到有效的位置。如果位置有效,它會將位置資料和參與定位的衛星數量一起傳送到序列監控器。 - -1. 編譯並上傳程式碼到 Wio Terminal。 - -1. 上傳完成後,你可以使用序列監控器來監控 GPS 位置數據。 - - ```output - > Executing task: platformio device monitor < - - --- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time - --- More details at http://bit.ly/pio-monitor-filters - --- Miniterm on /dev/cu.usbmodem1201 9600,8,N,1 --- - --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- - 47.6423109,-122.1390293 - from 3 satellites - ``` - -> 💁 你可以在 [code-gps-decode/wio-terminal](../../../../../3-transport/lessons/1-location-tracking/code-gps-decode/wio-terminal) 資料夾中找到這段程式碼。 - -😀 你的 GPS 感測器程式成功解碼數據了! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/3-transport/lessons/1-location-tracking/wio-terminal-gps-sensor.md b/translations/hk/3-transport/lessons/1-location-tracking/wio-terminal-gps-sensor.md deleted file mode 100644 index fb5978fd9..000000000 --- a/translations/hk/3-transport/lessons/1-location-tracking/wio-terminal-gps-sensor.md +++ /dev/null @@ -1,154 +0,0 @@ - -# 讀取 GPS 數據 - Wio Terminal - -在本課程中,你將為 Wio Terminal 添加一個 GPS 感測器,並從中讀取數據。 - -## 硬件 - -Wio Terminal 需要一個 GPS 感測器。 - -你將使用的感測器是 [Grove GPS Air530 感測器](https://www.seeedstudio.com/Grove-GPS-Air530-p-4584.html)。這個感測器可以連接多個 GPS 系統,提供快速且準確的定位。感測器由兩部分組成——感測器的核心電子元件,以及通過細線連接的外部天線,用於接收來自衛星的無線電波。 - -這是一個 UART 感測器,因此它通過 UART 傳輸 GPS 數據。 - -### 連接 GPS 感測器 - -Grove GPS 感測器可以連接到 Wio Terminal。 - -#### 任務 - 連接 GPS 感測器 - -連接 GPS 感測器。 - -![Grove GPS 感測器](../../../../../translated_images/zh-HK/grove-gps-sensor.247943bf69b03f0d1820ef6ed10c587f9b650e8db55b936851c92412180bd3e2.png) - -1. 將 Grove 線纜的一端插入 GPS 感測器的插槽。它只能以一種方式插入。 - -1. 確保 Wio Terminal 未連接到電腦或其他電源,然後將 Grove 線纜的另一端連接到 Wio Terminal 左側的 Grove 插槽(面向螢幕時)。這是靠近電源按鈕的插槽。 - - ![Grove GPS 感測器連接到左側插槽](../../../../../translated_images/zh-HK/wio-gps-sensor.19fd52b81ce58095.webp) - -1. 將 GPS 感測器放置在天線可以看到天空的位置——理想情況下靠近窗戶或在室外。天線周圍沒有障礙物時,信號會更清晰。 - -1. 現在可以將 Wio Terminal 連接到電腦。 - -1. GPS 感測器有兩個 LED 指示燈——藍色 LED 在傳輸數據時閃爍,綠色 LED 在接收到衛星數據時每秒閃爍一次。啟動 Wio Terminal 時,確保藍色 LED 正在閃爍。幾分鐘後,綠色 LED 會開始閃爍——如果沒有,可能需要重新調整天線的位置。 - -## 編程 GPS 感測器 - -現在可以為 Wio Terminal 編程以使用連接的 GPS 感測器。 - -### 任務 - 編程 GPS 感測器 - -為設備編程。 - -1. 使用 PlatformIO 創建一個全新的 Wio Terminal 專案,命名為 `gps-sensor`。在 `setup` 函數中添加代碼以配置串口。 - -1. 在 `main.cpp` 文件的頂部添加以下 include 指令。這將包含一個用於配置左側 Grove 插槽為 UART 的標頭文件。 - - ```cpp - #include - ``` - -1. 在此之下,添加以下代碼行以聲明一個與 UART 插槽連接的串口: - - ```cpp - static Uart Serial3(&sercom3, PIN_WIRE_SCL, PIN_WIRE_SDA, SERCOM_RX_PAD_1, UART_TX_PAD_0); - ``` - -1. 需要添加一些代碼將內部信號處理程序重定向到此串口。在 `Serial3` 聲明之後添加以下代碼: - - ```cpp - void SERCOM3_0_Handler() - { - Serial3.IrqHandler(); - } - - void SERCOM3_1_Handler() - { - Serial3.IrqHandler(); - } - - void SERCOM3_2_Handler() - { - Serial3.IrqHandler(); - } - - void SERCOM3_3_Handler() - { - Serial3.IrqHandler(); - } - ``` - -1. 在 `setup` 函數中,配置 `Serial` 串口之後,使用以下代碼配置 UART 串口: - - ```cpp - Serial3.begin(9600); - - while (!Serial3) - ; // Wait for Serial3 to be ready - - delay(1000); - ``` - -1. 在 `setup` 函數中的此代碼下方,添加以下代碼以將 Grove 插槽連接到串口: - - ```cpp - pinPeripheral(PIN_WIRE_SCL, PIO_SERCOM_ALT); - ``` - -1. 在 `loop` 函數之前,添加以下函數以將 GPS 數據發送到串口監視器: - - ```cpp - void printGPSData() - { - Serial.println(Serial3.readStringUntil('\n')); - } - ``` - -1. 在 `loop` 函數中,添加以下代碼以從 UART 串口讀取數據並將輸出打印到串口監視器: - - ```cpp - while (Serial3.available() > 0) - { - printGPSData(); - } - - delay(1000); - ``` - - 此代碼從 UART 串口讀取數據。`readStringUntil` 函數會讀取直到終止符(在此情況下為換行符)的內容。這將讀取整個 NMEA 語句(NMEA 語句以換行符結尾)。只要可以從 UART 串口讀取數據,數據就會被讀取並通過 `printGPSData` 函數發送到串口監視器。一旦無法再讀取數據,`loop` 會延遲 1 秒(1,000 毫秒)。 - -1. 編譯並上傳代碼到 Wio Terminal。 - -1. 上傳完成後,可以使用串口監視器查看 GPS 數據。 - - ```output - > Executing task: platformio device monitor < - - --- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time - --- More details at http://bit.ly/pio-monitor-filters - --- Miniterm on /dev/cu.usbmodem1201 9600,8,N,1 --- - --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H --- - $GNGGA,020604.001,4738.538654,N,12208.341758,W,1,3,,164.7,M,-17.1,M,,*67 - $GPGSA,A,1,,,,,,,,,,,,,,,*1E - $BDGSA,A,1,,,,,,,,,,,,,,,*0F - $GPGSV,1,1,00*79 - $BDGSV,1,1,00*68 - ``` - -> 💁 你可以在 [code-gps/wio-terminal](../../../../../3-transport/lessons/1-location-tracking/code-gps/wio-terminal) 資料夾中找到此代碼。 - -😀 恭喜!你的 GPS 感測器程式成功運行! - ---- - -**免責聲明**: -本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。 \ No newline at end of file diff --git a/translations/hk/3-transport/lessons/2-store-location-data/README.md b/translations/hk/3-transport/lessons/2-store-location-data/README.md deleted file mode 100644 index 69dbb647a..000000000 --- a/translations/hk/3-transport/lessons/2-store-location-data/README.md +++ /dev/null @@ -1,477 +0,0 @@ - -# 儲存位置數據 - -![本課程的手繪筆記概述](../../../../../translated_images/zh-HK/lesson-12.ca7f53039712a3ec14ad6474d8445361c84adab643edc53fa6269b77895606bb.jpg) - -> 手繪筆記由 [Nitya Narasimhan](https://github.com/nitya) 提供。點擊圖片查看更大版本。 - -## 課前測驗 - -[課前測驗](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/23) - -## 簡介 - -在上一課中,你學習了如何使用 GPS 感應器來捕捉位置數據。要將這些數據用於可視化一輛載滿食物的卡車的位置及其行程,需要將數據發送到雲端的 IoT 服務,並存儲在某個地方。 - -在本課中,你將學習存儲 IoT 數據的不同方法,並學習如何使用無伺服器代碼來存儲來自 IoT 服務的數據。 - -本課將涵蓋以下內容: - -* [結構化數據與非結構化數據](../../../../../3-transport/lessons/2-store-location-data) -* [將 GPS 數據發送到 IoT Hub](../../../../../3-transport/lessons/2-store-location-data) -* [熱路徑、暖路徑和冷路徑](../../../../../3-transport/lessons/2-store-location-data) -* [使用無伺服器代碼處理 GPS 事件](../../../../../3-transport/lessons/2-store-location-data) -* [Azure 儲存帳戶](../../../../../3-transport/lessons/2-store-location-data) -* [將無伺服器代碼連接到儲存](../../../../../3-transport/lessons/2-store-location-data) - -## 結構化數據與非結構化數據 - -計算機系統處理數據,而這些數據的形狀和大小各不相同。它可以是單一的數字、大量的文本、視頻和圖片,或者 IoT 數據。數據通常可以分為兩類——*結構化數據* 和 *非結構化數據*。 - -* **結構化數據** 是具有明確定義、固定結構的數據,通常映射到具有關聯的數據表。例如,一個人的詳細信息,包括姓名、出生日期和地址。 - -* **非結構化數據** 是沒有明確定義、固定結構的數據,包括結構可能頻繁變化的數據。例如,文檔如書面文件或電子表格。 - -✅ 做些研究:你能想到其他結構化和非結構化數據的例子嗎? - -> 💁 還有一種半結構化數據,它具有結構但不符合固定的數據表。 - -IoT 數據通常被認為是非結構化數據。 - -想像一下,你正在為一家大型商業農場的車隊添加 IoT 設備。你可能希望為不同類型的車輛使用不同的設備。例如: - -* 對於農場車輛如拖拉機,你需要 GPS 數據以確保它們在正確的田地工作 -* 對於運送食物到倉庫的送貨卡車,你需要 GPS 數據以及速度和加速度數據,以確保司機安全駕駛,還需要駕駛身份和啟動/停止數據以確保司機遵守當地法律的工作時間規定 -* 對於冷藏卡車,你還需要溫度數據以確保食物在運輸過程中不會過熱或過冷而變質 - -這些數據可能會不斷變化。例如,如果 IoT 設備位於卡車駕駛室中,那麼它發送的數據可能會隨著拖車的更換而改變,例如只有在使用冷藏拖車時才發送溫度數據。 - -✅ 還有什麼其他 IoT 數據可能被捕捉到?想想卡車可能運載的貨物類型,以及維護數據。 - -這些數據因車輛而異,但它們都被發送到同一個 IoT 服務進行處理。IoT 服務需要能夠處理這些非結構化數據,並以一種既能搜索或分析又能適應不同數據結構的方式存儲它。 - -### SQL 與 NoSQL 儲存 - -數據庫是允許存儲和查詢數據的服務。數據庫分為兩種類型——SQL 和 NoSQL。 - -#### SQL 數據庫 - -最早的數據庫是關係型數據庫管理系統 (RDBMS),或稱為關係型數據庫。它們也被稱為 SQL 數據庫,因為它們使用結構化查詢語言 (SQL) 與數據庫交互以添加、刪除、更新或查詢數據。這些數據庫由一個模式組成——一組明確定義的數據表,類似於電子表格。每個表有多個命名列。插入數據時,你向表中添加一行,並將值放入每個列中。這使得數據保持非常固定的結構——儘管你可以留空列,但如果你想添加新列,必須在數據庫中進行操作,並為現有行填充值。這些數據庫是關係型的——即一個表可以與另一個表有關聯。 - -![一個關係型數據庫,其中用戶表的 ID 與購買表的用戶 ID 列相關,產品表的 ID 與購買表的產品 ID 列相關](../../../../../translated_images/zh-HK/sql-database.be160f12bfccefd3.webp) - -例如,如果你在一個表中存儲用戶的個人詳細信息,你會為每個用戶設置某種內部唯一 ID,該 ID 用於存儲用戶的姓名和地址。如果你想在另一個表中存儲該用戶的其他詳細信息,例如購買記錄,你會在新表中設置一列來存儲該用戶的 ID。當你查詢用戶時,可以使用其 ID 從一個表中獲取個人詳細信息,並從另一個表中獲取購買記錄。 - -SQL 數據庫非常適合存儲結構化數據,並且可以確保數據符合你的模式。 - -✅ 如果你之前沒有使用過 SQL,請花點時間閱讀 [Wikipedia 上的 SQL 頁面](https://wikipedia.org/wiki/SQL)。 - -一些知名的 SQL 數據庫包括 Microsoft SQL Server、MySQL 和 PostgreSQL。 - -✅ 做些研究:閱讀一些這些 SQL 數據庫及其功能。 - -#### NoSQL 數據庫 - -NoSQL 數據庫之所以被稱為 NoSQL,是因為它們沒有 SQL 數據庫的固定結構。它們也被稱為文檔數據庫,因為它們可以存儲非結構化數據,例如文檔。 - -> 💁 儘管名稱如此,一些 NoSQL 數據庫允許使用 SQL 查詢數據。 - -![NoSQL 數據庫中的文件夾中的文檔](../../../../../translated_images/zh-HK/noqsl-database.62d24ccf5b73f60d35c245a8533f1c7147c0928e955b82cb290b2e184bb434df.png) - -NoSQL 數據庫沒有預定義的模式來限制數據存儲方式,你可以插入任何非結構化數據,通常使用 JSON 文檔。這些文檔可以像電腦上的文件一樣組織到文件夾中。每個文檔可以與其他文檔有不同的字段——例如,如果你存儲農場車輛的 IoT 數據,有些可能有加速度計和速度數據字段,其他可能有拖車內部溫度字段。如果你添加了一種新型卡車,例如內置秤來跟蹤運載的農產品重量,那麼你的 IoT 設備可以添加這個新字段,並且可以在不更改數據庫的情況下存儲。 - -一些知名的 NoSQL 數據庫包括 Azure CosmosDB、MongoDB 和 CouchDB。 - -✅ 做些研究:閱讀一些這些 NoSQL 數據庫及其功能。 - -在本課中,你將使用 NoSQL 儲存來存儲 IoT 數據。 - -## 將 GPS 數據發送到 IoT Hub - -在上一課中,你從連接到 IoT 設備的 GPS 感應器捕捉了 GPS 數據。要在雲端存儲這些 IoT 數據,你需要將其發送到 IoT 服務。你將再次使用 Azure IoT Hub,這是你在上一個項目中使用的同一個 IoT 雲服務。 - -![將 GPS 遙測數據從 IoT 設備發送到 IoT Hub](../../../../../translated_images/zh-HK/gps-telemetry-iot-hub.8115335d51cd2c1285d20e9d1b18cf685e59a8e093e7797291ef173445af6f3d.png) - -### 任務 - 將 GPS 數據發送到 IoT Hub - -1. 使用免費層創建一個新的 IoT Hub。 - - > ⚠️ 如果需要,可以參考 [項目 2,第 4 課中創建 IoT Hub 的指導](../../../2-farm/lessons/4-migrate-your-plant-to-the-cloud/README.md#create-an-iot-service-in-the-cloud)。 - - 記得創建一個新的資源組。將新的資源組命名為 `gps-sensor`,並為新的 IoT Hub 命名一個基於 `gps-sensor` 的唯一名稱,例如 `gps-sensor-<你的名字>`。 - - > 💁 如果你仍然保留上一個項目的 IoT Hub,可以重複使用它。創建其他服務時,記得使用該 IoT Hub 的名稱及其所在的資源組。 - -1. 在 IoT Hub 中添加一個新設備。將此設備命名為 `gps-sensor`。獲取該設備的連接字符串。 - -1. 更新你的設備代碼,使用上一步中的設備連接字符串將 GPS 數據發送到新的 IoT Hub。 - - > ⚠️ 如果需要,可以參考 [項目 2,第 4 課中將設備連接到 IoT 的指導](../../../2-farm/lessons/4-migrate-your-plant-to-the-cloud/README.md#connect-your-device-to-the-iot-service)。 - -1. 發送 GPS 數據時,請使用以下 JSON 格式: - - ```json - { - "gps" : - { - "lat" : , - "lon" : - } - } - ``` - -1. 每分鐘發送一次 GPS 數據,以免超過每日消息配額。 - -如果你使用 Wio Terminal,記得添加所有必要的庫,並使用 NTP 伺服器設置時間。你的代碼還需要確保在發送 GPS 位置之前已從串口讀取所有數據,使用上一課的現有代碼。使用以下代碼構建 JSON 文檔: - -```cpp -DynamicJsonDocument doc(1024); -doc["gps"]["lat"] = gps.location.lat(); -doc["gps"]["lon"] = gps.location.lng(); -``` - -如果你使用虛擬 IoT 設備,記得使用虛擬環境安裝所有需要的庫。 - -對於 Raspberry Pi 和虛擬 IoT 設備,使用上一課的現有代碼獲取緯度和經度值,然後使用以下代碼以正確的 JSON 格式發送它們: - -```python -message_json = { "gps" : { "lat":lat, "lon":lon } } -print("Sending telemetry", message_json) -message = Message(json.dumps(message_json)) -``` - -> 💁 你可以在 [code/wio-terminal](../../../../../3-transport/lessons/2-store-location-data/code/wio-terminal)、[code/pi](../../../../../3-transport/lessons/2-store-location-data/code/pi) 或 [code/virtual-device](../../../../../3-transport/lessons/2-store-location-data/code/virtual-device) 文件夾中找到這些代碼。 - -運行你的設備代碼,並使用 `az iot hub monitor-events` CLI 命令確保消息流入 IoT Hub。 - -## 熱路徑、暖路徑和冷路徑 - -從 IoT 設備流向雲端的數據並不總是實時處理。有些數據需要實時處理,其他數據可以稍後處理,還有一些數據可以更晚處理。數據流向不同服務以在不同時間處理的方式被稱為熱路徑、暖路徑和冷路徑。 - -### 熱路徑 - -熱路徑指需要實時或接近實時處理的數據。你可以使用熱路徑數據進行警報,例如接收到車輛接近倉庫的警報,或者冷藏卡車內溫度過高的警報。 - -要使用熱路徑數據,你的代碼需要在雲服務接收到事件時立即做出響應。 - -### 暖路徑 - -暖路徑指可以在接收到數據後稍作處理的數據,例如報告或短期分析。你可以使用暖路徑數據生成每日車輛里程報告,使用前一天收集的數據。 - -暖路徑數據在雲服務接收到後存儲在某種可以快速訪問的儲存中。 - -### 冷路徑 - -冷路徑指歷史數據,存儲長期數據以便隨時處理。例如,你可以使用冷路徑生成年度車輛里程報告,或者對路線進行分析以找到最優路線以降低燃料成本。 - -冷路徑數據存儲在數據倉庫中——設計用於存儲大量不會更改且可以快速輕鬆查詢的數據的數據庫。通常,你的雲應用程序會定期運行任務,每天、每週或每月定期將數據從暖路徑儲存移動到數據倉庫。 - -✅ 思考一下你在這些課程中捕捉到的數據。它是熱路徑、暖路徑還是冷路徑數據? - -## 使用無伺服器代碼處理 GPS 事件 - -一旦數據流入你的 IoT Hub,你可以編寫一些無伺服器代碼來監聽發佈到 Event-Hub 兼容端點的事件。這是暖路徑——這些數據將被存儲並在下一課中用於報告行程。 - -![將 GPS 遙測數據從 IoT 設備發送到 IoT Hub,然後通過事件中心觸發器發送到 Azure Functions](../../../../../translated_images/zh-HK/gps-telemetry-iot-hub-functions.24d3fa5592455e9f4e2fe73856b40c3915a292b90263c31d652acfd976cfedd8.png) - -### 任務 - 使用無伺服器代碼處理 GPS 事件 - -1. 使用 Azure Functions CLI 創建一個 Azure Functions 應用。使用 Python 運行時,並在名為 `gps-trigger` 的文件夾中創建它,並使用相同的名稱作為 Functions App 項目名稱。確保創建一個虛擬環境來使用它。 -> ⚠️ 你可以參考[如何從第2部分,第5課建立 Azure Functions 專案的指引](../../../2-farm/lessons/5-migrate-application-to-the-cloud/README.md#create-a-serverless-application)(如有需要)。 -1. 添加一個 IoT Hub 事件觸發器,使用 IoT Hub 的 Event Hub 兼容端點。 - - > ⚠️ 如果需要,可以參考[項目 2,第 5 課中創建 IoT Hub 事件觸發器的指引](../../../2-farm/lessons/5-migrate-application-to-the-cloud/README.md#create-an-iot-hub-event-trigger)。 - -1. 在 `local.settings.json` 文件中設置 Event Hub 兼容端點的連接字串,並在 `function.json` 文件中使用該條目的鍵。 - -1. 使用 Azurite 應用作為本地存儲模擬器。 - -1. 運行你的 Functions 應用,確保它能接收來自 GPS 設備的事件。確保你的 IoT 設備也在運行並發送 GPS 數據。 - - ```output - Python EventHub trigger processed an event: {"gps": {"lat": 47.73481, "lon": -122.25701}} - ``` - -## Azure 存儲帳戶 - -![Azure Storage 標誌](../../../../../translated_images/zh-HK/azure-storage-logo.605c0f602c640d482a80f1b35a2629a32d595711b7ab1d7ceea843250615ff32.png) - -Azure 存儲帳戶是一種通用存儲服務,可以以多種方式存儲數據。你可以存儲數據為 Blob、隊列、表或文件,並且可以同時使用這些方式。 - -### Blob 存儲 - -*Blob* 的意思是二進制大型對象,但已成為任何非結構化數據的術語。你可以在 Blob 存儲中存儲任何數據,從包含 IoT 數據的 JSON 文檔到圖片和影片文件。Blob 存儲有 *容器* 的概念,容器是命名的存儲桶,用於存儲數據,類似於關係型數據庫中的表。這些容器可以包含一個或多個文件夾來存儲 Blob,每個文件夾可以包含其他文件夾,類似於電腦硬碟上的文件存儲方式。 - -在本課中,你將使用 Blob 存儲來存儲 IoT 數據。 - -✅ 做一些研究:閱讀 [Azure Blob Storage](https://docs.microsoft.com/azure/storage/blobs/storage-blobs-overview?WT.mc_id=academic-17441-jabenn) - -### 表存儲 - -表存儲允許你存儲半結構化數據。表存儲實際上是一個 NoSQL 數據庫,因此不需要事先定義表,但它設計用於在一個或多個表中存儲數據,每行都有唯一鍵。 - -✅ 做一些研究:閱讀 [Azure Table Storage](https://docs.microsoft.com/azure/storage/tables/table-storage-overview?WT.mc_id=academic-17441-jabenn) - -### 隊列存儲 - -隊列存儲允許你在隊列中存儲最多 64KB 的消息。你可以將消息添加到隊列的末尾,並從隊列的前端讀取。隊列會長期存儲消息,只要還有存儲空間,因此允許消息長期存儲,然後在需要時讀取。例如,如果你想每月運行一次作業來處理 GPS 數據,你可以每天將數據添加到隊列中,然後在月底處理所有消息。 - -✅ 做一些研究:閱讀 [Azure Queue Storage](https://docs.microsoft.com/azure/storage/queues/storage-queues-introduction?WT.mc_id=academic-17441-jabenn) - -### 文件存儲 - -文件存儲是雲端的文件存儲,任何應用或設備都可以使用行業標準協議進行連接。你可以將文件寫入文件存儲,然後將其掛載為 PC 或 Mac 上的驅動器。 - -✅ 做一些研究:閱讀 [Azure File Storage](https://docs.microsoft.com/azure/storage/files/storage-files-introduction?WT.mc_id=academic-17441-jabenn) - -## 將無伺服器代碼連接到存儲 - -你的 Functions 應用現在需要連接到 Blob 存儲,以存儲來自 IoT Hub 的消息。有兩種方法可以做到: - -* 在函數代碼內,使用 Blob 存儲的 Python SDK 連接到 Blob 存儲並將數據寫入 Blob。 -* 使用輸出函數綁定,將函數的返回值綁定到 Blob 存儲,並自動保存 Blob。 - -在本課中,你將使用 Python SDK 來了解如何與 Blob 存儲交互。 - -![將 GPS 遙測數據從 IoT 設備發送到 IoT Hub,然後通過事件觸發器發送到 Azure Functions,最後保存到 Blob 存儲](../../../../../translated_images/zh-HK/save-telemetry-to-storage-from-functions.ed3b1820980097f1.webp) - -數據將保存為以下格式的 JSON Blob: - -```json -{ - "device_id": , - "timestamp" :