What you will learn
You will learn how to create an ESP8266-based device prototype and how to connect it to the Bosch IoT Suite services in the cloud.
By following this tutorial, you will also learn basics about what the Bosch IoT Suite is and how to work with its services. Additionally, you will experience your first steps with Bosch IoT Things and Bosch IoT Hub.
What you need to prepare and download
Hardware:
- ESP8266-based prototyping board (e.g. NodeMCU or Adafruit Feather HUZZAH)
- Breadboard
- Breadboard jumper wires
- Illuminance sensor
- 10k Ohm resistor
- USB data cable (USB-A to MICRO-USB)
Software:
- Download and setup the Arduino IDE for development with ESP8266 Core (version 2.4.1). In order to do so, follow the instructions on the Arduino repository on github.com/esp8266/Arduino.
- Download and install the CP210x USB to UART Bridge VCP Drivers.
- We recommend using the Mozilla Firefox browser for this tutorial.
Tasks to complete
Make sure to unplug the hardware from any power source before working on it. In the example setup below, we use a NodeMCU v2 and a 400 point breadboard. Feel free to replace them with any other ESP8266-based device and a fitting breadboard size.
Connect your illuminance sensor to the device as shown below.

Login with your existing Bosch IoT Suite account or create a new one by following these steps:
- Navigate to the Bosch IoT Suite.
- Create a Bosch IoT Suite account.
- Login with your credentials.

Subscribe to the Bosch IoT Suite for Asset Communication package by following these steps:
- Navigate to Service Subscriptions.
- Click New Subscription.
- Follow the subscription wizard.

Configure your unique Bosch IoT Things namespace by following these steps:
- Navigate to Bosch IoT Things by clicking Go to Dashboard.
- Click Solution/Namespace.
- Insert a new globally unique namespace using java package notation, e.g. “your.things.namespace”.
- Click Submit namespace.

Create an OAuth2 Client with full access to Bosch IoT Things and Bosch IoT Hub by following the next steps:
- Navigate to the Bosch IoT Suite.
- Click the icon user account
, then click Sign In in the overlay.
- Once you are authenticated, click the icon user account
.
- Click OAuth2 Clients in the overlay.
- In the section New OAuth2 Client insert a new name, e.g. “Asset Communication Client”.
- Select the Hub and the Things full access scopes.
- Click Create.

Generate and copy a test token by following the next steps:
- Navigate to your Bosch IoT Suite.
- Click the icon user account
.
- Click OAuth2 Clients in the overlay.
- Click Use next to your active OAuth2 Client.
- Copy the test token by clicking Copy to clipboard.

Register your device with the Bosch IoT Suite for Asset Communication package at the Device Provisioning API by following these steps:
- Click Authorize. A dialog opens. Paste the test token as value of the bearerAuth (http, Bearer) section. Proceed with Authorize, then Close the dialog.
- Unfold the provisioning section and click the POST method “/{service-instance-id}/devices”.
- Click Try it out.
- Add your Service Instance ID (you will find all important information as described in the “Service Instance ID” infobox).
- Click Edit below the Edit Value field.
- Copy the following code and replace everything in the field Edit Value:
{ "id":"<com.bosch.example:my-device-id-4711>", "hub":{ "device":{ "enabled":true }, "credentials":{ "type":"hashed-password", "secrets":[ { "password":"<yourPassword>" } ] } }, "things":{ "thing":{ "attributes":{ "manufacturer":"<Robert Bosch GmbH>" }, "features":{ "illuminance":{ "definition":[ "org.eclipse.vorto:Illuminance:1.0.0" ], "properties":{ "status":{ "value":{ "currentMeasured":555, "minMeasured":0, "maxMeasured":999 } } } } } } } }
- Define your credentials (check the “Device credentials” infobox) by editing everything in <pointy brackets> (make sure to delete the <pointy brackets>).
- Click Execute to run your API call and register your device.
You should get a response with the code HTTP 201 created and a payload reflecting the device you just registered.

Generate the boilerplate code for your firmware
Generate parts of the Bosch IoT Suite-related boilerplate code using a code generator and upload it to your device by following these steps:
Generate parts of the Bosch IoT Suite-related boilerplate by following these steps:
- Open https://vorto.eclipse.org in a new browser window.
- Select the Bosch IoT Suite plugin from the list of Official Plugins on the right.
- Select the option Integrate device with Arduino.
- Click Source Code.
This will download a ZIP file containing an Arduino project file and an additional src folder.
Upload the firmware to your device by following these steps:
- Extract the downloaded package containing the Arduino project file “SimpleIlluminanceApp.ino”.
- Open Arduino IDE and open the project file.
- Import the PubSubClient library under “Sketch/Include Library/Manage Libraries…”. In case of problems, you can download and add the “pubsubclient-master.zip” file manually.
- Adapt the PubSubClient.h header file (the path to the file is described in the infobox “Where to find the PubSubClient.h file”) by changing the “MQTT_MAX_PACKET_SIZE to 1024”.
- Connect your prototyping board to your computer via USB. Make sure to use a data cable. A charging cable will not work.
- Open Preferences/Settings and add the URL “https://arduino.esp8266.com/stable/package_esp8266com_index.json” in the Additional Boards Manager URLs field.
- Install the “ESP8266 by ESP8266 Community version 2.4.1” (no newer version will work) under Tools/Board/Boards Manager. In order to install the right board follow the instructions at the esp8266 GitHub site.
- Select your ESP8266 prototyping board (e.g. NodeMCU 1.0) in Arduino under Tools/Board.
- Select the port your board has registered with under Tools/Port.
- Optional but recommended: Change the upload speed to the highest possible setting.
Configure your device firmware in the first 50 lines with the following details (replace everything in <pointy brackets> and delete them afterwards).
- Asset Communication subscription credentials:
tenantId: "<txxxxxxxxxxxxxxxxxxxx_hub>" hub_adapter_host: "mqtt.bosch-iot-hub.com"
- SHA-1 server fingerprint of the Bosch IoT Hub MQTT adapter:
mqttServerFingerprint: "84 5D F3 5D 1E 74 7A 55 9C CF 66 47 9E 3F 3D 13 10 30 67 84"
- Your device provisioning request:
deviceId: "<your.namespace>:<your-device-id-4711>" authId: "<your.namespace>_<your-device-id-4711>" device_password: "<your-device-password>" ditto_topic: "<your.namespace>/<your-device-id-4711>"
- Your WiFi details:
ssid: "<ENTER YOUR WIFI SSID>" password: "<ENTER YOUR WIFI PASSWORD>"
Adapt the firmware on your device to send real data by following these steps:
- Initialize (initial and default values) the illuminance sensors by adding the following code at the end of the section “setup()”:
void setup() { ... org_eclipse_vorto_types::SensorValue value; /* minimum measured value since last reboot */ value.setminMeasured(1024.0); /* maximum measured value since last reboot */ value.setmaxMeasured(0.0); infoModel.illuminance.setvalue(value); }
- Read the current illuminance sensor reading, set the illuminance message payload accordingly, and publish the data by replacing the “loop()” function with:
void loop() { /* Check if connection to MQTT broker is still good */ if (!mqttClient.connected()) { /* Reconnect if not */ reconnect(); } /* Event handling of the MQTT client */ mqttClient.loop(); /* Publish the telemetry data periodically */ long now = millis(); if (now - lastMqttMsg > MQTT_DATA_PERIOD) { lastMqttMsg = now; /* Read the values from input pin */ int illuminanceValue = analogRead(A0); delay(3); //walk-around a bug in NodeMCU //Status Properties org_eclipse_vorto_types::SensorValue value = infoModel.illuminance.getvalue(); /* current value */ value.setcurrentMeasured(illuminanceValue); /* minimum value measured since last reboot */ if (illuminanceValue < value.getminMeasured()) { value.setminMeasured(illuminanceValue); }; /* maximum value measured since last reboot */ if (illuminanceValue > value.getmaxMeasured()) { value.setmaxMeasured(illuminanceValue); }; infoModel.illuminance.setvalue(value); /* publish data to MQTT endpoint */ publishIlluminance(); } }
- Compile/verify the changes of the firmware by clicking the check button in the top left.
- Upload the firmware to your device by clicking the arrow button next to the check button.
- Open the serial monitor to check for debug information. If the output you received is unreadable output, change the baud rate of the serial monitor to 115200.
Check your work
Check your updated digital twin in the Bosch IoT Things. Open the API docs and switch to “GET/things/{thingId}”. Create a new test token (if the previous one is expired) by clicking Use on your OAuth2.0 client list.
- Click Authorize on the top right.
- In the dialog that opened, paste your test token as value of the bearer Auth (http, Baerer) section. Proceed with Authorize, then close the dialog.
- Click GET on the Things resource.
- Click Try it out.
- Add your Thing ID to the thingId field as follows:
your.namespace:your-device-id-4711
- Click Execute to run your API call.
You should receive a response with the code “HTTP 200 OK” and a payload reflecting your Thing with the updated values for your sensors:
{
"thingId":"com.bosch.example:my-device-id-4711",
"policyId":"com.bosch.example:my-device-id-4711",
"attributes":{
"manufacturer":"Robert Bosch GmbH"
},
"features":{
"illuminance":{
"definition":[
"org.eclipse.vorto:Illuminance:1.0.0"
],
"properties":{
"status":{
"value":{
"currentMeasured":555,
"minMeasured":0,
"maxMeasured":999
}
}
}
}
}
}
In the sections above, you only used MQTT to push your device’s telemetry data to the Bosch IoT Hub MQTT Connector.
In order to receive data from the Bosch IoT Hub, the MQTT configuration of the device needs to be changed. After implementing the MQTT subscription, the device should send a response after receiving and processing the command.
To enable the command & control functionality via MQTT, you need to modify the firmware you already created by following these steps:
- Open Arduino IDE and open your project file “SimpleIlluminanceApp.ino” with which you have been working before.
- Replace the code of the “reconnect()” function with:
void reconnect() { String username = String(authId) + "@" + String(tenantId); /* Loop while not connected */ while (!mqttClient.connected()) { /* If connected to the MQTT broker... */ if (mqttClient.connect(clientId.c_str(),username.c_str(),device_password)) { /* Attempt to Connect successful */ Serial.println("Successfully connected to MQTT Broker"); /* Subscribe for command messages from Bosch IoT Hub */ String topic = "command///req/#"; mqttClient.subscribe(topic.c_str()); Serial.println("Subscribed to topic: " + topic); } else { /* otherwise wait for 5 seconds before retrying */ Serial.println("Waiting for next attempt to connect to MQTT Broker"); delay(5000); } } }
- Replace the original “mqttDataReceived()” function code with the following code:
void mqttDataReceived(char* mqttTopic, byte* mqttPayload, unsigned int mqttLength) { String reqTopic = (String) mqttTopic; reqTopic.toLowerCase(); /* If this is a 'switch' command, flip the lights */ if(reqTopic.startsWith("command") && reqTopic.endsWith("switch")) { String newState; Serial.println("Switch command received, switching ..."); /* flip the lights */ if (digitalRead(2)==LOW) { digitalWrite(2,HIGH); newState="LED is now off!"; } else { digitalWrite(2,LOW); newState="LED is now on!"; } /* parse Bosch IoT Hub's message ID for response */ String messageId = reqTopic.substring(reqTopic.indexOf("req/")+4); messageId = messageId.substring(0, messageId.indexOf("/")); /* if this is a 2-way command, respond */ if (messageId != "") { /*Serial.println("Sender expects reply, responding to message with ID: " + messageId);*/ /* create MQTT response topic */ String resTopic = ("command///res/" + messageId + "/200").c_str(); /* parse Bosch IoT Things correlation ID for response*/ String reqPayload = (String) (char*) mqttPayload; /* 17 is the length of 'correlation-id' and subsequent '":"' */ String correlationId; correlationId = reqPayload.substring(reqPayload.indexOf("correlation-id")+17); String correlationId2; correlationId2 = correlationId; correlationId.remove(correlationId2.indexOf('"')); Serial.println("Sender expects reply, responding to message with Correlation-Id: " + correlationId); /* create Ditto compliant MQTT response payload */ String resPayload = "{\"topic\":\"" + ditto_topic + "/things/live/messages/switch\","; resPayload += "\"headers\":{\"correlation-id\":\"" + correlationId + "\","; resPayload += "\"version\":2,\"content-type\":\"text/plain\"},"; resPayload += "\"path\":\"/inbox/messages/switch\","; resPayload += "\"value\":\"" + newState + "\","; resPayload += "\"status\": 200 }"; mqttClient.publish(resTopic.c_str(), resPayload.c_str()); } } }
- Add the following code lines at the end of the “setup()” function in order to integrate the LED functionality:
/* Set GPIO 2 pin as output and switch the LED to LOW == 'on' */ pinMode( 2, OUTPUT); digitalWrite( 2, LOW);
- Compile and upload the firmware to your prototyping device.
Check your work
Check your extended device functionality by sending commands out of Bosch IoT Things to switch the LED. In order to do, so open your digital twin in Bosch IoT Things – API V2.
- Scroll down and open the messages section by clicking Messages.
- Use the second Post method “/things/{thingId}/inbox/messages/{messageSubject}”.
- Click Try it out.
- Add your Thing ID into the thingId field.
- Insert “switch” into the messageSubject field.
- Click Edit below the Edit value field.
- Create an empty JSON object by inserting “{}”.
- Click Execute.
The LED of your device should now switch its state and you should receive a response with the code “200” and the text “LED on” or “LED off” depending on its current state.
- If you cannot download the list of additional boards, you might need to setup your proxy in the Arduino IDE. Go to File/Preferences/Network and setup your proxy accordingly.
- If you can find the ESP8266 board plugin, but downloading does not work and gives you an SSL error, you might be behind a TSL terminating proxy. You need to add your proxy’s SSL certificate to your Arduino IDE’s shipped Java truststore, or simply replace the truststore with your system’s Java truststore. To do so, you need to copy the cacerts file from:
C:/Program Files (x86)/Java/jre8/lib/security/
and replace the existing cacerts file in:
C:/Program Files (x86)/Arduino/java/lib/security/
Device is behind a proxy
This is currently not supported by the “ESP8266 Core libraries”. Your device needs to be connected in a proxy-free environment.
Find the right serial port
Arduino IDE does not discover the correct port for the ESP8266-based boards automatically. You need to set it using the Tools/Port menu. To find what you need to set, check the descriptions below:
Windows:
- Find the “COM port number” in the Windows Device Manager. Silicon Labs CP210x USB to UART Bridge (COM) indicates that the port is properly configured.
- If your prototyping board shows up with a yellow exclamation mark, you might need to install the driver for it manually. You can find the driver for the NodeMCU on their website.
Serial monitor displays “Secure connection failed, restart”
Make sure you have installed version 2.4.1 of the ESP8266 Core in Tools/Board/Boards Manager.
Serial monitor displays “Verification failed, restart Device”
Make sure you are using the correct SHA-1 fingerprint and version 2.4.1 of the ESP8266 Core.
Serial monitor displays unreadable characters or “wdt reset”
Try to reset your device by pushing the reset button (e.g. RST Button in case of NodeMCU).
The Things API V.2 responds “timeout”
Reset the connection between your Bosch IoT Things and your Bosch IoT Hub service instance. Follow these steps:
- Navigate to Bosch IoT Things and open Solution/Connections/Integrations.
- Click the Devices via Bosch IoT Suite button.
- Open Management.
- Click the Close connection, wait a few seconds, and click the Open connection button.
Other problems concerning the device code
After all modifications, your code should look as follows (except the contents in the brackets):
// Generated by Vorto from com.bosch.iotacademy.tutorial.SimpleIlluminance:1.0.0
#define USE_SECURE_CONNECTION 1
#include
#include
#if (USE_SECURE_CONNECTION == 1)
#include
#endif
#include "src/model/functionblock/Illuminance.h"
#include "src/model/infomodel/SimpleIlluminance.h"
/**************************************************************************/
/* Configuration section, adjust to your settings */
/**************************************************************************/
#define tenantId "<txxxxxxxxxxxxxxxxxxxx_hub>"
/* MQTT broker endpoint */
const char* hub_adapter_host = "mqtt.bosch-iot-hub.com";
#if (USE_SECURE_CONNECTION == 1)
/* SHA-1 fingerprint of the server certificate of the MQTT broker, UPPERCASE and spacing */
const char* mqttServerFingerprint = "84 5D F3 5D 1E 74 7A 55 9C CF 66 47 9E 3F 3D 13 10 30 67 84";
#endif
/* Define the period of data transmission in ms */
#define MQTT_DATA_PERIOD 10000
/* Define the buffer size for payload strings */
#define MQTT_MAX_SIZE 50
/* Device Configuration */
String deviceId = "<your.namespace>:<your-device-id-4711>";
String authId = "<your.namespace>_<your-device-id-4711>";
const char* device_password = "<xxxxxx>";
/* Payload Configuration*/
String ditto_topic = "<your.namespace>/<your-device-id-4711>";
/* WiFi Configuration */
const char* ssid = "<Your SSID>";
const char* password = "<Your Wifi Password>";
/* BEGIN SAMPLE CODE */
/* dummy numeric */
long dummy_value = 0;
/* Sample text value */
char msg[MQTT_MAX_SIZE];
/* END SAMPLE CODE */
/**************************************************************************/
/* Implementation */
/**************************************************************************/
/* Port on which the MQTT broker is listening */
#if (USE_SECURE_CONNECTION == 1)
#define MQTT_SERVER_PORT 8883
#else
#define MQTT_SERVER_PORT 1883
#endif
/* Topic on which the telemetry data is published */
String telemetryTopic = String("telemetry/") + String(tenantId) + String("/");
/* This variables stores the client ID in the MQTT protocol */
String clientId;
/* Timestamp of previous data transmission */
long lastMqttMsg;
/* Setup WiFi mqttClient and MQTT mqttClient */
#if (USE_SECURE_CONNECTION == 1)
WiFiClientSecure wifiClient;
#else
WiFiClient wifiClient;
#endif
PubSubClient mqttClient(wifiClient);
/* The information model object */
com_bosch_iotacademy_tutorial::SimpleIlluminance infoModel;
/**************************************************************************/
/* Function to connect to the WiFi network */
/**************************************************************************/
void setup_wifi() {
delay(10);
/* We start by connecting to a WiFi network */
Serial.print("Connecting to WiFi with SSID: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
/* Wait for succesful connection, hang if there is none? */
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("MAC address: ");
Serial.println(WiFi.macAddress());
}
/**************************************************************************/
/* Function called when data on a subscribed topic arrives */
/**************************************************************************/
void mqttDataReceived(char* mqttTopic, byte* mqttPayload, unsigned int mqttLength) {
String reqTopic = (String) mqttTopic;
reqTopic.toLowerCase();
/* If this is a 'switch' command, flip the lights */
if(reqTopic.startsWith("command") && reqTopic.endsWith("switch")) {
String newState;
Serial.println("Switch command received, switching ...");
/* flip the lights */
if (digitalRead(2)==LOW) {
digitalWrite(2,HIGH);
newState="LED is now off!";
} else {
digitalWrite(2,LOW);
newState="LED is now on!";
}
/* parse Bosch IoT Hub's message ID for response */
String messageId = reqTopic.substring(reqTopic.indexOf("req/")+4);
messageId = messageId.substring(0, messageId.indexOf("/"));
/* if this is a 2-way command, respond */
if (messageId != "") {
/*Serial.println("Sender expects reply, responding to message with ID: " + messageId);*/
/* create MQTT response topic */
String resTopic = ("command///res/" + messageId + "/200").c_str();
/* parse Bosch IoT Things correlation ID for response*/
String reqPayload = (String) (char*) mqttPayload;
/* 17 is the length of 'correlation-id' and subsequent '":"' */
String correlationId;
correlationId = reqPayload.substring(reqPayload.indexOf("correlation-id")+17);
String correlationId2;
correlationId2 = correlationId;
correlationId.remove(correlationId2.indexOf('"'));
Serial.println("Sender expects reply, responding to message with Correlation-Id: " + correlationId);
/* create Ditto compliant MQTT response payload */
String resPayload = "{\"topic\":\"" + ditto_topic + "/things/live/messages/switch\",";
resPayload += "\"headers\":{\"correlation-id\":\"" + correlationId + "\",";
resPayload += "\"version\":2,\"content-type\":\"text/plain\"},";
resPayload += "\"path\":\"/inbox/messages/switch\",";
resPayload += "\"value\":\"" + newState + "\",";
resPayload += "\"status\": 200 }";
mqttClient.publish(resTopic.c_str(), resPayload.c_str());
}
}
}
/**************************************************************************/
/* Reconnect to MQTT broker in case the connection dropped */
/**************************************************************************/
void reconnect() {
String username = String(authId) + "@" + String(tenantId);
/* Loop while not connected */
while (!mqttClient.connected()) {
/* If connected to the MQTT broker... */
if (mqttClient.connect(clientId.c_str(),username.c_str(),device_password)) {
/* Attempt to Connect successful */
Serial.println("Successfully connected to MQTT Broker");
/* Subscribe for command messages from Bosch IoT Hub */
String topic = "command///req/#";
mqttClient.subscribe(topic.c_str());
Serial.println("Subscribed to topic: " + topic);
} else {
/* otherwise wait for 5 seconds before retrying */
Serial.println("Waiting for next attempt to connect to MQTT Broker");
delay(5000);
}
}
}
/**************************************************************************/
/* Functions for publishing data using MQTT */
/**************************************************************************/
boolean publishIlluminance() {
String mqttPayload = infoModel.illuminance.serialize(ditto_topic ,deviceId, "illuminance");
/* Debug output on console */
Serial.print("Publishing Payload for illuminance: ");
Serial.print(mqttPayload);
Serial.print(" to topic: ");
Serial.println(telemetryTopic);
/* Publish all available illuminance data to the MQTT broker */
if(!mqttClient.publish(telemetryTopic.c_str(), mqttPayload.c_str())) {
Serial.println("Publish illuminance failed, if this happens repeatedly increase MQTT_MAX_PACKET_SIZE in PubSubClient.h");
return false;
} else {
return true;
}
}
/**************************************************************************/
/* Arduino Setup and Loop Functions */
/**************************************************************************/
void setup() {
Serial.begin(115200);
setup_wifi();
/* Create a MQTT client ID */
clientId = deviceId;
Serial.print("Device ID: ");
Serial.println(clientId);
/* Add the device ID to the telemetry topic as the final element */
telemetryTopic += deviceId;
/* Configure the MQTT client with the server and callback data */
mqttClient.setServer(hub_adapter_host, MQTT_SERVER_PORT);
mqttClient.setCallback(mqttDataReceived);
#if (USE_SECURE_CONNECTION == 1)
if (!wifiClient.connect(hub_adapter_host, MQTT_SERVER_PORT)) {
Serial.println("Secure connection failed, restart Device");
ESP.restart();
} else {
Serial.println("Successfully established secure connection to broker");
}
if (!wifiClient.verify(mqttServerFingerprint, hub_adapter_host)) {
Serial.println("Verification failed, restart Device");
ESP.restart();
} else {
Serial.println("Successfully verified server certificate");
}
#endif
/*Test MQQT Client*/
mqttClient.publish("","");
org_eclipse_vorto_types::SensorValue value;
/* minimum measured value since last reboot */
value.setminMeasured(1024.0);
/* maximum measured value since last reboot */
value.setmaxMeasured(0.0);
infoModel.illuminance.setvalue(value);
/* Set GPIO 2 pin as output and switch the LED to LOW == 'on' */
pinMode( 2, OUTPUT);
digitalWrite( 2, LOW);
}
void loop() {
/* Check if connection to MQTT broker is still good */
if (!mqttClient.connected()) {
/* Reconnect if not */
reconnect();
}
/* Event handling of the MQTT client */
mqttClient.loop();
/* Publish the telemetry data periodically */
long now = millis();
if (now - lastMqttMsg > MQTT_DATA_PERIOD) {
lastMqttMsg = now;
/* Read the values from input pin */
int illuminanceValue = analogRead(A0);
delay(3); //walk-around a bug in NodeMCU
//Status Properties
org_eclipse_vorto_types::SensorValue value = infoModel.illuminance.getvalue();
/* current value */
value.setcurrentMeasured(illuminanceValue);
/* minimum value measured since last reboot */
if (illuminanceValue < value.getminMeasured()) { value.setminMeasured(illuminanceValue); }; /* maximum value measured since last reboot */ if (illuminanceValue > value.getmaxMeasured()) {
value.setmaxMeasured(illuminanceValue);
};
infoModel.illuminance.setvalue(value);
/* publish data to MQTT endpoint */
publishIlluminance();
}
}