Simple IOT temperature sensor

IOT stands for Internet of Things and refers to small devices equipped with direct internet connectivity, without needing an external computer. They are usually composed of a main microcontroller and an additional connectivity (eg. ESP8266 for Wifi, ENC28J60 for Ethernet) or they are complete SOC including both the processing unit and the connectivity modems in one single package. Regardless of their form or shape, these devices open new applications for remote sensing and controlling, and bring system automation to a new level of performance.
Examples of such devices were presented on this blog even before IOT was coined as a buzzword. See the Ethernet controlled power relay based on enc28j60 or the development of first prototypes of what is now uRADMonitor.

Simple IOT temperature sensor

To exemplify the IOT concept I propose the following architecture:

  • several hardware remote nodes, each using a microcontroller, a sensor and a communication modem
  • the backend, comprised of a data interface (data API and a database)
  • Requirements

    On the hardware side, we need the following components:

  • Wemos D1 Lite
  • DS18B20 temperature shield
  • Battery shield
  • External Lithium rechargeable battery

  • For the backend I will demo using the open source EXP server protocol introduced with the KIT1. This is handled by the uRADMonitor server, but alternatively a simple PHP script can be used too.

    Assembling the hardware

    There are multiple way of putting these together, I went for putting the Wemos D1 at the bottom, the battery shield in the middle, and the Temperature shield at the top for better air contact and less internal heating interference. This means we solder the female headers to the Wemos D1, the mixed headers to the Battery Shield and the male headers to the DS18B20 Temperature shield. Here's the result:

    The PCBs can now be stacked in the proposed order, with the Wemos at the bottom and the DS18B20 sensor at the top. Here's the assembled device:

    The software

    With the simple IOT device ready, we need to program it. The firmware needs to setup the Wifi connection to an Internet Wireless Router, then periodically read the temperature values and send them to the backend.
    Before we can start, there are few tools we need to install:

  • Arduino IDE download
  • CH340G driver also available here.
  • Install Wemos D1 support over Arduino. First, open Preferences and add "" to Additional Boards Manager URLs:

  • Open Boards Manager and install the esp8266:

    Finally, we're all set and you can select the Wemos D1 R2 & mini from the Tools -> Board menu:

    Under Tools -> Port, select the corresponding serial port as allocated to the CH340G connected to your USB port. Go to Tools->Upload speed, and select 921600.

    Simple IOT firmware

    Create a new Arduino sketch. We will be using a few extra libraries: the OneWire and the DallasTemperature, so go to Sketch->Include Library->Manage Libraries and add them:

    The following code reads the DS18B20 temperature:

    1. // DS18B20 libraries
    2. #include <OneWire.h>
    3. #include <DallasTemperature.h>
    4. // sensor settings
    5. #define SENSOR_BUS D2 // the DS18B20 has the sensor connected to D2
    6. OneWire oneWire(SENSOR_BUS); // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
    7. DallasTemperature sensor(&oneWire); // Pass our oneWire reference to Dallas Temperature.
    8. ...
    9. sensor.begin(); // Start up the sensor library
    10. ...
    11. sensor.requestTemperatures(); // Send the command to get temperature readings
    12. Serial.print("Temperature is: ");
    13. Serial.println(sensor.getTempCByIndex(0));

    Now we need to add ESP8266 code logic, to make the module connect to an Internet Wireless Router at program start, then periodically read temperature and send it to the backend.

    The uRADMonitor EXP Protocol

    The EXP protocol was introduced with the open source KIT1 design, to implement a way of building custom IOT devices with a variable number of sensors. It defines the communication rules between an IOT Device ("Device") and a backend server ("Server") for the purpose of exchanging Sensor Data ("Data"). Each Device is uniquely identified using a Device ID allocated by the Server. All devices must have an Owner, identified with a UserId / UserKey pair.

    The EXP Protocol is a bidirectional communication protocol - for the very minimum, the Server needs to return a unique allocated Device ID, the very first time a device connects (or in case the device reports an invalid Device ID). Most of the traffic however is for uploading data from the IOT Device to the Server.
    The EXP Protocol uses HTTP Post to send the data to the server. Each call needs to be authenticated with a userId/userKey pair sent along with the HTTP Post headers, for every call. The HTTP Post headers must also contain the Device ID:

    1. http.addHeader("X-User-id", userId);
    2. http.addHeader("X-User-hash", userKey);
    3. http.addHeader("X-Device-id", deviceID);

    Device ID
    The Device ID is an 8 digit HEX number in the form 13XXXXXX. 13 is the Device class and is immutable, while the following digits are the Device number and are unique from one unit to another. The Device ID is saved in the EEPROM at address 0, using 4 bytes.
    On startup, the Device reads its own Device ID from the EEPROM. The very first time, the Device number will be 0 (invalid) but the Device class will be added (13000000). Every minute, the temperature sensor is read, and a HTTP Post is issued to the Server. If the Server receives an invalid Device ID (like 13000000), it will allocate a new valid Device ID and will return it as a response to the HTTP Post in JSON Format. The IOT Device will parse the response, and if a Device ID is received, it will store it to the EEPROM so on the next call will report with a valid Device ID.
    Owner Authentication

    Each Device belongs to an Owner, identified with a User Id / User Key pair. You will need to register a user account on the uRADMonitor Dashboard.
    The User Id / User Key pair needs to be sent out with each HTTP Post call.
    The sensor Data
    Each HTTP Post calls a RESTful API on the server. The format is:, followed by a variable number of key-value pairs separated by slash sign "/". Each key identifies a specific sensor parameters, as per the following list:

    1. // expProtocol
    2. #define ID_TIME_SECONDS "01" // compulsory: local time in seconds
    3. #define ID_TEMPERATURE_CELSIUS "02" // optional: temperature in degrees celsius
    4. #define ID_PRESSURE_PASCALS "03" // optional: barometric pressure in pascals
    5. #define ID_HUMIDITY_RH "04" // optional: humidity as relative humidity in percentage %
    6. #define ID_LUMINOSITY_RL "05" // optional: luminosity as relative luminosity in percentage ‰
    7. #define ID_VOC_OHM "06" // optional: volatile organic compounds in ohms
    8. #define ID_CO2_PPM "07" // optional: carbon dioxide in ppm
    9. #define ID_CH2O_PPM "08" // optional: formaldehyde in ppm
    10. #define ID_PM25_UGCM "09" // optional: particulate matter in micro grams per cubic meter
    11. #define ID_BATTERY_VOLTS "0A" // optional: device battery voltage in volts
    12. #define ID_GEIGER_CPM "0B" // optional: radiation measured on geiger tube in cpm
    13. #define ID_INVERTERVOLTAGE_VOLTS "0C" // optional: high voltage geiger tube inverter voltage in volts
    14. #define ID_INVERTERDUTY_PM "0D" // optional: high voltage geiger tube inverter duty in ‰
    15. #define ID_VERSION_HW "0E" // optional: hardware version
    16. #define ID_VERSION_SW "0F" // optional: software firmware version
    17. #define ID_TUBE "10" // optional: tube type ID

    To report a temperature reading, the HTTP Post call URL must be:
    Reporting the time with "01" is compulsory.

    The code becomes:

    1. // send a HTTP Post to the backend, using the EXP protocol
    2. bool sendSensorData (uint32_t seconds, float temperature, String userId, String userKey, uint32_t devId) {
    3. Serial.println("sendSensorData " + String(seconds) + " " + String(temperature) + " " + userId + " device:" + String(devId, HEX));
    4. // prepare post data
    5. String postUrl = String(ID_TIME_SECONDS) + "/" + String(seconds, DEC) + "/" + String(ID_VERSION_HW) + "/" + String(VER_HW, DEC) + "/" + String(ID_VERSION_SW) + "/" + String(VER_SW, DEC) + "/" + String(ID_TEMPERATURE_CELSIUS) + "/" + String(temperature);
    6. Serial.println(postUrl);
    7. // prepare http client to do the post
    8. HTTPClient http;
    9. // data is sent as URL / RESTful API
    10. http.begin(URADMONITOR_SERVER + postUrl);
    11. http.addHeader("Content-Type", "application/x-www-form-urlencoded");
    12. // the expProtocol requires these customs headers
    13. http.addHeader("X-User-id", userId);
    14. http.addHeader("X-User-hash", userKey);
    15. http.addHeader("X-Device-id", String(devId, HEX));
    16. int httpCode = http.POST("");
    17. // check server results
    18. if (httpCode != 200) {
    19. Serial.println("not successful");
    20. http.end();
    21. return false;
    22. } else {
    23. char buffer[200] = {0};
    24. http.getString().toCharArray(buffer, 200);
    25. Serial.print("Server response:"); Serial.println(buffer);
    26. // check response
    27. char value[10] = {0}; // this has to be inited, or when parsing we could have extra junk screwing results
    28. if (jsonKeyFind(buffer, "setid", value, 10)) {
    29. Serial.print("Server allocated new ID:"); Serial.println(value);
    30. deviceID = hex2int(value);
    31. eeprom_write_dword(EEPROM_ADDR_DEVID, deviceID);
    32. EEPROM.commit();
    33. }
    34. http.end();
    35. return true;
    36. }
    37. }

    Data access and visualisation

    The automated device ID allocation means the same code can be installed on multiple devices, and they will be individually addressable via their distinct Device IDs. Once connected, the device will start uploading measurements automatically, and these can be accessed via the API in numerical form, or charts can be seen on the uRADMonitor website. To open a specific device ID, you can use where the parameter needs to be a valid device ID. Here are the measurements taken by the assembled device:


    This simple construction illustrates the IOT concept and provides a solid base for expanding to more complicated sensors. The EXP Protocol can be used to add additional readings to the "Simple IOT Device", including sensors for CO2 or PM2.5.
    Since the Simple IOT device runs on a battery, power consumption can be implemented in the code, turning the ESP8266 Radio off to save power, and only reconnecting before data upload. A good approach is to have it take measurements every 30-60 minutes and sleep in between. There are four types of sleep modes for the ESP8266: No-sleep, Modem-sleep, Light-sleep, and Deep-sleep. They all have different functions and different current cost:

  • NO-SLEEP: The No-Sleep setting will keep everything on at all times. This will drain the most current.
  • MODEM-SLEEP: Modem-sleep is the default mode for the ESP8266. It's only enabled when you're connected to an access point. In this mode, the ESP8266 will disable the modem (WiFi) as much as possible. It turns off the modem between DTIM Beacon intervals. This interval is set by your router.
  • LIGHT-SLEEP: Light-sleep performs the same function as Modem-sleep, but also turns off the system clock and suspends the CPU. The CPU isn't off; it's just idling.
  • DEEP-SLEEP: Everything is off but the Real Time Clock (RTC), which is how the computer keeps time. Since everything is off, this is the most power efficient option.
  • You can use the Deep-sleep mode to have the ESP8266 go idle when measurements are not needed . You can take temperature readings every 60minutes and have the Wemos D1 go to sleep in between. Then on wake-up, make sure the connection is up before retransmitting. The logic is as follows:

  • Read sensor
  • Sleep for n microseconds
  • Repeat
  • The sleep time is specified in microseconds (µs), and you can call it with:
    ESP.deepSleep(50e6); // 50e6 is 50 seconds


    Simple IOT code REPO, you will need to configure your wifi settings in the code, the AP_SSID and AP_KEY . Code mirror here.
    Project presentation

    Balccon2k17 September 15, 2017

    I did a workshop on "Simple IOT" at the Balccon2k17 in Novi Sad, now at its fifth edition:

    The participants received tens of kits to assemble (including some soldering) their "Simple IOT" devices, a cube like unit that runs on battery, measures temperature and sends all data online via Wifi. The workshop went great, and at the end we got several units up and running, talking to the server to report remote temperature readings.

    More on this on the uRADMonitor blog.

    Related Post

    Leave a Reply