本記事では、クラウド上の FIWARE 基盤に IoT デバイスから測定データを定期的に送信したり、FIWARE 基盤から IoT デバイスを制御したり、する方法を紹介します。
本記事を含め、次の複数の記事から構成されています。
- FIWARE Big Bang と M5Stack による FIWARE 対応 IoT システムの構築
- MQTT に対応した FIWARE 基盤の構築
- MQTT に対応した IoT デバイス (センサ) の作成とセンサ・データの送信
- MQTT に対応した IoT デバイス (アクチュエータ) の作成とデバイス制御
- MQTT の通信チャネルのセキュア化
通信チャネルのセキュア化
構築した FIWARE 基盤は、MQTT over TLS をサポートしていて、MQTT メッセージを送ることができる暗号化された通信チャネルを提供します。TLS は通信内容が第三者によって読み取られたり変更されたりすることを困難にします。
IoT デバイスを MQTT over TLS 対応にすれば、クラウドの FIWARE 基盤と安全な通信ができます。プログラムに次の変更を加えることで、MQTT over TLS に対応できます。
MQTT over TLS 対応の設定変更
ルート証明書の追加
ルート証明書をグローバル変数として追加します。ルート証明書は、openssl コマンドを使って取得します。
openssl s_client -connect mosquitto.example.com:8883 -showcerts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
$ openssl s_client -connect mosquitto.big-bang.letsfiware.jp:8883 -showcerts CONNECTED(00000003) depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1 verify return:1 depth=1 C = US, O = Let's Encrypt, CN = R3 verify return:1 depth=0 CN = mosquitto.big-bang.letsfiware.jp verify return:1 --- Certificate chain 0 s:CN = mosquitto.big-bang.letsfiware.jp i:C = US, O = Let's Encrypt, CN = R3 a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256 v:NotBefore: Jan 6 21:07:08 2023 GMT; NotAfter: Apr 6 21:07:07 2023 GMT -----BEGIN CERTIFICATE----- MIIFRzCCBC+gAwIBAgISAxE0A81qEh3kzfw36zCiJTBbMA0GCSqGSIb3DQEBCwUA MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD ... 途中省略 /e0FYC9cZO4ASk7C6XQ6u5LHUJOr/+G7wUmR7Zw7HAVxhB7zgaiaw8n3q4flVthz cabTe9IhB6Oektc= -----END CERTIFICATE----- 1 s:C = US, O = Let's Encrypt, CN = R3 i:C = US, O = Internet Security Research Group, CN = ISRG Root X1 a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256 v:NotBefore: Sep 4 00:00:00 2020 GMT; NotAfter: Sep 15 16:00:00 2025 GMT -----BEGIN CERTIFICATE----- MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw ... 途中省略 HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX nLRbwHOoq7hHwg== -----END CERTIFICATE----- 2 s:C = US, O = Internet Security Research Group, CN = ISRG Root X1 i:O = Digital Signature Trust Co., CN = DST Root CA X3 a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256 v:NotBefore: Jan 20 19:14:03 2021 GMT; NotAfter: Sep 30 18:14:03 2024 GMT -----BEGIN CERTIFICATE----- MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/ MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT ... 途中省略 he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5 -----END CERTIFICATE----- --- Server certificate subject=CN = mosquitto.big-bang.letsfiware.jp issuer=C = US, O = Let's Encrypt, CN = R3 --- No client certificate CA names sent Peer signing digest: SHA256 Peer signature type: RSA-PSS Server Temp Key: X25519, 253 bits --- SSL handshake has read 4740 bytes and written 427 bytes Verification: OK --- New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384 Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.2 Cipher : ECDHE-RSA-AES256-GCM-SHA384 Session-ID: 3C9523E9774F93FBBE7B3C79DC448D56BF8D165567B833E4442391CBBF7C8201 Session-ID-ctx: Master-Key: A8BC09F3800CF6B9DBA4BF38F581984CCB9F150F4E9A3512D113F2112BA84B31575688D245C6971251B737B786BD3463 PSK identity: None PSK identity hint: None SRP username: None TLS session ticket lifetime hint: 300 (seconds) TLS session ticket: 0000 - 25 b6 40 e0 8f 5e ac ac-09 18 c2 28 16 6c 69 43 %.@..^.....(.liC 0010 - 10 6e 75 89 99 95 9d 0a-41 ed c2 15 3f 75 17 86 .nu.....A...?u.. 0020 - 0f 18 dd 82 8f b4 53 68-e1 36 04 63 c9 13 b0 c6 ......Sh.6.c.... 0030 - 84 a5 13 02 35 5b 1e c9-cc ea 5a 21 fc 47 8b a9 ....5[....Z!.G.. 0040 - 01 67 75 90 76 0a 5d 97-5e 70 66 7e 7b 33 08 03 .gu.v.].^pf~{3.. 0050 - 2b fd 67 a4 48 b6 24 d1-a1 bc 1b 68 a7 60 38 d6 +.g.H.$....h.`8. 0060 - 0a 0c 67 fe c0 28 ea cb-71 e6 19 f7 69 b9 14 70 ..g..(..q...i..p 0070 - f9 e3 22 60 15 fc 74 c7-ec 9b cc 61 14 2f 66 a1 .."`..t....a./f. 0080 - 4b ea 85 1d 4e 2e 36 6e-de 1a 38 db e3 44 41 1b K...N.6m..8..DA. 0090 - bd 2c fd d6 dd be 20 94-59 cc 35 2c da 35 e1 ed .,.... .Y.5,.5.. 00a0 - d1 8e e4 0a 38 70 27 84-44 e1 ce 54 f1 d4 2e f1 ....8p'.D..T.... 00b0 - 25 1b f5 97 90 a4 58 01-b1 6f 06 44 d7 a3 06 df %.....X..o.D.... 00c0 - 84 e7 b9 08 2b 17 c2 4f-37 71 a9 61 d0 25 87 30 ....+..O7q.a.%.0 00d0 - 25 a9 28 be ea 4e 86 87-bb f8 a3 eb 47 e1 25 ea %.(..N......G.%. Start Time: 1674039909 Timeout : 7200 (sec) Verify return code: 0 (ok) Extended master secret: yes --- |
構築した FIWARE 基盤は、Let’s encrypt のサーバ証明書を使っているので、以下の行のあとにある、BEGIN CERTIFICATE から END CERTIFICATE の行です。
i:O = Digital Signature Trust Co., CN = DST Root CA X3
プログラムには次のように追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
const char* gRootCa = R"( -----BEGIN CERTIFICATE----- MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/ MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK 4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5 bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4 FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1 c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx +tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC 5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW 9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5 -----END CERTIFICATE----- )"; |
WiFiClient のセキュア化
WiFiClentSecure.h のヘッダファイルを追加します。
#include <WiFiClientSecure.h>
WiFiClient クラスの代わりに、WiFiClientSecure クラスを使って、gWifiClient オブジェクトをグローバル変数として定義します。
WiFiClientSecure gWifiClient;
そして、setup() 関数の中に、gWifiClient に対してルート証明書のグローバル変数 gRootCa をセットする処理を追加します。
gWifiClient.setCACert(gRootCa);
MQTT ポートの変更
MQTT 通信に使用するポートを 1883 から 8883 に変更します。
const int gMqttPort = 8883;
MQTT over TLS 対応プログラム
MQTT over TLS に対応したプログラムは以下です。
- m5stack_fire/m5stack_fire_sensor_mqtt_over_tls/m5stack_fire_sensor_mqtt_over_tls.ino
- m5stack_fire/m5stack_fire_actuator_mqtt_over_tls/m5stack_fire_actuator_mqtt_over_tls.ino
プログラム全体
センサの測定値を FIWARE 基盤に送信するプログラム を以下に示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
#include <M5Stack.h> #include <PubSubClient.h> #include <WiFi.h> #include <WiFiClientSecure.h> #include "time.h" #include "M5_ENV.h" WiFiClientSecure gWifiClient; PubSubClient gClient(gWifiClient); SHT3X gSht30; QMP6988 gQmp6988; // Configure parameters const char* gEssid = "ESSID"; const char* gPassword = "ESSID_PASSWORD"; const char* gMqttServer = "MOSQUITTO.EXAMPLE.COM"; const char* gMqttUsername = "MQTT_USER"; const char* gMqttPassword = "MQTT_PASSOWRD"; const int gMqttPort = 8883; const String gMqttCientId = "sensor001-"; const char *gMqttTopic = "/izqetq603ovjcqpx627e4eib1u/sensor001/attrs"; const int gInterval = 2 * 1000; // milli seconds // openssl s_client -connect mosquitto.example.com:8883 -showcerts // // 2 s:C = US, O = Internet Security Research Group, CN = ISRG Root X1 // i:O = Digital Signature Trust Co., CN = DST Root CA X3 // a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256 // v:NotBefore: Jan 20 19:14:03 2021 GMT; NotAfter: Sep 30 18:14:03 2024 GMT const char* gRootCa = R"( -----BEGIN CERTIFICATE----- MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/ MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK 4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5 bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4 FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1 c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx +tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC 5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW 9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5 -----END CERTIFICATE----- )"; #define MSG_MAX_SIZE (100) char msg[MSG_MAX_SIZE]; void setup() { M5.begin(); M5.Power.begin(); M5.Lcd.setTextSize(2); Wire.begin(); gQmp6988.init(); WiFi.mode(WIFI_STA); WiFi.begin(gEssid, gPassword); while (WiFi.status() != WL_CONNECTED) { delay(500); M5.Lcd.print("."); } gWifiClient.setCACert(gRootCa); gClient.setServer(gMqttServer, gMqttPort); configTime(9 * 3600, 0, "ntp.jst.mfeed.ad.jp"); } void loop() { static int count = 0; static unsigned long previous = 0; while (!gClient.connected()) { String clientId = gMqttCientId + String(random(0xffff), HEX); if (!gClient.connect(clientId.c_str(), gMqttUsername, gMqttPassword)) { M5.Lcd.setTextColor(RED, BLACK); M5.Lcd.print("Failed. state="); M5.Lcd.print(gClient.state()); delay(3000); } } gClient.loop(); unsigned long now = millis(); if (now - previous > gInterval) { previous = now; ++count; struct tm timeinfo; getLocalTime(&timeinfo); float pressure = gQmp6988.calcPressure() / 100; float temperature = 0.0, humidity = 0.0; if (gSht30.get() == 0) { temperature = gSht30.cTemp; humidity = gSht30.humidity; } snprintf(msg, MSG_MAX_SIZE, "d|%04d-%02d-%02dT%02d:%02d:%02d+0000|t|%2.1f|h|%2.1f|p|%2.1f", timeinfo.tm_year+1900, timeinfo.tm_mon+1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, temperature, humidity, pressure ); gClient.publish(gMqttTopic, msg); M5.Lcd.fillScreen(BLACK); M5.Lcd.setTextColor(WHITE, BLACK); M5.Lcd.setCursor(0, 0); M5.Lcd.print("Temperature: "); M5.Lcd.setTextColor(GREEN, BLACK); M5.Lcd.print(String(temperature, 1)); M5.Lcd.setTextColor(WHITE, BLACK); M5.Lcd.print("c"); M5.Lcd.setCursor(0, 20); M5.Lcd.print("Humidity: "); M5.Lcd.setTextColor(GREEN, BLACK); M5.Lcd.print(String(humidity, 1)); M5.Lcd.setTextColor(WHITE, BLACK); M5.Lcd.print("%"); M5.Lcd.setCursor(0, 40); M5.Lcd.print("Pressure: "); M5.Lcd.setTextColor(GREEN, BLACK); M5.Lcd.print(String(pressure, 1)); M5.Lcd.setTextColor(WHITE, BLACK); M5.Lcd.print("hPa"); M5.Lcd.setCursor(0, 60); M5.Lcd.print("Send: "); M5.Lcd.setTextColor(GREEN, BLACK); M5.Lcd.println(String(count)); } } |