Chapter 4. Data Emulation

I see, hear, and smell.

Should I transmit a warning?

False alarm. Ignore.

Processing sensor data from simulators and generating log output as part of an actuation event is pretty cool, but wouldn’t it be even cooler to use sliders to adjust the values your sensor task is reading? Even better would be the ability to display messages on a real (or emulated) LED display when an actuation event is triggered, right?

This chapter explores how to set up, configure, and use an emulator that can provide a virtual LED display, plus temperature, pressure, humidity, and other readings.

What You’ll Learn in This Chapter

Much of this chapter is dedicated to setting up and configuring the Sense-Emu emulator, which is a virtual Sense HAT1 device you can run on Windows, Mac, or Linux platforms (although I’ve found it easiest to run on Linux).

Note

The Sense HAT is a board that can attach to a Raspberry Pi’s 40-pin GPIO and provides a variety of sensing capabilities, including humidity, pressure, and temperature. The LED matrix displays text or graphics in multiple colors on an 8x8 grid. It also contains a built-in inertial measurement unit (IMU) and magnetometer to measure compass readings, x/y/z-axis orientation, and acceleration. The Sense-Emu emulator provides a virtual instance of the Sense HAT, which will be the focus of this chapter.

Configuring an IoT device can present you with interesting challenges, and so can installing and configuring an emulator. I’ll focus on the emulator in this chapter and assume you’ll be using it for future exercises, but you can certainly opt to use real hardware, such as a Raspberry Pi, to run your CDA (and GDA).2 Should you decide to move forward with this deployment strategy, setting up your device so it can work as both a gateway device and a constrained device is a bit involved and in most cases will be specific to your device.

Note

If you decide to set up Jenkins 2 to help with CI/CD on your local system, check out Brent Laster’s book Jenkins 2 Up & Running (O’Reilly). If you want to run this directly on a Raspberry Pi running a Linux-based operating system (such as Raspbian), pay attention to the setup and configuration for Linux-based environments. While it is outside the scope of this book, you might want to consider this extra configuration step, as it can help with automating your development environment.

As a friendly reminder, this chapter focuses exclusively on the CDA, so the code is written in Python. As with the previous chapters, be sure to follow the steps listed in PIOT-CDA-04-000 and check out a new branch for this chapter.

Emulating Sensors and Actuators

Sensors read data from a system and cover a wide range of capabilities. For this next application, you’ll build emulators that simulate sensing functionality and allow you to trigger one or more actuation events, should the simulated sensors generate data that requires your device to take action.

Eventually, the determination of this action will lie in the hands of the Gateway Device App and, perhaps later, the analytics component of your cloud service infrastructure. For now, you will build a simple test function within your Constrained Device App that looks for a threshold crossing and will use that to send an actuation event to the simulated actuator. Once this is functioning properly, you’ll see how the infrastructure you’ve developed will be an important part of the next set of exercises.

Setting Up and Configuring an Emulator

The modularity of the CDA design will allow you to add new emulator functionality without making too many changes—essentially, you’ll add functionality to the SensorAdapterManager and ActuatorAdapterManager classes to support the switch from simulation to emulation and then add in new emulator tasks for sensing and actuation.

The emulator tasks for sensing will be derived from BaseSensorSimTask, while those related to actuation will be derived from BaseActuatorSimTask. Within the manager classes, the switch between simulation and emulation will be managed within the constructor of each class.

Before I dig into the exercises and code, let’s explore some of the dependencies to make the emulator functionality work properly.

The Sense-Emu Sense HAT Emulator

Building emulators can be complicated, and we’re fortunate to have access to a number of different tools that support testing and emulating various hardware components useful in IoT Edge Tier processing. One such tool mentioned previously is the Sense-Emu Sense HAT emulator, which includes a graphical user interface (GUI) with levers that allow you to emulate the sensor readings from a Sense HAT card.3

Note

The Sense HAT is a board that can be added to a Raspberry Pi using the 40-pin general-purpose input/output (GPIO) header. While a detailed discussion of this capability is beyond the scope of this book, a quick online search will list numerous resources with detailed descriptions of the board and its sensors, example projects, and sample code that may be useful if you plan to incorporate real hardware into your design.

Figure 4-1 is a screenshot of the Sense-Emu emulator running on my local system.

Sense-Emu Sense HAT emulator screenshot
Figure 4-1. Sense-Emu Sense HAT emulator screenshot

As you can see, there are several ways to generate data within the GUI. Before taking this screenshot, I adjusted the temperature to 28.8°C, the pressure to 984.5 mbar, and humidity to 40.2%.

Once the emulator is up and running, you’ll need a way to interface with it using your CDA. The Python library pisense supports integration with the Sense-Emu emulator and the actual Sense HAT board. You can switch between them by setting a flag within the initialization of the Sense HAT class in the pisense library.

Before we dig into the code, review PIOT-CFG-04-001, which walks through the installation and configuration of the Sense-Emu emulator. I won’t spend much time discussing it here, but there will likely be other dependencies to get the emulator working on your system. I’ve successfully run the Sense-Emu emulator on Windows using WSL (with a separate X11 server) and on macOS.

Last, the PiotConfig.props configuration file has a property under the ConstrainedDevice section named enableEmulator. Once your emulator is set up and properly configured, and after you’ve completed the exercises in this section, flip the property value to “True” and you should be able to test your code against the emulator.

Let’s start coding.

Programming Exercises

You’ll notice a common pattern in the component relationships for the emulator functionality—it’s the same as your design and implementation from Chapter 3 regarding the simulator functionality. This provides a great deal of flexibility for your implementation, as you’ll be able to add new capabilities to your CDA that exceed the requirements specified in these exercises.

Integrating Sensing and Actuation Emulation Within Your Application Design

Figure 4-2 provides a simple design view of the CDA once these new features are incorporated.

Constrained Device Application—integrated sensing and actuation app design
Figure 4-2. Constrained Device Application—integrated sensing and actuation app design

Notice the design looks strikingly similar to the design in Figure 3-3, where you integrated simulation functionality into your CDA. Its modularity permits adding emulator tasks with minor modifications to the manager logic.

Now let’s take a look at the detailed design represented as UML. Figure 4-3 depicts one way to represent the key components of this exercise, with some of the details from the previous exercise left out for clarity.

As with the high-level design in Figure 4-2, this detailed design is similar to the UML depicted in Figure 3-4.

Constrained Device Application—integrated sensing and actuation app UML
Figure 4-3. Constrained Device Application—integrated sensing and actuation app UML

Let’s look at the requirements and implementation for Lab Module 04.

Emulating Sensors

Recall the two key functions that BaseSensorSimTask performs: it creates a SensorData instance, which it uses to store the latest sensor simulation data, and it provides a public interface to generate a new instance and access its data.

In PIOT-CDA-04-001, you’ll create three new sensor emulator tasks, each of which will derive from BaseSensorSimTask, just as the sensor simulator tasks did in Chapter 3. The difference will be in the constructor and the generateTelemetry() method. These sensor tasks reside in the ./programmingtheiot/cda/emulated package. Here’s a summary of the actions involved:

  • Create (or edit) HumiditySensorEmulatorTask, PressureSensorEmulatorTask, and TemperatureSensorEmulatorTask.

  • Initialize the SenseHAT class instance using the pisense library.

  • Update generateTelemetry() to retrieve data from the Sense HAT emulator.

Let’s unpack these actions step by step. I’ll focus on TemperatureSensorEmulatorTask, since the others will look very similar.

Like the other Python modules you’ve edited or created, you’ll need to create the class and add the relevant import statements. If you’re using the python-components code base, this is already done for you and looks similar to the following:

from programmingtheiot.data.SensorData import SensorData

import programmingtheiot.common.ConfigConst as ConfigConst

from programmingtheiot.common.ConfigUtil import ConfigUtil
from programmingtheiot.cda.sim.BaseSensorSimTask import BaseSensorSimTask
from programmingtheiot.cda.sim.SensorDataGenerator import SensorDataGenerator

The class declaration for TemperatureSensorEmulatorTask will look like this:

class TemperatureSensorEmulatorTask(BaseSensorSimTask):

When you create the constructor, notice its similarity to the simulator tasks you’ve already written. The primary differences are the construction logic and the SenseHAT class instance logic from the pisense library:

def __init__(self):
  super(TemperatureSensorEmulatorTask, self).__init__( \
    name = ConfigConst.TEMP_SENSOR_NAME, \
    typeID = ConfigConst.TEMP_SENSOR_TYPE)
  
  self.sh = SenseHAT(emulate = True)
Note

If you decide to use a real Sense HAT board and Raspberry Pi single-board computer combination, you can add another parameter to your PiotConfig.props within the ConstrainedDevice section indicating that hardware is (or isn’t) present. If real hardware will be used in your CDA, the SenseHAT class should be initialized with emulate = False instead. If you want to dynamically switch between simulated data, emulated data, and real data (using hardware), consider using an integer value to reflect this initialization behavior in your configuration file instead.

With this complete, let’s move on to the generateTelemetry() function. Here’s an example you might want to use (again, for the TemperatureSensorEmulatorTask class):

def generateTelemetry(self) -> SensorData:
  sensorData = \
    SensorData(name = self.getName(), typeID = self.getTypeID())
  
  sensorVal = self.sh.environ.temperature
  
  sensorData.setValue(sensorVal)
  self.latestSensorData = sensorData
          
  return sensorData

It’s actually a bit simpler without the simulated (or randomized) data, isn’t it?

Now you can move on to the other two tasks: HumiditySensorEmulatorTask and PressureSensorEmulatorTask. Just be sure to use the correct SenseHAT function call to retrieve the appropriate value for each.

Time for the actuation emulators.

Emulating Actuators

Remember the BaseActuatorSimTask base class from Chapter 3? Like BaseSensorSimTask, it performs two primary functions—in this case, abstracting the “activate” and “deactivate” functions of an actuator, using the public method updateActuator(ActuatorData). This will be very useful for the emulated actuator tasks, so we’ll create those by deriving from BaseActuatorSimTask.

Now for the tricky part. What types of actuation events do you want to trigger? Recall our problem statement from Chapter 1:

I want to understand the environment in my home, how it changes over time, and make adjustments to enhance comfort while saving money.

One way to interpret “comfort” is through temperature control and humidity (which plays a role in how humans feel temperature and contributes to the overall comfort and health of an indoor environment). Let’s use both measurements as potential actuation triggers and name two of the actuator tasks HvacEmulatorTask and HumidifierEmulatorTask. The HVAC will be used to control the temperature, and the humidifier will be used to adjust relative humidity.

Note

The actuator task-naming convention drops the word “Actuator” since I think it’s quite clear these devices will be turned on or off. A real-world system may also collect sensor readings from these devices. For simplicity, our virtual devices will merely accept actuation commands.

Since you’ll also be using the LED matrix on the emulator, it might be helpful to dedicate an actuator to illuminating the screen, so PIOT-CDA-04-002 also specifies an LedDisplayEmulatorTask.

Let’s review the actions listed in PIOT-CDA-04-002:

  • Create (or edit) HvacEmulatorTask, HumidifierEmulatorTask, and LedDisplayEmulatorTask.

  • Initialize the SenseHAT instance using the pisense library.

  • Update _handleActuation() to write the appropriate data to the LED display using the SenseHAT instance.

Consider that last bullet. Since the emulator doesn’t actually emulate an HVAC system or a humidifier, we’ll just use its LED display to notify the user that an actuation event is taking place, turning something ON or OFF. You can certainly get creative with the implementation, but for now, we’ll simply scroll a message on the screen.

Here’s an example of the HvacEmulatorTask, including the imports, class declaration, and constructor initialization. It looks very similar to TemperatureSensorEmulatorTask, doesn’t it?

import logging

from time import sleep

import programmingtheiot.common.ConfigConst as ConfigConst

from programmingtheiot.common.ConfigUtil import ConfigUtil
from programmingtheiot.cda.sim.BaseActuatorSimTask import BaseActuatorSimTask

from pisense import SenseHAT

class HvacEmulatorTask(BaseActuatorSimTask):
  def __init__(self):
    super(HvacEmulatorTask, self).__init__( \
      name = ConfigConst.HVAC_ACTUATOR_NAME, \
      typeID = ConfigConst.HVAC_ACTUATOR_TYPE, \
      simpleName = "HVAC")
  
  self.sh = SenseHAT(emulate = True)

Remember that the base class, BaseActuatorSimTask, defines two private methods named _activateActuator() and _deactivateActuator(). Both will automatically get called based on the command being sent to the updateActuator() method defined within the base class. Granted, this isn’t ideal for every situation, but it will suit our needs.

Here’s an example implementation for each of these methods:

def _activateActuator(self, \
  val: float = ConfigConst.DEFAULT_VAL, \
  stateData: str = None) -> int:
  
  if self.sh.screen:
    msg = self.getSimpleName() + ' ON: ' + \
    str(val) + 'C'

    self.sh.screen.scroll_text(msg)
    return 0
  else:
    logging.warning('No LED screen instance available.')
    return -1

The implementation looks rather similar to the simulated actuator that logged a message to the console, doesn’t it?

The deactivation functionality is similar. Here’s a look at the implementation:

def _deactivateActuator(self, \
  val: float = ConfigConst.DEFAULT_VAL, \
  stateData: str = None) -> int:
  
  if self.sh.screen:
    msg = self.getSimpleName() + ' OFF'
    self.sh.screen.scroll_text(msg)
    
    sleep(5)
    
    # optionally, clear the screen when done scrolling
    self.sh.screen.clear()
    return 0
  else:
    logging.warning('No LED screen instance available.')
    return -1

The big difference here is the sleep delay. While this is completely optional, I included it here to give the message time to scroll; however, this does interfere with the timing of the call, since it will block and hold up other CDA processing unless executed within a thread.

Tip

If you decide to pursue a multithreaded approach as part of your design, consider using a queue to store messages in case they are sent in faster than the display can process them. This brings up another interesting challenge: how can you know the rate at which to poll an emulated (or real) sensor, or how long an emulated (or real) actuation event will take?

The short answer is that you need to know the hardware specification of the sensor or actuator, the timing constraints of the device (or emulator), how often it will be invoked, and how to factor these timings into your CDA design. It can be a real problem if not managed appropriately. It’s out of our scope here, but I encourage you to keep it in mind as you design your edge solution.

What, then, would be the better path forward for the deactivation functionality? If the message must have time to scroll and you know how long each character will take (let’s assume it’s about one half of a second, or 500 milliseconds), just calculate the length of the string to display and then multiply it by the number of seconds (or partial seconds). If, for example, the string is 20 characters, and each character takes 500 milliseconds to display and scroll, you’d need to wait for approximately 10 seconds for every character to scroll.

Perhaps a better option is to maintain a graphical 8x8 rendition with icons and/or colors indicating the state and leave it illuminated while the given state is in effect. Ah, yes—that would be an excellent supplemental exercise, wouldn’t it? But I digress. Now that you know the challenge and a plausible path forward to address it, you can choose a different implementation approach!

You’re almost done. There are just two more steps: integration with SensorAdapterManager and integration with ActuatorAdapterManager.

Connecting Emulated Sensors with the Sensor Adapter Manager

This exercise, defined in PIOT-CDA-04-003, updates SensorAdapterManager by introducing the newly created emulator tasks within the constructor initialization. The design approach will let you switch easily between emulated sensor tasks and simulated sensor tasks by flipping the enableEmulator flag in PiotConfig.props from True to False.

The key actions are as follows:

  • Add support for the enableEmulator flag from PiotConfig.props.

  • Add the emulator sensor tasks for temperature, pressure, and humidity (and any others you wish to include).

  • Use the enableEmulator flag to switch between using simulated sensor tasks and using emulated sensor tasks.

Since you’re already familiar with the constructor of SensorAdapterManager, here’s the code that follows immediately after declaring self.dataMsgListener = None (which should be the last line of code in the constructor initialization):

if not self.useEmulator:
  self.dataGenerator = SensorDataGenerator()
  
  tempFloor = \
    configUtil.getFloat( \
      section = ConfigConst.CONSTRAINED_DEVICE, \
      key = ConfigConst.TEMP_SIM_FLOOR_KEY, \
      defaultVal = SensorDataGenerator.LOW_NORMAL_INDOOR_TEMP)

  tempCeiling = \
    configUtil.getFloat( \
      section = ConfigConst.CONSTRAINED_DEVICE, \
      key = ConfigConst.TEMP_SIM_CEILING_KEY, \
      defaultVal = SensorDataGenerator.HI_NORMAL_INDOOR_TEMP)
  
  tempData = \
    self.dataGenerator.generateDailyIndoorTemperatureDataSet( \
      minValue = tempFloor, \
      maxValue = tempCeiling, \
      useSeconds = False)
               
  self.tempAdapter = \
    TemperatureSensorSimTask(dataSet = tempData)
  
  # TODO: add other sensor simulator tasks

else:
  # load the Temperature emulator
  tempModule = \
    import_module( \
      'programmingtheiot.cda.emulated.TemperatureSensorEmulatorTask', \
      'TemperatureSensorEmulatorTask')

  teClazz = \
    getattr(tempModule, 'TemperatureSensorEmulatorTask')

  self.tempAdapter = teClazz()
    
  # TODO: add other sensor emulator tasks

If the self.useEmulator flag is False, then the simulated sensor functionality will be used. The remaining code within this section is the same code you implemented in Chapter 3. But what about the else clause? Let’s break it down.

The first line after the else clause is as follows:

  tempModule = \
    import_module( \
      'programmingtheiot.cda.emulated.TemperatureSensorEmulatorTask', \
      'TemperatureSensorEmulatorTask')

If you’ve been coding in Python for a while, this may be familiar to you. The code instructs the Python interpreter to dynamically load the TemperatureSensorEmulatorTask module (and the like-named class) using import_module. This simply wraps the __import__ built-in with a slightly more user-friendly interface to perform the module load (again, only if self.useEmulator is True).

Tip

If you’d prefer to use the __import__ built-in instead, you can replace the import_module() call with the following:

tempModule = \
  __import__( \

    'programmingtheiot.cda.emulated.TemperatureSens
orEmulatorTask', \

    fromlist =
['TemperatureSensorEmulatorTask']).

Is this dynamic module and class-loading capability strictly required for the purposes of this chapter? No, it is not. It is, however, a tool you can use for future integration. It serves two purposes, neither of which is needed at this time:

  1. It establishes a pattern for dynamically loading modules that may not exist using a simple configuration file flag. Interesting exercise, but not critical. Yet.

  2. With this pattern in place, you can implement hardware-specific code within the existing `programmingtheiot.cda.embedded` python-components package. This might be useful if you decide to tackle some of the optional Lab Module 04 exercises.

You can follow this same pattern to dynamically load the humidity and pressure tasks, HumiditySensorEmulatorTask and PressureSensorEmulatorTask, respectively. Be sure to add those components into your code before running the manual integration test.

Let’s test things out and ensure you can flip between simulated sensing and emulated sensing. Check out the test at the end of the requirement card—you’ll see the instructions are a bit more involved than in previous tests. Here’s a summary:

  • Make sure the enableEmulator flag in PiotConfig.props is set to True and then start the Sense-Emu emulator. From the command line, you should be able to just type sense_emu_gui.

  • Run SensorAdapterManagerTest within the python-components ./src/test/python/programmingtheiot/part02/integration/system path.

If you’re running the Sense-Emu emulator under WSL and your IDE under Windows, you may find that the manual integration tests for SensorAdapterManagerTest generate an error. This is probably because your IDE isn’t able to communicate with the X11 server. The easy (and quick) fix is simply to run your test from the command line. You can do this by navigating to the path containing SensorAdapterManagerTest and executing it using the following command within your virtualenv:

python -m unittest SensorAdapterManagerTest
Note

If you decided to set up your Python environment without using a virtualenv, make sure you’re using the appropriate Python interpreter executable (such as Python 3). Also, be sure your PYTHONPATH is set within your environment. It will need to include both the ./src/main/python and ./src/test/python paths.

The output should include various message sequences similar to the following (depending on how long you run your tests):

2021-01-01 15:06:39,776:SensorAdapterManagerTest:INFO:Testing SensorAdapterManager
class...
.
.
2021-01-01 15:06:45,126:SensorAdapterManager:INFO:Generated humidity data:
name=HumiditySensor,typeID=1,timeStamp=2021-01-
01T20:06:45.125842+00:00,statusCode=0,hasError=False,locationID=constraineddevice00
1,elevation=0.0,latitude=0.0,longitude=0.0,value=0.0
2021-01-01 15:06:45,128:SensorAdapterManager:INFO:Generated pressure data:
name=PressureSensor,typeID=2,timeStamp=2021-01-
01T20:06:45.125956+00:00,statusCode=0,hasError=False,locationID=constraineddevice00
1,elevation=0.0,latitude=0.0,longitude=0.0,value=848.703369140625
2021-01-01 15:06:45,129:SensorAdapterManager:INFO:Generated temp data:
name=TempSensor,typeID=3,timeStamp=2021-01-
01T20:06:45.125996+00:00,statusCode=0,hasError=False,locationID=constraineddevice00
1,elevation=0.0,latitude=0.0,longitude=0.0,value=24.984375
.
.
2021-01-01 15:07:20,139:base:INFO:Job "SensorAdapterManager.handleTelemetry
(trigger: interval[0:00:05], next run at: 2021-01-01 15:07:25 EST)" executed
successfully
.
.

If this log output aligns reasonably well with your own, great! Time to move on to the actuator emulators.

Connecting Emulated Actuators with the Actuator Adapter Manager

The last formal exercise in this chapter, defined in PIOT-CDA-04-004, follows the same pattern as the previous one for ActuatorAdapterManager:

  • Add support for the enableEmulator flag from PiotConfig.props.

  • Add the emulator actuator tasks for the HVAC, humidifier, and LED matrix.

  • Use the enableEmulator flag to switch between using simulated actuator tasks and using emulated actuator tasks.

Keep in mind that the construction pattern for ActuatorAdapterManager will only change to support the emulator/simulator switch. Immediately after declaring self.dataMsgListener = None (which, again, should be the last line of code in the constructor initialization), add the following:

if not self.useEmulator:
  # create the humidifier actuator
  self.humidifierActuator = HumidifierActuatorSimTask()

  # create the HVAC actuator
  self.hvacActuator = HvacActuatorSimTask()
else:
  # load the HVAC emulator
  hvacModule = \
    import_module( \
      'programmingtheiot.cda.emulated.HvacEmulatorTask', \
      'HvacEmulatorTask')
  
  hveClazz = \
    getattr(hvacModule, 'HvacEmulatorTask')
  
  self.hvacActuator = hveClazz()
  
  # TODO: add other actuator tasks

Nothing new here, right? This time, you’re dynamically loading the HvacEmulatorTask but using the same logic as for the TemperatureSensorEmulatorTask.

Be sure to add in the HumidifierEmulatorTask and LedDisplayEmulatorTask before moving on to the manual integration tests. The same constraints hold here as for the SensorAdapterManager when using the Sense-Emu emulator: make sure the enable​Emulator flag is True in PiotConfig.props and start the emulator.

As with the SensorAdapterManagerTest, you may want to run ActuatorAdapterManagerTest from within the python-components path ./src/test/python/programmingtheiot/part02/integration/system/:

python -m unittest ActuatorAdapterManagerTest

For this test, be sure to also monitor the Sense-Emu GUI and observe the LED display along with the log output.

Figure 4-4 provides an example screenshot depicting the first letter of the LED “ON” message.

Sending an “ON” message to the Sense-Emu LED matrix
Figure 4-4. Sending an “ON” message to the Sense-Emu LED matrix

Figure 4-4 shows one small part of the scrolling text message that indicates the HVAC is being turned “ON” (the “O” is shown). One exercise to consider is triggering two actuation events when the temperature needs to be adjusted: one to the HVAC emulator (which can technically just log a message) and the other to the LED matrix, where you can provide a more creative visualization of the HVAC and humidifier within the 8×8 matrix.

As for log messages, they’ll look similar to the pattern expressed here:

2021-01-01 16:58:17,797:ActuatorAdapterManagerTest:INFO:Testing ActuatorAdapterManager
class...
.
.
.
2021-01-01 16:58:25,909:ActuatorAdapterManager:INFO:Actuator command received for
location ID constraineddevice001. Processing...
2021-01-01 16:58:25,910:BaseActuatorSimTask:INFO:Deactivating actuator...
2021-01-01 16:58:36,729:DefaultDataMessageListener:INFO:Actuator Command: 0
2021-01-01 16:58:36,736:ActuatorAdapterManager:INFO:Actuator command received for
location ID constraineddevice001. Processing...
2021-01-01 16:58:36,745:BaseActuatorSimTask:INFO:Activating actuator...
2021-01-01 16:58:42,325:DefaultDataMessageListener:INFO:Actuator Command: 1
2021-01-01 16:58:42,326:ActuatorAdapterManager:INFO:Actuator command received for
location ID constraineddevice001. Processing...
2021-01-01 16:58:42,327:BaseActuatorSimTask:INFO:Deactivating actuator...
2021-01-01 16:58:51,120:DefaultDataMessageListener:INFO:Actuator Command: 0
.
.
.

If your tests indicate success, then it’s time to celebrate. You now have not only the simulated CDA functionality working but also sensor and actuator emulation! The next chapter will look at ways to manage the data you’re now generating so it can be collected, transmitted (eventually), and interpreted.

Additional Exercises

The Sense-Emu emulator’s GUI allows you to adjust values via the given sliders and the joystick control. This can be a particularly useful tool for manually changing values within your emulated sensor environment to test various threshold crossing events.

Threshold Management

Create another emulator adapter that interprets the Sense-Emu GUI’s joystick controls and adjusts the floor (down button) and ceiling (up button) of either your temperature or your humidity threshold crossing value.

With this in place, see if you can exercise the hysteresis management function you implemented in the additional exercise of Chapter 3 using these new threshold crossings and the simulated data values generated from SensorDataGenerator (or your own custom version).

Conclusion

In this chapter, you learned about hardware emulation and how to integrate the Sense HAT emulator into your CDA. You also learned about some of the challenges associated with triggering actuation events, and how the logic to handle such events isn’t always as simple as just sending an ON or OFF command. Design-wise, you learned more about the modularity of the CDA’s design and how it can help you overcome some of these challenges.

You’re now ready to dive into the final chapter of Part IIChapter 5—where you’ll learn about data transformation, which will help you eventually integrate your CDA and GDA.

1 The Sense HAT is a board that can be attached to various Raspberry Pi single-board computer modules via its 40-pin GPIO interface. You can read more about the Sense HAT on the Raspberry Pi website.

2 If you’re interested in using the Raspberry Pi, see Raspberry Pi Cookbook, Third Edition, by Simon Monk (O’Reilly).

3 You can read more about the Sense HAT board online. If you’re interested in other features of the emulator, you can review the online documentation and check out the built-in demos located under the emulator GUI’s File → “Open example” menu.

Get Programming the Internet of Things now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.