Hardware Interaction 1: MAS

Introduction

In this tutorial we introduce the Memory Access Service (MAS), and show how it is used within Flightkit to access hardware.

To demonstrate this you will develop a Component Type to run on a Raspberry Pi Zero W which communicates with an attached light sensor using MAS.

While this tutorial produces a Component Type for running on a Raspberry Pi Zero W, the ideas and techniques introduced are useful for developing Component Types which interact with subsystems on any of the platforms which Flightkit supports.

For simplicity, whenever we reference the Raspberry Pi Zero W in this tutorial, we will refer to it as the "Pi".

In this tutorial, you will:

  • Learn when and how to use the Memory Access Service (MAS).

  • Gain a broader understanding of Services used in Flightkit.

  • Learn about the typical process for developing Component Types which interact with subsystems, by writing a new Component Type to support a light sensor connected to your Pi.

Before You Begin

Guidance on the styles and conventions used in these tutorials is available here.

Unless otherwise directed, all commands in this tutorial must be run in the developing_components/HardwareInteraction1 workspace.

You must have Flightkit installed as described in the Getting Started Guide.

Navigate to the developing_components/HardwareInteraction1 workspace in a terminal and run hfk workspace prepare to ensure it is ready to use.

If you haven’t already, follow the steps in our Raspberry Pi Zero How-to Guide to set up your Pi and install the correct toolchain in order to complete this tutorial.

Memory Access Service (MAS)

In space systems it is common for different functionality to be divided between independent subsystems. Typically these subsystems will be connected to onboard computers (OBCs) which monitor and control them.

There are a variety of interaction patterns which might be used to monitor and control subsystems. A common one is a "request-response" or "controller-peripheral" pattern.

In this interaction pattern:

  • The controller (OBC) initiates an interaction by issuing a request to the peripheral (the subsystem).

  • The request is either a request to write data to the subsystem (a command or register value, for example) or read data from the subsystem (some telemetry values, perhaps).

  • The peripheral never sends unprompted data to the controller. It is only permitted to send data in response to requests received from the controller.

MAS is a Service which supports this interaction pattern, and specifically contains Service Operations for:

  • Writing data to a peripheral.

  • Reading data from a peripheral.

  • Sequentially or simultaneously writing and reading data to and from a peripheral.

Typically Component Types which represent the high-level capabilities of this kind of subsystem will consume MAS. Component Types supporting interactions with hardware which follow this pattern (like I2C or SPI drivers) will provide it.

In the earlier Using a Service tutorial, you learned how the File Store Service (FSS) is a contract which defines Operations for writing to and reading from files in a POSIX-like filesystem.

MAS defines Operations for writing to and reading from hardware, rather than files, but can be thought of in very similar terms.

Analysis and Design

In the following sections this tutorial will guide you through implementing a new Component Type to interact with the Pi’s light sensor.

This Component Type will:

  1. Use Actions and Parameters to represent the high-level functionality of the sensor,

  2. Consume MAS, and use it to make requests of the hardware.

  3. Need to be connected to a MAS Provider once deployed. This MAS Provider will send hardware requests to the sensor.

This leaves some questions to answer. What kind of functionality does the sensor have? What requests can we make of it, and how? What MAS Provider should we use to issue those requests?

To answer these questions you will need to understand how the hardware operates. Without this understanding, designing and developing a Component Type like this is impossible.

The Peripheral

First consider the peripheral itself.

In this tutorial the aim is to interact with a specific light sensor connected to the Pi - an M5 DLight Unit. This is an integrated light sensor built around the BH1750FVI ambient light sensor IC.

Technical detail about this device can be found in its datasheet, available online here.

The datasheet includes a lot of information. Much of it concerns the physical and electrical properties of the sensor. These are not relevant to your task of interacting with it from software.

Of more interest to you are the details of its data interface, and how to use it to measure light levels.

The datasheet states that the sensor uses an I2C interface. I2C is a physical layer protocol which is a common choice for communicating with sensors like this one. Importantly, it closely matches the "request-response" pattern supported by MAS.

The data gives further information on the interface which you’ll need to implement your Component Type:

  • Page 4 includes a state diagram showing that "one time" or "continuous" measurement can be carried out.

  • Page 5 provides a table of instructions which can be sent to the sensor.

  • Page 7 gives step-by-step procedures for requesting a measurement and reading it back from the sensor.

  • Page 10 gives more detail on the I2C interface, including the 2 addresses the sensor uses.

Assuming a one time measurement is sufficient, use the datasheet to plan what steps your Component Implementation will need to carry out.

Show the procedure which your Component Implementation will use.
  • Write the "one time H-resolution mode" command via I2C to the sensor.

  • Wait 180ms to allow the measurement to complete.

  • Read the measurement from the sensor.

The simplicity of this procedure means you will be able to give your component type a single read-only Parameter for getting the light level.

The Controller

The other piece of hardware you need to consider is the controller. In this case the controller is the Broadcom Serial Controller (BSC) onboard the Pi, which is designed for writing and reading data to and from an I2C bus.

As with the peripheral in the previous section, you can find out more about how this controller works by referring to its datasheet.

If you were starting from scratch, you would need to write a Component Type which provided MAS. It would need to provide MAS Operations by manipulating the BSC such that a MAS write would trigger an I2C write, for example.

To save time here you will use our I2CController Component Type provided in the FlightkitFoundation bundle. This Component Type Provides MAS in exactly the way you require.

The Connected System

The following sketch shows how our Component Types connect with the hardware on the board to enable interaction with the BH1750FVI subsystem.

System diagram

Notice how the I2CController Component Type uses MAS to represent the general capabilities of the Broadcomm Serial Controller.

In a similar way, notice how the BH1750FVI Component Type represents the capabilities of the BH1750FVI sensor with Actions and Parameters.

In constructing the system like this, and using MAS between the Component Types, they are decoupled. Importantly, the BH1750FVI Component Type you are about to implement can be instantiated on any platform with a Component Type which Provides MAS and uses it to give access to an I2C bus.

Now that the key pieces of hardware in the system have been identified, you can move on to implementing the new BH1750FVI component type.

The BH1750FVI Component Type

The outcome of the last section was that, in order to access the Pi’s light sensor, you will need to create a new Component Type.

As you have learned from previous tutorials, the first step in producing a new Component Type is to define its interfaces and characteristics in its componentType.xml.

When developing new Component Types it is often best to think carefully about how to define them before thinking too hard about how to implement them.

This approach ensures the Component Type you produce will meet your needs, and fit into the system neatly.

While some iteration between definition and implementation is usually needed, more time spent at the definition stage tends to reduce the time spent at the implementation stage.

We have suggested you name it BH1750FVI after the specific device it supports. Naming it LightSensor might be easier on the eye, but it would be unclear exactly which light sensor it can communicate with.

Definition

Create the Component Type using the following command:

hfk component-type create BH1750FVI

This creates a componentType.xml for your new Component Type in the library/component_types/BH1750FVI directory.

Open the componentType.xml file.

Update the description and documentation elements to give more detail about what the Component Type does:

<Description>
  This component provides an interface to the BH1750FVI digital ambient light sensor.
</Description>
<Documentation><Markdown>
  Datasheet available from:

  https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/hat/BH1750FVI.pdf

  This component supports one time H-Resolution mode.
</Markdown></Documentation>

Most of the elements in componentType.xml files allow you to associate <Description> and <Documentation> elements with them. We recommend you make liberal use of these elements.

They make sharing Component Types between developers much easier, and give developers a way to communicate important implementation details to systems engineers and spacecraft operators. Remember that comparatively few end-users of your Component Types will have access to the C code you write.

You will use MAS to issue commands to the light sensor. To do this, you will need to declare that BH1750FVI requires MAS.

Add the <Services> element just below the <!-- Services (Provided, Required, Subscribed, Brokered and Published) --> comment:

<Services>
  <Required><Service name="bus" type="io.MAS">
    <Description>
      Provides access to the I2C bus connected to the sensor.
    </Description>
  </Service></Required>
</Services>

As you learned in the earlier Using a Service tutorial, adding this Service requirement makes BH1750FVI a MAS Consumer, which, when instantiated, must be connected to a MAS Provider. The BH1750FVI implementation can safely assume this MAS requirement will be fulfilled.

Finally, your Component Type needs to expose the functionality it has to end users. We assumed above that the only required functionality is to get one time light readings. This fits well with implementing a single Read-Only Parameter.

Add a <Parameters> element just below the <!-- Parameters --> comment, and define a light parameter which will return the current light reading:

<Parameters>
  <Parameter name="light" readOnly="true">
    <Description>
      The current ambient light reading, in lux.
    </Description>
    <Type>
      <Integer signed="false" bits="16"/>
    </Type>
  </Parameter>
</Parameters>

The type for the Parameter is set to a 16-bit unsigned integer because the sensor returns 16-bit values which are then scaled down by a calibration factor to give a reading in lux.

Generate the BH1750FVI Component Type using the usual command:

hfk component-type generate BH1750FVI

Implementation

Having generated the Component Type, the Flightkit Tooling has produced the BH1750FVI.c file which you need to modify to complete the implementation.

Open library/component_types/BH1750FVI/BH1750FVI.c.

This Component Type's implementation is relatively simple. The majority of your effort will go into writing the BH1750FVI_getLight() Parameter getter function.

The procedure this function must implement was outlined above:

  1. Write the "one time H-resolution mode" command via I2C to the sensor.

  2. Wait 180ms to allow the measurement to complete.

  3. Read the measurement from the sensor.

As shown in the table on page 5 of the BH1750FVI datasheet, the "one time H-resolution mode" command code is a single byte: 0010 0000 in binary, or 0x20 in hexadecimal.

In addition to this command code, notice that the examples on page 7 of the datasheet show a variety of other fields that need to be set in the I2C transaction. One of the benefits of using MAS is that these fields are all the responsibility of the MAS Provider. The BH1750FVI Component Type, as the Consumer, only needs to provide the data to write.

In BH1750FVI_getLight(), add a call to BH1750FVI_MAS_write() to write the "one time H-resolution mode" command to the sensor.

You can find the signature of BH1750FVI_MAS_write() in the BH1750FVI_MAS_package.h header file.

Show the MAS write added to BH1750FVI_getLight().
status_t BH1750FVI_getLight
(
    BH1750FVI_t *pt_BH1750FVI,
    uint16_t *pu16_Value
)
{
    status_t t_Status;          /* The current status */

    TASK_PROTECTED_START(&pt_BH1750FVI->t_Lock, &gt_LockTimeout, t_Status)
    {
-       /* TODO: Implement light get accessor */
-       t_Status = STATUS_NOT_IMPLEMENTED;
+       uint8_t u8_WriteData;
+
+       /* Send the "one time H-resolution mode" command */
+       u8_WriteData = 0x20;
+
+       /* Write the data to the device */
+       t_Status = BH1750FVI_MAS_writeBus(
+           pt_BH1750FVI,
+           &gt_LockTimeout,
+           0,
+           0,
+           NULL,
+           &u8_WriteData,
+           1);
    }
    TASK_PROTECTED_END(&pt_BH1750FVI->t_Lock, &gt_LockTimeout, t_Status);

    return t_Status;
}

Add a call to Task_Time_delayShortTime() to BH1750FVI_getLight() to wait for the required 180ms measurement time.

Show the delay added to BH1750FVI_getLight().
status_t BH1750FVI_getLight
(
    BH1750FVI_t *pt_BH1750FVI,
    uint16_t *pu16_Value
)
{
    status_t t_Status;          /* The current status */

    TASK_PROTECTED_START(&pt_BH1750FVI->t_Lock, &gt_LockTimeout, t_Status)
    {
        uint8_t u8_WriteData;

        /* Send the "one time H-resolution mode" command */
        u8_WriteData = 0x20;

        /* Write the data to the device */
        t_Status = BH1750FVI_MAS_writeBus(
            pt_BH1750FVI,
            &gt_LockTimeout,
            0,
            0,
            NULL,
            &u8_WriteData,
            1);
+
+       if (t_Status == STATUS_SUCCESS)
+       {
+           /* Wait 180ms time for the measurement to be taken */
+           t_Status = Task_Time_delayShortTime(180uL * 1000);
+       }
    }
    TASK_PROTECTED_END(&pt_BH1750FVI->t_Lock, &gt_LockTimeout, t_Status);

    return t_Status;
}

After waiting for the appropriate length of time, the final step is to read the measurement result. To do this, all you need to do is issue a 2-byte MAS read.

In BH1750FVI_getLight(), add a call to BH1750FVI_MAS_read() to read the 2-byte measured value from the sensor.

Show the MAS read added to BH1750FVI_getLight().
status_t BH1750FVI_getLight
(
    BH1750FVI_t *pt_BH1750FVI,
    uint16_t *pu16_Value
)
{
    status_t t_Status;          /* The current status */

    TASK_PROTECTED_START(&pt_BH1750FVI->t_Lock, &gt_LockTimeout, t_Status)
    {
        uint8_t u8_WriteData;

        /* Send the "one time H-resolution mode" command */
        u8_WriteData = 0x20;

        /* Write the data to the device */
        t_Status = BH1750FVI_MAS_writeBus(
            pt_BH1750FVI,
            &gt_LockTimeout,
            0,
            0,
            NULL,
            &u8_WriteData,
            1);

        if (t_Status == STATUS_SUCCESS)
        {
            /* Wait 180ms time for the measurement to be taken */
            t_Status = Task_Time_delayShortTime(180uL * 1000);
        }
+
+       if (t_Status == STATUS_SUCCESS)
+       {
+           /* A buffer to read into, and its length. */
+           uint8_t ru8_ReadData[2];
+           uint32_t u32_ReadDataLength = sizeof(ru8_ReadData);
+
+           /* Read the measurement from the device */
+           t_Status = BH1750FVI_MAS_readBus(
+               pt_BH1750FVI,
+               &gt_LockTimeout,
+               0,
+               0,
+               NULL,
+               &ru8_ReadData[0],
+               &u32_ReadDataLength);
+       }
    }
    TASK_PROTECTED_END(&pt_BH1750FVI->t_Lock, &gt_LockTimeout, t_Status);

    return t_Status;
}

Finally, you need to add code to convert the retrieved sample from raw counts to lux. The examples on page 7 of the BH1750FVI datasheet use a nominal conversion factor of 1.2.

Convert the contents of ru8_ReadData to a uint16_t representing light level in lux, and return it to the user.

This will require you to convert the network-endian byte buffer to a uint16_t, promote it to a float32_t, divide by 1.2f, then convert back to a uint16_t.

Show the value conversion code added to BH1750FVI_getLight().
        if (t_Status == STATUS_SUCCESS)
        {
            /* A buffer to read into, and its length. */
            uint8_t ru8_ReadData[2];
            uint32_t u32_ReadDataLength = sizeof(ru8_ReadData);

            /* Read the measurement from the device */
            t_Status = BH1750FVI_MAS_readBus(
                pt_BH1750FVI,
                &gt_LockTimeout,
                0,
                0,
                NULL,
                &ru8_ReadData[0],
                &u32_ReadDataLength);
+
+           if (t_Status == STATUS_SUCCESS)
+           {
+               uint16_t u16_RawValue;
+               float32_t f32_LuxValue;
+
+               Util_Endian_u16FromNetwork(&ru8_ReadData[0], &u16_RawValue);
+
+               /* Convert the sample to lux by dividing by 1.2 */
+               f32_LuxValue = (float32_t)u16_RawValue / 1.2f;
+
+               *pu16_Value = (uint16_t)f32_LuxValue;
+           }
        }

    }
    TASK_PROTECTED_END(&pt_BH1750FVI->t_Lock, &gt_LockTimeout, t_Status);

    return t_Status;
}

In order for the provided implementation to work, you also need to include header files for functions you’re using to the top of the implementation file.

Include the following additional files so that you can access the relevant endian handling functions:

#include "Util_Endian.h"

It is good practice to now address the remaining TODO comments in the autogenerated files to ensure that you haven’t forgotten to implement anything important. In your case these are all in BH1750FVI.h.

Open BH1750FVI.h and update the TODO in the BH1750FVI_Init_t data structure so that it informs the user that no Initialisation Data is required.

Remove the TODO comment in the BH1750FVI_t Component Type Structure, as the implementation requires no additional state.

It would be preferable to use #define symbols to remove some of the literals from the BH1750FVI_getLight() function. These symbols typically go in one of 3 places:

  • If the symbol is set at build time but a user might wish to modify it, it should be added to BH1750FVI_config.h. None of the values you’ve used in BH1750FVI_getLight() fall into this category.

  • If the symbol is fixed, but it might be useful for users of your component type to see, it should be added to BH1750FVI.h. Again, users don’t particularly need to know about these symbols.

  • If the symbol is purely internal to the implementation, we usually create a "private" header to hold them. In your case you could create BH1750FVI_private.h in the same directory as BH1750FVI.c to hold symbols for the BH1750FVI measurement delay, conversion factor, and so on.

If you choose to do this you will need to #include "BH1750FVI_private.h" at the top of the implementation file.

The LightSensorDemo Deployment Type

Before using the BH1750FVI Component Type to interact with the light sensor, you must instantiate it in a Deployment Type, and instantiate that Deployment Type in a Mission.

To help with this, a Deployment Type and Mission have been provided. The Deployment Type contains all the necessary Component Instances needed to communicate with the Deployment Type via Lab.

You’ll now update the provided deploymentType.xml to include instances of your new Component Type and the I2CController Component Type it will connect to. Once updated, you will build the Mission, then run and test the Deployment Instance using Lab.

New Component Instances

Open the deploymentType.xml file which can be found in the library/deployment_types/LightSensorDemo directory.

Add the following two Component Instances to the bottom of the <Implementation> element:

Show the Component Instances added to the deploymentType.xml file.
      <Component name="comms.services.PASTarget" type="core.component.pas.PASTarget"/>
      <Component name="comms.services.PASMessaging" type="io.ams.AMSTargetPS">
        <Connections>
          <Services>
            <Service name="initiator" component="comms.SpacePacket" service="dataPacket" channel="0" />
            <Service name="serviceTarget" component="comms.services.PASTarget" service="remote" />
          </Services>
        </Connections>
        <Tasks>
          <SporadicTask name="receive" priority="2" />
          <SporadicTask name="waitForReplyComplete" priority="2" />
          <SporadicTask name="sendCompleteServiceTarget" priority="2" />
          <SporadicTask name="requestCompleteServiceTarget" priority="2" />
        </Tasks>
      </Component>
+     <Component name="BSC" type="drivers.linux.io.bus.I2CController">
+       <Description>
+         The Broadcomm Serial Controller which controls the platform I2C bus.
+       </Description>
+     </Component>
+     <Component name="M5DLightUnit" type="BH1750FVI">
+       <Description>
+         Provides access to the M5 DLight Unit sensor connected via I2C.
+       </Description>
+       <Connections>
+         <Services>
+           <Service name="bus" component="BSC" service="data" channel="0" />
+         </Services>
+       </Connections>
+     </Component>
    </Implementation>
  </DeploymentType>
</ModelElement>

BSC is an instance of our drivers.linux.io.bus.I2CController Component Type. It is a MAS Provider and is the controller on the I2C bus.

M5DLightUnit is an instance of your new BH1750FVI Component Type. It is a MAS Consumer which issues MAS Operations to control the peripheral - the M5 DLight Unit.

As you would expect, this addition to the deploymentType.xml expresses the design we discussed above, but in XML instead of as a block diagram.

Notice how the io.MAS requirement which you previously gave your BH1750FVI Component Type has been satisfied in the connections section of your M5DLightUnit Component Instance. The <Service> element can be read as follows: "the Service requirement named bus has been fulfilled by the BSC Component Instance's data Service Provision".

Notice also that we have specified channel="0". The I2CController Component Type Provides multiple channels of io.MAS. This lets it fulfil the Service requirements of multiple Consumers. It offers multiple channels by accessing a different I2C address on each channel. You only require access to a single I2C peripheral, so below you’ll set up your BSC Component Instance to only provide a single channel - channel 0.

Now that you have updated the Deployment Type, you can generate the Mission in which it is instantiated. This will verify that you have connected the various Component Instances in the Deployment Type correctly.

Generate the HardwareInteraction1 Mission using the following command:

hfk mission generate HardwareInteraction1

This produces a Mission Database which will allow you to interact with the Deployment Instance via Lab. It also produces empty Initialisation Data for the new Component Instances which you will populate later.

Setting the Initialisation Data

The final step before building the HardwareInteraction1 Mission is to populate the Initialisation Data for the two new Component Instances.

Open missions/HardwareInteraction1/LightSensorDemo/Default/init/BSC_Init.c.

Here you must specify the Initialisation Data for the BSC Component Instance using the definition of the I2CController_Init_t structure defined in I2CController.h.

You will need to use values in this structure as follows:

  • u8_BusIndex needs to be set to 1, because the light sensor is connected to the Pi’s I2C bus 1.

  • pt_Channels needs to be a valid pointer to an array of I2CController_Channel_t structures.

  • u32_NumOfChannels needs to be set to the length of the array pointed to by pt_Channels.

  • b_Enabled should be set to true so that the BSC peripheral is enabled at start up.

The number of Service channels which BSC Provides is determined by the u32_NumOfChannels Initialisation Data member.

As discussed above, only one channel is required, because you only require access to the single M5 DLight Unit on the I2C bus.

This means the array pointed to by pt_Channels only needs to contain a single I2CController_Channel_t structure. That structure has only a single member, u8_PeripheralAddress, which should be set to the peripheral address of the connected peripheral. This is documented in the BH1750FVI datasheet as 0x23.

Set the Initialisation Data for the BSC Component Instance using the values given above.

Show the new Initialisation Data
+/** The list of channels for BSC */
+static const I2CController_Channel_t grt_Channels[] =
+   {
+       {
+           /* Unshifted peripheral address of the connected BH1750FVI */
+           .u8_PeripheralAddress = 0x23
+       }
+   };

/**The BSC initialisation data */
const I2CController_Init_t gt_BSCInit =
    {
-       /* TODO: Add initialisation data for the BSC component */
+       /** The index of the I2C bus */
+       .u8_BusIndex = 1,
+       /** Channel list */
+       .pt_Channels = &grt_Channels[0],
+       /** Number of available channels */
+       .u32_NumOfChannels = ARRAY_COUNT(grt_Channels),
+       /** Whether the I2C should be enabled by default */
+       .b_Enabled = true
    };

With the BSC Component Instance set up properly, the same must be done for the M5DLightUnit.

Open missions/HardwareInteraction1/LightSensorDemo/Default/init/M5DLightUnit_Init.c.

Recall that you updated the definition of BH1750FVI_Init_t in BH1750FVI.h with a comment indicating that no Initialisation Data is required. The Initialisation Data in the M5DLightUnit instance should therefore be empty, but with a similar comment.

Update the comment in the Initialisation Data structure to state that no Initialisation Data is required.

With the Initialisation Data complete, you can now move on to testing your flight software on the target hardware.

Testing the Light Sensor

In order to build and run Flightkit flight software on your Pi you will need to follow the steps in our Raspberry Pi Zero How-to Guide!

The steps below will fail if your machine or the Pi are not correctly configured.

If you haven’t already, connect and power up the Pi.

Ensure that the light sensor is properly connected to I2C bus 1 of the Pi.

Build HardwareInteraction1 using the following command:

hfk mission build HardwareInteraction1

This builds a binary for the Deployment Instance that can now be run on the Pi. The binary is found here:

output/missions/HardwareInteraction1/LightSensorDemo/Default/Default

To run this binary on the Pi, you first need to copy it over to the device.

Copy the binary to the platform using the following command and entering your password when prompted.

If you’ve changed your username or hostname from the default values, be sure to use your own.

scp output/missions/HardwareInteraction1/LightSensorDemo/Default/Default pi@raspberrypi:

With the binary on the Pi filesystem, you can now log in to the Pi and run it.

Using another terminal, SSH into the platform using the following command, again ensuring to use your own username and hostname if required:

ssh pi@raspberrypi

Once connected, you can run the binary like you would any other executable.

Using the same terminal, run the binary using the following command:

./Default

If successful, the following output should be printed to the terminal:

INF: Deployment.c:311 Running deployment:
INF: Deployment.c:312 - Deployment instance: Default
INF: Deployment.c:313 - Deployment type:     LightSensorDemo
INF: Deployment.c:314 - Target:              LightSensorDemo
INF: Deployment.c:315 - Mission:             HardwareInteraction1
INF: Deployment.c:455 Deployment initialisation successful

For more in depth instructions on how to run Deployment Instances on the Pi, refer to the remaining sections of the Raspberry Pi Zero How-to Guide.

Now that the Deployment Instance is running on the platform, you can connect to it using Lab and test out the new light sensor functionality.

Open Lab, load the MDB and connect to the Deployment Instance.

Once connected, use the Commanding view to get the current value of the light Parameter.

Experiment with shining different amounts of light on the sensor and requesting updated readings, and verify that the values returned are as expected.

Wrap Up

In this tutorial you have:

  • Learned how to interact with subsystems using the Memory Access Service (MAS).

  • Gained a deeper understanding of how Services are used in Flightkit and how they can be used to connect Component Instances together.

  • Learned about how the Flightkit development workflow can be used to produce Component Types which interact with hardware subsystems.

In the next tutorial, we’ll introduce a new Service for interacting with hardware in a "peer-to-peer" fashion, and you’ll create a Component Type which Consumes this Service.

Click here to move on to that now.