Hackster is hosting Hackster Holidays, Finale: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Tuesday!Stream Hackster Holidays, Finale on Tuesday!
Attila Tőkés
Published © CC BY

Smart Thermostat with Data Logging and BLE connectivity

Bluetooth Low Energy Thermostat created with the NXP Rapid IoT kit.

BeginnerFull instructions provided8 hours3,655

Things used in this project

Hardware components

Rapid IoT Prototyping Kit
NXP Rapid IoT Prototyping Kit
×1
Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
Relay Module (Generic)
×1

Software apps and online services

NXP Rapid IoT Studio

Story

Read more

Schematics

Schematics

Code

Rapid Iot Studio project

JSON
{
  "name": "BLE Thermostat",
  "createVersion": "2017-08-12",
  "description": "New Project",
  "lastModified": "2019-02-15T15:02:44.927Z",
  "created": "2019-02-15T15:02:44.927Z",
  "meta": {
    "projectTypeName": "NXP Rapid IoT",
    "projectTypeId": "NxpRpk"
  },
  "planes": {
    "NXP Rapid IoT": {
      "type": "mcuxpresso",
      "compilerVersion": "latest",
      "variants": [
        "NxpRpk"
      ],
      "meta": {},
      "elements": [
        {
          "name": "Interval",
          "type": "EmbeddedInterval",
          "variants": [
            "embedded",
            "triggers",
            "abilities",
            "properties",
            "variables"
          ],
          "properties": {
            "errorData": {},
            "code": {
              "trigger": "\treturn ATMO_Status_Success;",
              "setup": "\n\tATMO_INTERVAL_Handle_t intervalHandle;\n    ATMO_INTERVAL_AddAbilityInterval(\n\t\tATMO_PROPERTY(Interval, instance), \n\t\tATMO_ABILITY(Interval, interval), \n\t\tATMO_PROPERTY(Interval, time), \n\t\t&intervalHandle\n\t);\n\t\n\treturn ATMO_Status_Success;\n\t",
              "interval": "\treturn ATMO_Status_Success;"
            },
            "variables": {},
            "embeddedPropertyConversions": {},
            "codeUserChanged": {
              "setup": false,
              "interval": false
            },
            "instance": "ATMO_DRIVERINSTANCE_INTERVAL_INTERVAL1",
            "time": 1000
          },
          "meta": {
            "editorX": 99,
            "editorY": 141,
            "lastTrigger": "interval"
          },
          "triggers": {
            "triggered": [],
            "interval": [
              {
                "mapping": {},
                "targetOrder": [],
                "targetElement": "TSL2572AmbientLight",
                "targetAbility": "readAmbientLight"
              },
              {
                "mapping": {},
                "targetOrder": [],
                "targetElement": "ENS210TemperatureHumidity",
                "targetAbility": "readHumidity"
              },
              {
                "mapping": {},
                "targetOrder": [],
                "targetElement": "MPL3115Pressure",
                "targetAbility": "readPressure"
              },
              {
                "mapping": {},
                "targetOrder": [],
                "targetElement": "ENS210TemperatureHumidity",
                "targetAbility": "readTemperature"
              }
            ]
          },
          "interruptAbilities": {
            "trigger": false,
            "setup": false,
            "interval": true
          },
          "abilities": [
            {
              "name": "trigger",
              "triggers": [
                "triggered"
              ]
            },
            {
              "name": "setup",
              "triggers": []
            },
            {
              "name": "interval",
              "triggers": [
                "interval"
              ]
            }
          ]
        },
        {
          "name": "TSL2572AmbientLight",
          "type": "EmbeddedTSL2572",
          "variants": [
            "embedded",
            "triggers",
            "abilities",
            "properties",
            "variables"
          ],
          "properties": {
            "errorData": {},
            "code": {
              "trigger": "\treturn ATMO_Status_Success;",
              "setup": "\tATMO_TSL2572_Config_t config;\n\tconfig.address = ATMO_PROPERTY(TSL2572AmbientLight, i2cAddress);\n\tconfig.i2cDriverInstance = ATMO_PROPERTY(TSL2572AmbientLight, i2cInstance);\n\n\treturn ( ATMO_TSL2572_Init(&config) == ATMO_TSL2572_Status_Success ) ? ATMO_Status_Success : ATMO_Status_Fail;\n",
              "setEnabled": "ATMO_TSL2572_SetEnabled(true);\nreturn ATMO_Status_Success;",
              "setDisabled": "ATMO_TSL2572_SetEnabled(false);\nreturn ATMO_Status_Success;",
              "setEnabledDisabled": "bool enabled = false;\nATMO_GetBool(in, &enabled);\nATMO_TSL2572_SetEnabled(enabled);\nreturn ATMO_Status_Success;",
              "readAmbientLight": "    float lightLux;\n    if(ATMO_TSL2572_GetAmbientLight(&lightLux) != ATMO_TSL2572_Status_Success)\n    {\n        ATMO_CreateValueVoid(out);\n        return ATMO_Status_Fail;\n    }\n    ATMO_CreateValueInt(out, (int)lightLux);\n    return ATMO_Status_Success;"
            },
            "variables": {},
            "embeddedPropertyConversions": {},
            "codeUserChanged": {
              "setup": false,
              "setEnabled": false,
              "setDisabled": false,
              "setEnabledDisabled": false,
              "readAmbientLight": false
            },
            "i2cInstance": "ATMO_DRIVERINSTANCE_I2C_I2C2",
            "i2cAddress": "0x39"
          },
          "meta": {
            "editorX": 259,
            "editorY": 134,
            "lastTrigger": "ambientLightRead"
          },
          "triggers": {
            "triggered": [],
            "ambientLightRead": [
              {
                "mapping": {},
                "targetOrder": [],
                "targetElement": "BLEChracteristicAmbientLight",
                "targetAbility": "setValue"
              },
              {
                "mapping": {},
                "targetOrder": [],
                "targetElement": "LightToStr",
                "targetAbility": "trigger"
              }
            ]
          },
          "interruptAbilities": {
            "trigger": false,
            "setup": false,
            "setEnabled": false,
            "setDisabled": false,
            "setEnabledDisabled": false,
            "readAmbientLight": false
          },
          "abilities": [
            {
              "name": "trigger",
              "triggers": [
                "triggered"
              ]
            },
            {
              "name": "setup",
              "triggers": []
            },
            {
              "name": "setEnabled",
              "triggers": []
            },
            {
              "name": "setDisabled",
              "triggers": []
            },
            {
              "name": "setEnabledDisabled",
              "triggers": []
            },
            {
              "name": "readAmbientLight",
              "triggers": [
                "ambientLightRead"
              ]
            }
          ]
        },
        {
          "name": "BLEChracteristicAmbientLight",
          "type": "EmbeddedBLECharacteristicCustom",
          "variants": [
            "embedded",
            "triggers",
            "abilities",
            "properties",
            "variables",
            "ble"
          ],
          "properties": {
            "errorData": {},
            "code": {
              "trigger": "\treturn ATMO_Status_Success;",
              "setup": "\n\tATMO_BLE_GATTSAddService(\n\t\tATMO_PROPERTY(BLEChracteristicAmbientLight, instance),\n\t\t&ATMO_VARIABLE(BLEChracteristicAmbientLight, bleServiceHandle), \n\t\tATMO_PROPERTY(BLEChracteristicAmbientLight, bleServiceUuid));\n\t\n\tuint8_t property = 0;\n\tuint8_t permission = 0;\n\t\n\tproperty |= ATMO_PROPERTY(BLEChracteristicAmbientLight, read) ? ATMO_BLE_Property_Read : 0;\n\tproperty |= ATMO_PROPERTY(BLEChracteristicAmbientLight, write) ? ATMO_BLE_Property_Write : 0;\n\tproperty |= ATMO_PROPERTY(BLEChracteristicAmbientLight, notify) ? ATMO_BLE_Property_Notify : 0;\n\n\tpermission |= ATMO_PROPERTY(BLEChracteristicAmbientLight, read) ? ATMO_BLE_Permission_Read : 0;\n\tpermission |= ATMO_PROPERTY(BLEChracteristicAmbientLight, write) ? ATMO_BLE_Permission_Write : 0;\n\n\tATMO_DATATYPE types[3] = {ATMO_PROPERTY(BLEChracteristicAmbientLight, writeDataType), ATMO_PROPERTY(BLEChracteristicAmbientLight, readDataType), ATMO_PROPERTY(BLEChracteristicAmbientLight, notifyDataType)};\n\t\n\tATMO_BLE_GATTSAddCharacteristic(\n\t\tATMO_PROPERTY(BLEChracteristicAmbientLight, instance),\n\t\t&ATMO_VARIABLE(BLEChracteristicAmbientLight, bleCharacteristicHandle), \n\t\tATMO_VARIABLE(BLEChracteristicAmbientLight, bleServiceHandle), \n\t\tATMO_PROPERTY(BLEChracteristicAmbientLight, bleCharacteristicUuid), \n\t\tproperty, permission, ATMO_GetMaxValueSize(3, 64, types));\n\t\n\tATMO_BLE_GATTSRegisterCharacteristicAbilityHandle(\n\t\tATMO_PROPERTY(BLEChracteristicAmbientLight, instance),\n\t\tATMO_VARIABLE(BLEChracteristicAmbientLight, bleCharacteristicHandle), \n\t\tATMO_BLE_Characteristic_Written, \n\t\tATMO_ABILITY(BLEChracteristicAmbientLight, written));\n\t\n\treturn ATMO_Status_Success;\n\t",
              "setValue": "\n\t\n\t// Convert to the desired write data type\n\tATMO_Value_t convertedValue;\n\tATMO_InitValue(&convertedValue);\n\tATMO_CreateValueConverted(&convertedValue, ATMO_PROPERTY(BLEChracteristicAmbientLight, readDataType), in);\n\n\tATMO_BLE_GATTSSetCharacteristic(\n\t\tATMO_PROPERTY(BLEChracteristicAmbientLight, instance),\n\t\tATMO_VARIABLE(BLEChracteristicAmbientLight, bleCharacteristicHandle),\n\t\tconvertedValue.size, \n\t\t(uint8_t *)convertedValue.data,\n\t\tNULL);\n\t\n\tATMO_FreeValue(&convertedValue);\n\t\t\n\treturn ATMO_Status_Success;\n\t",
              "written": "\n\tATMO_CreateValueConverted(out, ATMO_PROPERTY(BLEChracteristicAmbientLight, writeDataType), in);\n\treturn ATMO_Status_Success;\n\t",
              "subscibed": "\treturn ATMO_Status_Success;",
              "unsubscribed": "\treturn ATMO_Status_Success;"
            },
            "variables": {
              "bleServiceHandle": {
                "type": "ATMO_BLE_Handle_t"
              },
              "bleCharacteristicHandle": {
                "type": "ATMO_BLE_Handle_t"
              }
            },
            "embeddedPropertyConversions": {
              "bleServiceUuid": "string",
              "bleCharacteristicUuid": "string"
            },
            "codeUserChanged": {
              "setup": false,
              "setValue": false,
              "written": false,
              "subscibed": false,
              "unsubscribed": false
            },
            "instance": "ATMO_DRIVERINSTANCE_BLE_BLE1",
            "bleServiceUuid": "647b1c04-ce8f-4e1f-aea6-09e28d875c51",
            "bleCharacteristicUuid": "647b1c04-ce8f-4e1f-aea6-09e28d875c52",
            "read": true,
            "write": false,
            "notify": false,
            "readDataType": "ATMO_DATATYPE_FLOAT",
            "writeDataType": "ATMO_DATATYPE_STRING",
            "notifyDataType": "ATMO_DATATYPE_STRING"
          },
          "meta": {
            "editorX": 450,
            "editorY": 165,
            "lastTrigger": "written"
          },
          "triggers": {
            "triggered": [],
            "written": [],
            "subscibed": [],
            "unsubscribed": []
          },
          "interruptAbilities": {
            "trigger": false,
            "setup": false,
            "setValue": "valueSet",
            "written": true,
            "subscibed": true,
            "unsubscribed": true
          },
          "abilities": [
            {
              "name": "trigger",
              "triggers": [
                "triggered"
              ]
            },
            {
              "name": "setup",
              "triggers": []
            },
            {
              "name": "setValue",
              "triggers": []
            },
            {
              "name": "written",
              "triggers": [
                "written"
              ]
            },
            {
              "name": "subscibed",
              "triggers": [
                "subscibed"
              ]
            },
            {
              "name": "unsubscribed",
              "triggers": [
                "unsubscribed"
              ]
            }
          ]
        },
        {
          "name": "AmbientLightLabel",
          "type": "EmbeddedIconLabelDisplay",
          "variants": [
            "embedded",
            "triggers",
            "abilities",
            "properties",
            "variables",
            "rpk"
          ],
          "properties": {
            "errorData": {},
            "code": {
              "trigger": "\treturn ATMO_Status_Success;",
              "displayPage": "\n\tATMO_UI_Page_DisplayPageByCoord(ATMO_PROPERTY(AmbientLightLabel, x), ATMO_PROPERTY(AmbientLightLabel, y), false);\n\treturn ATMO_Status_Success;\n\t",
              "onDisplayed": "\n\treturn ATMO_Status_Success;\n    ",
              "topRightButtonPressed": "\n\treturn ATMO_Status_Success;\n\t",
              "bottomRightButtonPressed": "\n\treturn ATMO_Status_Success;\n\t",
              "topLeftButtonPressed": "\n\treturn ATMO_Status_Success;\n\t",
              "bottomLeftButtonPressed": "\n\treturn ATMO_Status_Success;\n\t",
              "setup": "\n    ATMO_UI_PAGE_Config_t config;\n\tconfig.hidden = ATMO_PROPERTY(AmbientLightLabel, pageHidden);\n\tconfig.textColor = ATMO_PROPERTY(AmbientLightLabel, textColor);\n    config.activeButtons = ATMO_UI_Page_GetButtonMask(ATMO_PROPERTY(AmbientLightLabel, topRightButtonEnabled),\n    ATMO_PROPERTY(AmbientLightLabel,bottomRightButtonEnabled), ATMO_PROPERTY(AmbientLightLabel, topLeftButtonEnabled), ATMO_PROPERTY(AmbientLightLabel, bottomLeftButtonEnabled));\n\tconfig.x = ATMO_PROPERTY(AmbientLightLabel, x);\n    config.x = ATMO_PROPERTY(AmbientLightLabel, x);\n    config.y = ATMO_PROPERTY(AmbientLightLabel, y);\n\tstrncpy(config.topLeftButtonLabel, ATMO_PROPERTY(AmbientLightLabel, topLeftButtonLabel), ATMO_BUTTON_LABEL_MAXLEN);\n\tstrncpy(config.topRightButtonLabel, ATMO_PROPERTY(AmbientLightLabel, topRightButtonLabel), ATMO_BUTTON_LABEL_MAXLEN);\n\tstrncpy(config.bottomLeftButtonLabel, ATMO_PROPERTY(AmbientLightLabel, bottomLeftButtonLabel), ATMO_BUTTON_LABEL_MAXLEN);\n\tstrncpy(config.bottomRightButtonLabel, ATMO_PROPERTY(AmbientLightLabel, bottomRightButtonLabel), ATMO_BUTTON_LABEL_MAXLEN);\n    config.spanX = ATMO_PROPERTY(AmbientLightLabel, spanX);\n\tconfig.spanY = ATMO_PROPERTY(AmbientLightLabel, spanY);\n    config.title = ATMO_PROPERTY(AmbientLightLabel, pageTitle);\n    config.titleHidden = ATMO_PROPERTY(AmbientLightLabel, titleHidden);\n\tATMO_UI_SINGLEICONTEXT_Init(&config);\n\tATMO_VARIABLE(AmbientLightLabel, pageHandle) = config.templateInstance;\n    ATMO_UI_SINGLEICONTEXT_SetMainText(config.templateInstance, ATMO_PROPERTY(AmbientLightLabel, label));\n    ATMO_UI_SINGLEICONTEXT_SetIcon(config.templateInstance, ATMO_PROPERTY(AmbientLightLabel, icon));\n    ATMO_UI_SINGLEICONTEXT_RegisterOnDisplayedAbilityHandle(ATMO_VARIABLE(AmbientLightLabel,pageHandle), ATMO_ABILITY(AmbientLightLabel, onDisplayed));\n    ATMO_UI_SINGLEICONTEXT_RegisterButtonAbilityHandle(ATMO_VARIABLE(AmbientLightLabel,pageHandle), 1, ATMO_ABILITY(AmbientLightLabel, topRightButtonPressed));\n\tATMO_UI_SINGLEICONTEXT_RegisterButtonAbilityHandle(ATMO_VARIABLE(AmbientLightLabel,pageHandle), 2, ATMO_ABILITY(AmbientLightLabel, bottomRightButtonPressed));\n\tATMO_UI_SINGLEICONTEXT_RegisterButtonAbilityHandle(ATMO_VARIABLE(AmbientLightLabel,pageHandle), 3, ATMO_ABILITY(AmbientLightLabel, topLeftButtonPressed));\n    ATMO_UI_SINGLEICONTEXT_RegisterButtonAbilityHandle(ATMO_VARIABLE(AmbientLightLabel,pageHandle), 4, ATMO_ABILITY(AmbientLightLabel, bottomLeftButtonPressed));\n    ATMO_UI_SINGLEICONTEXT_RegisterOnLeaveAbilityHandle(config.templateInstance, ATMO_ABILITY(AmbientLightLabel, onLeave));\n\treturn ATMO_Status_Success;\n    ",
              "onLeave": "\n\treturn ATMO_Status_Success;\n\t",
              "setLabel": "\n    char label[32];\n    if(ATMO_GetString(in, label, 32) == ATMO_Status_Success)\n    {\n        ATMO_UI_SINGLEICONTEXT_SetMainText(ATMO_VARIABLE(AmbientLightLabel,pageHandle), label);\n    }\n    else\n    {\n        return ATMO_Status_Fail;\n    }\n\n    return ATMO_Status_Success;\n    "
            },
            "variables": {
              "pageHandle": {
                "type": "ATMO_DriverInstanceHandle_t"
              }
            },
            "embeddedPropertyConversions": {
              "pageTitle": "string",
              "topRightButtonLabel": "string",
              "bottomRightButtonLabel": "string",
              "topLeftButtonLabel": "string",
              "bottomLeftButtonLabel": "string",
              "label": "string"
            },
            "codeUserChanged": {
              "displayPage": false,
              "onDisplayed": false,
              "topRightButtonPressed": false,
              "bottomRightButtonPressed": false,
              "topLeftButtonPressed": false,
              "bottomLeftButtonPressed": false,
              "setup": false,
              "onLeave": false,
              "setLabel": false
            },
            "textColor": "GUI_BLUE",
            "pageTitle": "Ambient Light",
            "titleHidden": false,
            "pageHidden": false,
            "topRightButtonLabel": "",
            "topRightButtonEnabled": false,
            "bottomRightButtonLabel": "",
            "bottomRightButtonEnabled": false,
            "topLeftButtonLabel": "",
            "topLeftButtonEnabled": false,
            "bottomLeftButtonLabel": "",
            "bottomLeftButtonEnabled": false,
            "x": 0,
            "y": 0,
            "spanX": 1,
            "spanY": 1,
            "icon": "icon_applications_light",
            "label": ""
          },
          "meta": {
            "editorX": 591,
            "editorY": 84,
            "lastTrigger": "onDisplayed"
          },
          "triggers": {
            "triggered": [],
            "onDisplayed": [],
            "topRightButtonPressed": [],
            "bottomRightButtonPressed": [],
            "topLeftButtonPressed": [],
            "bottomLeftButtonPressed": [],
            "onLeave": []
          },
          "interruptAbilities": {
            "trigger": false,
            "displayPage": false,
            "onDisplayed": false,
            "topRightButtonPressed": false,
            "bottomRightButtonPressed": false,
            "topLeftButtonPressed": false,
            "bottomLeftButtonPressed": false,
            "setup": false,
            "onLeave": false,
            "setLabel": false
          },
          "abilities": [
            {
              "name": "trigger",
              "triggers": [
                "triggered"
              ]
            },
            {
              "name": "displayPage",
              "triggers": []
            },
            {
              "name": "onDisplayed",
              "triggers": [
                "onDisplayed"
              ]
            },
            {
              "name": "topRightButtonPressed",
              "triggers": [
                "topRightButtonPressed"
              ]
            },
            {
              "name": "bottomRightButtonPressed",
              "triggers": [
                "bottomRightButtonPressed"
              ]
            },
            {
              "name": "topLeftButtonPressed",
              "triggers": [
                "topLeftButtonPressed"
              ]
            },
            {
              "name": "bottomLeftButtonPressed",
              "triggers": [
                "bottomLeftButtonPressed"
              ]
            },
            {
              "name": "setup",
              "triggers": []
            },
            {
              "name": "onLeave",
              "triggers": [
                "onLeave"
              ]
            },
            {
              "name": "setLabel",
              "triggers": []
            }
          ]
        },
        {
          "name": "ENS210TemperatureHumidity",
          "type": "EmbeddedENS210",
          "variants": [
            "embedded",
            "triggers",
            "abilities",
            "properties",
            "variables"
          ],
          "properties": {
            "errorData": {},
            "code": {
              "trigger": "\treturn ATMO_Status_Success;",
              "setup": "\tATMO_ENS210_Config_t config;\n\tconfig.address = ATMO_PROPERTY(ENS210TemperatureHumidity, i2cAddress);\n\tconfig.i2cDriverInstance = ATMO_PROPERTY(ENS210TemperatureHumidity, i2cInstance);\n\tconfig.tempCalibrationOffset = ATMO_PROPERTY(ENS210TemperatureHumidity, tempCalibrationOffset);\n\n\treturn ( ATMO_ENS210_Init(&config) == ATMO_ENS210_Status_Success ) ? ATMO_Status_Success : ATMO_Status_Fail;\n",
              "setEnabled": "ATMO_ENS210_SetEnabled(true);\nreturn ATMO_Status_Success;",
              "setDisabled": "ATMO_ENS210_SetEnabled(false);\nreturn ATMO_Status_Success;",
              "setEnabledDisabled": "bool enabled = false;\nATMO_GetBool(in, &enabled);\nATMO_ENS210_SetEnabled(enabled);\nreturn ATMO_Status_Success;",
              "readTemperature": "    float tempC;\n    \n    if(ATMO_ENS210_GetTemperatureFloat(&tempC) == ATMO_ENS210_Status_Success)\n    {\n        ATMO_CreateValueFloat(out, tempC);\n    }\n    else\n    {\n        ATMO_CreateValueVoid(out);\n    }\n    \n    return ATMO_Status_Success;",
              "readHumidity": "    float humidityPct;\n\n    if(ATMO_ENS210_GetHumidityFloat(&humidityPct) == ATMO_ENS210_Status_Success)\n    {\n        ATMO_CreateValueFloat(out, humidityPct);\n    }\n    else\n    {\n        ATMO_CreateValueVoid(out);\n    }\n    \n    return ATMO_Status_Success;"
            },
            "variables": {},
            "embeddedPropertyConversions": {},
            "codeUserChanged": {
              "setup": false,
              "setEnabled": false,
              "setDisabled": false,
              "setEnabledDisabled": false,
              "readTemperature": false,
              "readHumidity": false
            },
            "i2cInstance": "ATMO_DRIVERINSTANCE_I2C_I2C2",
            "i2cAddress": "0x43",
            "tempCalibrationOffset": -7
          },
          "meta": {
            "editorX": 282,
            "editorY": 348,
            "lastTrigger": "humidityRead"
          },
          "triggers": {
            "triggered": [],
            "temperatureRead": [
              {
                "mapping": {},
                "targetOrder": [],
                "targetElement": "BLECharacteristicTemperature",
                "targetAbility": "setValue"
              },
              {
                "mapping": {},
                "targetOrder": [],
                "targetElement": "TempToStr",
                "targetAbility": "trigger"
              },
              {
                "mapping": {},
                "targetOrder": [],
                "targetElement": "HumidityToStr",
                "targetAbility": "trigger"
              }
            ],
            "humidityRead": [
              {
                "mapping": {},
                "targetOrder": [],
                "targetElement": "BLECharacteristicHumidity",
                "targetAbility": "setValue"
              }
            ]
          },
          "interruptAbilities": {
            "trigger": false,
            "setup": false,
            "setEnabled": false,
            "setDisabled": false,
            "setEnabledDisabled": false,
            "readTemperature": false,
            "readHumidity": false
          },
          "abilities": [
            {
              "name": "trigger",
              "triggers": [
                "triggered"
              ]
            },
            {
              "name": "setup",
              "triggers": []
            },
            {
              "name": "setEnabled",
              "triggers": []
            },
            {
              "name": "setDisabled",
              "triggers": []
            },
            {
              "name": "setEnabledDisabled",
              "triggers": []
            },
            {
              "name": "readTemperature",
              "triggers": [
                "temperatureRead"
              ]
            },
            {
              "name": "readHumidity",
              "triggers": [
                "humidityRead"
              ]
            }
          ]
        },
        {
          "name": "BLECharacteristicTemperature",
          "type": "EmbeddedBLECharacteristicCustom",
          "variants": [
            "embedded",
            "triggers",
            "abilities",
            "properties",
            "variables",
            "ble"
          ],
          "properties": {
            "errorData": {},
            "code": {
              "trigger": "\treturn ATMO_Status_Success;",
              "setup": "\n\tATMO_BLE_GATTSAddService(\n\t\tATMO_PROPERTY(BLECharacteristicTemperature, instance),\n\t\t&ATMO_VARIABLE(BLECharacteristicTemperature, bleServiceHandle), \n\t\tATMO_PROPERTY(BLECharacteristicTemperature, bleServiceUuid));\n\t\n\tuint8_t property = 0;\n\tuint8_t permission = 0;\n\t\n\tproperty |= ATMO_PROPERTY(BLECharacteristicTemperature, read) ? ATMO_BLE_Property_Read : 0;\n\tproperty |= ATMO_PROPERTY(BLECharacteristicTemperature, write) ? ATMO_BLE_Property_Write : 0;\n\tproperty |= ATMO_PROPERTY(BLECharacteristicTemperature, notify) ? ATMO_BLE_Property_Notify : 0;\n\n\tpermission |= ATMO_PROPERTY(BLECharacteristicTemperature, read) ? ATMO_BLE_Permission_Read : 0;\n\tpermission |= ATMO_PROPERTY(BLECharacteristicTemperature, write) ? ATMO_BLE_Permission_Write : 0;\n\n\tATMO_DATATYPE types[3] = {ATMO_PROPERTY(BLECharacteristicTemperature, writeDataType), ATMO_PROPERTY(BLECharacteristicTemperature, readDataType), ATMO_PROPERTY(BLECharacteristicTemperature, notifyDataType)};\n\t\n\tATMO_BLE_GATTSAddCharacteristic(\n\t\tATMO_PROPERTY(BLECharacteristicTemperature, instance),\n\t\t&ATMO_VARIABLE(BLECharacteristicTemperature, bleCharacteristicHandle), \n\t\tATMO_VARIABLE(BLECharacteristicTemperature, bleServiceHandle), \n\t\tATMO_PROPERTY(BLECharacteristicTemperature, bleCharacteristicUuid), \n\t\tproperty, permission, ATMO_GetMaxValueSize(3, 64, types));\n\t\n\tATMO_BLE_GATTSRegisterCharacteristicAbilityHandle(\n\t\tATMO_PROPERTY(BLECharacteristicTemperature, instance),\n\t\tATMO_VARIABLE(BLECharacteristicTemperature, bleCharacteristicHandle), \n\t\tATMO_BLE_Characteristic_Written, \n\t\tATMO_ABILITY(BLECharacteristicTemperature, written));\n\t\n\treturn ATMO_Status_Success;\n\t",
              "setValue": "\n\t\n\t// Convert to the desired write data type\n\tATMO_Value_t convertedValue;\n\tATMO_InitValue(&convertedValue);\n\tATMO_CreateValueConverted(&convertedValue, ATMO_PROPERTY(BLECharacteristicTemperature, readDataType), in);\n\n\tATMO_BLE_GATTSSetCharacteristic(\n\t\tATMO_PROPERTY(BLECharacteristicTemperature, instance),\n\t\tATMO_VARIABLE(BLECharacteristicTemperature, bleCharacteristicHandle),\n\t\tconvertedValue.size, \n\t\t(uint8_t *)convertedValue.data,\n\t\tNULL);\n\t\n\tATMO_FreeValue(&convertedValue);\n\t\t\n\treturn ATMO_Status_Success;\n\t",
              "written": "\n\tATMO_CreateValueConverted(out, ATMO_PROPERTY(BLECharacteristicTemperature, writeDataType), in);\n\treturn ATMO_Status_Success;\n\t",
              "subscibed": "\treturn ATMO_Status_Success;",
              "unsubscribed": "\treturn ATMO_Status_Success;"
            },
            "variables": {
              "bleServiceHandle": {
                "type": "ATMO_BLE_Handle_t"
              },
              "bleCharacteristicHandle": {
                "type": "ATMO_BLE_Handle_t"
              }
            },
            "embeddedPropertyConversions": {
              "bleServiceUuid": "string",
              "bleCharacteristicUuid": "string"
            },
            "codeUserChanged": {
              "setup": false,
              "setValue": false,
              "written": false,
              "subscibed": false,
              "unsubscribed": false
            },
            "instance": "ATMO_DRIVERINSTANCE_BLE_BLE1",
            "bleServiceUuid": "647b1c04-ce8f-4e1f-aea6-09e28d875c51",
            "bleCharacteristicUuid": "647b1c04-ce8f-4e1f-aea6-09e28d875c53",
            "read": true,
            "write": false,
            "notify": false,
            "readDataType": "ATMO_DATATYPE_FLOAT",
            "writeDataType": "ATMO_DATATYPE_STRING",
            "notifyDataType": "ATMO_DATATYPE_STRING"
          },
          "meta": {
            "editorX": 453,
            "editorY": 333,
            "lastTrigger": "written"
          },
          "triggers": {
            "triggered": [],
            "written": [],
            "subscibed": [],
            "unsubscribed": []
          },
          "interruptAbilities": {
            "trigger": false,
            "setup": false,
            "setValue": "valueSet",
            "written": true,
            "subscibed": true,
            "unsubscribed": true
          },
          "abilities": [
            {
              "name": "trigger",
              "triggers": [
                "triggered"
              ]
            },
            {
              "name": "setup",
              "triggers": []
            },
            {
              "name": "setValue",
              "triggers": []
            },
            {
              "name": "written",
              "triggers": [
                "written"
              ]
            },
            {
              "name": "subscibed",
              "triggers": [
                "subscibed"
              ]
            },
            {
              "name": "unsubscribed",
              "triggers": [
                "unsubscribed"
              ]
            }
          ]
        },
        {
          "name": "TemperatureLabel",
          "type": "EmbeddedIconLabelDisplay",
          "variants": [
            "embedded",
            "triggers",
            "abilities",
            "properties",
            "variables",
            "rpk"
          ],
          "properties": {
            "errorData": {},
            "code": {
              "trigger": "\treturn ATMO_Status_Success;",
              "displayPage": "\n\tATMO_UI_Page_DisplayPageByCoord(ATMO_PROPERTY(TemperatureLabel, x), ATMO_PROPERTY(TemperatureLabel, y), false);\n\treturn ATMO_Status_Success;\n\t",
              "onDisplayed": "\n\treturn ATMO_Status_Success;\n    ",
              "topRightButtonPressed": "\n\treturn ATMO_Status_Success;\n\t",
              "bottomRightButtonPressed": "\n\treturn ATMO_Status_Success;\n\t",
              "topLeftButtonPressed": "\n\treturn ATMO_Status_Success;\n\t",
              "bottomLeftButtonPressed": "\n\treturn ATMO_Status_Success;\n\t",
              "setup": "\n    ATMO_UI_PAGE_Config_t config;\n\tconfig.hidden = ATMO_PROPERTY(TemperatureLabel, pageHidden);\n\tconfig.textColor = ATMO_PROPERTY(TemperatureLabel, textColor);\n    config.activeButtons = ATMO_UI_Page_GetButtonMask(ATMO_PROPERTY(TemperatureLabel, topRightButtonEnabled),\n    ATMO_PROPERTY(TemperatureLabel,bottomRightButtonEnabled), ATMO_PROPERTY(TemperatureLabel, topLeftButtonEnabled), ATMO_PROPERTY(TemperatureLabel, bottomLeftButtonEnabled));\n\tconfig.x = ATMO_PROPERTY(TemperatureLabel, x);\n    config.x = ATMO_PROPERTY(TemperatureLabel, x);\n    config.y = ATMO_PROPERTY(TemperatureLabel, y);\n\tstrncpy(config.topLeftButtonLabel, ATMO_PROPERTY(TemperatureLabel, topLeftButtonLabel), ATMO_BUTTON_LABEL_MAXLEN);\n\tstrncpy(config.topRightButtonLabel, ATMO_PROPERTY(TemperatureLabel, topRightButtonLabel), ATMO_BUTTON_LABEL_MAXLEN);\n\tstrncpy(config.bottomLeftButtonLabel, ATMO_PROPERTY(TemperatureLabel, bottomLeftButtonLabel), ATMO_BUTTON_LABEL_MAXLEN);\n\tstrncpy(config.bottomRightButtonLabel, ATMO_PROPERTY(TemperatureLabel, bottomRightButtonLabel), ATMO_BUTTON_LABEL_MAXLEN);\n    config.spanX = ATMO_PROPERTY(TemperatureLabel, spanX);\n\tconfig.spanY = ATMO_PROPERTY(TemperatureLabel, spanY);\n    config.title = ATMO_PROPERTY(TemperatureLabel, pageTitle);\n    config.titleHidden = ATMO_PROPERTY(TemperatureLabel, titleHidden);\n\tATMO_UI_SINGLEICONTEXT_Init(&config);\n\tATMO_VARIABLE(TemperatureLabel, pageHandle) = config.templateInstance;\n    ATMO_UI_SINGLEICONTEXT_SetMainText(config.templateInstance, ATMO_PROPERTY(TemperatureLabel, label));\n    ATMO_UI_SINGLEICONTEXT_SetIcon(config.templateInstance, ATMO_PROPERTY(TemperatureLabel, icon));\n    ATMO_UI_SINGLEICONTEXT_RegisterOnDisplayedAbilityHandle(ATMO_VARIABLE(TemperatureLabel,pageHandle), ATMO_ABILITY(TemperatureLabel, onDisplayed));\n    ATMO_UI_SINGLEICONTEXT_RegisterButtonAbilityHandle(ATMO_VARIABLE(TemperatureLabel,pageHandle), 1, ATMO_ABILITY(TemperatureLabel, topRightButtonPressed));\n\tATMO_UI_SINGLEICONTEXT_RegisterButtonAbilityHandle(ATMO_VARIABLE(TemperatureLabel,pageHandle), 2, ATMO_ABILITY(TemperatureLabel, bottomRightButtonPressed));\n\tATMO_UI_SINGLEICONTEXT_RegisterButtonAbilityHandle(ATMO_VARIABLE(TemperatureLabel,pageHandle), 3, ATMO_ABILITY(TemperatureLabel, topLeftButtonPressed));\n    ATMO_UI_SINGLEICONTEXT_RegisterButtonAbilityHandle(ATMO_VARIABLE(TemperatureLabel,pageHandle), 4, ATMO_ABILITY(TemperatureLabel, bottomLeftButtonPressed));\n    ATMO_UI_SINGLEICONTEXT_RegisterOnLeaveAbilityHandle(config.templateInstance, ATMO_ABILITY(TemperatureLabel, onLeave));\n\treturn ATMO_Status_Success;\n    ",
              "onLeave": "\n\treturn ATMO_Status_Success;\n\t",
              "setLabel": "\n    char label[32];\n    if(ATMO_GetString(in, label, 32) == ATMO_Status_Success)\n    {\n        ATMO_UI_SINGLEICONTEXT_SetMainText(ATMO_VARIABLE(TemperatureLabel,pageHandle), label);\n    }\n    else\n    {\n        return ATMO_Status_Fail;\n    }\n\n    return ATMO_Status_Success;\n    "
            },
            "variables": {
              "pageHandle": {
                "type": "ATMO_DriverInstanceHandle_t"
              }
            },
            "embeddedPropertyConversions": {
              "pageTitle": "string",
              "topRightButtonLabel": "string",
              "bottomRightButtonLabel": "string",
              "topLeftButtonLabel": "string",
              "bottomLeftButtonLabel": "string",
              "label": "string"
            },
            "codeUserChanged": {
              "displayPage": false,
              "onDisplayed": false,
              "topRightButtonPressed": false,
              "bottomRightButtonPressed": false,
              "topLeftButtonPressed": false,
              "bottomLeftButtonPressed": false,
              "setup": false,
              "onLeave": false,
              "setLabel": false
            },
            "textColor": "GUI_BLUE",
            "pageTitle": "Temperature",
            "titleHidden": false,
            "pageHidden": false,
            "topRightButtonLabel": "",
            "topRightButtonEnabled": false,
            "bottomRightButtonLabel": "",
            "bottomRightButtonEnabled": false,
            "topLeftButtonLabel": "",
            "topLeftButtonEnabled": false,
            "bottomLeftButtonLabel": "",
            "bottomLeftButtonEnabled": false,
            "x": "1",
            "y": 0,
            "spanX": 1,
            "spanY": 1,
            "icon": "icon_sensors_temphum",
            "label": ""
          },
          "meta": {
            "editorX": 597,
            "editorY": 256,
            "lastTrigger": "onDisplayed"
          },
          "triggers": {
            "triggered": [],
            "onDisplayed": [],
            "topRightButtonPressed": [],
            "bottomRightButtonPressed": [],
            "topLeftButtonPressed": [],
            "bottomLeftButtonPressed": [],
            "onLeave": []
          },
          "interruptAbilities": {
            "trigger": false,
            "displayPage": false,
            "onDisplayed": false,
            "topRightButtonPressed": false,
            "bottomRightButtonPressed": false,
            "topLeftButtonPressed": false,
            "bottomLeftButtonPressed": false,
            "setup": false,
            "onLeave": false,
            "setLabel": false
          },
          "abilities": [
            {
              "name": "trigger",
              "triggers": [
                "triggered"
              ]
            },
            {
              "name": "displayPage",
              "triggers": []
            },
            {
              "name": "onDisplayed",
              "triggers": [
                "onDisplayed"
              ]
            },
            {
              "name": "topRightButtonPressed",
              "triggers": [
                "topRightButtonPressed"
              ]
            },
            {
              "name": "bottomRightButtonPressed",
              "triggers": [
                "bottomRightButtonPressed"
              ]
            },
            {
              "name": "topLeftButtonPressed",
              "triggers": [
                "topLeftButtonPressed"
              ]
            },
            {
              "name": "bottomLeftButtonPressed",
              "triggers": [
                "bottomLeftButtonPressed"
              ]
            },
            {
              "name": "setup",
              "triggers": []
            },
            {
              "name": "onLeave",
              "triggers": [
                "onLeave"
              ]
            },
            {
              "name": "setLabel",
              "triggers": []
            }
          ]
        },
        {
          "name": "Humidity",
          "type": "EmbeddedIconLabelDisplay",
          "variants": [
            "embedded",
            "triggers",
            "abilities",
            "properties",
            "variables",
            "rpk"
          ],
          "properties": {
            "errorData": {},
            "code": {
              "trigger": "\treturn ATMO_Status_Success;",
              "displayPage": "\n\tATMO_UI_Page_DisplayPageByCoord(ATMO_PROPERTY(Humidity, x), ATMO_PROPERTY(Humidity, y), false);\n\treturn ATMO_Status_Success;\n\t",
              "onDisplayed": "\n\treturn ATMO_Status_Success;\n    ",
              "topRightButtonPressed": "\n\treturn ATMO_Status_Success;\n\t",
              "bottomRightButtonPressed": "\n\treturn ATMO_Status_Success;\n\t",
              "topLeftButtonPressed": "\n\treturn ATMO_Status_Success;\n\t",
              "bottomLeftButtonPressed": "\n\treturn ATMO_Status_Success;\n\t",
              "setup": "\n    ATMO_UI_PAGE_Config_t config;\n\tconfig.hidden = ATMO_PROPERTY(Humidity, pageHidden);\n\tconfig.textColor = ATMO_PROPERTY(Humidity, textColor);\n    config.activeButtons = ATMO_UI_Page_GetButtonMask(ATMO_PROPERTY(Humidity, topRightButtonEnabled),\n    ATMO_PROPERTY(Humidity,bottomRightButtonEnabled), ATMO_PROPERTY(Humidity, topLeftButtonEnabled), ATMO_PROPERTY(Humidity, bottomLeftButtonEnabled));\n\tconfig.x = ATMO_PROPERTY(Humidity, x);\n    config.x = ATMO_PROPERTY(Humidity, x);\n    config.y = ATMO_PROPERTY(Humidity, y);\n\tstrncpy(config.topLeftButtonLabel, ATMO_PROPERTY(Humidity, topLeftButtonLabel), ATMO_BUTTON_LABEL_MAXLEN);\n\tstrncpy(config.topRightButtonLabel, ATMO_PROPERTY(Humidity, topRightButtonLabel), ATMO_BUTTON_LABEL_MAXLEN);\n\tstrncpy(config.bottomLeftButtonLabel, ATMO_PROPERTY(Humidity, bottomLeftButtonLabel), ATMO_BUTTON_LABEL_MAXLEN);\n\tstrncpy(config.bottomRightButtonLabel, ATMO_PROPERTY(Humidity, bottomRightButtonLabel), ATMO_BUTTON_LABEL_MAXLEN);\n    config.spanX = ATMO_PROPERTY(Humidity, spanX);\n\tconfig.spanY = ATMO_PROPERTY(Humidity, spanY);\n    config.title = ATMO_PROPERTY(Humidity, pageTitle);\n    config.titleHidden = ATMO_PROPERTY(Humidity, titleHidden);\n\tATMO_UI_SINGLEICONTEXT_Init(&config);\n\tATMO_VARIABLE(Humidity, pageHandle) = config.templateInstance;\n    ATMO_UI_SINGLEICONTEXT_SetMainText(config.templateInstance, ATMO_PROPERTY(Humidity, label));\n    ATMO_UI_SINGLEICONTEXT_SetIcon(config.templateInstance, ATMO_PROPERTY(Humidity, icon));\n    ATMO_UI_SINGLEICONTEXT_RegisterOnDisplayedAbilityHandle(ATMO_VARIABLE(Humidity,pageHandle), ATMO_ABILITY(Humidity, onDisplayed));\n    ATMO_UI_SINGLEICONTEXT_RegisterButtonAbilityHandle(ATMO_VARIABLE(Humidity,pageHandle), 1, ATMO_ABILITY(Humidity, topRightButtonPressed));\n\tATMO_UI_SINGLEICONTEXT_RegisterButtonAbilityHandle(ATMO_VARIABLE(Humidity,pageHandle), 2, ATMO_ABILITY(Humidity, bottomRightButtonPressed));\n\tATMO_UI_SINGLEICONTEXT_RegisterButtonAbilityHandle(ATMO_VARIABLE(Humidity,pageHandle), 3, ATMO_ABILITY(Humidity, topLeftButtonPressed));\n    ATMO_UI_SINGLEICONTEXT_RegisterButtonAbilityHandle(ATMO_VARIABLE(Humidity,pageHandle), 4, ATMO_ABILITY(Humidity, bottomLeftButtonPressed));\n    ATMO_UI_SINGLEICONTEXT_RegisterOnLeaveAbilityHandle(config.templateInstance, ATMO_ABILITY(Humidity, onLeave));\n\treturn ATMO_Status_Success;\n    ",
              "onLeave": "\n\treturn ATMO_Status_Success;\n\t",
              "setLabel": "\n    char label[32];\n    if(ATMO_GetString(in, label, 32) == ATMO_Status_Success)\n    {\n        ATMO_UI_SINGLEICONTEXT_SetMainText(ATMO_VARIABLE(Humidity,pageHandle), label);\n    }\n    else\n    {\n        return ATMO_Status_Fail;\n    }\n\n    return ATMO_Status_Success;\n    "
            },
            "variables": {
              "pageHandle": {
                "type": "ATMO_DriverInstanceHandle_t"
              }
            },
            "embeddedPropertyConversions": {
              "pageTitle": "string",
              "topRightButtonLabel": "string",
              "bottomRightButtonLabel": "string",
              "topLeftButtonLabel": "string",
              "bottomLeftButtonLabel": "string",
              "label": "string"
            },
            "codeUserChanged": {
              "displayPage": false,
              "onDisplayed": false,
              "topRightButtonPressed": false,
              "bottomRightButtonPressed": false,
              "topLeftButtonPressed": false,
              "bottomLeftButtonPressed": false,
              "setup": false,
              "onLeave": false,
              "setLabel": false
            },
            "textColor": "GUI_BLUE",
            "pageTitle": "Humidity",
            "titleHidden": false,
            "pageHidden": false,
            "topRightButtonLabel": "",
            "topRightButtonEnabled": false,
            "bottomRightButtonLabel": "",
            "bottomRightButtonEnabled": false,
            "topLeftButtonLabel": "",
            "topLeftButtonEnabled": false,
            "bottomLeftButtonLabel": "",
            "bottomLeftButtonEnabled": false,
            "x": "2",
            "y": 0,
            "spanX": 1,
            "spanY": 1,
            "icon": "icon_sensors_temphum",
            "label": ""
          },
          "meta": {
            "editorX": 594,
            "editorY": 408,
            "lastTrigger": "onDisplayed"
          },
          "triggers": {
            "triggered": [],
            "onDisplayed": [],
            "topRightButtonPressed": [],
            "bottomRightButtonPressed": [],
            "topLeftButtonPressed": [],
            "bottomLeftButtonPressed": [],
            "onLeave": []
          },
          "interruptAbilities": {
            "trigger": false,
            "displayPage": false,
            "onDisplayed": false,
            "topRightButtonPressed": false,
            "bottomRightButtonPressed": false,
            "topLeftButtonPressed": false,
            "bottomLeftButtonPressed": false,
            "setup": false,
            "onLeave": false,
            "setLabel": false
          },
          "abilities": [
            {
              "name": "trigger",
              "triggers": [
                "triggered"
              ]
            },
            {
              "name": "displayPage",
              "triggers": []
            },
            {
              "name": "onDisplayed",
              "triggers": [
                "onDisplayed"
              ]
            },
            {
              "name": "topRightButtonPressed",
              "triggers": [
                "topRightButtonPressed"
              ]
            },
            {
              "name": "bottomRightButtonPressed",
              "triggers": [
                "bottomRightButtonPressed"
              ]
            },
            {
              "name": "topLeftButtonPressed",
              "triggers": [
                "topLeftButtonPressed"
              ]
            },
            {
              "name": "bottomLeftButtonPressed",
              "triggers": [
                "bottomLeftButtonPressed"
              ]
            },
            {
              "name": "setup",
              "triggers": []
            },
            {
              "name": "onLeave",
              "triggers": [
                "onLeave"
              ]
            },
            {
              "name": "setLabel",
              "triggers": []
            }
          ]
        },
        {
          "name": "BLECharacteristicHumidity",
          "type": "EmbeddedBLECharacteristicCustom",
          "variants": [
            "embedded",
            "triggers",
            "abilities",
            "properties",
            "variables",
            "ble"
          ],
          "properties": {
            "errorData": {},
            "code": {
              "trigger": "\treturn ATMO_Status_Success;",
              "setup": "\n\tATMO_BLE_GATTSAddService(\n\t\tATMO_PROPERTY(BLECharacteristicHumidity, instance),\n\t\t&ATMO_VARIABLE(BLECharacteristicHumidity, bleServiceHandle), \n\t\tATMO_PROPERTY(BLECharacteristicHumidity, bleServiceUuid));\n\t\n\tuint8_t property = 0;\n\tuint8_t permission = 0;\n\t\n\tproperty |= ATMO_PROPERTY(BLECharacteristicHumidity, read) ? ATMO_BLE_Property_Read : 0;\n\tproperty |= ATMO_PROPERTY(BLECharacteristicHumidity, write) ? ATMO_BLE_Property_Write : 0;\n\tproperty |= ATMO_PROPERTY(BLECharacteristicHumidity, notify) ? ATMO_BLE_Property_Notify : 0;\n\n\tpermission |= ATMO_PROPERTY(BLECharacteristicHumidity, read) ? ATMO_BLE_Permission_Read : 0;\n\tpermission |= ATMO_PROPERTY(BLECharacteristicHumidity, write) ? ATMO_BLE_Permission_Write : 0;\n\n\tATMO_DATATYPE types[3] = {ATMO_PROPERTY(BLECharacteristicHumidity, writeDataType), ATMO_PROPERTY(BLECharacteristicHumidity, readDataType), ATMO_PROPERTY(BLECharacteristicHumidity, notifyDataType)};\n\t\n\tATMO_BLE_GATTSAddCharacteristic(\n\t\tATMO_PROPERTY(BLECharacteristicHumidity, instance),\n\t\t&ATMO_VARIABLE(BLECharacteristicHumidity, bleCharacteristicHandle), \n\t\tATMO_VARIABLE(BLECharacteristicHumidity, bleServiceHandle), \n\t\tATMO_PROPERTY(BLECharacteristicHumidity, bleCharacteristicUuid), \n\t\tproperty, permission, ATMO_GetMaxValueSize(3, 64, types));\n\t\n\tATMO_BLE_GATTSRegisterCharacteristicAbilityHandle(\n\t\tATMO_PROPERTY(BLECharacteristicHumidity, instance),\n\t\tATMO_VARIABLE(BLECharacteristicHumidity, bleCharacteristicHandle), \n\t\tATMO_BLE_Characteristic_Written, \n\t\tATMO_ABILITY(BLECharacteristicHumidity, written));\n\t\n\treturn ATMO_Status_Success;\n\t",
              "setValue": "\n\t\n\t// Convert to the desired write data type\n\tATMO_Value_t convertedValue;\n\tATMO_InitValue(&convertedValue);\n\tATMO_CreateValueConverted(&convertedValue, ATMO_PROPERTY(BLECharacteristicHumidity, readDataType), in);\n\n\tATMO_BLE_GATTSSetCharacteristic(\n\t\tATMO_PROPERTY(BLECharacteristicHumidity, instance),\n\t\tATMO_VARIABLE(BLECharacteristicHumidity, bleCharacteristicHandle),\n\t\tconvertedValue.size, \n\t\t(uint8_t *)convertedValue.data,\n\t\tNULL);\n\t\n\tATMO_FreeValue(&convertedValue);\n\t\t\n\treturn ATMO_Status_Success;\n\t",
              "written": "\n\tATMO_CreateValueConverted(out, ATMO_PROPERTY(BLECharacteristicHumidity, writeDataType), in);\n\treturn ATMO_Status_Success;\n\t",
              "subscibed": "\treturn ATMO_Status_Success;",
              "unsubscribed": "\treturn ATMO_Status_Success;"
            },
            "variables": {
              "bleServiceHandle": {
                "type": "ATMO_BLE_Handle_t"
              },
              "bleCharacteristicHandle": {
...

This file has been truncated, please download it to see its full contents.

BLE to InfluxDB script

Python
import sys
import binascii
import struct
import time
from bluepy.btle import UUID, Peripheral
from influxdb import InfluxDBClient
import datetime
import logging

def persists(measurement, value):
    json_body = [
        {
            "measurement": measurement,
            "tags": {
                "ble_address" : sys.argv[1]
            },
            "time": datetime.datetime.utcnow().isoformat(),
            "fields": {
                "value": value
            }
        }
    ]
    logging.info(json_body)
    influx_client.write_points(json_body)


logging.basicConfig(level=logging.INFO)
influx_client = InfluxDBClient('localhost', 8086, database='thermostat')

service_uuid = UUID("647b1c04-ce8f-4e1f-aea6-09e28d875c51")
char_light_uuid = UUID("647b1c04-ce8f-4e1f-aea6-09e28d875c52")
char_temp_uuid = UUID("647b1c04-ce8f-4e1f-aea6-09e28d875c53")
char_humid_uuid = UUID("647b1c04-ce8f-4e1f-aea6-09e28d875c54")
char_pressure_uuid = UUID("647b1c04-ce8f-4e1f-aea6-09e28d875c55")

if len(sys.argv) != 2:
    print("Fatal, must pass device address:", sys.argv[0], "<device address="">")
    quit()

p = Peripheral(sys.argv[1])
print("Connected")

#for service in p.getServices():
#    print(service.uuid)

ButtonService=p.getServiceByUUID(service_uuid)
print("Got service")
try:
    ch_ligth = ButtonService.getCharacteristics(char_light_uuid)[0]
    ch_temp = ButtonService.getCharacteristics(char_temp_uuid)[0]
    ch_humid = ButtonService.getCharacteristics(char_humid_uuid)[0]
    ch_pressure = ButtonService.getCharacteristics(char_pressure_uuid)[0]

    print("Got characteristics")
    while 1:
        print("read")
        lightVal = struct.unpack('<f', ch_ligth.read())[0]
        tempVal = struct.unpack('<f', ch_temp.read())[0]
        humidVal = struct.unpack('<f', ch_humid.read())[0]
        pressureVal = struct.unpack('<f', ch_pressure.read())[0]

        print (lightVal)
        print (tempVal)
        print (humidVal)
        print (pressureVal)

        persists("ambientLight", lightVal);
        persists("temperature", tempVal);
        persists("humidity", humidVal);
        persists("pressure", pressureVal);

        time.sleep(1)

finally:
    p.disconnect()

Grafana Dashboard

JSON
{
  "annotations": {
    "list": [
      {
        "builtIn": 1,
        "datasource": "-- Grafana --",
        "enable": true,
        "hide": true,
        "iconColor": "rgba(0, 211, 255, 1)",
        "name": "Annotations & Alerts",
        "type": "dashboard"
      }
    ]
  },
  "editable": true,
  "gnetId": null,
  "graphTooltip": 0,
  "hideControls": false,
  "id": 1,
  "links": [],
  "rows": [
    {
      "collapse": false,
      "height": 395,
      "panels": [
        {
          "aliasColors": {},
          "bars": false,
          "dashLength": 10,
          "dashes": false,
          "datasource": "Thermostat",
          "fill": 1,
          "id": 1,
          "legend": {
            "avg": false,
            "current": false,
            "max": false,
            "min": false,
            "show": false,
            "total": false,
            "values": false
          },
          "lines": true,
          "linewidth": 1,
          "links": [],
          "nullPointMode": "null",
          "percentage": false,
          "pointradius": 5,
          "points": false,
          "renderer": "flot",
          "seriesOverrides": [
            {
              "alias": "temperature.mean",
              "color": "#f9934e"
            }
          ],
          "spaceLength": 10,
          "span": 6,
          "stack": false,
          "steppedLine": false,
          "targets": [
            {
              "dsType": "influxdb",
              "groupBy": [
                {
                  "params": [
                    "$__interval"
                  ],
                  "type": "time"
                },
                {
                  "params": [
                    "null"
                  ],
                  "type": "fill"
                }
              ],
              "measurement": "temperature",
              "orderByTime": "ASC",
              "policy": "default",
              "refId": "A",
              "resultFormat": "time_series",
              "select": [
                [
                  {
                    "params": [
                      "value"
                    ],
                    "type": "field"
                  },
                  {
                    "params": [],
                    "type": "mean"
                  }
                ]
              ],
              "tags": []
            }
          ],
          "thresholds": [],
          "timeFrom": null,
          "timeShift": null,
          "title": "Temperature (°C)",
          "tooltip": {
            "shared": true,
            "sort": 0,
            "value_type": "individual"
          },
          "type": "graph",
          "xaxis": {
            "buckets": null,
            "mode": "time",
            "name": null,
            "show": true,
            "values": []
          },
          "yaxes": [
            {
              "format": "short",
              "label": null,
              "logBase": 1,
              "max": null,
              "min": null,
              "show": true
            },
            {
              "format": "short",
              "label": null,
              "logBase": 1,
              "max": null,
              "min": null,
              "show": true
            }
          ]
        },
        {
          "aliasColors": {},
          "bars": false,
          "dashLength": 10,
          "dashes": false,
          "datasource": "Thermostat",
          "fill": 1,
          "id": 3,
          "legend": {
            "avg": false,
            "current": false,
            "max": false,
            "min": false,
            "show": false,
            "total": false,
            "values": false
          },
          "lines": true,
          "linewidth": 1,
          "links": [],
          "nullPointMode": "null",
          "percentage": false,
          "pointradius": 5,
          "points": false,
          "renderer": "flot",
          "seriesOverrides": [
            {
              "alias": "humidity.mean",
              "color": "#65c5db"
            }
          ],
          "spaceLength": 10,
          "span": 6,
          "stack": false,
          "steppedLine": false,
          "targets": [
            {
              "dsType": "influxdb",
              "groupBy": [
                {
                  "params": [
                    "$__interval"
                  ],
                  "type": "time"
                },
                {
                  "params": [
                    "null"
                  ],
                  "type": "fill"
                }
              ],
              "measurement": "humidity",
              "orderByTime": "ASC",
              "policy": "default",
              "refId": "A",
              "resultFormat": "time_series",
              "select": [
                [
                  {
                    "params": [
                      "value"
                    ],
                    "type": "field"
                  },
                  {
                    "params": [],
                    "type": "mean"
                  }
                ]
              ],
              "tags": []
            }
          ],
          "thresholds": [],
          "timeFrom": null,
          "timeShift": null,
          "title": "Relative Humidity (%)",
          "tooltip": {
            "shared": true,
            "sort": 0,
            "value_type": "individual"
          },
          "type": "graph",
          "xaxis": {
            "buckets": null,
            "mode": "time",
            "name": null,
            "show": true,
            "values": []
          },
          "yaxes": [
            {
              "format": "short",
              "label": null,
              "logBase": 1,
              "max": null,
              "min": null,
              "show": true
            },
            {
              "format": "short",
              "label": null,
              "logBase": 1,
              "max": null,
              "min": null,
              "show": true
            }
          ]
        }
      ],
      "repeat": null,
      "repeatIteration": null,
      "repeatRowId": null,
      "showTitle": false,
      "title": "Dashboard Row",
      "titleSize": "h6"
    },
    {
      "collapse": false,
      "height": 367,
      "panels": [
        {
          "aliasColors": {},
          "bars": false,
          "dashLength": 10,
          "dashes": false,
          "datasource": "Thermostat",
          "fill": 1,
          "id": 2,
          "legend": {
            "avg": false,
            "current": false,
            "max": false,
            "min": false,
            "show": false,
            "total": false,
            "values": false
          },
          "lines": true,
          "linewidth": 1,
          "links": [],
          "nullPointMode": "null",
          "percentage": false,
          "pointradius": 5,
          "points": false,
          "renderer": "flot",
          "seriesOverrides": [
            {
              "alias": "ambientLight.mean",
              "color": "#e0f9d7"
            }
          ],
          "spaceLength": 10,
          "span": 6,
          "stack": false,
          "steppedLine": false,
          "targets": [
            {
              "dsType": "influxdb",
              "groupBy": [
                {
                  "params": [
                    "$__interval"
                  ],
                  "type": "time"
                },
                {
                  "params": [
                    "null"
                  ],
                  "type": "fill"
                }
              ],
              "measurement": "ambientLight",
              "orderByTime": "ASC",
              "policy": "default",
              "refId": "A",
              "resultFormat": "time_series",
              "select": [
                [
                  {
                    "params": [
                      "value"
                    ],
                    "type": "field"
                  },
                  {
                    "params": [],
                    "type": "mean"
                  }
                ]
              ],
              "tags": []
            }
          ],
          "thresholds": [],
          "timeFrom": null,
          "timeShift": null,
          "title": "Ambient Light (lux)",
          "tooltip": {
            "shared": true,
            "sort": 0,
            "value_type": "individual"
          },
          "type": "graph",
          "xaxis": {
            "buckets": null,
            "mode": "time",
            "name": null,
            "show": true,
            "values": []
          },
          "yaxes": [
            {
              "format": "short",
              "label": null,
              "logBase": 1,
              "max": null,
              "min": null,
              "show": true
            },
            {
              "format": "short",
              "label": null,
              "logBase": 1,
              "max": null,
              "min": null,
              "show": true
            }
          ]
        },
        {
          "aliasColors": {},
          "bars": false,
          "dashLength": 10,
          "dashes": false,
          "datasource": "Thermostat",
          "fill": 1,
          "id": 4,
          "legend": {
            "avg": false,
            "current": false,
            "max": false,
            "min": false,
            "show": false,
            "total": false,
            "values": false
          },
          "lines": true,
          "linewidth": 1,
          "links": [],
          "nullPointMode": "null",
          "percentage": false,
          "pointradius": 5,
          "points": false,
          "renderer": "flot",
          "seriesOverrides": [
            {
              "alias": "pressure.mean"
            }
          ],
          "spaceLength": 10,
          "span": 6,
          "stack": false,
          "steppedLine": false,
          "targets": [
            {
              "dsType": "influxdb",
              "groupBy": [
                {
                  "params": [
                    "$__interval"
                  ],
                  "type": "time"
                },
                {
                  "params": [
                    "null"
                  ],
                  "type": "fill"
                }
              ],
              "measurement": "pressure",
              "orderByTime": "ASC",
              "policy": "default",
              "refId": "A",
              "resultFormat": "time_series",
              "select": [
                [
                  {
                    "params": [
                      "value"
                    ],
                    "type": "field"
                  },
                  {
                    "params": [],
                    "type": "mean"
                  }
                ]
              ],
              "tags": []
            }
          ],
          "thresholds": [],
          "timeFrom": null,
          "timeShift": null,
          "title": "Air Pressure (Pa)",
          "tooltip": {
            "shared": true,
            "sort": 0,
            "value_type": "individual"
          },
          "type": "graph",
          "xaxis": {
            "buckets": null,
            "mode": "time",
            "name": null,
            "show": true,
            "values": []
          },
          "yaxes": [
            {
              "format": "short",
              "label": null,
              "logBase": 1,
              "max": null,
              "min": null,
              "show": true
            },
            {
              "format": "short",
              "label": null,
              "logBase": 1,
              "max": null,
              "min": null,
              "show": true
            }
          ]
        }
      ],
      "repeat": null,
      "repeatIteration": null,
      "repeatRowId": null,
      "showTitle": false,
      "title": "Dashboard Row",
      "titleSize": "h6"
    }
  ],
  "schemaVersion": 14,
  "style": "dark",
  "tags": [],
  "templating": {
    "list": []
  },
  "time": {
    "from": "now-1h",
    "to": "now"
  },
  "timepicker": {
    "refresh_intervals": [
      "5s",
      "10s",
      "30s",
      "1m",
      "5m",
      "15m",
      "30m",
      "1h",
      "2h",
      "1d"
    ],
    "time_options": [
      "5m",
      "15m",
      "1h",
      "6h",
      "12h",
      "24h",
      "2d",
      "7d",
      "30d"
    ]
  },
  "timezone": "",
  "title": "Thermostat",
  "version": 3
}

Control Script

Python
import time
import RPi.GPIO as GPIO
import struct
from bluepy.btle import UUID, Peripheral

RL_OFF = GPIO.HIGH
RL_ON = GPIO.LOW
RL_PIN = 7

TARGET_TEMP = 23.0

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(RL_PIN, GPIO.OUT, initial=RL_OFF)

def heatingOn():
    GPIO.output(RL_PIN, RL_ON)

def heatingOff():
    GPIO.output(RL_PIN, RL_OFF)


service_uuid = UUID("647b1c04-ce8f-4e1f-aea6-09e28d875c51")
char_temp_uuid = UUID("647b1c04-ce8f-4e1f-aea6-09e28d875c53")

if len(sys.argv) != 2:
    print("Fatal, must pass device address:", sys.argv[0], "<device address="">")
    quit()

p = Peripheral(sys.argv[1])
sevice=p.getServiceByUUID(service_uuid)
try:
    ch_temp = sevice.getCharacteristics(char_temp_uuid)[0]

    while 1:
        tempVal = struct.unpack('<f', ch_temp.read())[0]

        if tempVal >= TARGET_TEMP + 0.5:
            heatingOff();

        elif tempVal <= TARGET_TEMP - 0.5:
            heatingOn();

        time.sleep(30)

finally:
    p.disconnect()

Credits

Attila Tőkés
37 projects • 221 followers
Software Engineer experimenting with hardware projects involving IoT, Computer Vision, ML & AI, FPGA, Crypto and other related technologies.

Comments