Embedded World 2021 - Infineon ModusToolbox PSoC 6 Drone 

 

Summary

In lesson 4 we will finish the cloud task by adding a connection to the MQTT broker.  Then I will make a very minor update to the CapSense and Joystick tasks to send their data to the Cloud.  At the end of this lesson you will have the Cloud truly colored green.  Here is the architecture:

Learning Objectives

  1. MQTT
  2. AnyCloud use of AWS libraries

Procedure

  1. Create a new project by copying lesson 3
  2. Add the MQTT Library
  3. Copy the core MQTT configuration
  4. Update the Makefile
  5. Update the cloud_task.h
  6. Update the cloud_task.c
  7. Update joystick_task.c
  8. Update capsense_task.c
  9. Test (using an MQTT Client)
  10. Test (using our C++/Qt GUI)

1. Create a new project by copying lesson 3

Start the new project creator.  Pick the CY8CPROTO-062-4343W and press next.

Click on the import button

And select project three to start from.

Give it a sensible name

2. Add the MQTT Library

Start the library browser and pick out the MQTT library.  Notice that it brings in a bunch of other libraries including the AWS libraries.

3. Copy the core MQTT configuration

When you look at the README for the MQTT it tells you to copy the file core_mqtt_config.h into your project.  Do so.

4. Update the Makefile

For this to work you need to make two changes to your Makefile.  First enable the secure sockets.

COMPONENTS= FREERTOS LWIP MBEDTLS SECURE_SOCKETS

Then ignore a few files from the AWS library.

CY_IGNORE+=$(SEARCH_aws-iot-device-sdk-embedded-C)/libraries/standard/coreHTTP
CY_IGNORE+=libs/aws-iot-device-sdk-embedded-C/libraries/standard/coreHTTP

5. Update the cloud_task.h

In order for other tasks to send a message I will create a function where they can send a “speed” also known as a percent 0-100.  This function will be called from both the CapSense task as well as the JoyStick task.

void cloud_sendMotorSpeed(int speed);

6. Update the cloud_task.c

In the previous lesson we created the cloud task which connects to the WiFI network.  In this lesson we add MQTT.  Start by adding the includes for the MQTT and the RTOS queue.

#include "cy_mqtt_api.h"
#include "queue.h"

Rather than have magic numbers (or values) hidden in my code I provide #defines for:

  1. The MQTT broker
  2. My Client ID
  3. The Topic to broadcast to
  4. The JSON key for the motor speed

For other tasks to talk to this task they will push a value of the speed into a queue.  So I need to define that queue.  I will also use a static local variable to hold the handle of the MQTT connection.

I then define functions to

  1. Connect to MQTT
  2. Start MQTT
  3. Publish to MQTT
#define CLOUD_MQTT_BROKER        "mqtt.eclipseprojects.io"


#define CLOUD_MQTT_CLIENT_PREFIX "remote"
#define CLOUD_MQTT_TOPIC         "motor_speed"

#define MOTOR_KEY                "motor"

static QueueHandle_t motor_value_q;
static cy_mqtt_t mqtthandle;

static void cloud_connectWifi();
static void cloud_startMQTT();
static void cloud_mqtt_event_cb( cy_mqtt_t mqtt_handle, cy_mqtt_event_t event, void *user_data);

static void cloud_publishMessage(char *topic,char *message);

The updates to the cloud task are:

  1. Start WiFi
  2. Start MQTT
  3. Start the queue
  4. In the main loop, wait for a message, then publish it.
void cloud_task(void* param)
{
    (void)param;

    cloud_connectWifi();
    cloud_startMQTT();

    motor_value_q = xQueueCreate(1,sizeof(uint32_t));

    for(;;)
    {
        int motorSpeed;
        char message[32];

    	xQueueReceive(motor_value_q, &motorSpeed, portMAX_DELAY);
        snprintf(message, sizeof(message)-1, "{\"%s\":%d}",MOTOR_KEY,motorSpeed);
        cloud_publishMessage(CLOUD_MQTT_TOPIC,message);
    }
}

This function is used in other tasks to send the motor speed into the queue.

void cloud_sendMotorSpeed(int speed)
{
    if(motor_value_q)
        xQueueOverwrite(motor_value_q,&speed);
}

To start MQTT you need to

  1. Define the MQTT broker structure
  2. Create the MQTT system
  3. Define your connection information
  4. Create a random clientID (so there are no duplicated clients on the MQTT broker)
  5. Make the connection
static void cloud_startMQTT()
{
    static cy_mqtt_connect_info_t    	connect_info;
    static cy_mqtt_broker_info_t     	broker_info;
    static uint8_t buffer[1024];

    cy_rslt_t result;

    result = cy_mqtt_init();
    broker_info.hostname = CLOUD_MQTT_BROKER;
    broker_info.hostname_len = strlen(broker_info.hostname);
    broker_info.port = 1883;

    result = cy_mqtt_create( buffer, sizeof(buffer),
                              NULL, &broker_info,
                              cloud_mqtt_event_cb, NULL,
                              &mqtthandle );

    CY_ASSERT(result == CY_RSLT_SUCCESS);

    static char clientId[32];
    srand(xTaskGetTickCount());
    snprintf(clientId,sizeof(clientId),"%s%6d",CLOUD_MQTT_CLIENT_PREFIX,rand());
    memset( &connect_info, 0, sizeof( cy_mqtt_connect_info_t ) );
    connect_info.client_id      = clientId;
    connect_info.client_id_len  = strlen(connect_info.client_id);
    connect_info.keep_alive_sec = 60;
    connect_info.will_info      = 0;
    connect_info.clean_session = true;


    result = cy_mqtt_connect( mqtthandle, &connect_info );
    CY_ASSERT(result == CY_RSLT_SUCCESS);
    printf("MQTT Connect Success to %s Client=%s\n",CLOUD_MQTT_BROKER,clientId);

}

You need an MQTT callback.  This is unused in this lesson but we will modify it for the drone.  Just copy it into your project

static void cloud_mqtt_event_cb( cy_mqtt_t mqtt_handle, cy_mqtt_event_t event, void *user_data )
{
    cy_mqtt_publish_info_t *received_msg;
    printf( "\nMQTT App callback with handle : %p \n", mqtt_handle );
    (void)user_data;
    switch( event.type )
    {
        case CY_MQTT_EVENT_TYPE_DISCONNECT :
            if( event.data.reason == CY_MQTT_DISCONN_TYPE_BROKER_DOWN )
            {
                printf( "\nCY_MQTT_DISCONN_TYPE_BROKER_DOWN .....\n" );
            }
            else
            {
                printf( "\nCY_MQTT_DISCONN_REASON_NETWORK_DISCONNECTION .....\n" );
            }
            break;
        case CY_MQTT_EVENT_TYPE_PUBLISH_RECEIVE :
            received_msg = &(event.data.pub_msg.received_message);
            printf( "Incoming Publish Topic Name: %.*s\n", received_msg->topic_len, received_msg->topic );
            printf( "Incoming Publish message Packet Id is %u.\n", event.data.pub_msg.packet_id );
            printf( "Incoming Publish Message : %.*s.\n\n", ( int )received_msg->payload_len, ( const char * )received_msg->payload );
            break;
        default :
            printf( "\nUNKNOWN EVENT .....\n" );
            break;
    }
}

To publish a message you

  1. Create the message
  2. Send the publish
static void cloud_publishMessage(char *topic,char *message)
{
    cy_mqtt_publish_info_t  pub_msg;
        
    pub_msg.qos = CY_MQTT_QOS0;
    pub_msg.topic = topic;
    pub_msg.topic_len = strlen(pub_msg.topic);
    pub_msg.payload = message;
    pub_msg.payload_len = strlen(message);
    
    cy_mqtt_publish( mqtthandle, &pub_msg );
    printf("Published to Topic=%s Message=%s\n",topic,message);
    
}

7. Update joystick_task.c

We want the joystick task to be able to send joystick positions.  Include the cloud_task.h so that it gets access to the function to send messages.

#include "cloud_task.h"

Update the joystick function to call the send message function.

/* Only update/print new value if it has changed by more than the hysteresis value */
if((joystick_curr.x > (joystick_prev.x + JOYSTICK_HYSTERESIS)) || (joystick_curr.x < (joystick_prev.x - JOYSTICK_HYSTERESIS)))
{
    printf("Joystick Position: %d\n", joystick_curr.x);
    cloud_sendMotorSpeed(joystick_curr.x); 

}

8. Update capsense_task.c

We want the CapSense task to be able to send CapSense positions.  Include the cloud_task.h so that it gets access to the function to send messages.

#include "cloud_task.h"

Update the button code to send either 0 or 75 (0=STOP!!!#*##&&#) (75=take off)

if((0u != button0_status) && (0u == button0_status_prev))
{
    printf("Button 0 pressed\n");
    cloud_sendMotorSpeed(0); // Stop the Motor
}

if((0u != button1_status) && (0u == button1_status_prev))
{
    printf("Button 1 pressed\n");
    cloud_sendMotorSpeed(75); // Set the motor to 75%
}

The slider just sends the current position.

if((0u != slider_touched) && (slider_pos_prev != slider_pos ))
{
    printf("Slider position %d\n",slider_pos);
    cloud_sendMotorSpeed(slider_pos);
}

9. Test (using an MQTT Client)

You can download/install MQTTBox from here  This is a Mac/Linux/Windows MQTT client.  This will enable you to attach to the MQTT broker and then subscribe to the motor speed topic.

Setup the configuration for the MQTT broker by pressing the gear button

Then program your remote control and send some joystick and CapSense values.

And you will see them coming out on the MQTTBox client

10. Test (using our C++/Qt GUI)

My friend Butch created a C++/Qt GUI to “simulate” the drone.  You can find it in the “simulator” directory of project 4.  Here is what it looks like.  You will need to put in

  1. Which broker you are using
  2. The name of your Topic

Resources for Project

You can find this completed project in your project creator dialog by filtering for “IoT Expert Embedded”.  This is lesson 4

You can also clone this project at git@github.com:iotexpert/ew21-lesson4.git or https://github.com/iotexpert/ew21-lesson4

Here is the final cloud_task.c

#include <stdio.h>

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

#include "cy_wcm.h"
#include "cy_mqtt_api.h"

#include "cloud_task.h"


#define CLOUD_WIFI_AP        "ew2021"
#define CLOUD_WIFI_PW        "ew2021ap"
#define CLOUD_WIFI_SECURITY  CY_WCM_SECURITY_WPA2_AES_PSK
#define CLOUD_WIFI_BAND      CY_WCM_WIFI_BAND_ANY

#define CLOUD_MQTT_BROKER        "mqtt.eclipseprojects.io"


#define CLOUD_MQTT_CLIENT_PREFIX "remote"
#define CLOUD_MQTT_TOPIC         "motor_speed"

#define MOTOR_KEY                "motor"

static QueueHandle_t motor_value_q;
static cy_mqtt_t mqtthandle;

static void cloud_connectWifi();
static void cloud_startMQTT();
static void cloud_mqtt_event_cb( cy_mqtt_t mqtt_handle, cy_mqtt_event_t event, void *user_data);

static void cloud_publishMessage(char *topic,char *message);

void cloud_task(void* param)
{
    (void)param;

    cloud_connectWifi();
    cloud_startMQTT();

    motor_value_q = xQueueCreate(1,sizeof(uint32_t));

    for(;;)
    {
        int motorSpeed;
        char message[32];

    	xQueueReceive(motor_value_q, &motorSpeed, portMAX_DELAY);
        snprintf(message, sizeof(message)-1, "{\"%s\":%d}",MOTOR_KEY,motorSpeed);
        cloud_publishMessage(CLOUD_MQTT_TOPIC,message);
    }
}

void cloud_sendMotorSpeed(int speed)
{
    if(motor_value_q)
        xQueueSend(motor_value_q,&speed,0);
}

static void cloud_connectWifi()
{
    cy_rslt_t result;

    cy_wcm_connect_params_t connect_param = {
        .ap_credentials.SSID = CLOUD_WIFI_AP,
        .ap_credentials.password = CLOUD_WIFI_PW,
        .ap_credentials.security = CLOUD_WIFI_SECURITY,
    	.static_ip_settings = 0,
        .BSSID = {0},
        .band = CLOUD_WIFI_BAND,
    };
    cy_wcm_config_t config = {.interface = CY_WCM_INTERFACE_TYPE_STA}; // We are a station (not a Access Point)

    cy_wcm_init(&config); // Initialize the connection manager

    printf("\nWi-Fi Connection Manager initialized.\n");

    do
    {
        cy_wcm_ip_address_t ip_address;

        printf("Connecting to Wi-Fi AP '%s'\n", connect_param.ap_credentials.SSID);
        result = cy_wcm_connect_ap(&connect_param, &ip_address);

        if (result == CY_RSLT_SUCCESS)
        {
            printf("Successfully connected to Wi-Fi network '%s'.\n",
                    connect_param.ap_credentials.SSID);

            // Print IP Address
            if (ip_address.version == CY_WCM_IP_VER_V4)
            {
                printf("IPv4 Address Assigned: %d.%d.%d.%d\n", (uint8_t)ip_address.ip.v4,
                        (uint8_t)(ip_address.ip.v4 >> 8), (uint8_t)(ip_address.ip.v4 >> 16),
                        (uint8_t)(ip_address.ip.v4 >> 24));
            }
            else if (ip_address.version == CY_WCM_IP_VER_V6)
            {
                printf("IPv6 Address Assigned: %0X:%0X:%0X:%0X\n", (unsigned int)ip_address.ip.v6[0],
                        (unsigned int)ip_address.ip.v6[1], (unsigned int)ip_address.ip.v6[2],
                        (unsigned int)ip_address.ip.v6[3]);
            }
            break; /* Exit the for loop once the connection has been made */
        }
        else
        {
            printf("WiFi Connect Failed Retrying\n");
            vTaskDelay(2000); // wait 2 seconds and try again;
        }

    } while (result != CY_RSLT_SUCCESS);

}

static void cloud_startMQTT()
{
    static cy_mqtt_connect_info_t    	connect_info;
    static cy_mqtt_broker_info_t     	broker_info;
    static uint8_t buffer[1024];

    cy_rslt_t result;

    result = cy_mqtt_init();
    broker_info.hostname = CLOUD_MQTT_BROKER;
    broker_info.hostname_len = strlen(broker_info.hostname);
    broker_info.port = 1883;

    result = cy_mqtt_create( buffer, sizeof(buffer),
                              NULL, &broker_info,
                              cloud_mqtt_event_cb, NULL,
                              &mqtthandle );

    CY_ASSERT(result == CY_RSLT_SUCCESS);

    static char clientId[32];
    srand(xTaskGetTickCount());
    snprintf(clientId,sizeof(clientId),"%s%6d",CLOUD_MQTT_CLIENT_PREFIX,rand());
    memset( &connect_info, 0, sizeof( cy_mqtt_connect_info_t ) );
    connect_info.client_id      = clientId;
    connect_info.client_id_len  = strlen(connect_info.client_id);
    connect_info.keep_alive_sec = 60;
    connect_info.will_info      = 0;
    connect_info.clean_session = true;


    result = cy_mqtt_connect( mqtthandle, &connect_info );
    CY_ASSERT(result == CY_RSLT_SUCCESS);
    printf("MQTT Connect Success to %s Client=%s\n",CLOUD_MQTT_BROKER,clientId);

}

static void cloud_mqtt_event_cb( cy_mqtt_t mqtt_handle, cy_mqtt_event_t event, void *user_data )
{
    cy_mqtt_publish_info_t *received_msg;
    printf( "\nMQTT App callback with handle : %p \n", mqtt_handle );
    (void)user_data;
    switch( event.type )
    {
        case CY_MQTT_EVENT_TYPE_DISCONNECT :
            if( event.data.reason == CY_MQTT_DISCONN_TYPE_BROKER_DOWN )
            {
                printf( "\nCY_MQTT_DISCONN_TYPE_BROKER_DOWN .....\n" );
            }
            else
            {
                printf( "\nCY_MQTT_DISCONN_REASON_NETWORK_DISCONNECTION .....\n" );
            }
            break;
        case CY_MQTT_EVENT_TYPE_PUBLISH_RECEIVE :
            received_msg = &(event.data.pub_msg.received_message);
            printf( "Incoming Publish Topic Name: %.*s\n", received_msg->topic_len, received_msg->topic );
            printf( "Incoming Publish message Packet Id is %u.\n", event.data.pub_msg.packet_id );
            printf( "Incoming Publish Message : %.*s.\n\n", ( int )received_msg->payload_len, ( const char * )received_msg->payload );
            break;
        default :
            printf( "\nUNKNOWN EVENT .....\n" );
            break;
    }
}

static void cloud_publishMessage(char *topic,char *message)
{
    cy_mqtt_publish_info_t  pub_msg;
        
    pub_msg.qos = CY_MQTT_QOS0;
    pub_msg.topic = topic;
    pub_msg.topic_len = strlen(pub_msg.topic);
    pub_msg.payload = message;
    pub_msg.payload_len = strlen(message);
    
    cy_mqtt_publish( mqtthandle, &pub_msg );
    printf("Published to Topic=%s Message=%s\n",topic,message);
    
}

Recommended Posts

No comment yet, add your voice below!


Add a Comment

Your email address will not be published. Required fields are marked *