Step 6: Add Functionality to Your Product

After you complete the previous steps, a customer can now turn the LED light provided with your Alexa Connect Kit (ACK) product on or off. In this step, you add functionality to your product. You add support for the Alexa.BrightnessController capability interface so the user can brighten or dim the light.

By default, all virtual products come with the Alexa.PowerController capability interface. You can add any supported interfaces that you want. For ideas about how to combine Alexa interfaces in different products, see Get Started with Device Templates.

To add functionality to your product, you update the embedded code of the host microcontroller for your device. In this example, the host microcontroller is an Arduino Zero development board.

Update the list of Alexa capability interfaces that your product supports

To update the Alexa capabilities of your product

  1. Open the ACK developer console and log in.

  2. Open the Products page.

  3. Click the product that you want to customize.

  4. Open the Alexa capabilities tab. The code that appears on this tab corresponds to the Alexa.Discovery response event that an Alexa skill sends to Alexa when a customer connects a new device to Alexa.

  5. In the section Alexa smart home capabilities, click Edit.

  6. Add new capabilities from the menu, and then click Add. In this example, add Alexa.BrightnessController.

  7. In the Editing pane, edit the JSON for existing capabilities if necessary. For example, Alexa.ModeController requires that you specify your modes in the configuration.

  8. Verify that the updated JSON looks similar to the following.

    [
      {
        "type": "AlexaInterface",
        "interface": "Alexa.PowerController",
        "version": "3",
        "properties": {
          "proactivelyReported": true,
          "retrievable": true,
          "supported": [
            {
              "name": "powerState"
            }
          ]
        }
      },
      {
        "type": "AlexaInterface",
        "interface": "Alexa.BrightnessController",
        "version": "3",
        "properties": {
          "supported": [
            {
              "name": "brightness"
            }
          ],
          "proactivelyReported": true,
          "retrievable": true
        }
      }
    ]
    
  9. Click Save

Add support for international languages

When you build an ACK product, your customers automatically get support for utterances in their own language, for the smart home interfaces that support international languages. For details, see Support for international languages and List of Alexa Interfaces and Supported Languages.

When you implement the Alexa.ModeController, Alexa.RangeController, and Alexa.ToggleController interfaces in your product, you can provide additional language support for your customers. You can provide friendly names for your mode settings and preset values in foreign languages.

For details about friendly names, and example json code, see Friendly Name Resources and Assets.

To add friendly names to your product

  1. Open the ACK developer console and log in.

  2. Open the Products page.

  3. Click the product that you want to customize.

  4. Open the Alexa capabilities tab.

  5. In the section Alexa smart home capabilities, click Edit.

  6. In the Editing pane, edit the JSON for your product.

    For each Alexa.ModeController, Alexa.RangeController, or Alexa.ToggleController interface, add the friendly names for your product. For example, your code might look like the following.

    [
      {
        "type": "AlexaInterface",
        "interface": "Alexa.PowerController",
        "version": "3",
        "properties": {
          "supported": [
            {
              "name": "powerState"
            }
          ],
          "proactivelyReported": true,
          "retrievable": true
        }
      },
      {
        "type": "AlexaInterface",
        "interface": "Alexa.RangeController",
        "instance": "1",
        "version": "3",
        "properties": {
          "supported": [
            {
              "name": "rangeValue"
            }
          ],
          "proactivelyReported": true,
          "retrievable": true,
          "nonControllable": false
        },
        "capabilityResources": {
          "friendlyNames": [
            {
              "@type": "asset",
              "value": {
                "assetId": "Alexa.Setting.FanSpeed"
              }
            }
          ]
        },
        "configuration": {
          "supportedRange": {
            "minimumValue": 1,
            "maximumValue": 10,
            "precision": 1
          },
          "presets": [
            {
              "rangeValue": 10,
              "presetResources": {
                "friendlyNames": [
                  {
                    "@type": "asset",
                    "value": {
                      "assetId": "Alexa.Value.Maximum"
                    }
                  },
                  {
                    "@type": "asset",
                    "value": {
                      "assetId": "Alexa.Value.High"
                    }
                  },
                  {
                    "@type": "text",
                    "value": {
                      "text": "Highest",
                      "locale": "en-US"
                    }
                  },
                  {
                    "@type": "text",
                    "value": {
                      "text": "Fast",
                      "locale": "en-US"
                    }
                  },
                  {
                    "@type": "text",
                    "value": {
                      "text": "Alta",
                      "locale": "es-MX"
                    }
                  },
                  {
                    "@type": "text",
                    "value": {
                      "text": "Élevée",
                      "locale": "fr-CA"
                    }
                  }
                ]
              }
            },
            {
              "rangeValue": 1,
              "presetResources": {
                "friendlyNames": [
                  {
                    "@type": "asset",
                    "value": {
                      "assetId": "Alexa.Value.Minimum"
                    }
                  },
                  {
                    "@type": "asset",
                    "value": {
                      "assetId": "Alexa.Value.Low"
                    }
                  },
                  {
                    "@type": "text",
                    "value": {
                      "text": "Lowest",
                      "locale": "en-US"
                    }
                  },
                  {
                    "@type": "text",
                    "value": {
                      "text": "Slow",
                      "locale": "en-US"
                    }
                  },
                  {
                    "@type": "text",
                    "value": {
                      "text": "Baja",
                      "locale": "es-MX"
                    }
                  },
                  {
                    "@type": "text",
                    "value": {
                      "text": "Faible",
                      "locale": "fr-CA"
                    }
                  }
                ]
              }
            }
          ]
        }
      }
    ]
    
  7. Click Save

Set up your source code editor

Prepare to edit your source code by opening your preferred source code editor. Do not use the Arduino IDE to edit the code for your host microcontroller firmware. Use the Arduino IDE only to compile your code and upload your compiled image to your Arduino board. Specify in the Arduino IDE that you are using an external editor.

To specify in the Arduino IDE that you are using an external editor

  1. Open the Arduino IDE.
  2. On the File menu, click Preferences.
  3. On the Settings tab, select the Use external editor check box.
  4. Click OK.

Locate the ACK Device SDK and the HelloWorld example application

  1. Identify the location where you unzipped the ACK Device SDK. The files for the ACK Device SDK are not the files in the Arduino Libraries folder. You unzip the ACK Device SDK files to a different location. For details, see Run the setup script.

  2. Change directory to the location where the ACK Device SDK files were unzipped, for example /Users/<username>/<Downloads>/<ack-device-sdk>/.

  3. Locate the HelloWorld example application, which is at ./applications/HelloWorld.

Declare support for your new functionality in your code

  1. To support the new functionality, in the ack_user_config.h file, increase the memory allocation by changing ACK memory pool size to 1024.

    #define ACK_MEMORY_POOL_SIZE 1024
    
  2. In ack_user_config.h, declare support for Alexa.BrightnessController. Near line 30, insert the following.

    #define ACK_BRIGHTNESS_CONTROLLER
    
  3. In HelloWorld_Alexa.c, declare support for Alexa.BrightnessController. Near line 18, insert the following.

    #include "ack_brightness_controller.h"
    
  4. In HelloWorld_Alexa.c, add handler code for the brightness controller properties. After static bool AddPowerStateProperty(uint32_t propertyOrdinal, unsigned propertyFlags);, insert the following:

    static bool AddBrightnessProperty(uint32_t propertyOrdinal, unsigned propertyFlags);
    
  5. In HelloWorld_Alexa.c, after #define POWER_STATE_PROPERTY 1, insert the following.

    #define BRIGHTNESS_PROPERTY 2
    
  6. In HelloWorld_Alexa.c, after { POWER_STATE_PROPERTY, AddPowerStateProperty },, insert the following.

    { BRIGHTNESS_PROPERTY, AddBrightnessProperty },
    

Implement the code for your new functionality

  1. In HelloWorld_Alexa.c, create a function named AddBrightnessProperty by copying and pasting the function AddPowerStateProperty.

  2. In the AddBrightnessProperty function, in ACK_DEBUG_PRINT_E("Error %u adding power state property.", error);, replace power state with brightness.

  3. In the AddBrightnessProperty function, replace the call to ACK_AddPowerControllerProperty with a call to ACK_AddBrightnessControllerProperty.

  4. In the call to ACK_AddBrightnessControllerProperty, replace Hardware_IsPowerOn() with 42. This temporary change allows for handling Alexa commands without needing to make any device hardware changes. The Alexa app responds to every change that it receives by setting the brightness to 42.

    The following code illustrates an implementation of the ACK_AddBrightnessControllerProperty function.

    static bool AddBrightnessProperty(uint32_t propertyOrdinal, unsigned propertyFlags)
    {
        ACKStateCommon_t propertyState = { 0, 0, propertyFlags };
        ACKError_t error;
    
        error = ACK_AddBrightnessControllerProperty(&propertyState, 42);
    
        if (ACK_NO_ERROR != error)
        {
            ACK_DEBUG_PRINT_E("Error %u adding brightness state property.", error);
            return false;
        }
    
        return true;
    }
    
  5. In HelloWorld_Alexa.c, create a function ACKUser_OnBrightnessControllerDirective to handle the brightness controller directives. To verify the handler signature, see the relevant file in the include directory, for example include/ack_brightness_controller.h.

    The following code illustrates an implementation of the ACKUser_OnBrightnessControllerDirective function.

    void ACKUser_OnBrightnessControllerDirective(int32_t correlationId, bool isDelta, int32_t value)
    {
    
        ACK_DEBUG_PRINT_I("Received brightness controller directive; value %d.", value);
    
        ACK_CompleteDirectiveWithSuccess(
                correlationId,
                ACK_PROPERTY_BIT(BRIGHTNESS_PROPERTY),
                0);
    }
    

Upload your code and test your new functionality

  1. Use the setup.py script provided in the ACK Device SDK to install your new application code in a way that Arduino can compile and upload. For details, see Run the setup script. Use the --force option so that the setup.py script overwrites the code that you installed previously.

    python3 ./setup.py --force
    
  2. Compile and upload your code. For details, see Upload Code to the Arduino Zero. The instructions explain the process by using the Development Kit for ACK, but you can complete the steps with your own host microcontroller.

  3. Use the Arduino IDE to review logs generated by your host microcontroller application, for example methods such as ACK_DEBUG_PRINT_I(). In the Arduino IDE, on the Tools menu, click Serial Monitor. Set the baud rate to 115200.

  4. Experiment with your device by using the Alexa app. Use the slider control to alter the brightness. The brightness should reset to 42. Keep in mind that the behavior of the device LED is unchanged because you haven't made any changes yet to the device hardware.

Implement hardware logic for your new functionality

The following steps show how to implement a function to set the brightness in hardware and a function to get the current brightness from hardware.

  1. In the header file HelloWorld_Hardware.h, declare the function signatures.

    In HelloWorld_Hardware.h, review lines 29-32 to see the corresponding function signatures for handling the state related to PowerController, which controls whether the LED is on or off.

    The example assumes the following function signatures:

    • void Hardware_SetBrightnessState(int32_t value);, which accepts the brightness value and sets it in hardware.
    • int32_t Hardware_GetBrightnessState(void);, which gets the brightness value from hardware.
  2. In HelloWord_Hardware.c, implement the following functions:

    Simplify the implementation by not reading the brightness value from hardware. Instead, create a global variable g_brightness and call the value as if reading from hardware. In HelloWorld_Hardware.c, below the #include lines, add int32_t g_brightness;.

    Implement the simplified Hardware_GetBrightnessState as follows:

        int32_t Hardware_GetBrightnessState(void)
        {
            return g_brightness;
        }
    
  3. In HelloWord_Hardware.c, do the following to implement Hardware_SetBrightnessState, which receives a value for the brightness level to set in hardware.

    Specify g_brightness = value; to set the brightness level in a global variable for later retrieval.

    Specify ACKPlatform_SetDigitalPinPWMLevel(ACK_HW_PIN_SAMPLE_APPLICATIONS_LED, (uint8_t)((255 * value) / 100) );. The ACK Device SDK provides the function ACKPlatform_SetDigitalPinPWMLevel as part of its Arduino support. The file .\user\platform\arduino\ack_arduino_platform.cpp shows all functions provided for Arduino. The example uses ACKPlatform_SetDigitalPinPWMLevel because setting a pin to a brightness value requires pulse width modulation (PWM). Alexa provides values for brightness between 0 and 100, while Arduino accepts PWM brightness levels 0 and 255, so the example multiplies the Alexa value by 2.55.

  4. Do the following to connect the hardware logic to the Alexa logic that handles directives from the user:

    In HelloWorld_Alexa.c, in the AddBrightnessProperty function where you previously set the value to 42, replace 42 with a call to Hardware_GetBrightnessState(), to get the brightness stored in the hardware. The line should look as follows:

    error = ACK_AddBrightnessControllerProperty(&propertyState, Hardware_GetBrightnessState());
    

    In HelloWorld_Alexa.c, to set the value in hardware as each Alexa command arrives. In ACKUser_OnBrightnessControllerDirective, before the call to ACK_CompleteDirectiveWithSuccess, add the line Hardware_SetBrightnessState(value);.

Extend your new functionality

The implementation of brightness controller doesn't handle all scenarios. Extend and improve the functionality by addressing issues similar to the following:

  • The implementation doesn't handle when power is off and the user, for example, changes the brightness from 0 to 50. The power controller commands stop working because the power controller and brightness controller affect the same pins and have a shared state.

  • The implementation assumes that all incoming brightness controller directives provide the absolute desired value for brightness, for example "Alexa, change brightness to fifty". However, a user can provide a relative instruction, for example "Alexa, increase brightness by fifty.". To handle the relative instruction, see the delta value in the function signature for ACKUser_OnBrightnessControllerDirective.

  • Check for situations like a user increasing the brightness by 50 when brightness is already at 90. More generally, recognize all user situations for each controller.

  • Consider supporting a local mechanism such as a button press that turns the power on and off and maintains the state with Alexa. In HelloWorld_Hardware.c, see the stub function Hardware_IsPowerToggleButtonDown.

Error handling

The ACK console capability editor helps you to specifying capabilities in the following ways:

  1. You can add capabilities by using a menu of supported capabilities to reduce copy and paste errors.
  2. The console indicates JSON syntax errors.
  3. The console indicates errors related to capability-specific schemas or the API schema as a whole. For example, if you specify two copies of the same capability interface, you get a DUPLICATED_BASIC_CAPABILITIES error. For details, see ACK Console Error Reference.