This post is part of my Roadtest Review of Cypress PSoC 62S2 Wi-Fi & BT5.0 Pioneer Dev Kit - Review. My review is splitted into multiple reviews and tutorials. Main page of review contains brief description of every chapter. Some projects are implemented in multiple variants. Main page of review contains brief description about every chapter, project and variants and contains some reccomendations for reading.
Table of Contents
- Intruction, Table of Contents, Overview of Table of Contets
- Review of Development Board
- Review of Microcontroller
- Review of Wi-FI and BT Module
- Review of Onboard Peripherals
- Review of Onboard Programmer and Debugger
- Review of Development Software
- Review of Documentation
- Project 1 - Serial Interfaces
- Project 2 - Simple Cloud Application
- Project done using AWS Service
- Project done using Azure services (this article)
- Project 3 - FRAM
- Project 4 - CapSense and Bluetooth
- Project 5 - Dual Core Application
- Project 6 - Profiler
- Summary and Score
Project 2 – Simple Cloud Application
In this project I will build application that sends measured temperature and humidity using MQTT protocol to cloud. This project is implemented in 2 variants. One variant use AWS as cloud service provider and second one use Microsoft Azure. Both variant implement the same but use quite different services and approaches to do that.
Disclaimer: This project shows how to build application using paid cloud services. Author of this article do not hold any reliability for any of your charges (even the high charges). Pricing may vary between region and pricing policies may change over time.
This is second variant of project 2. This variant implements the same project but uses Azure services.
Create project based on template AnyCloud MQTT Client template. Tutorial with pictures is as part of Project 1 in low level variant. We do not start with blank project because we will require AWS SDK (yes really AWS SDK even we will connect to Azure) and it is not easy to integrate it (even using Library Manager) to empty project. Template contains two parts - MQTT subscriber and MQTT publisher. Publisher publish message when button is pressed or released, and subscriber waits for messages from cloud and then turn on or off LED based on the received message. I will not use subscriber so at beginning we remove all subscriber related code from template.
Removing subscriber support from example
Delete subscriber_task.c and subscriber_task.h file from source folder. Then open mqtt_task.c file and remove include to subscriber_task.h file. Do the same deletion also for publisher_task.c file. In mqtt_task.c file remove following section which creates FreeRTOS task for subscriber thread.
/* Create the subscriber task and cleanup if the operation fails. */ if (pdPASS != xTaskCreate(subscriber_task, "Subscriber task", SUBSCRIBER_TASK_STACK_SIZE, NULL, SUBSCRIBER_TASK_PRIORITY, &subscriber_task_handle)) { printf("Failed to create the Subscriber task!\n"); goto exit_cleanup; }
In the infinite loop in the same is also check if subscriber failed and in this case subscriber thread is removed. Delete following code.
if (subscriber_task_handle != NULL) { vTaskDelete(subscriber_task_handle); }
Remove also mqtt_unsubscribe unsubscribe from the same switch and remove publisher_cleanup because we will modify publisher later. So delete
/* Unsubscribe from the topic before cleanup. */ mqtt_unsubscribe();
and
publisher_cleanup();
Now go to config folder and open wifi_config.h file. Configure WIFI_SSID, WIFI_PASSWORD and optionally WIFI_SECURITY constants.
Until there it was the same as for AWS variant of this tutorial. Now login to the Azure portal and search for IoT Hub. Then go to IoT Hub administration.
Create new IoT Hub using Add button.
Select the Azure subscription under what you want to run project and create new resource group for all resources created in this tutorial.
Then select nearest Azure region, enter some name for new IoT hub and continue by Next: Networking button.
At this page do not change anything. Continue using Next: Size and Scale button.
Open the Pricing and scale tier selector and select F1: Free tier. Then press Review + create button.
Confirm by Create button.
After a while you will see that Deployment was successful and IoT Hub was created. Press Go to resource.
Now we need to setup private keys and certificates that we will use to authorize device when connecting to Azure. We need to create certificate for our own root certificate authority (root CA). We register certificate of our authority in Azure IoT Hub so IoT Hub will trust all our devices that attempts to authorize using certificate signed by our registered authority. I am using openssl commands on Linux. If you have no Linux available, you can use OepnSSL for Windows build or you can use Linux Virtual Machine created in cloud instead.
As first we need generate private key for our root CA. We will use openssl req command to generate new private key and self-signed certificate. For generating private key we specify -newkey parameter. We must specify type of key. I use RSA algorithm with 4096 bit wide key. We need to export key so I specify -keyout parameter with rootCA.key filename. I do not want to encrypt key, so I use -nodes parameter. Now I specify parameters for certificate. It should be certificate in x509 format (-x509 param) with SHA256 algorithm used for hashing (-sha256 param), with validity 365 days (-days 365 paramter) with extension that it is used for certification autohority (-extensions v3_ca) and should be stored to rootCA.crt file. Full command is following.
openssl req -newkey rsa:4096 -keyout rootCA.key -x509 -nodes -sha256 -days 365 -extensions v3_ca -out rootCA.crt
Security notice: I am doing it as minimalistic as possible. It results into lot of security weakness. Never use this approach in production. Before securing ÍoT devices this way I recommend learning a little about cryptography and security risks coming from self-hosting root CA. If you want to do it yourself in production generate root CA priovate key in ultra secure environment outside computer connected to internet and store it outside that computer on some offline media. Except root CA make some Intermediate CAs. Also remember to correctly setup revocation lists. There are plenty of other things to do If you host CA yourself and I recommend study something about cryptography before doing that.
Open SSL will ask for some information that will be as part of certificate. After filling that you should have two new files in working directory. Crt file is certificate and It can be published to anyone (even hackers). Key is private key and nobody should get it.
Now we will generate certificate for device. As first we will generate RSA key for device using genrsa command and stores it into psoc6-devboard.key file.
openssl genrsa -out psoc6-devboard.key 2048
Now we will generate certificate signing request (CSR). Usually CSR is the thing you send to CA but we host CA yourself so we will use it in one of next command. Req command requires similar parameters as we used for generating root CA key and certificate. The filename in -key parameter must be file generated in previous step. It should also asks us for parameters that should be as part of signed certificate. There are only single requirements – Common name (CN) must be the same name as we should use later as the name for device. Type something with basic ascii characters. I will use psoc6-devboard.
openssl req -new -sha256 -out psoc6-devboard.csr -key psoc6-devboard.key
Example output:
You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:CZ State or Province Name (full name) [Some-State]: Locality Name (eg, city) []: Organization Name (eg, company) [Internet Widgits Pty Ltd]:Misaz Organizational Unit Name (eg, section) []:IoT Common Name (e.g. server FQDN or YOUR name) []:psoc6-devboard Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
Now we can sign CSR using our root CA. It is done as part of x509 command. We must specify that we are working with request (-req param), want to use SHA256 for hashing, we must specify CSR file (-in parameter), certificate of authority (-CA which is rootCA.crt), key for authority (it is impossible to sign anything without key) (-Cakey parameter) then we need serial number. Because we have not generated any serial number for certificate yet, we let openssl generate automatically new one using –Cacreateserial parameter, then we specify validity period of generated signed certificate to one year and last we specify output file where openssl should store signed certificate. Full command is following.
openssl x509 -req -sha256 -in psoc6-devboard.csr -CA rootCA.crt -CAkey rootCA.key --CAcreateserial -out psoc6-devboard.crt -days 365
Now we have in our folder 4 new files. Private key for device (psoc6-devboard.key), signed certificate of device (psoc6-devboard.crt), CSR which we do not need anymore (psoc6-devboard.csr) and generated random serial which was used when signing certificate (rootCA.srl).
Register Root CA in Azure
Now we will register certificate our CA in Azure. In IoT Hub detail go to Certificates section and press Add.
Enter some name for certificate and select rootCA.crt file (please note that you must rename extension of that files because Azure accepts only .pem or .cer extension).
Azure do not trust us until we proof that we are owner of the root CA (this means that we are able to operate under it and in other words this mean that we own private key of that root CA). The verification is done that Azure gives us some random string and we must create signed certificate with that random text in CN field. If we are able generate valid and signed certificate It means that we can do whatever we want with root CA and Azure will trust us.
Click the newly uploaded certificate and then click Generate Verification Code.
Copy the code (this is the random string mentioned before) from the Verification Code. Now we need to generate (and sign) certificate. The certificate is generated using following 3 commands that are very similar to commands that we use to generate certificate for device. They differs in file names and third cerficate use instead of -CAcreateserial already created serial number from rootCA.srl file). The second command asks you to some informations. You must specify as Common Name the coped code. Other fields you could let empty (just enter).
openssl genrsa -out possession.key 2048 openssl req -new -sha256 -out possession.csr -key possession.key openssl x509 -req -sha256 -in possession.csr -CA rootCA.crt -CAkey rootCA.key -CAserial rootCA.srl -out possession.crt -days 365
Then upload possession.crt (also rename extension to cer or pem) to the Azure portal.
You will receive a notification that verification was successful. Refresh the page. You can see that status of certificate is verified now. So now Azure trust to all devices that attempts to authenticate with any certificate signed by our own certificate authority.
Go to IoT devices and click New.
Specify Device ID as the same name which you have used as CN in device certificate created before. Select Authentication type to X.509 CA Signed and confirm by Save.
Configuring connection to Azure
Now go to mqtt_client_config.h file. Copy hostname from main page of IoT Hub to MQTT_BROKER_ADDRESS constant.
AWS_IOT_MQTT_MODE switch to false.
MQTT_USERNAME specify to format hostname/deviceid/?api-version=2018-06-30. In my case (hostname=roadtest-psoc6-iot-hub.azure-devices.net, deviceid=psoc6-devboard) it is
roadtest-psoc6-iot-hub.azure-devices.net/psoc6-devboard/?api-version=2018-06-30
MQTT_CLIENT_IDENTIFIER_PREFIX set to deviceId (psoc6-devboard in my case).
Now copy the content of psoc6-devboard.crt file to the CLIENT_CERTIFICATE property. Every line must begin with
"
And ends with
\n" \
If you are familiar with linux you can use awk to format lines before copying instead of doing that manually.
cat psoc6-devboard.crt | awk '{print "\"" $0 "\\n\" \\";}'
Similarly copy contents of psoc6-devboard.key to the CLIENT_PRIVATE_KEY macro and lastly you must copy certificate of root authority that signed certificate for Azure (otherwise your device won’t trust Azure while Azure will trust your device). You can find that certificate in https://github.com/Azure/azure-iot-sdk-c/blob/master/certs/certs.c . It is DigiCert Baltimore certificate. Copy them to ROOT_CA_CERTIFICATE macro.
Security notice: It should be considered as security risk placing private key directly in source code. Private key should be private. It should not be shared between multiple subjects even If they are completely the same devices.
When one of your devices became compromised It should act as any other because device sharing the same key. Similarly, if your leek your firmware file all your devices (and data) may be compromised.
Better solution is to generate private key (key) in device at first run of device, generate signing request (csr) for the certificate, transfer CSR to computer where you can sign csr. You can do that transfer for example using USB. Then sing device certificate using some certificate authority (CA) (even by your own authority, there are usually no need to pay any fees for security) and then deploy signed certificate (crt) back to device. There should not exists any way how to read private key outside from the device. Device should be the only one actor who can read private key. It should be stored in internal flash for example. Flash must be locked for reading by external programmer of course (for hacker it is easy to read flash with private key simple using external programmer).
Now we have to do small modification which was not done in AWS variant. Azure is very sensitive to username and password fields in authentication process even in time we are successfully authenticated using certificate. Open the mqtt_task.c file and in mqtt_connect function there are following condition.
if ((connectionInfo.awsIotMqttMode) && (strlen(MQTT_USERNAME) > 0)) { connectionInfo.pUserName = MQTT_USERNAME; connectionInfo.pPassword = MQTT_PASSWORD; connectionInfo.userNameLength = sizeof(MQTT_USERNAME); connectionInfo.passwordLength = sizeof(MQTT_PASSWORD); }
Remove the condition (so the code became being executed every time) and comment password related operations. At last, add - 1 after sizeof because username should not include terminating \0 character when authenticating to Azure.
The code should now looks as follows.
connectionInfo.pUserName = MQTT_USERNAME; //connectionInfo.pPassword = MQTT_PASSWORD; connectionInfo.userNameLength = sizeof(MQTT_USERNAME) - 1; //connectionInfo.passwordLength = sizeof(MQTT_PASSWORD);
Integrate HTU21DHTU21D library
Go to https://github.com/misaz/HTU21DLibrary. It is my library for HTU21DHTU21D temperature sensor ported to PSoC6 platform. Download repository and copy HTU21DHTU21D.c, HTU21DHTU21D.h, HTU21DHTU21D_PlatformSpecificPSoC6.c and HTU21DHTU21D_PlatformSpecificPSoC6.h files from src folder to your project. Rename Platform specific files by removing PSoC6 from their name. For you should have 4 new additional files in your project. They are:
- HTU21D.c
- HTU21D.h
- HTU21D_PlatformSpecific.c
- HTU21D_PlatformSpecific.h
Include HTU21D.h in main.c and publisher_task.c files. In main add initialization call after initialization of retarget-io library.
HTU21D_Init();
Open HTU21D_PlatformSpecific.h and change HTU21D_SCB_CLOCK_DIVIDER_VALUE from 64 to 40 because project is configured to lower peripheral clock frequency (PERI) then I have used in project 1 so divider must divide frequency by lower value to reach correct I2C clock frequency.
Writing code for publisher thread
Now go to publsiher_task.c file. I will most of them completely rewrite.
Delete whole publisher_cleanup and isr_button_press functions and USER_BTN_INTR_PRIORITY macro because I do not use button at all.
Delete publishInfo global variable because I will use similar local variable.
Delete content of publisher_task because I will write a new one.
Now let’s write logic that will in infinite loop read humidity and temperature from the sensor and publish it using MQTT client to the cloud. Declare following variables and initialize crypto engine. I will use random numbers as IDs of messages sent to cloud.
float temperature, humidity; cyhal_trng_t trng; cy_rslt_t result; result = cyhal_trng_init(&trng); CY_ASSERT(result == CY_RSLT_SUCCESS); Write a new infinite loop. while (1) { } Read temperature and humidity using HTU21D library. if (HTU21D_ReadTemperature(&temperature) != HTU21D_E_OK) { printf("Error while reading temperature from HTU21D sensor.\r\n"); break; } if (HTU21D_ReadHumidity(&humidity) != HTU21D_E_OK) { printf("Error while reading humidity from HTU21D sensor.\r\n"); break; }
Generate random 32 hexadecimal characters as ID of message.
char messageId[33]; for (int i = 0; i < 4; i++) { uint32_t randomValue = cyhal_trng_generate(&trng); snprintf(messageId + i * 8, 9, "%08lx", randomValue); }
And finally complete the full message.
char message[1024]; snprintf(message, 1023, "{\"id\": \"%s\", \"temperature\":\"%.2f\", \"humidity\": \"%.2f\"}", messageId, temperature, humidity);
Now setup the IotMqttPublishInfo_t structure with data which will be published to cloud. MQTT topic name must be in format devices/{deviceId}/messages/events/. Replace {deviceId} with your own (in my case it is psoc6-devboard). QOS set to 1, retain to false, retry timeout to 1 second and retry limit to 10 attempts.
IotMqttPublishInfo_t publishInfo; publishInfo.pTopicName = "devices/psoc6-devboard/messages/events/"; publishInfo.topicNameLength = strlen(publishInfo.pTopicName); publishInfo.pPayload = message; publishInfo.payloadLength = strlen(message); publishInfo.qos = IOT_MQTT_QOS_1; publishInfo.retain = false; publishInfo.retryMs = 1000; publishInfo.retryLimit = 10;
Then publish the message to cloud and print result.
result = IotMqtt_PublishSync(mqttConnection, &publishInfo, 0, MQTT_TIMEOUT_MS); if (result == IOT_MQTT_SUCCESS) { printf("temperature and humidity was successfully published.\r\n"); } else { printf("Error while publishing message.\r\n"); }
After that I wait 1 minute before processing next iteration.
cyhal_system_delay_ms(60 * 1000);
Testing device
Now If you run application. On the terminal you should see some header which comes from original code example, information’s about WLAN firmware and message that first message was successfully sent to cloud.
Saving data to Azure Table Storage
Go to Storage Accounts administration in Azure portal.
Click add.
Select subscription and resource group. Enter some name for storage account, select data center near to you and switch replication to LRS if you want lower prices. Then continue by Review + Create.
Confirm by Create.
After a while your storage Account become create. Click Go to resource.
Go to Tables.
Press Add button. Enter some name for table and confirm by OK.
We have successfully created table for humidity and temperature data. Now we need to make some interconnection between IoT Hub (which receives data from device) to the DB. IoT Hub supports forwarding data to multiple kinds of services. We will use service bus to queueing received data and logic app for processing. Let’s create service bus.
Press Add to create new service bus.
Select subscription and resource group. Enter some name for service bus, choose data center near to you and select Basic pricing tier which is the cheapest. Continue by Review + Create.
Confirm by Create.
After a while service bus become created for you. Continue by Go to resource.
Click add queue. Enter some name for queue, change Max delivery count to 0, time to live to 5 minutes and lock duration to 30 seconds. Then confirm by Create.
Go back to your IoT Hub and navigate to Message Routing in menu.
Switch to Custom endpoints tab and click Add and continue by Service bus queue.
Type some name, select service bus and queue created in previous step. Confirm by Create.
Switch to Routes tab and click Add.
Enter some name, choose endpoint created in previous step, and confirm by Save.
Now the data are from IoT Hub are routed to the queue but there is nothing which will consume data from queue and saves it to the DB.
Go to Logic Apps.
Create new logic app.
Select subscription and resource group. Enter some name and confirm by Review + Create.
Confirm by Create.
After a while, Logic app become created for you. Continue by Go to resource.
Editor of Logic app will occur. AS the trigger choose When a message is received in Service Bus Queue.
Continue by Create button.
Enter some name of the connection and select service bus.
Click predefined name and rights for connection. Continue by Create.
Then continue by Continue button.
Select queue and change checking frequency to 1 minute. Then continue by New step.
This logic app gets the data from Iot Hub as input. It receives data in RAW format. Because IoT Hub supports receiving any data It encodes it in base64 format. So as first we will decode received data and stores them in variable. Search for Initialize variable and select that action.
Enter some name for variable. Type set as String. Click to Enter initial value. Switch to Expression tab and type decodeBase64(). Place cursor between brackets.
Switch to Dynamic content and select Content of the message.
Confirm by OK. Create New step.
Now we need parse JSON from data. We will reformat it a little and then build new data. Search for parse JSON and select that action.
Select variable from previous step as source for Content.
Copy following schema and continue by New step.
{ "properties": { "humidity": { "type": "string" }, "id": { "type": "string" }, "temperature": { "type": "string" } }, "type": "object" }
Search for Compose operation.
Now create a new JSON in format for Azure table DB. It must have RowKey and PartitionKey set. Use 0 as partition key and id field from parsed JSON as value of RowKey. Also include Humidity and Temperature fields. Use Dynamic content to include parsed fields. You can safely use enter in this field. Actions should look as follows. Then continue by New step.
Search for Insert Entity of Azure Table Storage and select them.
Enter some name for connection to Azure Storage, select storage account and continue by Create.
Select created table and outputs of Compose command as source for inserted entity.
Then press Save.
And that is all. I create web administration in ASP.NET Core which is out of scope of this tutorial. You can find them with source codes at the end of this article. You must modify it with credentials for your Azure Account.
Web interface to cloud data
I have created ASP.NET Core application that connects to Azure Table and renders chart with measured data. His description is outside of scope of this roadtest review. This application is available to download with source at the bottom of this article. It is Visual Studio solution. You have to update Azure credentials in appsettings.json file before running. You need to generate SAS. You can do that in Azure Portal in Shared Access Signature of Storage account.
After some minutes you can see data in charts in web application.