Repository: espressif/esp-azure Branch: master Commit: 15ef01eed284 Files: 48 Total size: 134.4 KB Directory structure: gitextract_6f4ybc5i/ ├── .github/ │ └── workflows/ │ ├── issue_comment.yml │ ├── new_issues.yml │ └── new_prs.yml ├── .gitignore ├── .gitlab-ci.yml ├── .gitmodules ├── README.md ├── component.mk ├── doc/ │ └── azure_cli_iot_hub.md ├── examples/ │ ├── iothub_client_sample_mqtt/ │ │ ├── CMakeLists.txt │ │ ├── Makefile │ │ ├── README.md │ │ ├── main/ │ │ │ ├── CMakeLists.txt │ │ │ ├── Kconfig.projbuild │ │ │ ├── azure_main.c │ │ │ ├── component.mk │ │ │ ├── iothub_client_sample_mqtt.c │ │ │ └── iothub_client_sample_mqtt.h │ │ └── sdkconfig.defaults │ ├── iothub_devicetwin_samples_and_methods/ │ │ ├── CMakeLists.txt │ │ ├── Makefile │ │ ├── README.md │ │ ├── main/ │ │ │ ├── CMakeLists.txt │ │ │ ├── Kconfig.projbuild │ │ │ ├── azure_main.c │ │ │ ├── component.mk │ │ │ └── iothub_client_device_twin_and_methods_sample.c │ │ └── sdkconfig.defaults │ └── prov_dev_client_ll_sample/ │ ├── CMakeLists.txt │ ├── Makefile │ ├── README.md │ ├── main/ │ │ ├── CMakeLists.txt │ │ ├── Kconfig.projbuild │ │ ├── azure_main.c │ │ ├── certs/ │ │ │ └── .dummy │ │ ├── component.mk │ │ ├── custom_hsm_x509.c │ │ └── prov_dev_client_ll_sample.c │ └── sdkconfig.defaults └── port/ ├── CMakeLists.txt ├── inc/ │ ├── certs.h │ ├── sntp_os.h │ ├── socket_async_os.h │ └── tlsio_pal.h └── src/ ├── agenttime_esp.c ├── certs.c ├── platform_esp.c └── tlsio_esp_tls.c ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/issue_comment.yml ================================================ name: Sync issue comments to JIRA # This workflow will be triggered when new issue comment is created (including PR comments) on: issue_comment jobs: sync_issue_comments_to_jira: name: Sync Issue Comments to Jira runs-on: ubuntu-latest steps: - uses: actions/checkout@master - name: Sync issue comments to JIRA uses: espressif/github-actions/sync_issues_to_jira@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} JIRA_PASS: ${{ secrets.JIRA_PASS }} JIRA_PROJECT: CA JIRA_URL: ${{ secrets.JIRA_URL }} JIRA_USER: ${{ secrets.JIRA_USER }} ================================================ FILE: .github/workflows/new_issues.yml ================================================ name: Sync issues to Jira # This workflow will be triggered when a new issue is opened on: issues jobs: sync_issues_to_jira: name: Sync issues to Jira runs-on: ubuntu-latest steps: - uses: actions/checkout@master - name: Sync GitHub issues to Jira project uses: espressif/github-actions/sync_issues_to_jira@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} JIRA_PASS: ${{ secrets.JIRA_PASS }} JIRA_PROJECT: CA JIRA_URL: ${{ secrets.JIRA_URL }} JIRA_USER: ${{ secrets.JIRA_USER }} ================================================ FILE: .github/workflows/new_prs.yml ================================================ name: Sync remain PRs to Jira # This workflow will be triggered every hour, to sync remaining PRs (i.e. PRs with zero comment) to Jira project # Note that, PRs can also get synced when new PR comment is created on: schedule: - cron: "0 * * * *" jobs: sync_prs_to_jira: name: Sync PRs to Jira runs-on: ubuntu-latest steps: - uses: actions/checkout@master - name: Sync PRs to Jira project uses: espressif/github-actions/sync_issues_to_jira@master with: cron_job: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} JIRA_PASS: ${{ secrets.JIRA_PASS }} JIRA_PROJECT: CA JIRA_URL: ${{ secrets.JIRA_URL }} JIRA_USER: ${{ secrets.JIRA_USER }} ================================================ FILE: .gitignore ================================================ .settings .project .cproject .vscode/ build sdkconfig sdkconfig.old ================================================ FILE: .gitlab-ci.yml ================================================ stages: - build_esp32 - build_esp8266 variables: BATCH_BUILD: "1" V: "0" MAKEFLAGS: "-j5 --no-keep-going" IDF_PATH: "$CI_PROJECT_DIR/esp-idf" IDF_CI_BUILD: "1" build_esp32_demo: stage: build_esp32 image: $CI_DOCKER_REGISTRY/esp32-ci-env tags: - build script: # add gitlab ssh key - mkdir -p ~/.ssh - chmod 700 ~/.ssh - echo -n $GITLAB_KEY > ~/.ssh/id_rsa_base64 - base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - echo -e "Host gitlab.espressif.cn\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - git --version - git submodule update --init --recursive - git clone --recursive --depth 1 $GITLAB_SSH_SERVER/idf/esp-idf.git - export PATH="$IDF_PATH/tools:$PATH" - cd esp-idf - ./install.sh - . export.sh - cd .. - cd examples/iothub_client_sample_mqtt - make defconfig && make -j5 - make clean && rm sdkconfig && rm -r build - echo "CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y" >> sdkconfig.defaults - make defconfig && make -j5 - make clean && rm sdkconfig && rm -r build - idf.py build - cd ../../ - cd examples/prov_dev_client_ll_sample - make defconfig && make -j5 - make clean && rm sdkconfig && rm -r build - echo "CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y" >> sdkconfig.defaults - make defconfig && make -j5 - make clean && rm sdkconfig && rm -r build - idf.py build build_esp8266_demo: stage: build_esp8266 image: $CI_DOCKER_REGISTRY/esp8266-ci-env-new tags: - build script: # add gitlab ssh key - mkdir -p ~/.ssh - chmod 700 ~/.ssh - echo -n $GITLAB_KEY > ~/.ssh/id_rsa_base64 - base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - echo -e "Host gitlab.espressif.cn\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - git --version - git clone -b v3.3 --recursive --depth 1 $GITLAB_SSH_SERVER/sdk/ESP8266_RTOS_SDK.git - cd ESP8266_RTOS_SDK - source tools/ci/configure_ci_environment.sh - cd .. - git submodule update --init --recursive - export IDF_PATH=$CI_PROJECT_DIR/ESP8266_RTOS_SDK - cd examples/iothub_client_sample_mqtt - make defconfig - make - cd ../../ - cd examples/prov_dev_client_ll_sample - make defconfig - make ================================================ FILE: .gitmodules ================================================ [submodule "azure-iot-sdk-c"] path = azure-iot-sdk-c url = https://github.com/Azure/azure-iot-sdk-c.git ================================================ FILE: README.md ================================================ # ESP Azure IoT SDK ## Table of Contents - [Introduction](#introduction) - [Getting Started](#get-started) - [Creating an Azure IoT Device](#create-device) - [Monitoring Results](#monitoring) - [Troubleshooting](#troubleshooting) ## 2021 Update Since this library has been published, Microsoft has created newer versions of the Azure SDK for usage with the Espressif ESP32. This new library is better suited for microcontrollers, great for composition with your own network stack and officially supported by Microsoft. The first one, [Azure IoT middleware for FreeRTOS](https://github.com/Azure/azure-iot-middleware-freertos), is based on ESP-IDF and FreeRTOS and it has [samples](https://github.com/Azure-Samples/iot-middleware-freertos-samples) for IoT Hub and IoT Central using the device provisioning service (DPS). The second one is based on [Azure IoT for C library for Arduino](https://github.com/Azure/azure-sdk-for-c-arduino) and also has samples for IoT Hub. If you can, **avoid using this library for any new projects**. ## Introduction The ESP Azure IoT SDK is based on [Azure IoT C SDK](https://github.com/Azure/azure-iot-sdk-c) and enables users to connect their ESP32 based devices to the Azure IoT hub. It provides some examples which can help understand most common use cases. ## Getting Started ### Hardware You will basically just need a development host and an [ESP32 development board](https://www.espressif.com/en/products/hardware/development-boards) to get started. ### Development Host Setup This project is to be used with Espressif's IoT Development Framework, [ESP IDF](https://github.com/espressif/esp-idf). Follow these steps to get started: - Setup ESP IDF development environment by following the steps [here](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html). - In a separate folder, clone the esp-azure project as follows (please note the --recursive option, which is required to clone the various git submodules required by esp-azure) ``` bash $ git clone --recursive https://github.com/espressif/esp-azure.git ``` > Note that if you ever change the branch or the git head of either esp-idf or esp-azure, ensure that all the submodules of the git repo are in sync by executing `git submodule update --init --recursive` ## ### Setting up Azure IoT Hub - Create an Azure IoT Hub by following the documentation [here](https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-create-through-portal). > **Note: When selecting the "Pricing and scale tier", there is also an option to select , F1: Free tier, which should be sufficient for basic evaluation.** - Copy the IoT Hub `Connection string - primary key` from the Azure IoT Hub. This will be required later. The screenshot below will help you locate it. ![](doc/_static/connection_string.png) - Connection string - primary key sample: ``` HostName=.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey= ``` ### Setting up Azure CLI - Install [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) - From your terminal, execute the `az` command to verify that the installation was successful. Output will be like this: ``` $ az Welcome to Azure CLI! --------------------- Use `az -h` to see available commands or go to https://aka.ms/cli. ... ``` - Install the Azure IoT CLI extension using `$ az extension add --name azure-cli-iot-ext` After that, you should be able to use azure CLI to manage your iot-device. A list of useful Azure CLIs can be found [here](doc/azure_cli_iot_hub.md) ## Creating an Azure IoT Device - Login to Azure CLI using `$ az login` - Create a new device using `$ az iot hub device-identity create -n [IoTHub Name] -d [Device ID]` - Get connection string for your device using `$ az iot hub device-identity show-connection-string -n [IoTHub Name] -d [Device ID]` - Device connection string sample: ``` HostName=.azure-devices.net;DeviceId=;SharedAccessKey= ``` - This will be required in the examples ## Monitoring Results To see various events and the data being exchanged between the device and IoT hub from your command line, run the following command: `$ az iot hub monitor-events -n [IoTHub Name] --login '[Connection string - primary key]'` > Note the single quotes for the connection string. Without them, the command wont work as desired. To monitor activity on your ESP device, run: `$ make monitor` ## Troubleshooting 1. Some common problems can be fixed by disabling the firewall. 2. You can try with the followings, if your build fails: - `$ git submodule update --init --recursive` - Check the compiler version and verify that it is the correct one for your ESP IDF version. - Check if the IDF_PATH is set correctly - Clean the project with `make clean` and if required, using `rm -rf build sdkconfig sdkconfig.old` 3. Ensure that the device connection string received from Azure IoT Hub are correct. ================================================ FILE: component.mk ================================================ # # Component Makefile # # Component configuration in preprocessor defines CFLAGS += -DUSE_LWIP_SOCKET_FOR_AZURE_IOT COMPONENT_ADD_INCLUDEDIRS := \ azure-iot-sdk-c/c-utility/inc \ azure-iot-sdk-c/c-utility/deps/azure-macro-utils-c/inc \ azure-iot-sdk-c/c-utility/deps/umock-c/inc \ azure-iot-sdk-c/iothub_client/inc \ azure-iot-sdk-c/serializer/inc \ azure-iot-sdk-c/umqtt/inc \ azure-iot-sdk-c/umqtt/inc/azure_umqtt_c \ azure-iot-sdk-c/deps/parson \ azure-iot-sdk-c/provisioning_client/inc \ azure-iot-sdk-c/provisioning_client/adapters \ azure-iot-sdk-c/provisioning_client/deps/utpm/inc \ COMPONENT_PRIV_INCLUDEDIRS := \ port/inc \ azure-iot-sdk-c/c-utility/pal/inc \ azure-iot-sdk-c/c-utility/pal/freertos \ azure-iot-sdk-c/c-utility/pal/generic \ ifndef CONFIG_TARGET_PLATFORM_ESP8266 COMPONENT_ADD_INCLUDEDIRS += azure-iot-sdk-c/certs endif COMPONENT_OBJS = \ azure-iot-sdk-c/c-utility/pal/freertos/lock.o \ azure-iot-sdk-c/c-utility/pal/socket_async.o \ azure-iot-sdk-c/c-utility/pal/freertos/threadapi.o \ azure-iot-sdk-c/c-utility/pal/freertos/tickcounter.o \ azure-iot-sdk-c/c-utility/pal/tlsio_options.o \ \ port/src/agenttime_esp.o \ port/src/platform_esp.o \ port/src/tlsio_esp_tls.o \ \ azure-iot-sdk-c/c-utility/src/xlogging.o \ azure-iot-sdk-c/c-utility/src/singlylinkedlist.o \ azure-iot-sdk-c/c-utility/src/buffer.o \ azure-iot-sdk-c/c-utility/src/consolelogger.o \ azure-iot-sdk-c/c-utility/src/constbuffer.o \ azure-iot-sdk-c/c-utility/src/constmap.o \ azure-iot-sdk-c/c-utility/src/crt_abstractions.o \ azure-iot-sdk-c/c-utility/src/doublylinkedlist.o \ azure-iot-sdk-c/c-utility/src/gballoc.o \ azure-iot-sdk-c/c-utility/src/gb_stdio.o \ azure-iot-sdk-c/c-utility/src/gb_time.o \ azure-iot-sdk-c/c-utility/src/hmac.o \ azure-iot-sdk-c/c-utility/src/hmacsha256.o \ azure-iot-sdk-c/c-utility/src/httpapiex.o \ azure-iot-sdk-c/c-utility/src/httpapiexsas.o \ azure-iot-sdk-c/c-utility/src/httpheaders.o \ azure-iot-sdk-c/c-utility/src/map.o \ azure-iot-sdk-c/c-utility/src/optionhandler.o \ azure-iot-sdk-c/c-utility/src/sastoken.o \ azure-iot-sdk-c/c-utility/src/sha1.o \ azure-iot-sdk-c/c-utility/src/sha224.o \ azure-iot-sdk-c/c-utility/src/sha384-512.o \ azure-iot-sdk-c/c-utility/src/strings.o \ azure-iot-sdk-c/c-utility/src/string_tokenizer.o \ azure-iot-sdk-c/c-utility/src/urlencode.o \ azure-iot-sdk-c/c-utility/src/usha.o \ azure-iot-sdk-c/c-utility/src/vector.o \ azure-iot-sdk-c/c-utility/src/xio.o \ azure-iot-sdk-c/c-utility/src/azure_base64.o \ \ \ azure-iot-sdk-c/iothub_client/src/iothub_device_client_ll.o \ azure-iot-sdk-c/iothub_client/src/iothub_client_ll.o \ azure-iot-sdk-c/iothub_client/src/iothub_client_core_ll.o \ azure-iot-sdk-c/iothub_client/src/iothub_client_ll_uploadtoblob.o \ azure-iot-sdk-c/iothub_client/src/iothub_client_authorization.o \ azure-iot-sdk-c/iothub_client/src/iothub_client_retry_control.o \ azure-iot-sdk-c/iothub_client/src/iothub_client_diagnostic.o \ azure-iot-sdk-c/iothub_client/src/iothub_message.o \ azure-iot-sdk-c/iothub_client/src/iothubtransport.o \ azure-iot-sdk-c/iothub_client/src/iothubtransportmqtt.o \ azure-iot-sdk-c/iothub_client/src/iothubtransport_mqtt_common.o \ azure-iot-sdk-c/iothub_client/src/iothub_transport_ll_private.o \ azure-iot-sdk-c/iothub_client/src/version.o \ \ \ azure-iot-sdk-c/umqtt/src/mqtt_client.o \ azure-iot-sdk-c/umqtt/src/mqtt_codec.o \ azure-iot-sdk-c/umqtt/src/mqtt_message.o \ \ \ azure-iot-sdk-c/deps/parson/parson.o \ \ azure-iot-sdk-c/serializer/src/codefirst.o \ azure-iot-sdk-c/serializer/src/agenttypesystem.o \ azure-iot-sdk-c/serializer/src/commanddecoder.o \ azure-iot-sdk-c/serializer/src/datamarshaller.o \ azure-iot-sdk-c/serializer/src/datapublisher.o \ azure-iot-sdk-c/serializer/src/dataserializer.o \ azure-iot-sdk-c/serializer/src/iotdevice.o \ azure-iot-sdk-c/serializer/src/jsondecoder.o \ azure-iot-sdk-c/serializer/src/jsonencoder.o \ azure-iot-sdk-c/serializer/src/methodreturn.o \ azure-iot-sdk-c/serializer/src/multitree.o \ azure-iot-sdk-c/serializer/src/schema.o \ azure-iot-sdk-c/serializer/src/schemalib.o \ azure-iot-sdk-c/serializer/src/schemaserializer.o \ \ \ azure-iot-sdk-c/provisioning_client/src/prov_device_client.o \ azure-iot-sdk-c/provisioning_client/src/prov_transport_mqtt_client.o \ azure-iot-sdk-c/provisioning_client/src/prov_transport_mqtt_common.o \ azure-iot-sdk-c/provisioning_client/src/prov_security_factory.o \ azure-iot-sdk-c/provisioning_client/src/prov_device_ll_client.o \ azure-iot-sdk-c/provisioning_client/src/iothub_security_factory.o \ azure-iot-sdk-c/provisioning_client/adapters/hsm_client_data.o \ azure-iot-sdk-c/provisioning_client/adapters/hsm_client_tpm.o \ azure-iot-sdk-c/provisioning_client/src/prov_auth_client.o \ azure-iot-sdk-c/provisioning_client/deps/utpm/src/tpm_codec.o \ azure-iot-sdk-c/provisioning_client/deps/utpm/src/Marshal.o \ azure-iot-sdk-c/provisioning_client/deps/utpm/src/tpm_comm_emulator.o \ azure-iot-sdk-c/provisioning_client/deps/utpm/src/Memory.o \ azure-iot-sdk-c/provisioning_client/deps/utpm/src/tpm_socket_comm.o \ azure-iot-sdk-c/iothub_client/src/iothub.o \ azure-iot-sdk-c/c-utility/src/http_proxy_io.o \ azure-iot-sdk-c/c-utility/src/azure_base32.o \ ifdef CONFIG_DEVICE_COMMON_NAME COMPONENT_OBJS += azure-iot-sdk-c/provisioning_client/src/iothub_auth_client.o endif ifdef CONFIG_TARGET_PLATFORM_ESP8266 COMPONENT_OBJS += port/src/certs.o endif ifndef CONFIG_TARGET_PLATFORM_ESP8266 COMPONENT_OBJS += azure-iot-sdk-c/certs/certs.o endif COMPONENT_SRCDIRS := \ port/src \ azure-iot-sdk-c/c-utility/pal \ azure-iot-sdk-c/c-utility/pal/freertos \ azure-iot-sdk-c/c-utility/pal/lwip \ azure-iot-sdk-c/c-utility/src \ azure-iot-sdk-c/c-utility/adapters \ azure-iot-sdk-c/umqtt/src \ azure-iot-sdk-c/iothub_client/src \ azure-iot-sdk-c/serializer/src \ azure-iot-sdk-c/deps/parson \ azure-iot-sdk-c/prov_device_client/src \ azure-iot-sdk-c/iothub_client_mqtt_transport \ azure-iot-sdk-c/iothub_client_amqp_transport \ azure-iot-sdk-c/provisioning_client/src \ azure-iot-sdk-c/provisioning_client/adapters \ azure-iot-sdk-c/provisioning_client/deps/utpm/src \ ifndef CONFIG_TARGET_PLATFORM_ESP8266 COMPONENT_SRCDIRS += azure-iot-sdk-c/certs endif CFLAGS += -Wno-unused-function -Wno-missing-braces -Wno-missing-field-initializers -DHSM_TYPE_X509 -DHSM_TYPE_SAS_TOKEN -Wno-unknown-pragmas ifdef CONFIG_DEVICE_COMMON_NAME CFLAGS += -DUSE_PROV_MODULE endif azure-iot-sdk-c/iothub_client/src/iothubtransport_mqtt_common.o: CFLAGS+=-Wno-maybe-uninitialized ================================================ FILE: doc/azure_cli_iot_hub.md ================================================ # Azure CLI usage ## Login [Required for using any of the other commands] ``` az login ``` ## Create a device ``` az iot hub device-identity create -n [IoTHub Name] -d [Device ID] ``` ## List all devices ``` az iot hub device-identity list --hub-name [IoTHub Name] ``` ## Get device connection string ``` az iot hub device-identity show-connection-string -n [IoTHub Name] -d [Device ID] ``` ## Send message to device ``` az iot device c2d-message send -d [Device Id] -n [IoTHub Name] --data [Data_to_Send] ``` ## Delete a device ``` az iot hub device-identity delete -n [IoTHub Name] -d [Device ID] ``` ## Monitor events ``` az iot hub monitor-events -n [IoTHub Name] --login 'HostName=myhub.azuredevices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=12345' ``` ## Additional Information Additional information for Azure IoT CLI can be found [here](https://docs.microsoft.com/en-us/cli/azure/ext/azure-cli-iot-ext/iot?view=azure-cli-latest) ================================================ FILE: examples/iothub_client_sample_mqtt/CMakeLists.txt ================================================ # The following five lines of boilerplate have to be in your project's # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) set (EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_BINARY_DIR}/../../../port") project(iothub_client_sample_mqtt) ================================================ FILE: examples/iothub_client_sample_mqtt/Makefile ================================================ # # This is a project Makefile. It is assumed the directory this Makefile resides in is a # project subdirectory. # PROJECT_NAME := iothub_client_sample_mqtt EXTRA_COMPONENT_DIRS += $(PROJECT_PATH)/../../../esp-azure include $(IDF_PATH)/make/project.mk ================================================ FILE: examples/iothub_client_sample_mqtt/README.md ================================================ # IoT HUB MQTT Client This demonstrates MQTT send received using Azure IoT. ## Device Configuration - For this demo we will use the same Azure IoT device created using the steps defined in top level [README](../../README.md#creating-an-azure-iot-device). Copy the connection string for the device from the output of this command: ``` bash $ az iot hub device-identity show-connection-string -n [IoTHub Name] -d [Device ID] ``` Sample output: ``` { "connectionString": "HostName=.azure-devices.net;DeviceId=;SharedAccessKey=" } ``` > Note that the double quotes at both the ends of the string are not part of the connection string. So, for the above, just copy `HostName=.azure-devices.net;DeviceId=;SharedAccessKey=` > While changing the value, please ensure that you have completely cleared the older value, before pasting the new one. If you face any run time connection issues, double check this value. - Execute `make menuconfig`. In the menu, go to `Example Configuration` and configure `WiFi SSID` and `WiFi Password` so that the device can connect to the appropriate Wi-Fi network on boot up. Set `IOT Hub Device Connection String` with the string copied above ## Trying out the example - Run the following command to flash the example and monitor the output ``` bash $ make -j8 flash monitor ``` - In a separate window, monitor the Azure IoT events using the following: ``` $ az iot hub monitor-events -n [IoTHub Name] --login '[Connection string - primary key]' ``` - Once the device connects to the Wi-Fi network, it starts publishing MQTT messages. The Azure IoT monitor will show these messages like below: ``` { "event": { "origin": "", "payload": "{\"deviceId\":\"myFirstDevice\",\"windSpeed\":13.00,\"temperature\":22.00,\"humidity\":67.00}" } } ``` - You can also send MQTT messages to your device by using the following command: ``` $ az iot device c2d-message send -d [Device Id] -n [IoTHub Name] --data [Data_to_Send] ``` The `make monitor` output will print the received messages like below: ``` Received Message [1] Message ID: 635fd5a9-70a4-422f-9394-4cda9026c2e1 Correlation ID: Data: <<>> & Size=18 ``` ================================================ FILE: examples/iothub_client_sample_mqtt/main/CMakeLists.txt ================================================ set(COMPONENT_SRCS "iothub_client_sample_mqtt.c" "azure_main.c" ) set(COMPONENT_ADD_INCLUDEDIRS ".") register_component() component_compile_definitions(SET_TRUSTED_CERT_IN_SAMPLES) ================================================ FILE: examples/iothub_client_sample_mqtt/main/Kconfig.projbuild ================================================ menu "Example Configuration" config WIFI_SSID string "WiFi SSID" default "myssid" help SSID (network name) for the example to connect to. config WIFI_PASSWORD string "WiFi Password" default "myssid" help WiFi password (WPA or WPA2) for the example to use. Can be left blank if the network has no security set. config IOTHUB_CONNECTION_STRING string "IOT Hub Device Connection String" default "" help String containing Hostname, Device Id & Device Key in the format: HostName=;DeviceId=;SharedAccessKey= You can get this from the Azure IoT CLI or the Azure Portal. config MESSAGE_INTERVAL_TIME int "Time delay in Milliseconds between two consecutive messages" default 100 help Set time delay between two consecutive message sent to the cloud config MESSAGE_COUNT int "Total number of messages to be sent to the cloud" default 50 help This example will terminate after sending these many messages. If the message count is set as 0 then this example will send indefinite messages to the cloud. endmenu ================================================ FILE: examples/iothub_client_sample_mqtt/main/azure_main.c ================================================ /* esp-azure example This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" #include "esp_system.h" #include "esp_wifi.h" #ifdef CONFIG_IDF_TARGET_ESP8266 || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0)) #include "esp_event_loop.h" #else #include "esp_event.h" #endif #include "esp_log.h" #include "nvs_flash.h" #include "iothub_client_sample_mqtt.h" #define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID #define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD EventGroupHandle_t wifi_event_group; #ifndef BIT0 #define BIT0 (0x1 << 0) #endif /* The event group allows multiple bits for each event, but we only care about one event - are we connected to the AP with an IP? */ const int CONNECTED_BIT = BIT0; static const char *TAG = "azure"; #ifdef CONFIG_IDF_TARGET_ESP8266 || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0)) static esp_err_t event_handler(void *ctx, system_event_t *event) { switch(event->event_id) { case SYSTEM_EVENT_STA_START: esp_wifi_connect(); break; case SYSTEM_EVENT_STA_GOT_IP: xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); break; case SYSTEM_EVENT_STA_DISCONNECTED: /* This is a workaround as ESP platform WiFi libs don't currently auto-reassociate. */ esp_wifi_connect(); xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); break; default: break; } return ESP_OK; } #else static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { /* This is a workaround as ESP platform WiFi libs don't currently auto-reassociate. */ esp_wifi_connect(); xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); } } #endif static void initialise_wifi(void) { #ifdef CONFIG_IDF_TARGET_ESP8266 || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0)) tcpip_adapter_init(); wifi_event_group = xEventGroupCreate(); ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); #else ESP_ERROR_CHECK( esp_netif_init() ); wifi_event_group = xEventGroupCreate(); ESP_ERROR_CHECK( esp_event_loop_create_default() ); esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta(); assert(sta_netif); #endif wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); wifi_config_t wifi_config = { .sta = { .ssid = EXAMPLE_WIFI_SSID, .password = EXAMPLE_WIFI_PASS, }, }; ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); #ifdef CONFIG_IDF_TARGET_ESP8266 || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0)) #else ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) ); ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) ); #endif ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); ESP_ERROR_CHECK( esp_wifi_start() ); } void azure_task(void *pvParameter) { xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); ESP_LOGI(TAG, "Connected to AP success!"); iothub_client_sample_mqtt_run(); vTaskDelete(NULL); } void app_main() { // Initialize NVS esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK( ret ); initialise_wifi(); if ( xTaskCreate(&azure_task, "azure_task", 1024 * 5, NULL, 5, NULL) != pdPASS ) { printf("create azure task failed\r\n"); } } ================================================ FILE: examples/iothub_client_sample_mqtt/main/component.mk ================================================ # # "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) CFLAGS += -DSET_TRUSTED_CERT_IN_SAMPLES ================================================ FILE: examples/iothub_client_sample_mqtt/main/iothub_client_sample_mqtt.c ================================================ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include #include #include "iothub_client.h" #include "iothub_device_client_ll.h" #include "iothub_client_options.h" #include "iothub_message.h" #include "azure_c_shared_utility/threadapi.h" #include "azure_c_shared_utility/crt_abstractions.h" #include "azure_c_shared_utility/platform.h" #include "azure_c_shared_utility/shared_util_options.h" #include "iothubtransportmqtt.h" #include "iothub_client_options.h" #include "esp_system.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #ifdef MBED_BUILD_TIMESTAMP #define SET_TRUSTED_CERT_IN_SAMPLES #endif // MBED_BUILD_TIMESTAMP #ifdef SET_TRUSTED_CERT_IN_SAMPLES #include "certs.h" #endif // SET_TRUSTED_CERT_IN_SAMPLES /*String containing Hostname, Device Id & Device Key in the format: */ /* "HostName=;DeviceId=;SharedAccessKey=" */ /* "HostName=;DeviceId=;SharedAccessSignature=" */ #define EXAMPLE_IOTHUB_CONNECTION_STRING CONFIG_IOTHUB_CONNECTION_STRING static const char* connectionString = EXAMPLE_IOTHUB_CONNECTION_STRING; static int callbackCounter; static char msgText[1024]; static char propText[1024]; static bool g_continueRunning; #define MESSAGE_COUNT CONFIG_MESSAGE_COUNT #define DOWORK_LOOP_NUM 3 typedef struct EVENT_INSTANCE_TAG { IOTHUB_MESSAGE_HANDLE messageHandle; size_t messageTrackingId; // For tracking the messages within the user callback. } EVENT_INSTANCE; static IOTHUBMESSAGE_DISPOSITION_RESULT ReceiveMessageCallback(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback) { int* counter = (int*)userContextCallback; const char* buffer; size_t size; MAP_HANDLE mapProperties; const char* messageId; const char* correlationId; // Message properties if ((messageId = IoTHubMessage_GetMessageId(message)) == NULL) { messageId = ""; } if ((correlationId = IoTHubMessage_GetCorrelationId(message)) == NULL) { correlationId = ""; } // Message content if (IoTHubMessage_GetByteArray(message, (const unsigned char**)&buffer, &size) != IOTHUB_MESSAGE_OK) { (void)printf("unable to retrieve the message data\r\n"); } else { (void)printf("Received Message [%d]\r\n Message ID: %s\r\n Correlation ID: %s\r\n Data: <<<%.*s>>> & Size=%d\r\n", *counter, messageId, correlationId, (int)size, buffer, (int)size); // If we receive the work 'quit' then we stop running if (size == (strlen("quit") * sizeof(char)) && memcmp(buffer, "quit", size) == 0) { g_continueRunning = false; } } // Retrieve properties from the message mapProperties = IoTHubMessage_Properties(message); if (mapProperties != NULL) { const char*const* keys; const char*const* values; size_t propertyCount = 0; if (Map_GetInternals(mapProperties, &keys, &values, &propertyCount) == MAP_OK) { if (propertyCount > 0) { size_t index; printf(" Message Properties:\r\n"); for (index = 0; index < propertyCount; index++) { (void)printf("\tKey: %s Value: %s\r\n", keys[index], values[index]); } (void)printf("\r\n"); } } } /* Some device specific action code goes here... */ (*counter)++; return IOTHUBMESSAGE_ACCEPTED; } static void SendConfirmationCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback) { EVENT_INSTANCE* eventInstance = (EVENT_INSTANCE*)userContextCallback; size_t id = eventInstance->messageTrackingId; if (result == IOTHUB_CLIENT_CONFIRMATION_OK) { (void)printf("Confirmation[%d] received for message tracking id = %d with result = %s\r\n", callbackCounter, (int)id, MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result)); /* Some device specific action code goes here... */ callbackCounter++; } IoTHubMessage_Destroy(eventInstance->messageHandle); } void connection_status_callback(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* userContextCallback) { (void)printf("\n\nConnection Status result:%s, Connection Status reason: %s\n\n", MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONNECTION_STATUS, result), MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONNECTION_STATUS_REASON, reason)); } void iothub_client_sample_mqtt_run(void) { IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle; EVENT_INSTANCE message; g_continueRunning = true; srand((unsigned int)time(NULL)); double avgWindSpeed = 10.0; double minTemperature = 20.0; double minHumidity = 60.0; callbackCounter = 0; int receiveContext = 0; if (platform_init() != 0) { (void)printf("Failed to initialize the platform.\r\n"); } else { if ((iotHubClientHandle = IoTHubClient_LL_CreateFromConnectionString(connectionString, MQTT_Protocol)) == NULL) { (void)printf("ERROR: iotHubClientHandle is NULL!\r\n"); } else { bool traceOn = true; IoTHubClient_LL_SetOption(iotHubClientHandle, OPTION_LOG_TRACE, &traceOn); IoTHubClient_LL_SetConnectionStatusCallback(iotHubClientHandle, connection_status_callback, NULL); // Setting the Trusted Certificate. This is only necessary on system with without // built in certificate stores. #ifdef SET_TRUSTED_CERT_IN_SAMPLES IoTHubDeviceClient_LL_SetOption(iotHubClientHandle, OPTION_TRUSTED_CERT, certificates); #endif // SET_TRUSTED_CERT_IN_SAMPLES /* Setting Message call back, so we can receive Commands. */ if (IoTHubClient_LL_SetMessageCallback(iotHubClientHandle, ReceiveMessageCallback, &receiveContext) != IOTHUB_CLIENT_OK) { (void)printf("ERROR: IoTHubClient_LL_SetMessageCallback..........FAILED!\r\n"); } else { (void)printf("IoTHubClient_LL_SetMessageCallback...successful.\r\n"); /* Now that we are ready to receive commands, let's send some messages */ int iterator = 0; double temperature = 0; double humidity = 0; time_t sent_time = 0; time_t current_time = 0; do { //(void)printf("iterator: [%d], callbackCounter: [%d]. \r\n", iterator, callbackCounter); time(¤t_time); if ((MESSAGE_COUNT == 0 || iterator < MESSAGE_COUNT) && iterator <= callbackCounter && (difftime(current_time, sent_time) > ((CONFIG_MESSAGE_INTERVAL_TIME) / 1000))) { temperature = minTemperature + (rand() % 10); humidity = minHumidity + (rand() % 20); sprintf_s(msgText, sizeof(msgText), "{\"deviceId\":\"myFirstDevice\",\"windSpeed\":%.2f,\"temperature\":%.2f,\"humidity\":%.2f}", avgWindSpeed + (rand() % 4 + 2), temperature, humidity); if ((message.messageHandle = IoTHubMessage_CreateFromByteArray((const unsigned char*)msgText, strlen(msgText))) == NULL) { (void)printf("ERROR: iotHubMessageHandle is NULL!\r\n"); } else { message.messageTrackingId = iterator; MAP_HANDLE propMap = IoTHubMessage_Properties(message.messageHandle); (void)sprintf_s(propText, sizeof(propText), temperature > 28 ? "true" : "false"); if (Map_AddOrUpdate(propMap, "temperatureAlert", propText) != MAP_OK) { (void)printf("ERROR: Map_AddOrUpdate Failed!\r\n"); } if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, message.messageHandle, SendConfirmationCallback, &message) != IOTHUB_CLIENT_OK) { (void)printf("ERROR: IoTHubClient_LL_SendEventAsync..........FAILED!\r\n"); } else { time(&sent_time); (void)printf("IoTHubClient_LL_SendEventAsync accepted message [%d] for transmission to IoT Hub.\r\n", (int)iterator); } } iterator++; } IoTHubClient_LL_DoWork(iotHubClientHandle); ThreadAPI_Sleep(10); if (MESSAGE_COUNT != 0 && callbackCounter >= MESSAGE_COUNT) { printf("exit\n"); break; } } while (g_continueRunning); (void)printf("iothub_client_sample_mqtt has gotten quit message, call DoWork %d more time to complete final sending...\r\n", DOWORK_LOOP_NUM); size_t index = 0; for (index = 0; index < DOWORK_LOOP_NUM; index++) { IoTHubClient_LL_DoWork(iotHubClientHandle); ThreadAPI_Sleep(1); } } IoTHubClient_LL_Destroy(iotHubClientHandle); } platform_deinit(); } } int main(void) { iothub_client_sample_mqtt_run(); return 0; } ================================================ FILE: examples/iothub_client_sample_mqtt/main/iothub_client_sample_mqtt.h ================================================ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #ifndef IOTHUB_CLIENT_SAMPLE_MQTT_H #define IOTHUB_CLIENT_SAMPLE_MQTT_H #ifdef __cplusplus extern "C" { #endif #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" /* FreeRTOS event group to signal when we are connected & ready to make a request */ extern EventGroupHandle_t wifi_event_group; extern const int CONNECTED_BIT; void iothub_client_sample_mqtt_run(void); #ifdef __cplusplus } #endif #endif /* IOTHUB_CLIENT_SAMPLE_MQTT_H */ ================================================ FILE: examples/iothub_client_sample_mqtt/sdkconfig.defaults ================================================ # newlib for ESP32 and ESP8266 platform CONFIG_NEWLIB_ENABLE=y CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y CONFIG_NEWLIB_NANO_FORMAT= CONFIG_SSL_USING_MBEDTLS=y CONFIG_LWIP_IPV6=y ================================================ FILE: examples/iothub_devicetwin_samples_and_methods/CMakeLists.txt ================================================ # The following five lines of boilerplate have to be in your project's # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) set (EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_BINARY_DIR}/../../../port") project(iothub_devicetwin_samples_and_methods) ================================================ FILE: examples/iothub_devicetwin_samples_and_methods/Makefile ================================================ # # This is a project Makefile. It is assumed the directory this Makefile resides in is a # project subdirectory. # PROJECT_NAME := iothub_devicetwin_samples_and_methods EXTRA_COMPONENT_DIRS += $(PROJECT_PATH)/../../../esp-azure include $(IDF_PATH)/make/project.mk ================================================ FILE: examples/iothub_devicetwin_samples_and_methods/README.md ================================================ # Device Twin and Direct Method Example This example demonstrates Device twin and Direct method features of Azure IoT. ## Device Twin In this example, we use car object with desired and reported properties as described in the JSON blob below. ``` Car: { "lastOilChangeDate": "", \\ reported property "changeOilReminder": "", \\ desired property "maker": { \\ reported property "makerName": "", "style": "", "year": }, "state": { \\ reported property "reported_maxSpeed": , "softwareVersion": , "vanityPlate": "" }, "settings": { \\ desired property "desired_maxSpeed": , "location": { "longitude": , "latitude": }, }, } ``` ### Set Device Twin Desired Properties - To execute Device Twin, an IoT device will be required. We will use the same Azure IoT device created using the steps defined in top level [README](../../README.md#creating-an-azure-iot-device). - Set Device Twin desired properties by navigating to `Azure Portal` -> `your IoT Hub` -> `IoT Devices` -> `your IoT device` -> `Device Twin` and paste the following JSON blob under `desired` property. ``` "desired": { // paste from here.. "changeOilReminder": "LOW_FUEL", "settings": { "desired_maxSpeed": 120, "location": { "longitude": 71, "latitude": 25 } }, // desired continues.. ``` ## Device Configuration - For this demo we will use the same Azure IoT device created using the steps defined in top level [README](../../README.md#create_device). Copy the connection string for the device from the output of this command: ``` bash $ az iot hub device-identity show-connection-string -n [IoTHub Name] -d [Device ID] ``` Sample output: ``` { "connectionString": "HostName=.azure-devices.net;DeviceId=;SharedAccessKey=" } ``` > Note that the double quotes at both the ends of the string are not part of the connection string. So, for the above, just copy `HostName=.azure-devices.net;DeviceId=;SharedAccessKey=` While changing the value, please ensure that you have completely cleared the older value, before pasting the new one. If you face any run time connection issues, double check this value. - Execute `make menuconfig`. In the menu, go to `Example Configuration` and configure `WiFi SSID` and `WiFi Password` so that the device can connect to the appropriate Wi-Fi network on boot up. Set `IOT Hub Device Connection String` with the string copied above ## Trying out the example Run the following command to flash the example and monitor the output `$ make -j8 flash monitor` After running the application, you can check updated properties by navigating to `Azure Portal` -> `your IoT Hub` -> `IoT devices` -> `your IoT device` -> `Device Twin` If you change the above set desired field and click on "Save", they will be mirrored on to your ESP Monitor. ### Direct Method Invocation Navigate to `Azure Portal` -> `your IoT Hub` -> `IoT devices` -> `your IoT device` -> `Direct Method` Set the `Method Name` as `getCarVIN` and add some payload. Consider an example payload as below: ``` { "message": "Hello World" } ``` On invoking the method, the invocation request will be sent to the IoT device, which in turn will respond with a payload like below: ``` { "Response": "1HGCM82633A004352" } ``` ================================================ FILE: examples/iothub_devicetwin_samples_and_methods/main/CMakeLists.txt ================================================ set(COMPONENT_SRCS "azure_main.c" "iothub_client_device_twin_and_methods_sample.c" ) set(COMPONENT_ADD_INCLUDEDIRS ".") register_component() component_compile_definitions(SET_TRUSTED_CERT_IN_SAMPLES) ================================================ FILE: examples/iothub_devicetwin_samples_and_methods/main/Kconfig.projbuild ================================================ menu "Example Configuration" config WIFI_SSID string "WiFi SSID" default "myssid" help SSID (network name) for the example to connect to. config WIFI_PASSWORD string "WiFi Password" default "myssid" help WiFi password (WPA or WPA2) for the example to use. Can be left blank if the network has no security set. config IOTHUB_CONNECTION_STRING string "IOT Hub Device Connection String" default "" help String containing Hostname, Device Id & Device Key in the format: HostName=;DeviceId=;SharedAccessKey= You can get this from the Azure IoT CLI or the Azure Portal. endmenu ================================================ FILE: examples/iothub_devicetwin_samples_and_methods/main/azure_main.c ================================================ /* esp-azure example This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" #include "esp_system.h" #include "esp_wifi.h" #ifdef CONFIG_IDF_TARGET_ESP8266 || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0)) #include "esp_event_loop.h" #else #include "esp_event.h" #endif #include "esp_log.h" #include "nvs_flash.h" #define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID #define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD /* FreeRTOS event group to signal when we are connected & ready to make a request */ static EventGroupHandle_t wifi_event_group; #ifndef BIT0 #define BIT0 (0x1 << 0) #endif /* The event group allows multiple bits for each event, but we only care about one event - are we connected to the AP with an IP? */ const int CONNECTED_BIT = BIT0; static const char *TAG = "azure"; extern int iothhub_devicetwin_init(void); #ifdef CONFIG_IDF_TARGET_ESP8266 || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0)) static esp_err_t event_handler(void *ctx, system_event_t *event) { switch(event->event_id) { case SYSTEM_EVENT_STA_START: esp_wifi_connect(); break; case SYSTEM_EVENT_STA_GOT_IP: xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); break; case SYSTEM_EVENT_STA_DISCONNECTED: /* This is a workaround as ESP platform WiFi libs don't currently auto-reassociate. */ esp_wifi_connect(); xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); break; default: break; } return ESP_OK; } #else static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { /* This is a workaround as ESP platform WiFi libs don't currently auto-reassociate. */ esp_wifi_connect(); xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); } } #endif static void initialise_wifi(void) { #ifdef CONFIG_IDF_TARGET_ESP8266 || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0)) tcpip_adapter_init(); wifi_event_group = xEventGroupCreate(); ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); #else ESP_ERROR_CHECK( esp_netif_init() ); wifi_event_group = xEventGroupCreate(); ESP_ERROR_CHECK( esp_event_loop_create_default() ); esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta(); assert(sta_netif); #endif wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); wifi_config_t wifi_config = { .sta = { .ssid = EXAMPLE_WIFI_SSID, .password = EXAMPLE_WIFI_PASS, }, }; ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); #ifdef CONFIG_IDF_TARGET_ESP8266 || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0)) #else ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) ); ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) ); #endif ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); ESP_ERROR_CHECK( esp_wifi_start() ); } extern int iothub_client_device_twin_init(); void azure_task(void *pvParameter) { xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); ESP_LOGI(TAG, "Connected to AP success!"); iothub_client_device_twin_init(); vTaskDelete(NULL); } void app_main() { // Initialize NVS esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK( ret ); initialise_wifi(); if ( xTaskCreate(&azure_task, "azure_task", 1024 * 6, NULL, 5, NULL) != pdPASS ) { printf("create azure task failed\r\n"); } } ================================================ FILE: examples/iothub_devicetwin_samples_and_methods/main/component.mk ================================================ # # "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) CFLAGS += -DSET_TRUSTED_CERT_IN_SAMPLES ================================================ FILE: examples/iothub_devicetwin_samples_and_methods/main/iothub_client_device_twin_and_methods_sample.c ================================================ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // This sample shows how to translate the Device Twin json received from Azure IoT Hub into meaningful data for your application. // It uses the parson library, a very lightweight json parser. // There is an analogous sample using the serializer - which is a library provided by this SDK to help parse json - in devicetwin_simplesample. // Most applications should use this sample, not the serializer. // WARNING: Check the return of all API calls when developing your solution. Return checks ommited for sample simplification. #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "azure_macro_utils/macro_utils.h" #include "azure_c_shared_utility/threadapi.h" #include "azure_c_shared_utility/platform.h" #include "iothub_device_client.h" #include "iothub_client_options.h" #include "iothub.h" #include "iothub_message.h" #include "parson.h" #include "sdkconfig.h" // The protocol you wish to use should be uncommented // #define SAMPLE_MQTT //#define SAMPLE_MQTT_OVER_WEBSOCKETS //#define SAMPLE_AMQP //#define SAMPLE_AMQP_OVER_WEBSOCKETS //#define SAMPLE_HTTP #ifdef SAMPLE_MQTT #include "iothubtransportmqtt.h" #endif // SAMPLE_MQTT #ifdef SAMPLE_MQTT_OVER_WEBSOCKETS #include "iothubtransportmqtt_websockets.h" #endif // SAMPLE_MQTT_OVER_WEBSOCKETS #ifdef SAMPLE_AMQP #include "iothubtransportamqp.h" #endif // SAMPLE_AMQP #ifdef SAMPLE_AMQP_OVER_WEBSOCKETS #include "iothubtransportamqp_websockets.h" #endif // SAMPLE_AMQP_OVER_WEBSOCKETS #ifdef SAMPLE_HTTP #include "iothubtransporthttp.h" #endif // SAMPLE_HTTP #ifdef SET_TRUSTED_CERT_IN_SAMPLES #include "certs.h" #endif // SET_TRUSTED_CERT_IN_SAMPLES /* Paste in the your iothub device connection string */ static const char* connectionString = CONFIG_IOTHUB_CONNECTION_STRING; #define DOWORK_LOOP_NUM 3 typedef struct MAKER_TAG { char* makerName; char* style; int year; } Maker; typedef struct GEO_TAG { double longitude; double latitude; } Geo; typedef struct CAR_STATE_TAG { int32_t softwareVersion; // reported property uint8_t reported_maxSpeed; // reported property char* vanityPlate; // reported property } CarState; typedef struct CAR_SETTINGS_TAG { uint8_t desired_maxSpeed; // desired property Geo location; // desired property } CarSettings; typedef struct CAR_TAG { char* lastOilChangeDate; // reported property char* changeOilReminder; // desired property Maker maker; // reported property CarState state; // reported property CarSettings settings; // desired property } Car; // Converts the Car object into a JSON blob with reported properties that is ready to be sent across the wire as a twin. static char* serializeToJson(Car* car) { char* result; JSON_Value* root_value = json_value_init_object(); JSON_Object* root_object = json_value_get_object(root_value); // Only reported properties: (void)json_object_set_string(root_object, "lastOilChangeDate", car->lastOilChangeDate); (void)json_object_dotset_string(root_object, "maker.makerName", car->maker.makerName); (void)json_object_dotset_string(root_object, "maker.style", car->maker.style); (void)json_object_dotset_number(root_object, "maker.year", car->maker.year); (void)json_object_dotset_number(root_object, "state.reported_maxSpeed", car->state.reported_maxSpeed); (void)json_object_dotset_number(root_object, "state.softwareVersion", car->state.softwareVersion); (void)json_object_dotset_string(root_object, "state.vanityPlate", car->state.vanityPlate); result = json_serialize_to_string(root_value); json_value_free(root_value); return result; } // Converts the desired properties of the Device Twin JSON blob received from IoT Hub into a Car object. static Car* parseFromJson(const char* json, DEVICE_TWIN_UPDATE_STATE update_state) { Car* car = malloc(sizeof(Car)); JSON_Value* root_value = NULL; JSON_Object* root_object = NULL; if (NULL == car) { (void)printf("ERROR: Failed to allocate memory\r\n"); } else { (void)memset(car, 0, sizeof(Car)); root_value = json_parse_string(json); root_object = json_value_get_object(root_value); // Only desired properties: JSON_Value* changeOilReminder; JSON_Value* desired_maxSpeed; JSON_Value* latitude; JSON_Value* longitude; if (update_state == DEVICE_TWIN_UPDATE_COMPLETE) { changeOilReminder = json_object_dotget_value(root_object, "desired.changeOilReminder"); desired_maxSpeed = json_object_dotget_value(root_object, "desired.settings.desired_maxSpeed"); latitude = json_object_dotget_value(root_object, "desired.settings.location.latitude"); longitude = json_object_dotget_value(root_object, "desired.settings.location.longitude"); } else { changeOilReminder = json_object_dotget_value(root_object, "changeOilReminder"); desired_maxSpeed = json_object_dotget_value(root_object, "settings.desired_maxSpeed"); latitude = json_object_dotget_value(root_object, "settings.location.latitude"); longitude = json_object_dotget_value(root_object, "settings.location.longitude"); } if (changeOilReminder != NULL) { const char* data = json_value_get_string(changeOilReminder); if (data != NULL) { car->changeOilReminder = malloc(strlen(data) + 1); if (NULL != car->changeOilReminder) { (void)strcpy(car->changeOilReminder, data); } } } if (desired_maxSpeed != NULL) { car->settings.desired_maxSpeed = (uint8_t)json_value_get_number(desired_maxSpeed); } if (latitude != NULL) { car->settings.location.latitude = json_value_get_number(latitude); } if (longitude != NULL) { car->settings.location.longitude = json_value_get_number(longitude); } json_value_free(root_value); } return car; } static int deviceMethodCallback(const char* method_name, const unsigned char* payload, size_t size, unsigned char** response, size_t* response_size, void* userContextCallback) { (void)userContextCallback; (void)payload; (void)size; printf("Method Name: %s\n", method_name); int result; if (strcmp("getCarVIN", method_name) == 0) { const char deviceMethodResponse[] = "{ \"Response\": \"1HGCM82633A004352\" }"; *response_size = sizeof(deviceMethodResponse)-1; *response = malloc(*response_size); if (*response != NULL) { (void)memcpy(*response, deviceMethodResponse, *response_size); result = 200; } else { result = -1; } } else { // All other entries are ignored. const char deviceMethodResponse[] = "{ }"; *response_size = sizeof(deviceMethodResponse)-1; *response = malloc(*response_size); if (*response != NULL) { (void)memcpy(*response, deviceMethodResponse, *response_size); } result = -1; } return result; } static void deviceTwinCallback(DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size, void* userContextCallback) { (void)update_state; (void)size; Car* oldCar = (Car*)userContextCallback; Car* newCar = parseFromJson((const char*)payLoad, update_state); if (newCar != NULL) { if (newCar->changeOilReminder != NULL) { if ((oldCar->changeOilReminder != NULL) && (strcmp(oldCar->changeOilReminder, newCar->changeOilReminder) != 0)) { free(oldCar->changeOilReminder); } if (oldCar->changeOilReminder == NULL) { printf("Received a new changeOilReminder = %s\n", newCar->changeOilReminder); if ( NULL != (oldCar->changeOilReminder = malloc(strlen(newCar->changeOilReminder) + 1))) { (void)strcpy(oldCar->changeOilReminder, newCar->changeOilReminder); free(newCar->changeOilReminder); } } } if (newCar->settings.desired_maxSpeed != 0) { if (newCar->settings.desired_maxSpeed != oldCar->settings.desired_maxSpeed) { printf("Received a new desired_maxSpeed = %" PRIu8 "\n", newCar->settings.desired_maxSpeed); oldCar->settings.desired_maxSpeed = newCar->settings.desired_maxSpeed; } } if (newCar->settings.location.latitude != 0) { if (newCar->settings.location.latitude != oldCar->settings.location.latitude) { printf("Received a new latitude = %f\n", newCar->settings.location.latitude); oldCar->settings.location.latitude = newCar->settings.location.latitude; } } if (newCar->settings.location.longitude != 0) { if (newCar->settings.location.longitude != oldCar->settings.location.longitude) { printf("Received a new longitude = %f\n", newCar->settings.location.longitude); oldCar->settings.location.longitude = newCar->settings.location.longitude; } } free(newCar); } else { printf("Error: JSON parsing failed!\r\n"); } } static void reportedStateCallback(int status_code, void* userContextCallback) { (void)userContextCallback; printf("Device Twin reported properties update completed with result: %d\r\n", status_code); } static void iothub_client_device_twin_and_methods_sample_run(void) { IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol; IOTHUB_DEVICE_CLIENT_LL_HANDLE iotHubClientHandle; // Select the Protocol to use with the connection #ifdef SAMPLE_MQTT protocol = MQTT_Protocol; #endif // SAMPLE_MQTT #ifdef SAMPLE_MQTT_OVER_WEBSOCKETS protocol = MQTT_WebSocket_Protocol; #endif // SAMPLE_MQTT_OVER_WEBSOCKETS #ifdef SAMPLE_AMQP protocol = AMQP_Protocol; #endif // SAMPLE_AMQP #ifdef SAMPLE_AMQP_OVER_WEBSOCKETS protocol = AMQP_Protocol_over_WebSocketsTls; #endif // SAMPLE_AMQP_OVER_WEBSOCKETS #ifdef SAMPLE_HTTP protocol = HTTP_Protocol; #endif // SAMPLE_HTTP if (IoTHub_Init() != 0) { (void)printf("Failed to initialize the platform.\r\n"); } else { if ((iotHubClientHandle = IoTHubDeviceClient_LL_CreateFromConnectionString(connectionString, protocol)) == NULL) { (void)printf("ERROR: iotHubClientHandle is NULL!\r\n"); } else { // Uncomment the following lines to enable verbose logging (e.g., for debugging). //bool traceOn = true; //(void)IoTHubDeviceClient_SetOption(iotHubClientHandle, OPTION_LOG_TRACE, &traceOn); #ifdef SET_TRUSTED_CERT_IN_SAMPLES // For mbed add the certificate information if (IoTHubDeviceClient_LL_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK) { (void)printf("failure to set option \"TrustedCerts\"\r\n"); } #endif // SET_TRUSTED_CERT_IN_SAMPLES Car car; memset(&car, 0, sizeof(Car)); car.lastOilChangeDate = "2016"; car.maker.makerName = "Fabrikam"; car.maker.style = "sedan"; car.maker.year = 2014; car.state.reported_maxSpeed = 100; car.state.softwareVersion = 1; car.state.vanityPlate = "1I1"; char* reportedProperties = serializeToJson(&car); if (reportedProperties != NULL) { (void)IoTHubDeviceClient_LL_SendReportedState(iotHubClientHandle, (const unsigned char*)reportedProperties, strlen(reportedProperties), reportedStateCallback, NULL); (void)IoTHubDeviceClient_LL_SetDeviceMethodCallback(iotHubClientHandle, deviceMethodCallback, NULL); (void)IoTHubDeviceClient_LL_SetDeviceTwinCallback(iotHubClientHandle, deviceTwinCallback, &car); while (1) { IoTHubDeviceClient_LL_DoWork(iotHubClientHandle); ThreadAPI_Sleep(10); } free(reportedProperties); free(car.changeOilReminder); } else { printf("Error: JSON serialization failed!\r\n"); } IoTHubDeviceClient_LL_Destroy(iotHubClientHandle); } IoTHub_Deinit(); } } int iothub_client_device_twin_init(void) { iothub_client_device_twin_and_methods_sample_run(); return 0; } ================================================ FILE: examples/iothub_devicetwin_samples_and_methods/sdkconfig.defaults ================================================ # newlib for ESP32 and ESP8266 platform CONFIG_NEWLIB_ENABLE=y CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y CONFIG_NEWLIB_NANO_FORMAT= CONFIG_SSL_USING_MBEDTLS=y CONFIG_LWIP_IPV6=y ================================================ FILE: examples/prov_dev_client_ll_sample/CMakeLists.txt ================================================ # The following five lines of boilerplate have to be in your project's # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) set (EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_BINARY_DIR}/../../../port") project(prov_dev_client_ll_sample) ================================================ FILE: examples/prov_dev_client_ll_sample/Makefile ================================================ # # This is a project Makefile. It is assumed the directory this Makefile resides in is a # project subdirectory. # PROJECT_NAME := prov_dev_client_ll_sample EXTRA_COMPONENT_DIRS += $(PROJECT_PATH)/../../../esp-azure include $(IDF_PATH)/make/project.mk ================================================ FILE: examples/prov_dev_client_ll_sample/README.md ================================================ # Azure Provisioning Demo This example demonstrates Device Authentication using X.509 CA Certificates. Refer [this azure documentation](https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-x509ca-overview) to learn more about this. # Provisioning Setup ### Creating a Device - We will use the same IoT hub that was created as per the steps in the top level [README](../../README.md#setting-up-azure-iot-hub) - Go to the IoT Hub. Find and click on "IoT devices" under "Explorers" in the menu bar. - Click on "New". - Enter name of your IoT Device in "Device ID". - Select Authentication type as "X.509 CA Signed". - Click "Save". ### Certificate Generation - Here certificates will be generated with [OpenSSL](https://www.openssl.org/). Other services can also be used to generate certificates.These commands are UNIX/Linux specific. For other system, these commands may not work. - [Download](https://www.openssl.org/source/) and install openSSL. - After the installation is complete, use following commands: - Generate Root CA private key ``` $ openssl genrsa -out rootCA.key 4096 ``` - Generate Root CA certificate: ``` $ openssl req -x509 -new -key rootCA.key -days 1024 -out rootCA.pem ``` > You can keep all parameters at defaults (by pressing enter) except Common Name (CN). Give any user friendly common name to your root CA certificate. - Generate key for device (we will call it a leaf): ``` $ openssl genrsa -out leaf_private_key.pem 4096 ``` - Generate Certificate Signing Request for the device: ``` $ openssl req -new -key leaf_private_key.pem -out leaf.csr ``` > You can keep all parameters at defaults (by pressing enter) excpet Common Name (CN). **Give the name which was registered in IoT Hub ([Device ID](#creating-a-device)) as the CN.** - Generate device certificate (leaf certificate): ``` $ openssl x509 -req -in leaf.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out leaf_certificate.pem ``` ### CA Certificate Registration - Go to previously created IoT Hub and navigate to "Certificates" under "Settings" in menu bar. - Click on "Add". - Give a User friendly certificate name and add the `rootCA.pem` which was created in the above steps. Click on "Save". - Status of this certificate will be "Unverified". To verify this click on the certificate name. Click on `Generate Verification Code` at the bottom under `Certificate Details`. A verification code will be generated, copy it. - In the terminal, navigate to directory where `rootCA.pem` was created and run following command to generate a certificate signing request: ``` $ openssl req -new -key rootCA.key -out verification.csr ``` > You can keep all parameters at defaults (by pressing enter) except Common Name (CN). **Give the Verification Code copied in previous step as Common Name.** - Generate Verification Certificate: ``` $ openssl x509 -req -in verification.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out verification_certificate.pem ``` - Upload this certificate on the Azure Portal under `Certificate Details`. ### Device Provisioning Service - In the upper left-hand corner of the Azure portal, click Create a resource. - In the Search box, type "device provisioning" and select `IoT Hub Device Provisioning Service` from the suggestions. - Fill out the IoT Hub Device Provisioning Service form and click "Create" at the bottom. - Select this newly created resource, select `Linked IoT hubs` under `Settings` and click on `Add`. - In the Add link to IoT hub page - IoT hub: Select the IoT hub that you want to link with this Device Provisioning Service instance. - Access Policy: Select `iothubowner`. - Go to `Manage Enrollments` under `Settings` and click on `Add individual enrollment`. - Select Mechanism as "X.509". - Upload device certificate created earlier (`leaf_certificate.pem`) in place of "Primary Certificate". Leave "Secondary Certificate" blank. - Enter the appropriate IoT Hub Device ID. Mark IoT Edge device as "False". - Click "Save" at the top. - Copy device certificate created earlier (`leaf_certificate.pem`) to `main/certs/`. - Copy private key (`leaf_private_key.pem`) to `main/certs/`. ## Device Configuration - Execute `make menuconfig`. In the menu, go to `Example Configuration` and configure `WiFi SSID` and `WiFi Password` so that the device can connect to the appropriate Wi-Fi network on boot up. - Get the IoT Hub connection string using `az iot hub show-connection-string -n ` and paste in `IoT Hub Device Connection String`. > Note: While changing the value, please ensure that you have completely cleared the older value, before pasting the new one. If you face any run time connection issues, double check this value. - Enter the `Device leaf certificate common name` which was set during [certificate generation](#certificate-generation) - Copy the ID Scope of the device provisioning service which you can find on the Azure portal under "Overview" section of the service and paste in `ID Scope` field. - Save and exit `menuconfig`. > Note > In case following error occurs: > `cp: embed_txt/leaf_private_key > .pem: Permission denied` > give apt permissions to `leaf_certificate.pem` > eg: `$ cd main/certs/ && chmod 644 leaf_certificate.pem` ## Trying out the example - Run the following command to flash the example and monitor the output ``` bash $ make -j8 flash monitor ``` - In a separate window, monitor the Azure IoT events using the following: ``` $ az iot hub monitor-events -n [IoTHub Name] --login '[Connection string - primary key]' ``` - Once the device connects to the Wi-Fi network, it starts publishing MQTT messages. The Azure IoT monitor will show these messages like below: ``` { "event": { "origin": "", "payload": "{ \"message_index\" : \"0\" }" } } { "event": { "origin": "", "payload": "{ \"message_index\" : \"1\" }" } } ``` ================================================ FILE: examples/prov_dev_client_ll_sample/main/CMakeLists.txt ================================================ set(COMPONENT_SRCS "azure_main.c" "custom_hsm_x509.c" "prov_dev_client_ll_sample.c" ) set(COMPONENT_ADD_INCLUDEDIRS ".") set(COMPONENT_EMBED_TXTFILES "certs/leaf_certificate.pem" "certs/leaf_private_key.pem" ) register_component() component_compile_definitions(SET_TRUSTED_CERT_IN_SAMPLES) ================================================ FILE: examples/prov_dev_client_ll_sample/main/Kconfig.projbuild ================================================ menu "Example Configuration" config WIFI_SSID string "WiFi SSID" default "myssid" help SSID (network name) for the example to connect to. config WIFI_PASSWORD string "WiFi Password" default "myssid" help WiFi password (WPA or WPA2) for the example to use. Can be left blank if the network has no security set. config IOTHUB_CONNECTION_STRING string "IOT Hub Connection String" default "" help The IoT Hub Connection string - primary key in the format "HostName=;SharedAccessKeyName=;SharedAccessKey=." You can get this from the Azure IoT CLI or the Azure Portal. config DEVICE_COMMON_NAME string "Device Leaf Certificate Common Name" default "mydevice" help Common name of Leaf Certificate config DPS_ID_SCOPE string "ID Scope of Device provisioning service" default "myidscope" help This is the unique ID scope of Device Provisioning Service (DPS), and can be found under "Overview" section of your DPS on azure IoT portal endmenu ================================================ FILE: examples/prov_dev_client_ll_sample/main/azure_main.c ================================================ /* esp-azure example This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" #include "esp_system.h" #include "esp_wifi.h" #ifdef CONFIG_IDF_TARGET_ESP8266 || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0)) #include "esp_event_loop.h" #else #include "esp_event.h" #endif #include "esp_log.h" #include "nvs_flash.h" #define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID #define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD /* FreeRTOS event group to signal when we are connected & ready to make a request */ static EventGroupHandle_t wifi_event_group; #ifndef BIT0 #define BIT0 (0x1 << 0) #endif /* The event group allows multiple bits for each event, but we only care about one event - are we connected to the AP with an IP? */ const int CONNECTED_BIT = BIT0; static const char *TAG = "azure"; #ifdef CONFIG_IDF_TARGET_ESP8266 || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0)) static esp_err_t event_handler(void *ctx, system_event_t *event) { switch(event->event_id) { case SYSTEM_EVENT_STA_START: esp_wifi_connect(); break; case SYSTEM_EVENT_STA_GOT_IP: xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); break; case SYSTEM_EVENT_STA_DISCONNECTED: /* This is a workaround as ESP platform WiFi libs don't currently auto-reassociate. */ esp_wifi_connect(); xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); break; default: break; } return ESP_OK; } #else static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { /* This is a workaround as ESP platform WiFi libs don't currently auto-reassociate. */ esp_wifi_connect(); xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); } } #endif static void initialise_wifi(void) { #ifdef CONFIG_IDF_TARGET_ESP8266 || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0)) tcpip_adapter_init(); wifi_event_group = xEventGroupCreate(); ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); #else ESP_ERROR_CHECK( esp_netif_init() ); wifi_event_group = xEventGroupCreate(); ESP_ERROR_CHECK( esp_event_loop_create_default() ); esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta(); assert(sta_netif); #endif wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); wifi_config_t wifi_config = { .sta = { .ssid = EXAMPLE_WIFI_SSID, .password = EXAMPLE_WIFI_PASS, }, }; ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); #ifdef CONFIG_IDF_TARGET_ESP8266 || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0)) #else ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) ); ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) ); #endif ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); ESP_ERROR_CHECK( esp_wifi_start() ); } extern int prov_dev_client_ll_sample_run(); void azure_task(void *pvParameter) { xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); ESP_LOGI(TAG, "Connected to AP success!"); prov_dev_client_ll_sample_run(); vTaskDelete(NULL); } void app_main() { // Initialize NVS esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK( ret ); initialise_wifi(); if ( xTaskCreate(&azure_task, "azure_task", 1024 * 5, NULL, 5, NULL) != pdPASS ) { printf("create azure task failed\r\n"); } } ================================================ FILE: examples/prov_dev_client_ll_sample/main/certs/.dummy ================================================ ================================================ FILE: examples/prov_dev_client_ll_sample/main/component.mk ================================================ # # Main component makefile. # # This Makefile can be left empty. By default, it will take the sources in the # src/ directory, compile them and link them into lib(subdirectory_name).a # in the build directory. This behaviour is entirely configurable, # please read the ESP-IDF documents if you need to do this. # COMPONENT_EMBED_TXTFILES := certs/leaf_private_key.pem certs/leaf_certificate.pem ifndef IDF_CI_BUILD # Print an error if the certificate/key files are missing $(COMPONENT_PATH)/certs/leaf_private_key.pem $(COMPONENT_PATH)/certs/leaf_certificate.pem: @echo "Missing PEM file $@. This file identifies the ESP32 to Azure DPS for the example, see README for details." exit 1 else # IDF_CI_BUILD # this case is for the internal Continuous Integration build which # compiles all examples. Add some dummy certs so the example can # compile (even though it won't work) $(COMPONENT_PATH)/certs/leaf_private_key.pem $(COMPONENT_PATH)/certs/leaf_certificate.pem: echo "Dummy certificate data for continuous integration" > $@ endif CFLAGS += -DSET_TRUSTED_CERT_IN_SAMPLES ================================================ FILE: examples/prov_dev_client_ll_sample/main/custom_hsm_x509.c ================================================ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include #include #include #include #include #include "hsm_client_data.h" static const char* const COMMON_NAME = CONFIG_DEVICE_COMMON_NAME; extern const uint8_t leaf_cert_pem_start[] asm("_binary_leaf_certificate_pem_start"); extern const uint8_t leaf_pv_key_pem_start[] asm("_binary_leaf_private_key_pem_start"); // This sample is provided for sample only. Please do not use this in production // For more information please see the devdoc using_custom_hsm.md static const char* const CERTIFICATE = (char *)leaf_cert_pem_start; static const char* const PRIVATE_KEY = (char *)leaf_pv_key_pem_start; // Provided for sample only static const char* const SYMMETRIC_KEY = "Symmetric Key value"; static const char* const REGISTRATION_NAME = "Registration Name"; // Provided for sample only, canned values static const unsigned char EK[] = { 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6b, 0x65, 0x79, 0x0d, 0x0a }; static const size_t EK_LEN = sizeof(EK)/sizeof(EK[0]); static const unsigned char SRK[] = { 0x53, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x72, 0x6f, 0x6f, 0x74, 0x20, 0x6b, 0x65, 0x79, 0x0d, 0x0a }; static const size_t SRK_LEN = sizeof(SRK) / sizeof(SRK[0]); typedef struct CUSTOM_HSM_SAMPLE_INFO_TAG { const char* certificate; const char* common_name; const char* key; const unsigned char* endorsment_key; size_t ek_length; const unsigned char* storage_root_key; size_t srk_len; const char* symm_key; const char* registration_name; } CUSTOM_HSM_SAMPLE_INFO; int hsm_client_x509_init() { return 0; } void hsm_client_x509_deinit() { } HSM_CLIENT_HANDLE custom_hsm_create() { HSM_CLIENT_HANDLE result; CUSTOM_HSM_SAMPLE_INFO* hsm_info = malloc(sizeof(CUSTOM_HSM_SAMPLE_INFO)); if (hsm_info == NULL) { (void)printf("Failued allocating hsm info\r\n"); result = NULL; } else { // TODO: initialize any variables here hsm_info->certificate = CERTIFICATE; hsm_info->key = PRIVATE_KEY; hsm_info->common_name = COMMON_NAME; hsm_info->endorsment_key = EK; hsm_info->ek_length = EK_LEN; hsm_info->storage_root_key = SRK; hsm_info->srk_len = SRK_LEN; hsm_info->symm_key = SYMMETRIC_KEY; hsm_info->registration_name = REGISTRATION_NAME; result = hsm_info; } return result; } void custom_hsm_destroy(HSM_CLIENT_HANDLE handle) { if (handle != NULL) { CUSTOM_HSM_SAMPLE_INFO* hsm_info = (CUSTOM_HSM_SAMPLE_INFO*)handle; // Free anything that has been allocated in this module free(hsm_info); } } char* custom_hsm_get_certificate(HSM_CLIENT_HANDLE handle) { char* result; if (handle == NULL) { (void)printf("Invalid handle value specified\r\n"); result = NULL; } else { // TODO: Malloc the certificate for the iothub sdk to free // this value will be sent unmodified to the tlsio // layer to be processed CUSTOM_HSM_SAMPLE_INFO* hsm_info = (CUSTOM_HSM_SAMPLE_INFO*)handle; size_t len = strlen(hsm_info->certificate); if ((result = (char*)malloc(len + 1)) == NULL) { (void)printf("Failure allocating certificate\r\n"); result = NULL; } else { strcpy(result, hsm_info->certificate); } } return result; } char* custom_hsm_get_key(HSM_CLIENT_HANDLE handle) { char* result; if (handle == NULL) { (void)printf("Invalid handle value specified\r\n"); result = NULL; } else { // TODO: Malloc the private key for the iothub sdk to free // this value will be sent unmodified to the tlsio // layer to be processed CUSTOM_HSM_SAMPLE_INFO* hsm_info = (CUSTOM_HSM_SAMPLE_INFO*)handle; size_t len = strlen(hsm_info->key); if ((result = (char*)malloc(len + 1)) == NULL) { (void)printf("Failure allocating certificate\r\n"); result = NULL; } else { strcpy(result, hsm_info->key); } } return result; } char* custom_hsm_get_common_name(HSM_CLIENT_HANDLE handle) { char* result; if (handle == NULL) { (void)printf("Invalid handle value specified\r\n"); result = NULL; } else { // TODO: Malloc the common name for the iothub sdk to free // this value will be sent to dps CUSTOM_HSM_SAMPLE_INFO* hsm_info = (CUSTOM_HSM_SAMPLE_INFO*)handle; size_t len = strlen(hsm_info->common_name); if ((result = (char*)malloc(len + 1)) == NULL) { (void)printf("Failure allocating certificate\r\n"); result = NULL; } else { strcpy(result, hsm_info->common_name); } } return result; } // Defining the v-table for the x509 hsm calls static const HSM_CLIENT_X509_INTERFACE x509_interface = { custom_hsm_create, custom_hsm_destroy, custom_hsm_get_certificate, custom_hsm_get_key, custom_hsm_get_common_name }; const HSM_CLIENT_X509_INTERFACE* hsm_client_x509_interface() { // x509 interface pointer return &x509_interface; } ================================================ FILE: examples/prov_dev_client_ll_sample/main/prov_dev_client_ll_sample.c ================================================ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // CAVEAT: This sample is to demonstrate azure IoT client concepts only and is not a guide design principles or style // Checking of return codes and error values shall be omitted for brevity. Please practice sound engineering practices // when writing production code. #include #include #include "iothub.h" #include "iothub_message.h" #include "iothub_client_version.h" #include "azure_c_shared_utility/threadapi.h" #include "azure_c_shared_utility/tickcounter.h" #include "azure_c_shared_utility/shared_util_options.h" #include "azure_c_shared_utility/http_proxy_io.h" #include "iothub_device_client_ll.h" #include "iothub_client_options.h" #include "azure_prov_client/prov_device_ll_client.h" #include "azure_prov_client/prov_security_factory.h" #include "sdkconfig.h" #ifdef SET_TRUSTED_CERT_IN_SAMPLES #include "certs.h" #endif // SET_TRUSTED_CERT_IN_SAMPLES // // The protocol you wish to use should be uncommented // #define SAMPLE_MQTT //#define SAMPLE_MQTT_OVER_WEBSOCKETS //#define SAMPLE_AMQP //#define SAMPLE_AMQP_OVER_WEBSOCKETS //#define SAMPLE_HTTP #ifdef SAMPLE_MQTT #include "iothubtransportmqtt.h" #include "azure_prov_client/prov_transport_mqtt_client.h" #endif // SAMPLE_MQTT #ifdef SAMPLE_MQTT_OVER_WEBSOCKETS #include "iothubtransportmqtt_websockets.h" #include "azure_prov_client/prov_transport_mqtt_ws_client.h" #endif // SAMPLE_MQTT_OVER_WEBSOCKETS #ifdef SAMPLE_AMQP #include "iothubtransportamqp.h" #include "azure_prov_client/prov_transport_amqp_client.h" #endif // SAMPLE_AMQP #ifdef SAMPLE_AMQP_OVER_WEBSOCKETS #include "iothubtransportamqp_websockets.h" #include "azure_prov_client/prov_transport_amqp_ws_client.h" #endif // SAMPLE_AMQP_OVER_WEBSOCKETS #ifdef SAMPLE_HTTP #include "iothubtransportmqtt.h" #include "azure_prov_client/prov_transport_http_client.h" #endif // SAMPLE_HTTP #ifdef SET_TRUSTED_CERT_IN_SAMPLES #include "certs.h" #endif // SET_TRUSTED_CERT_IN_SAMPLES // This sample is to demostrate iothub reconnection with provisioning and should not // be confused as production code MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID(PROV_DEVICE_RESULT, PROV_DEVICE_RESULT_VALUE); MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID(PROV_DEVICE_REG_STATUS, PROV_DEVICE_REG_STATUS_VALUES); static const char* global_prov_uri = "global.azure-devices-provisioning.net"; static const char* id_scope = CONFIG_DPS_ID_SCOPE; static bool g_use_proxy = false; static const char* PROXY_ADDRESS = "127.0.0.1"; #define PROXY_PORT 8888 #define MESSAGES_TO_SEND 2 #define TIME_BETWEEN_MESSAGES 2 typedef struct CLIENT_SAMPLE_INFO_TAG { unsigned int sleep_time; char* iothub_uri; char* access_key_name; char* device_key; char* device_id; int registration_complete; } CLIENT_SAMPLE_INFO; typedef struct IOTHUB_CLIENT_SAMPLE_INFO_TAG { int connected; int stop_running; } IOTHUB_CLIENT_SAMPLE_INFO; static IOTHUBMESSAGE_DISPOSITION_RESULT receive_msg_callback(IOTHUB_MESSAGE_HANDLE message, void* user_context) { (void)message; IOTHUB_CLIENT_SAMPLE_INFO* iothub_info = (IOTHUB_CLIENT_SAMPLE_INFO*)user_context; (void)printf("Stop message recieved from IoTHub\r\n"); iothub_info->stop_running = 1; return IOTHUBMESSAGE_ACCEPTED; } static void registration_status_callback(PROV_DEVICE_REG_STATUS reg_status, void* user_context) { (void)user_context; (void)printf("Provisioning Status: %s\r\n", MU_ENUM_TO_STRING(PROV_DEVICE_REG_STATUS, reg_status)); } static void iothub_connection_status(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* user_context) { (void)reason; if (user_context == NULL) { printf("iothub_connection_status user_context is NULL\r\n"); } else { IOTHUB_CLIENT_SAMPLE_INFO* iothub_info = (IOTHUB_CLIENT_SAMPLE_INFO*)user_context; if (result == IOTHUB_CLIENT_CONNECTION_AUTHENTICATED) { iothub_info->connected = 1; } else { iothub_info->connected = 0; iothub_info->stop_running = 1; } } } static void register_device_callback(PROV_DEVICE_RESULT register_result, const char* iothub_uri, const char* device_id, void* user_context) { if (user_context == NULL) { printf("user_context is NULL\r\n"); } else { CLIENT_SAMPLE_INFO* user_ctx = (CLIENT_SAMPLE_INFO*)user_context; if (register_result == PROV_DEVICE_RESULT_OK) { (void)printf("Registration Information received from service: %s!\r\n", iothub_uri); (void)mallocAndStrcpy_s(&user_ctx->iothub_uri, iothub_uri); (void)mallocAndStrcpy_s(&user_ctx->device_id, device_id); user_ctx->registration_complete = 1; } else { (void)printf("Failure encountered on registration %s\r\n", MU_ENUM_TO_STRING(PROV_DEVICE_RESULT, register_result) ); user_ctx->registration_complete = 2; } } } const IO_INTERFACE_DESCRIPTION* socketio_get_interface_description(void) { return NULL; } int prov_dev_client_ll_sample_run() { SECURE_DEVICE_TYPE hsm_type; //hsm_type = SECURE_DEVICE_TYPE_TPM; hsm_type = SECURE_DEVICE_TYPE_X509; //hsm_type = SECURE_DEVICE_TYPE_SYMMETRIC_KEY; bool traceOn = false; (void)IoTHub_Init(); (void)prov_dev_security_init(hsm_type); // Set the symmetric key if using they auth type //prov_dev_set_symmetric_key_info("", ""); PROV_DEVICE_TRANSPORT_PROVIDER_FUNCTION prov_transport; HTTP_PROXY_OPTIONS http_proxy; CLIENT_SAMPLE_INFO user_ctx; memset(&http_proxy, 0, sizeof(HTTP_PROXY_OPTIONS)); memset(&user_ctx, 0, sizeof(CLIENT_SAMPLE_INFO)); // Protocol to USE - HTTP, AMQP, AMQP_WS, MQTT, MQTT_WS #ifdef SAMPLE_MQTT prov_transport = Prov_Device_MQTT_Protocol; #endif // SAMPLE_MQTT #ifdef SAMPLE_MQTT_OVER_WEBSOCKETS prov_transport = Prov_Device_MQTT_WS_Protocol; #endif // SAMPLE_MQTT_OVER_WEBSOCKETS #ifdef SAMPLE_AMQP prov_transport = Prov_Device_AMQP_Protocol; #endif // SAMPLE_AMQP #ifdef SAMPLE_AMQP_OVER_WEBSOCKETS prov_transport = Prov_Device_AMQP_WS_Protocol; #endif // SAMPLE_AMQP_OVER_WEBSOCKETS #ifdef SAMPLE_HTTP prov_transport = Prov_Device_HTTP_Protocol; #endif // SAMPLE_HTTP // Set ini user_ctx.registration_complete = 0; user_ctx.sleep_time = 10; printf("Provisioning API Version: %s\r\n", Prov_Device_LL_GetVersionString()); printf("Iothub API Version: %s\r\n", IoTHubClient_GetVersionString()); if (g_use_proxy) { http_proxy.host_address = PROXY_ADDRESS; http_proxy.port = PROXY_PORT; } PROV_DEVICE_LL_HANDLE handle; if ((handle = Prov_Device_LL_Create(global_prov_uri, id_scope, prov_transport)) == NULL) { (void)printf("failed calling Prov_Device_LL_Create\r\n"); } else { if (http_proxy.host_address != NULL) { Prov_Device_LL_SetOption(handle, OPTION_HTTP_PROXY, &http_proxy); } Prov_Device_LL_SetOption(handle, PROV_OPTION_LOG_TRACE, &traceOn); #ifdef SET_TRUSTED_CERT_IN_SAMPLES // Setting the Trusted Certificate. This is only necessary on system with without // built in certificate stores. Prov_Device_LL_SetOption(handle, OPTION_TRUSTED_CERT, certificates); #endif // SET_TRUSTED_CERT_IN_SAMPLES // This option sets the registration ID it overrides the registration ID that is // set within the HSM so be cautious if setting this value //Prov_Device_SetOption(prov_device_handle, PROV_REGISTRATION_ID, "[REGISTRATION ID]"); if (Prov_Device_LL_Register_Device(handle, register_device_callback, &user_ctx, registration_status_callback, &user_ctx) != PROV_DEVICE_RESULT_OK) { (void)printf("failed calling Prov_Device_LL_Register_Device\r\n"); } else { do { Prov_Device_LL_DoWork(handle); ThreadAPI_Sleep(user_ctx.sleep_time); } while (user_ctx.registration_complete == 0); } Prov_Device_LL_Destroy(handle); } if (user_ctx.registration_complete != 1) { (void)printf("registration failed!\r\n"); } else { IOTHUB_CLIENT_TRANSPORT_PROVIDER iothub_transport; // Protocol to USE - HTTP, AMQP, AMQP_WS, MQTT, MQTT_WS #if defined(SAMPLE_MQTT) || defined(SAMPLE_HTTP) // HTTP sample will use mqtt protocol iothub_transport = MQTT_Protocol; #endif // SAMPLE_MQTT #ifdef SAMPLE_MQTT_OVER_WEBSOCKETS iothub_transport = MQTT_WebSocket_Protocol; #endif // SAMPLE_MQTT_OVER_WEBSOCKETS #ifdef SAMPLE_AMQP iothub_transport = AMQP_Protocol; #endif // SAMPLE_AMQP #ifdef SAMPLE_AMQP_OVER_WEBSOCKETS iothub_transport = AMQP_Protocol_over_WebSocketsTls; #endif // SAMPLE_AMQP_OVER_WEBSOCKETS IOTHUB_DEVICE_CLIENT_LL_HANDLE device_ll_handle; (void)printf("Creating IoTHub Device handle\r\n"); if ((device_ll_handle = IoTHubDeviceClient_LL_CreateFromDeviceAuth(user_ctx.iothub_uri, user_ctx.device_id, iothub_transport) ) == NULL) { (void)printf("failed create IoTHub client from connection string %s!\r\n", user_ctx.iothub_uri); } else { IOTHUB_CLIENT_SAMPLE_INFO iothub_info; TICK_COUNTER_HANDLE tick_counter_handle = tickcounter_create(); tickcounter_ms_t current_tick; tickcounter_ms_t last_send_time = 0; size_t msg_count = 0; iothub_info.stop_running = 0; iothub_info.connected = 0; (void)IoTHubDeviceClient_LL_SetConnectionStatusCallback(device_ll_handle, iothub_connection_status, &iothub_info); // Set any option that are neccessary. // For available options please see the iothub_sdk_options.md documentation IoTHubDeviceClient_LL_SetOption(device_ll_handle, OPTION_LOG_TRACE, &traceOn); #ifdef SET_TRUSTED_CERT_IN_SAMPLES // Setting the Trusted Certificate. This is only necessary on system with without // built in certificate stores. IoTHubDeviceClient_LL_SetOption(device_ll_handle, OPTION_TRUSTED_CERT, certificates); #endif // SET_TRUSTED_CERT_IN_SAMPLES (void)IoTHubDeviceClient_LL_SetMessageCallback(device_ll_handle, receive_msg_callback, &iothub_info); (void)printf("Sending 1 messages to IoTHub every %d seconds for %d messages (Send any message to stop)\r\n", TIME_BETWEEN_MESSAGES, MESSAGES_TO_SEND); do { if (iothub_info.connected != 0) { // Send a message every TIME_BETWEEN_MESSAGES seconds (void)tickcounter_get_current_ms(tick_counter_handle, ¤t_tick); if ((current_tick - last_send_time) / 1000 > TIME_BETWEEN_MESSAGES) { static char msgText[1024]; sprintf_s(msgText, sizeof(msgText), "{ \"message_index\" : \"%zu\" }", msg_count++); IOTHUB_MESSAGE_HANDLE msg_handle = IoTHubMessage_CreateFromByteArray((const unsigned char*)msgText, strlen(msgText)); if (msg_handle == NULL) { (void)printf("ERROR: iotHubMessageHandle is NULL!\r\n"); } else { if (IoTHubDeviceClient_LL_SendEventAsync(device_ll_handle, msg_handle, NULL, NULL) != IOTHUB_CLIENT_OK) { (void)printf("ERROR: IoTHubClient_LL_SendEventAsync..........FAILED!\r\n"); } else { (void)tickcounter_get_current_ms(tick_counter_handle, &last_send_time); (void)printf("IoTHubClient_LL_SendEventAsync accepted message [%zu] for transmission to IoT Hub.\r\n", msg_count); } IoTHubMessage_Destroy(msg_handle); } } } IoTHubDeviceClient_LL_DoWork(device_ll_handle); ThreadAPI_Sleep(1); } while (iothub_info.stop_running == 0 && msg_count < MESSAGES_TO_SEND); size_t index = 0; for (index = 0; index < 10; index++) { IoTHubDeviceClient_LL_DoWork(device_ll_handle); ThreadAPI_Sleep(1); } tickcounter_destroy(tick_counter_handle); // Clean up the iothub sdk handle IoTHubDeviceClient_LL_Destroy(device_ll_handle); } } free(user_ctx.iothub_uri); free(user_ctx.device_id); prov_dev_security_deinit(); // Free all the sdk subsystem IoTHub_Deinit(); (void)getchar(); return 0; } ================================================ FILE: examples/prov_dev_client_ll_sample/sdkconfig.defaults ================================================ # newlib for ESP32 and ESP8266 platform CONFIG_NEWLIB_ENABLE=y CONFIG_NEWLIB_LIBRARY_LEVEL_NORMAL=y CONFIG_NEWLIB_NANO_FORMAT= CONFIG_SSL_USING_MBEDTLS=y CONFIG_LWIP_IPV6=y ================================================ FILE: port/CMakeLists.txt ================================================ # # Component Makefile # set (AZURE_IOT_SDK "${CMAKE_CURRENT_LIST_DIR}/../azure-iot-sdk-c") set (COMPONENT_ADD_INCLUDEDIRS "inc" "${AZURE_IOT_SDK}/certs" "${AZURE_IOT_SDK}/c-utility/inc" "${AZURE_IOT_SDK}/c-utility/deps/azure-macro-utils-c/inc" "${AZURE_IOT_SDK}/c-utility/deps/umock-c/inc" "${AZURE_IOT_SDK}/c-utility/pal/inc" "${AZURE_IOT_SDK}/c-utility/pal/freertos" "${AZURE_IOT_SDK}/c-utility/pal/generic" "${AZURE_IOT_SDK}/iothub_client/inc" "${AZURE_IOT_SDK}/serializer/inc" "${AZURE_IOT_SDK}/umqtt/inc" "${AZURE_IOT_SDK}/umqtt/inc/azure_umqtt_c" "${AZURE_IOT_SDK}/deps/parson" "${AZURE_IOT_SDK}/provisioning_client/inc" "${AZURE_IOT_SDK}/provisioning_client/adapters" "${AZURE_IOT_SDK}/provisioning_client/deps/utpm/inc" ) set (COMPONENT_SRCS "src/agenttime_esp.c" "src/platform_esp.c" "src/tlsio_esp_tls.c" "${AZURE_IOT_SDK}/certs/certs.c" "${AZURE_IOT_SDK}/c-utility/pal/freertos/lock.c" "${AZURE_IOT_SDK}/c-utility/pal/socket_async.c" "${AZURE_IOT_SDK}/c-utility/pal/freertos/threadapi.c" "${AZURE_IOT_SDK}/c-utility/pal/freertos/tickcounter.c" "${AZURE_IOT_SDK}/c-utility/pal/tlsio_options.c" "${AZURE_IOT_SDK}/c-utility/src/xlogging.c" "${AZURE_IOT_SDK}/c-utility/src/singlylinkedlist.c" "${AZURE_IOT_SDK}/c-utility/src/buffer.c" "${AZURE_IOT_SDK}/c-utility/src/consolelogger.c" "${AZURE_IOT_SDK}/c-utility/src/constbuffer.c" "${AZURE_IOT_SDK}/c-utility/src/constmap.c" "${AZURE_IOT_SDK}/c-utility/src/crt_abstractions.c" "${AZURE_IOT_SDK}/c-utility/src/doublylinkedlist.c" "${AZURE_IOT_SDK}/c-utility/src/gballoc.c" "${AZURE_IOT_SDK}/c-utility/src/gb_stdio.c" "${AZURE_IOT_SDK}/c-utility/src/gb_time.c" "${AZURE_IOT_SDK}/c-utility/src/hmac.c" "${AZURE_IOT_SDK}/c-utility/src/hmacsha256.c" "${AZURE_IOT_SDK}/c-utility/src/httpapiex.c" "${AZURE_IOT_SDK}/c-utility/src/httpapiexsas.c" "${AZURE_IOT_SDK}/c-utility/src/httpheaders.c" "${AZURE_IOT_SDK}/c-utility/src/map.c" "${AZURE_IOT_SDK}/c-utility/src/optionhandler.c" "${AZURE_IOT_SDK}/c-utility/src/sastoken.c" "${AZURE_IOT_SDK}/c-utility/src/sha1.c" "${AZURE_IOT_SDK}/c-utility/src/sha224.c" "${AZURE_IOT_SDK}/c-utility/src/sha384-512.c" "${AZURE_IOT_SDK}/c-utility/src/strings.c" "${AZURE_IOT_SDK}/c-utility/src/string_tokenizer.c" "${AZURE_IOT_SDK}/c-utility/src/urlencode.c" "${AZURE_IOT_SDK}/c-utility/src/usha.c" "${AZURE_IOT_SDK}/c-utility/src/vector.c" "${AZURE_IOT_SDK}/c-utility/src/xio.c" "${AZURE_IOT_SDK}/c-utility/src/azure_base64.c" "${AZURE_IOT_SDK}/iothub_client/src/iothub_device_client_ll.c" "${AZURE_IOT_SDK}/iothub_client/src/iothub_client_ll.c" "${AZURE_IOT_SDK}/iothub_client/src/iothub_client_core_ll.c" "${AZURE_IOT_SDK}/iothub_client/src/iothub_client_ll_uploadtoblob.c" "${AZURE_IOT_SDK}/iothub_client/src/iothub_client_authorization.c" "${AZURE_IOT_SDK}/iothub_client/src/iothub_client_retry_control.c" "${AZURE_IOT_SDK}/iothub_client/src/iothub_client_diagnostic.c" "${AZURE_IOT_SDK}/iothub_client/src/iothub_message.c" "${AZURE_IOT_SDK}/iothub_client/src/iothubtransport.c" "${AZURE_IOT_SDK}/iothub_client/src/iothubtransportmqtt.c" "${AZURE_IOT_SDK}/iothub_client/src/iothubtransport_mqtt_common.c" "${AZURE_IOT_SDK}/iothub_client/src/iothub_transport_ll_private.c" "${AZURE_IOT_SDK}/iothub_client/src/version.c" "${AZURE_IOT_SDK}/umqtt/src/mqtt_client.c" "${AZURE_IOT_SDK}/umqtt/src/mqtt_codec.c" "${AZURE_IOT_SDK}/umqtt/src/mqtt_message.c" "${AZURE_IOT_SDK}/deps/parson/parson.c" "${AZURE_IOT_SDK}/serializer/src/codefirst.c" "${AZURE_IOT_SDK}/serializer/src/agenttypesystem.c" "${AZURE_IOT_SDK}/serializer/src/commanddecoder.c" "${AZURE_IOT_SDK}/serializer/src/datamarshaller.c" "${AZURE_IOT_SDK}/serializer/src/datapublisher.c" "${AZURE_IOT_SDK}/serializer/src/dataserializer.c" "${AZURE_IOT_SDK}/serializer/src/iotdevice.c" "${AZURE_IOT_SDK}/serializer/src/jsondecoder.c" "${AZURE_IOT_SDK}/serializer/src/jsonencoder.c" "${AZURE_IOT_SDK}/serializer/src/methodreturn.c" "${AZURE_IOT_SDK}/serializer/src/multitree.c" "${AZURE_IOT_SDK}/serializer/src/schema.c" "${AZURE_IOT_SDK}/serializer/src/schemalib.c" "${AZURE_IOT_SDK}/serializer/src/schemaserializer.c" "${AZURE_IOT_SDK}/provisioning_client/src/prov_device_client.c" "${AZURE_IOT_SDK}/provisioning_client/src/prov_transport_mqtt_client.c" "${AZURE_IOT_SDK}/provisioning_client/src/prov_transport_mqtt_common.c" "${AZURE_IOT_SDK}/provisioning_client/src/prov_security_factory.c" "${AZURE_IOT_SDK}/provisioning_client/src/prov_device_ll_client.c" "${AZURE_IOT_SDK}/provisioning_client/src/iothub_security_factory.c" "${AZURE_IOT_SDK}/provisioning_client/adapters/hsm_client_data.c" "${AZURE_IOT_SDK}/provisioning_client/adapters/hsm_client_tpm.c" "${AZURE_IOT_SDK}/provisioning_client/src/prov_auth_client.c" "${AZURE_IOT_SDK}/provisioning_client/deps/utpm/src/tpm_codec.c" "${AZURE_IOT_SDK}/provisioning_client/deps/utpm/src/Marshal.c" "${AZURE_IOT_SDK}/provisioning_client/deps/utpm/src/tpm_comm_emulator.c" "${AZURE_IOT_SDK}/provisioning_client/deps/utpm/src/Memory.c" "${AZURE_IOT_SDK}/provisioning_client/deps/utpm/src/tpm_socket_comm.c" "${AZURE_IOT_SDK}/iothub_client/src/iothub.c" "${AZURE_IOT_SDK}/c-utility/src/http_proxy_io.c" "${AZURE_IOT_SDK}/c-utility/src/azure_base32.c" ) if (CONFIG_DEVICE_COMMON_NAME) list (APPEND COMPONENT_SRCS "${AZURE_IOT_SDK}/provisioning_client/src/iothub_auth_client.c") endif() set (COMPONENT_SUBMODULES "${AZURE_IOT_SDK}") set(COMPONENT_PRIV_REQUIRES "mbedtls esp-tls main") register_component() set_source_files_properties( ${AZURE_IOT_SDK}/iothub_client/src/iothubtransport_mqtt_common.c PROPERTIES COMPILE_FLAGS -Wno-maybe-uninitialized) component_compile_options ( -Wno-unused-function -Wno-missing-braces -Wno-missing-field-initializers -Wno-unknown-pragmas ) component_compile_definitions ( USE_LWIP_SOCKET_FOR_AZURE_IOT HSM_TYPE_X509 HSM_TYPE_SAS_TOKEN ) if (CONFIG_DEVICE_COMMON_NAME) add_definitions(-DUSE_PROV_MODULE) endif() ================================================ FILE: port/inc/certs.h ================================================ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #ifndef CERTS_H #define CERTS_H #ifdef __cplusplus extern "C" { #endif extern const char certificates[]; #ifdef __cplusplus } #endif #endif /* CERTS_H */ ================================================ FILE: port/inc/sntp_os.h ================================================ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. //This file pulls in OS-specific header files to allow compilation of socket_async.c under // most OS's except for Windows. // For ESP platform lwIP systems which use the ESP-IDF's non-standard lwIP include structure // Tested with: // ESP platform #ifndef LWIP_SNTP_OS_H #define LWIP_SNTP_OS_H #include "apps/sntp/sntp.h" #endif // LWIP_SNTP_OS_H ================================================ FILE: port/inc/socket_async_os.h ================================================ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. //This file pulls in OS-specific header files to allow compilation of socket_async.c under // most OS's except for Windows. // For lwIP systems // Tested with: // ESP platform #ifndef SOCKET_ASYNC_OS_H #define SOCKET_ASYNC_OS_H #include "sdkconfig.h" #include #include #endif // SOCKET_ASYNC_OS_H ================================================ FILE: port/inc/tlsio_pal.h ================================================ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #ifndef TLSIO_PAL_H #define TLSIO_PAL_H #include "azure_c_shared_utility/tlsio.h" #include "umock_c/umock_c_prod.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ MOCKABLE_FUNCTION(, const IO_INTERFACE_DESCRIPTION*, tlsio_pal_get_interface_description); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* TLSIO_PAL_H */ ================================================ FILE: port/src/agenttime_esp.c ================================================ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "lwip/apps/sntp.h" #include "azure_c_shared_utility/agenttime.h" #include "azure_c_shared_utility/xlogging.h" void initialize_sntp(void) { printf("Initializing SNTP\n"); sntp_setoperatingmode(SNTP_OPMODE_POLL); sntp_setservername(0, "pool.ntp.org"); sntp_init(); } static void obtain_time(void) { // wait for time to be set time_t now = 0; struct tm timeinfo = { 0 }; int retry = 0; while(timeinfo.tm_year < (2016 - 1900) ) { printf("Waiting for system time to be set... tm_year:%d[times:%d]\n", timeinfo.tm_year, ++retry); vTaskDelay(2000 / portTICK_PERIOD_MS); time(&now); localtime_r(&now, &timeinfo); } } time_t sntp_get_current_timestamp() { time_t now; struct tm timeinfo; time(&now); localtime_r(&now, &timeinfo); // Is time set? If not, tm_year will be (1970 - 1900). if (timeinfo.tm_year < (2016 - 1900)) { printf("Time is not set yet. Connecting to WiFi and getting time over NTP. timeinfo.tm_year:%d\n",timeinfo.tm_year); obtain_time(); // update 'now' variable with current time time(&now); } localtime_r(&now, &timeinfo); return now; } time_t get_time(time_t* currentTime) { return sntp_get_current_timestamp(); } double get_difftime(time_t stopTime, time_t startTime) { return (double)stopTime - (double)startTime; } struct tm* get_gmtime(time_t* currentTime) { return NULL; } char* get_ctime(time_t* timeToGet) { return NULL; } ================================================ FILE: port/src/certs.c ================================================ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. /* This file contains minimalistic cert needed to communicate with Azure (IoT) */ /* The cert from this file is used in case of azure connection on esp8266 */ #include "certs.h" const char certificates[] = /* DigiCert Baltimore Root */ "-----BEGIN CERTIFICATE-----\r\n" "MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ\r\n" "RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD\r\n" "VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX\r\n" "DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y\r\n" "ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy\r\n" "VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr\r\n" "mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr\r\n" "IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK\r\n" "mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu\r\n" "XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy\r\n" "dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye\r\n" "jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1\r\n" "BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3\r\n" "DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92\r\n" "9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx\r\n" "jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0\r\n" "Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz\r\n" "ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS\r\n" "R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp\r\n" "-----END CERTIFICATE-----\r\n"; ================================================ FILE: port/src/platform_esp.c ================================================ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include #ifdef _CRTDBG_MAP_ALLOC #include #endif #include "azure_c_shared_utility/platform.h" #include "azure_c_shared_utility/xio.h" #include "azure_c_shared_utility/tlsio_openssl.h" #include "azure_c_shared_utility/xlogging.h" #include "esp_log.h" #include "tlsio_pal.h" #include "lwip/apps/sntp.h" static const char* TAG = "platform"; time_t sntp_get_current_timestamp(); void initialize_sntp(void); int platform_init(void) { initialize_sntp(); printf("ESP platform sntp inited!\n"); time_t now = sntp_get_current_timestamp(); char strftime_buf[64]; struct tm timeinfo; localtime_r(&now, &timeinfo); strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); ESP_LOGI(TAG, "The current date/time is: %s", strftime_buf); return 0; } const IO_INTERFACE_DESCRIPTION* platform_get_default_tlsio(void) { return tlsio_pal_get_interface_description(); return NULL; } void platform_deinit(void) { sntp_stop(); } STRING_HANDLE platform_get_platform_info(PLATFORM_INFO_OPTION options) { // Expected format: "(; ; )" return STRING_construct("(native; freertos; esp platform)"); } ================================================ FILE: port/src/tlsio_esp_tls.c ================================================ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // This component was written to conform to the tlsio_requirements.md specification located // in the Azure IoT C Utility: // https://github.com/Azure/azure-c-shared-utility/blob/master/devdoc/tlsio_requirements.md // Comments throughout this code refer to requirements in that spec. #include #include #include #include #include "tlsio_pal.h" #include "azure_c_shared_utility/optimize_size.h" #include "azure_c_shared_utility/gballoc.h" #include "azure_c_shared_utility/xlogging.h" #include "azure_c_shared_utility/agenttime.h" #include "azure_c_shared_utility/singlylinkedlist.h" #include "azure_c_shared_utility/crt_abstractions.h" #include "azure_c_shared_utility/tlsio_options.h" #include "esp_tls.h" typedef struct { unsigned char* bytes; size_t size; size_t unsent_size; ON_SEND_COMPLETE on_send_complete; void* callback_context; } PENDING_TRANSMISSION; #define MAX_VALID_PORT 0xffff // The TLSIO_RECEIVE_BUFFER_SIZE has very little effect on performance, and is kept small // to minimize memory consumption. #define TLSIO_RECEIVE_BUFFER_SIZE 64 #define MAX_RCV_COUNT 5 typedef enum TLSIO_STATE_TAG { TLSIO_STATE_CLOSED, TLSIO_STATE_INIT, TLSIO_STATE_OPEN, TLSIO_STATE_ERROR, } TLSIO_STATE; bool is_an_opening_state(TLSIO_STATE state) { return state == TLSIO_STATE_INIT; } // This structure definition is mirrored in the unit tests, so if you change typedef struct TLS_IO_INSTANCE_TAG { ON_BYTES_RECEIVED on_bytes_received; ON_IO_ERROR on_io_error; ON_IO_OPEN_COMPLETE on_open_complete; void* on_bytes_received_context; void* on_io_error_context; void* on_open_complete_context; esp_tls_cfg_t esp_tls_cfg; esp_tls_t *esp_tls_handle; TLSIO_STATE tlsio_state; uint16_t port; char* hostname; SINGLYLINKEDLIST_HANDLE pending_transmission_list; TLSIO_OPTIONS options; } TLS_IO_INSTANCE; /* Codes_SRS_TLSIO_30_005: [ The phrase "enter TLSIO_STATE_EXT_ERROR" means the adapter shall call the on_io_error function and pass the on_io_error_context that was supplied in tlsio_open_async. ]*/ static void enter_tlsio_error_state(TLS_IO_INSTANCE* tls_io_instance) { if (tls_io_instance->tlsio_state != TLSIO_STATE_ERROR) { tls_io_instance->tlsio_state = TLSIO_STATE_ERROR; tls_io_instance->on_io_error(tls_io_instance->on_io_error_context); } } // Return true if a message was available to remove static bool process_and_destroy_head_message(TLS_IO_INSTANCE* tls_io_instance, IO_SEND_RESULT send_result) { bool result; LIST_ITEM_HANDLE head_pending_io; if (send_result == IO_SEND_ERROR) { /* Codes_SRS_TLSIO_30_095: [ If the send process fails before sending all of the bytes in an enqueued message, the tlsio_dowork shall call the message's on_send_complete along with its associated callback_context and IO_SEND_ERROR. ]*/ enter_tlsio_error_state(tls_io_instance); } head_pending_io = singlylinkedlist_get_head_item(tls_io_instance->pending_transmission_list); if (head_pending_io != NULL) { PENDING_TRANSMISSION* head_message = (PENDING_TRANSMISSION*)singlylinkedlist_item_get_value(head_pending_io); // Must remove the item from the list before calling the callback because // SRS_TLSIO_30_091: [ If tlsio_esp_tls_dowork is able to send all the bytes in an enqueued message, it shall first dequeue the message then call the messages's on_send_complete along with its associated callback_context and IO_SEND_OK . ] if (singlylinkedlist_remove(tls_io_instance->pending_transmission_list, head_pending_io) != 0) { // This particular situation is a bizarre and unrecoverable internal error /* Codes_SRS_TLSIO_30_094: [ If the send process encounters an internal error or calls on_send_complete with IO_SEND_ERROR due to either failure or timeout, it shall also call on_io_error and pass in the associated on_io_error_context. ]*/ enter_tlsio_error_state(tls_io_instance); LogError("Failed to remove message from list"); } // on_send_complete is checked for NULL during PENDING_TRANSMISSION creation /* Codes_SRS_TLSIO_30_095: [ If the send process fails before sending all of the bytes in an enqueued message, the tlsio_dowork shall call the message's on_send_complete along with its associated callback_context and IO_SEND_ERROR. ]*/ head_message->on_send_complete(head_message->callback_context, send_result); free(head_message->bytes); free(head_message); result = true; } else { result = false; } return result; } static void internal_close(TLS_IO_INSTANCE* tls_io_instance) { /* Codes_SRS_TLSIO_30_009: [ The phrase "enter TLSIO_STATE_EXT_CLOSING" means the adapter shall iterate through any unsent messages in the queue and shall delete each message after calling its on_send_complete with the associated callback_context and IO_SEND_CANCELLED. ]*/ /* Codes_SRS_TLSIO_30_006: [ The phrase "enter TLSIO_STATE_EXT_CLOSED" means the adapter shall forcibly close any existing connections then call the on_io_close_complete function and pass the on_io_close_complete_context that was supplied in tlsio_close_async. ]*/ /* Codes_SRS_TLSIO_30_051: [ On success, if the underlying TLS does not support asynchronous closing, then the adapter shall enter TLSIO_STATE_EXT_CLOSED immediately after entering TLSIO_STATE_EX_CLOSING. ]*/ esp_tls_conn_delete(tls_io_instance->esp_tls_handle); while (process_and_destroy_head_message(tls_io_instance, IO_SEND_CANCELLED)); // singlylinkedlist_destroy gets called in the main destroy tls_io_instance->on_bytes_received = NULL; tls_io_instance->on_io_error = NULL; tls_io_instance->on_bytes_received_context = NULL; tls_io_instance->on_io_error_context = NULL; tls_io_instance->tlsio_state = TLSIO_STATE_CLOSED; tls_io_instance->on_open_complete = NULL; tls_io_instance->on_open_complete_context = NULL; } static void tlsio_esp_tls_destroy(CONCRETE_IO_HANDLE tls_io) { if (tls_io == NULL) { /* Codes_SRS_TLSIO_30_020: [ If tlsio_handle is NULL, tlsio_destroy shall do nothing. ]*/ LogError("NULL tlsio"); } else { TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; if (tls_io_instance->tlsio_state != TLSIO_STATE_CLOSED) { /* Codes_SRS_TLSIO_30_022: [ If the adapter is in any state other than TLSIO_STATE_EX_CLOSED when tlsio_destroy is called, the adapter shall enter TLSIO_STATE_EX_CLOSING and then enter TLSIO_STATE_EX_CLOSED before completing the destroy process. ]*/ LogError("tlsio_esp_tls_destroy called while not in TLSIO_STATE_CLOSED."); internal_close(tls_io_instance); } /* Codes_SRS_TLSIO_30_021: [ The tlsio_destroy shall release all allocated resources and then release tlsio_handle. ]*/ if (tls_io_instance->hostname != NULL) { free(tls_io_instance->hostname); } tlsio_options_release_resources(&tls_io_instance->options); if (tls_io_instance->pending_transmission_list != NULL) { /* Pending messages were cleared in internal_close */ singlylinkedlist_destroy(tls_io_instance->pending_transmission_list); } free(tls_io_instance); } } /* Codes_SRS_TLSIO_30_010: [ The tlsio_esp_tls_create shall allocate and initialize all necessary resources and return an instance of the tlsio_esp_tls. ]*/ static CONCRETE_IO_HANDLE tlsio_esp_tls_create(void* io_create_parameters) { TLS_IO_INSTANCE* result; if (io_create_parameters == NULL) { /* Codes_SRS_TLSIO_30_013: [ If the io_create_parameters value is NULL, tlsio_create shall log an error and return NULL. ]*/ LogError("NULL tls_io_config"); result = NULL; } else { /* Codes_SRS_TLSIO_30_012: [ The tlsio_create shall receive the connection configuration as a TLSIO_CONFIG* in io_create_parameters. ]*/ TLSIO_CONFIG* tls_io_config = (TLSIO_CONFIG*)io_create_parameters; if (tls_io_config->hostname == NULL) { /* Codes_SRS_TLSIO_30_014: [ If the hostname member of io_create_parameters value is NULL, tlsio_create shall log an error and return NULL. ]*/ LogError("NULL tls_io_config->hostname"); result = NULL; } else if (tls_io_config->port < 0 || tls_io_config->port > MAX_VALID_PORT) { /* Codes_SRS_TLSIO_30_015: [ If the port member of io_create_parameters value is less than 0 or greater than 0xffff, tlsio_esp_tls_create shall log an error and return NULL. ]*/ LogError("tls_io_config->port out of range"); result = NULL; } else { result = malloc(sizeof(TLS_IO_INSTANCE)); if (result == NULL) { /* Codes_SRS_TLSIO_30_011: [ If any resource allocation fails, tlsio_esp_tls_create shall return NULL. ]*/ LogError("malloc failed"); } else { int ms_result; memset(result, 0, sizeof(TLS_IO_INSTANCE)); result->port = (uint16_t)tls_io_config->port; result->tlsio_state = TLSIO_STATE_CLOSED; result->hostname = NULL; result->pending_transmission_list = NULL; tlsio_options_initialize(&result->options, TLSIO_OPTION_BIT_TRUSTED_CERTS | TLSIO_OPTION_BIT_x509_RSA_CERT | TLSIO_OPTION_BIT_x509_ECC_CERT); result->esp_tls_handle = calloc(1, sizeof(esp_tls_t)); if (result->esp_tls_handle == NULL) { /* Codes_SRS_TLSIO_30_011: [ If any resource allocation fails, tlsio_create shall return NULL. ]*/ LogError("malloc failed"); tlsio_esp_tls_destroy(result); result = NULL; } /* Codes_SRS_TLSIO_30_016: [ tlsio_create shall make a copy of the hostname member of io_create_parameters to allow deletion of hostname immediately after the call. ]*/ ms_result = mallocAndStrcpy_s(&result->hostname, tls_io_config->hostname); if (ms_result != 0) { /* Codes_SRS_TLSIO_30_011: [ If any resource allocation fails, tlsio_create shall return NULL. ]*/ LogError("malloc failed"); tlsio_esp_tls_destroy(result); result = NULL; } else { // Create the message queue result->pending_transmission_list = singlylinkedlist_create(); if (result->pending_transmission_list == NULL) { /* Codes_SRS_TLSIO_30_011: [ If any resource allocation fails, tlsio_create shall return NULL. ]*/ LogError("Failed singlylinkedlist_create"); tlsio_esp_tls_destroy(result); result = NULL; } } } } } return (CONCRETE_IO_HANDLE)result; } static int tlsio_esp_tls_open_async(CONCRETE_IO_HANDLE tls_io, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context) { int result; if (on_io_open_complete == NULL) { /* Codes_SRS_TLSIO_30_031: [ If the on_io_open_complete parameter is NULL, tlsio_open shall log an error and return FAILURE. ]*/ LogError("Required parameter on_io_open_complete is NULL"); result = MU_FAILURE; } else { if (tls_io == NULL) { /* Codes_SRS_TLSIO_30_030: [ If the tlsio_handle parameter is NULL, tlsio_open shall log an error and return FAILURE. ]*/ result = MU_FAILURE; LogError("NULL tlsio"); } else { if (on_bytes_received == NULL) { /* Codes_SRS_TLSIO_30_032: [ If the on_bytes_received parameter is NULL, tlsio_open shall log an error and return FAILURE. ]*/ LogError("Required parameter on_bytes_received is NULL"); result = MU_FAILURE; } else { if (on_io_error == NULL) { /* Codes_SRS_TLSIO_30_033: [ If the on_io_error parameter is NULL, tlsio_open shall log an error and return FAILURE. ]*/ LogError("Required parameter on_io_error is NULL"); result = MU_FAILURE; } else { TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; if (tls_io_instance->tlsio_state != TLSIO_STATE_CLOSED) { /* Codes_SRS_TLSIO_30_037: [ If the adapter is in any state other than TLSIO_STATE_EXT_CLOSED when tlsio_open is called, it shall log an error, and return FAILURE. ]*/ LogError("Invalid tlsio_state. Expected state is TLSIO_STATE_CLOSED."); result = MU_FAILURE; } else { /* Codes_SRS_TLSIO_30_034: [ The tlsio_open shall store the provided on_bytes_received, on_bytes_received_context, on_io_error, on_io_error_context, on_io_open_complete, and on_io_open_complete_context parameters for later use as specified and tested per other line entries in this document. ]*/ tls_io_instance->on_bytes_received = on_bytes_received; tls_io_instance->on_bytes_received_context = on_bytes_received_context; tls_io_instance->on_io_error = on_io_error; tls_io_instance->on_io_error_context = on_io_error_context; tls_io_instance->on_open_complete = on_io_open_complete; tls_io_instance->on_open_complete_context = on_io_open_complete_context; tls_io_instance->esp_tls_cfg.non_block = true; if (tls_io_instance->options.x509_key != NULL && tls_io_instance->options.x509_cert != NULL) { tls_io_instance->esp_tls_cfg.clientcert_pem_buf = (unsigned char *)tls_io_instance->options.x509_cert; tls_io_instance->esp_tls_cfg.clientcert_pem_bytes = strlen(tls_io_instance->options.x509_cert) + 1; tls_io_instance->esp_tls_cfg.clientkey_pem_buf = (unsigned char *)tls_io_instance->options.x509_key; tls_io_instance->esp_tls_cfg.clientkey_pem_bytes = strlen(tls_io_instance->options.x509_key) + 1; } if (tls_io_instance->options.trusted_certs != NULL) { tls_io_instance->esp_tls_cfg.cacert_pem_buf = (unsigned char *)tls_io_instance->options.trusted_certs; tls_io_instance->esp_tls_cfg.cacert_pem_bytes = strlen(tls_io_instance->options.trusted_certs) + 1; } tls_io_instance->tlsio_state = TLSIO_STATE_INIT; result = 0; } } } } /* Codes_SRS_TLSIO_30_039: [ On failure, tlsio_open_async shall not call on_io_open_complete. ]*/ } return result; } // This implementation does not have asynchronous close, but uses the _async name for consistency with the spec static int tlsio_esp_tls_close_async(CONCRETE_IO_HANDLE tls_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context) { int result; if (tls_io == NULL) { /* Codes_SRS_TLSIO_30_050: [ If the tlsio_handle parameter is NULL, tlsio_esp_tls_close_async shall log an error and return FAILURE. ]*/ LogError("NULL tlsio"); result = MU_FAILURE; } else { if (on_io_close_complete == NULL) { /* Codes_SRS_TLSIO_30_055: [ If the on_io_close_complete parameter is NULL, tlsio_esp_tls_close_async shall log an error and return FAILURE. ]*/ LogError("NULL on_io_close_complete"); result = MU_FAILURE; } else { TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; if (tls_io_instance->tlsio_state != TLSIO_STATE_OPEN && tls_io_instance->tlsio_state != TLSIO_STATE_ERROR) { /* Codes_SRS_TLSIO_30_053: [ If the adapter is in any state other than TLSIO_STATE_EXT_OPEN or TLSIO_STATE_EXT_ERROR then tlsio_close_async shall log that tlsio_close_async has been called and then continue normally. ]*/ // LogInfo rather than LogError because this is an unusual but not erroneous situation LogInfo("tlsio_esp_tls_close has been called when in neither TLSIO_STATE_OPEN nor TLSIO_STATE_ERROR."); } if (is_an_opening_state(tls_io_instance->tlsio_state)) { /* Codes_SRS_TLSIO_30_057: [ On success, if the adapter is in TLSIO_STATE_EXT_OPENING, it shall call on_io_open_complete with the on_io_open_complete_context supplied in tlsio_open_async and IO_OPEN_CANCELLED. This callback shall be made before changing the internal state of the adapter. ]*/ tls_io_instance->on_open_complete(tls_io_instance->on_open_complete_context, IO_OPEN_CANCELLED); } // This adapter does not support asynchronous closing /* Codes_SRS_TLSIO_30_056: [ On success the adapter shall enter TLSIO_STATE_EX_CLOSING. ]*/ /* Codes_SRS_TLSIO_30_051: [ On success, if the underlying TLS does not support asynchronous closing, then the adapter shall enter TLSIO_STATE_EX_CLOSED immediately after entering TLSIO_STATE_EX_CLOSING. ]*/ /* Codes_SRS_TLSIO_30_052: [ On success tlsio_close shall return 0. ]*/ internal_close(tls_io_instance); on_io_close_complete(callback_context); result = 0; } } /* Codes_SRS_TLSIO_30_054: [ On failure, the adapter shall not call on_io_close_complete. ]*/ return result; } static int dowork_read(TLS_IO_INSTANCE* tls_io_instance) { // TRANSFER_BUFFER_SIZE is not very important because if the message is bigger // then the framework just calls dowork repeatedly until it gets everything. So // a bigger buffer would just use memory without buying anything. // Putting this buffer in a small function also allows it to exist on the stack // rather than adding to heap fragmentation. unsigned char buffer[TLSIO_RECEIVE_BUFFER_SIZE]; int rcv_bytes = 0; int rcv_count = 0; if (tls_io_instance->tlsio_state == TLSIO_STATE_OPEN) { rcv_bytes = esp_tls_conn_read(tls_io_instance->esp_tls_handle, buffer, sizeof(buffer)); while (rcv_bytes > 0) { // tls_io_instance->on_bytes_received was already checked for NULL // in the call to tlsio_esp_tls_open_async /* Codes_SRS_TLSIO_30_100: [ As long as the TLS connection is able to provide received data, tlsio_dowork shall repeatedly read this data and call on_bytes_received with the pointer to the buffer containing the data, the number of bytes received, and the on_bytes_received_context. ]*/ tls_io_instance->on_bytes_received(tls_io_instance->on_bytes_received_context, buffer, rcv_bytes); if (++rcv_count > MAX_RCV_COUNT) { // Read no more than "MAX_RCV_COUNT" times to avoid starvation of other processes. // LogInfo("Skipping further reading to avoid starvation."); break; } rcv_bytes = esp_tls_conn_read(tls_io_instance->esp_tls_handle, buffer, sizeof(buffer)); } /* Codes_SRS_TLSIO_30_102: [ If the TLS connection receives no data then tlsio_dowork shall not call the on_bytes_received callback. ]*/ } return rcv_bytes; } static int dowork_send(TLS_IO_INSTANCE* tls_io_instance) { LIST_ITEM_HANDLE first_pending_io = singlylinkedlist_get_head_item(tls_io_instance->pending_transmission_list); int write_result = 0; if (first_pending_io != NULL) { PENDING_TRANSMISSION* pending_message = (PENDING_TRANSMISSION*)singlylinkedlist_item_get_value(first_pending_io); uint8_t* buffer = ((uint8_t*)pending_message->bytes) + pending_message->size - pending_message->unsent_size; write_result = esp_tls_conn_write(tls_io_instance->esp_tls_handle, buffer, pending_message->unsent_size); if (write_result > 0) { pending_message->unsent_size -= write_result; if (pending_message->unsent_size == 0) { /* Codes_SRS_TLSIO_30_091: [ If tlsio_esp_tls_dowork is able to send all the bytes in an enqueued message, it shall call the messages's on_send_complete along with its associated callback_context and IO_SEND_OK. ]*/ // The whole message has been sent successfully process_and_destroy_head_message(tls_io_instance, IO_SEND_OK); } else { /* Codes_SRS_TLSIO_30_093: [ If the TLS connection was not able to send an entire enqueued message at once, subsequent calls to tlsio_dowork shall continue to send the remaining bytes. ]*/ // Repeat the send on the next pass with the rest of the message // This empty else compiles to nothing but helps readability } } else { LogInfo("Error from SSL_write: %d", write_result); } } else { /* Codes_SRS_TLSIO_30_096: [ If there are no enqueued messages available, tlsio_esp_tls_dowork shall do nothing. ]*/ } return write_result; } static void tlsio_esp_tls_dowork(CONCRETE_IO_HANDLE tls_io) { if (tls_io == NULL) { /* Codes_SRS_TLSIO_30_070: [ If the tlsio_handle parameter is NULL, tlsio_dowork shall do nothing except log an error. ]*/ LogError("NULL tlsio"); } else { TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; // This switch statement handles all of the state transitions during the opening process switch (tls_io_instance->tlsio_state) { case TLSIO_STATE_CLOSED: /* Codes_SRS_TLSIO_30_075: [ If the adapter is in TLSIO_STATE_EXT_CLOSED then tlsio_dowork shall do nothing. ]*/ // Waiting to be opened, nothing to do break; case TLSIO_STATE_INIT: { int result = esp_tls_conn_new_async(tls_io_instance->hostname, strlen(tls_io_instance->hostname), tls_io_instance->port, &tls_io_instance->esp_tls_cfg, tls_io_instance->esp_tls_handle); if (result == 1) { tls_io_instance->tlsio_state = TLSIO_STATE_OPEN; tls_io_instance->on_open_complete(tls_io_instance->on_open_complete_context, IO_OPEN_OK); } else if (result == -1) { tls_io_instance->tlsio_state = TLSIO_STATE_ERROR; } } break; case TLSIO_STATE_OPEN: if (dowork_read(tls_io_instance) < 0 && errno != EAGAIN) { tls_io_instance->tlsio_state = TLSIO_STATE_ERROR; } if (dowork_send(tls_io_instance) < 0 && errno != EAGAIN) { tls_io_instance->tlsio_state = TLSIO_STATE_ERROR; } break; case TLSIO_STATE_ERROR: /* Codes_SRS_TLSIO_30_071: [ If the adapter is in TLSIO_STATE_EXT_ERROR then tlsio_dowork shall do nothing. ]*/ // There's nothing valid to do here but wait to be retried break; default: LogError("Unexpected internal tlsio state"); break; } } } static int tlsio_esp_tls_send_async(CONCRETE_IO_HANDLE tls_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context) { int result; if (on_send_complete == NULL) { /* Codes_SRS_TLSIO_30_062: [ If the on_send_complete is NULL, tlsio_esp_tls_send_async shall log the error and return FAILURE. ]*/ result = MU_FAILURE; LogError("NULL on_send_complete"); } else { if (tls_io == NULL) { /* Codes_SRS_TLSIO_30_060: [ If the tlsio_handle parameter is NULL, tlsio_esp_tls_send_async shall log an error and return FAILURE. ]*/ result = MU_FAILURE; LogError("NULL tlsio"); } else { if (buffer == NULL) { /* Codes_SRS_TLSIO_30_061: [ If the buffer is NULL, tlsio_esp_tls_send_async shall log the error and return FAILURE. ]*/ result = MU_FAILURE; LogError("NULL buffer"); } else { if (size == 0) { /* Codes_SRS_TLSIO_30_067: [ If the size is 0, tlsio_send shall log the error and return FAILURE. ]*/ result = MU_FAILURE; LogError("0 size"); } else { TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; if (tls_io_instance->tlsio_state != TLSIO_STATE_OPEN) { result = MU_FAILURE; LogError("tlsio_esp_tls_send_async without a prior successful open"); } else { PENDING_TRANSMISSION* pending_transmission = (PENDING_TRANSMISSION*)malloc(sizeof(PENDING_TRANSMISSION)); if (pending_transmission == NULL) { /* Codes_SRS_TLSIO_30_064: [ If the supplied message cannot be enqueued for transmission, tlsio_esp_tls_send shall log an error and return FAILURE. ]*/ result = MU_FAILURE; LogError("malloc failed"); } else { /* Codes_SRS_TLSIO_30_063: [ The tlsio_esp_tls_send_async shall enqueue for transmission the on_send_complete, the callback_context, the size, and the contents of buffer. ]*/ pending_transmission->bytes = (unsigned char*)malloc(size); if (pending_transmission->bytes == NULL) { /* Codes_SRS_TLSIO_30_064: [ If the supplied message cannot be enqueued for transmission, tlsio_esp_tls_send shall log an error and return FAILURE. ]*/ LogError("malloc failed"); free(pending_transmission); result = MU_FAILURE; } else { pending_transmission->size = size; pending_transmission->unsent_size = size; pending_transmission->on_send_complete = on_send_complete; pending_transmission->callback_context = callback_context; (void)memcpy(pending_transmission->bytes, buffer, size); if (singlylinkedlist_add(tls_io_instance->pending_transmission_list, pending_transmission) == NULL) { /* Codes_SRS_TLSIO_30_064: [ If the supplied message cannot be enqueued for transmission, tlsio_esp_tls_send_async shall log an error and return FAILURE. ]*/ LogError("Unable to add socket to pending list."); free(pending_transmission->bytes); free(pending_transmission); result = MU_FAILURE; } else { /* Codes_SRS_TLSIO_30_063: [ On success, tlsio_esp_tls_send_async shall enqueue for transmission the on_send_complete , the callback_context , the size , and the contents of buffer and then return 0. ]*/ dowork_send(tls_io_instance); result = 0; } } } } } } } /* Codes_SRS_TLSIO_30_066: [ On failure, on_send_complete shall not be called. ]*/ } return result; } static int tlsio_esp_tls_setoption(CONCRETE_IO_HANDLE tls_io, const char* optionName, const void* value) { TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; /* Codes_SRS_TLSIO_30_120: [ If the tlsio_handle parameter is NULL, tlsio_esp_tls_setoption shall do nothing except log an error and return FAILURE. ]*/ int result; if (tls_io_instance == NULL) { LogError("NULL tlsio"); result = MU_FAILURE; } else { /* Codes_SRS_TLSIO_30_121: [ If the optionName parameter is NULL, tlsio_esp_tls_setoption shall do nothing except log an error and return FAILURE. ]*/ /* Codes_SRS_TLSIO_30_122: [ If the value parameter is NULL, tlsio_esp_tls_setoption shall do nothing except log an error and return FAILURE. ]*/ /* Codes_SRS_TLSIO_ESP_TLS_COMPACT_30_520 [ The tlsio_esp_tls_setoption shall do nothing and return FAILURE. ]*/ TLSIO_OPTIONS_RESULT options_result = tlsio_options_set(&tls_io_instance->options, optionName, value); if (options_result != TLSIO_OPTIONS_RESULT_SUCCESS) { LogError("Failed tlsio_options_set"); result = MU_FAILURE; } else { result = 0; } } return result; } /* Codes_SRS_TLSIO_ESP_TLS_COMPACT_30_560: [ The tlsio_esp_tls_retrieveoptions shall do nothing and return an empty options handler. ]*/ static OPTIONHANDLER_HANDLE tlsio_esp_tls_retrieveoptions(CONCRETE_IO_HANDLE tls_io) { TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; /* Codes_SRS_TLSIO_30_160: [ If the tlsio_handle parameter is NULL, tlsio_esp_tls_retrieveoptions shall do nothing except log an error and return FAILURE. ]*/ OPTIONHANDLER_HANDLE result; if (tls_io_instance == NULL) { LogError("NULL tlsio"); result = NULL; } else { result = tlsio_options_retrieve_options(&tls_io_instance->options, tlsio_esp_tls_setoption); } return result; } /* Codes_SRS_TLSIO_30_008: [ The tlsio_get_interface_description shall return the VTable IO_INTERFACE_DESCRIPTION. ]*/ static const IO_INTERFACE_DESCRIPTION tlsio_esp_tls_interface_description = { tlsio_esp_tls_retrieveoptions, tlsio_esp_tls_create, tlsio_esp_tls_destroy, tlsio_esp_tls_open_async, tlsio_esp_tls_close_async, tlsio_esp_tls_send_async, tlsio_esp_tls_dowork, tlsio_esp_tls_setoption }; /* Codes_SRS_TLSIO_30_001: [ The tlsio_esp_tls shall implement and export all the Concrete functions in the VTable IO_INTERFACE_DESCRIPTION defined in the xio.h. ]*/ const IO_INTERFACE_DESCRIPTION* tlsio_pal_get_interface_description(void) { return &tlsio_esp_tls_interface_description; }