1

I have an Hvac host device (see interface below) connected to Weave using Libiota and I'm having some trouble understanding how to update the current ambient air temperature (measured on the device). I can measure it but I don't know how to update the state continuously so my Weave client displays the latest ambient air temperature.

I don't fully understand this implementation note in the documentation:

"Changes to the measured ambient temperature should be reported to the cloud service when the temperature value changes by the minimum level of granularity for the device UI, for example in 0.5 or 1 degree. If the device does not support event-based reporting and utilizes polling, the device should confirm a temperature value change of 0.5 or 1 degree, whichever is the UI's level of granularity, and update the cloud service at most once every 90 seconds."

I'm able to update the current air temperature at device initialization (inside hvac_controller.c) and upon set temperature setting (i.e. when the heating temperature changes -inside hvac_controller_traits.c-). I'm using this to update the ambient air temperature: IOTA_MAP_SET(ambient_air_temperature_state, degrees_celsius, myvalue);

Interface:

// Create the example hvac controller interface.
  g_hvac_controller = GoogHvacControllerDevice_create(
      GoogHvacControllerDevice_WITH_AMBIENT_AIR_TEMPERATURE |
      GoogHvacControllerDevice_WITH_AMBIENT_AIR_HUMIDITY |
      GoogHvacControllerDevice_WITH_DISPLAY_UNITS |
      GoogHvacControllerDevice_WITH_HEAT_SUBSYSTEM |
      GoogHvacControllerDevice_WITH_HEAT_SETTING);
diazdeteran
  • 1,144
  • 1
  • 13
  • 25
  • 1
    Can you clarify exactly what you are unable to do? Are you unable to use IOTA_MAP_SET() to update the temperature state when the sensor value changes? – devunwired Feb 02 '17 at 20:42
  • I'm able to use IOTA_MAP_SET() to update the temperature state but I don't know how to do that when the sensor value changes. I'm reading a sensor value from a gpio pin. How can I continuously poll (I know how to do this, perhaps with a daemon?) and send an update to Weave so it reflects the new temperature (this is the part where I'm lost)? Thanks! – diazdeteran Feb 02 '17 at 21:01
  • 1
    That part is up to the application you are writing, and would be specific to the platform you are developing with. Your code needs to loop somehow and monitor the sensor for changes. This part is not related to Weave, which only requires that you update the state with IOTA_MAP_SET() for a detected change. – devunwired Feb 02 '17 at 21:25
  • Gotcha, thanks for clarifying! – diazdeteran Feb 02 '17 at 21:57

2 Answers2

1

After understanding that the device (and not Weave) should poll and update the air temperature state with IOTA_MAP_SET(), I tackled this with a pthread.

Here's my main.c

...    
#include <pthread.h>

// Function ran by background thread
void *update_air_temperature() {
  GoogTempSensor* ambient_air_temperature =
      GoogHvacControllerDevice_get_ambient_air_temperature(g_hvac_controller);
  GoogTempSensor_State* ambient_air_temperature_state =
      GoogTempSensor_get_state(ambient_air_temperature);

  while(1) {
    // Update air temperature state every 90 seconds
    // Read air temperature from sensor
    // Code to read sensor and assign value to air_temperature
    float air_temperature;
    ...

    // Update air temperature state
    IOTA_MAP_SET(ambient_air_temperature_state, degrees_celsius, air_temperature);

    sleep(90);
  }
  return NULL;
}

static IotaDaemon* create_hvac_controller_daemon_(void) {
  IOTA_LOG_INFO("Inside create_hvac_controller_daemon_");

  // Create the example hvac controller interface.
  g_hvac_controller = GoogHvacControllerDevice_create(
      GoogHvacControllerDevice_WITH_AMBIENT_AIR_TEMPERATURE |
      GoogHvacControllerDevice_WITH_AMBIENT_AIR_HUMIDITY |
      GoogHvacControllerDevice_WITH_DISPLAY_UNITS |
      GoogHvacControllerDevice_WITH_HEAT_SUBSYSTEM |
      GoogHvacControllerDevice_WITH_HEAT_SETTING);

  IotaDevice* iota_device = iota_device_create_from_interface(
      (IotaInterface*)g_hvac_controller, (IotaModelManifestId){"AHXXX"});
  if (iota_device == NULL) {
    IOTA_LOG_ERROR("Device create from interface failed");
    GoogHvacControllerDevice_destroy(g_hvac_controller);
    return NULL;
  }

  g_iota_daemon = host_framework_create_daemon("hvac_controller", iota_device);

  // Set default state of traits on the hvac_controller.
  example_hvac_controller_configure(g_hvac_controller, g_iota_daemon,
                                    "Heating");

  // Background thread to update air temperature
  pthread_t thread1;
  pthread_create(&thread1, NULL, update_air_temperature, NULL);

  return g_iota_daemon;
}

int main(int argc, char** argv) {
  HostIotaFrameworkConfig config = (HostIotaFrameworkConfig){
      .base =
          (IotaFrameworkConfig){
              .cli_commands = NULL,
              .num_commands = 0,
              .builder = create_hvac_controller_daemon_,
          },
      .argc = argc,
      .argv = argv,
      .user_data = NULL,
      .name = "hvac controller",
  };

  return host_framework_main(&config);
}

Don't forget to add -lpthread to the makefile:

...
EXTRA_LIBS=-lpthread

EXAMPLE_OUT_DIR := $(ARCH_OUT_DIR)/examples/$(EXAMPLE)
...

$(EXAMPLE_BIN): $(LIBIOTA_STATIC_LIB) $(PLATFORM_STATIC_LIB) \
        $(EXAMPLES_COMMON_LIB) $(HOST_DEVFW_LIB) \
        $(EXAMPLE_OBJECTS) | $(EXAMPLE_OUT_DIR)
        $(CC) -Wl,--start-group $^ -Wl,--end-group -o $@ $(LDLIBS) $(PLATFORM_LDLIBS) $(EXTRA_LIBS)
...

Keep in mind that you should only report the new air temperature when it changes, and at a max frequency of 90 seconds. From the documentation:

Changes to the measured ambient temperature should be reported to the cloud service when the temperature value changes by the minimum level of granularity for the device UI, for example in 0.5 or 1 degree. If the device does not support event-based reporting and utilizes polling, the device should confirm a temperature value change of 0.5 or 1 degree, whichever is the UI's level of granularity, and update the cloud service at most once every 90 seconds.

diazdeteran
  • 1,144
  • 1
  • 13
  • 25
  • The documentation states that "If the system has multiple or remote temperature sensors, this state must only represent the sensor located on the HVAC controller itself". I'm actually reading the temperature from a remote sensor with curl. I'm doing this because I was planning to put the device close to the heater so it can control the heating with a relay. I see the point in what the documentation states, though. Perhaps the remote device should be the one controlling the relay. Also, it's unclear to me what value the air temp should be set to if fail to read from sensor (null?). – diazdeteran Feb 03 '17 at 22:45
0

Changing libiota-owned state from outside of the daemon thread could cause trouble. You should use host_iota_daemon_queue_application_job to post a callback to be invoked on the libiota daemon thread, rather than call IOTA_MAP_SET directly from your application thread.