Skip to content

Commit

Permalink
Re-sync with upstream (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
noahp authored Oct 31, 2023
1 parent 7f7438e commit 26d2511
Show file tree
Hide file tree
Showing 20 changed files with 910 additions and 141 deletions.
26 changes: 26 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ if(DEFINED IDF_VERSION_MAJOR)
endif()
endif()

# Look for the Memfault SDK in a subdirectory first
get_filename_component(memfault_firmare_sdk_dir third-party/memfault-firmware-sdk ABSOLUTE)
if(NOT EXISTS ${memfault_firmare_sdk_dir})
get_filename_component(memfault_firmare_sdk_dir ../../../../ ABSOLUTE)
endif()
include(${memfault_firmare_sdk_dir}/ports/esp_idf/memfault.cmake)

# NOTE: This include also applies global compiler options, make sure
Expand All @@ -24,6 +28,28 @@ include(${memfault_firmare_sdk_dir}/ports/esp_idf/memfault.cmake)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(${PROJECT_NAME})

# Check for invalid partition table configurations
if (
CONFIG_APP_MEMFAULT_TRANSPORT_HTTP AND
NOT CONFIG_PARTITION_TABLE_CUSTOM_FILENAME STREQUAL "partitions_example.csv"
)
message(WARNING "Data transport is HTTP but using partition table ${CONFIG_PARTITION_TABLE_CUSTOM_FILENAME}")
set(INVALID_PARTITION_TABLE true)
elseif(
CONFIG_APP_MEMFAULT_TRANSPORT_MQTT AND
NOT CONFIG_PARTITION_TABLE_CUSTOM_FILENAME STREQUAL "partitions_example_mqtt.csv"
)
message(WARNING "Data transport is MQTT but using partition table ${CONFIG_PARTITION_TABLE_CUSTOM_FILENAME}")
set(INVALID_PARTITION_TABLE true)
endif()

if (INVALID_PARTITION_TABLE)
message(FATAL_ERROR
"Invalid partition table configuration, check CONFIG_APP_MEMFAULT_TRANSPORT configs.\
If this error occurs repeatedly run `idf.py fullclean && rm sdkconfig`"
)
endif()

# Add the Memfault Build ID so each build can have a unique version.
set(IDF_PROJECT_EXECUTABLE ${PROJECT_NAME}.elf)
add_custom_command(TARGET ${IDF_PROJECT_EXECUTABLE}
Expand Down
50 changes: 29 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
# ESP32 Standalone Memfault Demo Application
# esp32 Demo Application

This Demo App is based on the example in the Memfault Firmware SDK:
This Demo App is based on the console example from ESP-IDF, which can be found
here relative to the ESP-IDF SDK root folder:

https://github.com/memfault/memfault-firmware-sdk/tree/0.39.0/examples/esp32
- `examples/system/console/advanced/`

It also showcases including the Memfault SDK as a submodule for an ESP-IDF
project.
## Configuring for MQTT

The submodule was added with this command:
This application includes an option to send Memfault data over MQTT. This option requires a few extra pieces to set up.
You can either follow the steps outlined here or use your own MQTT setup.

```bash
❯ git submodule add https://github.com/memfault/memfault-firmware-sdk.git \
third-party/memfault-firmware-sdk
```
### Broker Setup

When cloning this repo, either use the `--recursive` flag or update submodules
after cloning:
1. Install a local installtion of Cedalo by following the [installation guide](https://docs.cedalo.com/management-center/installation/)
2. Login to Cedalo at <http://localhost:8088>
3. Create a new client login for the device
- Ensure device client has the "client" role to allow publishing data
4. Create a new client login for the Python service
- Ensure Python service client has "client" role to allow subscribing to data

```bash
❯ git clone --recursive https://github.com/memfault/esp32-standalone-example
# or
❯ git clone https://github.com/memfault/esp32-standalone-example
cd esp32-standalone-example
❯ git submodule update --init --recursive
```
### Service Setup

## Building
1. Modify the script found in Docs->Best Practices->MQTT with Memfault with the the following:
1. The service client login information previously created
2. Connection info for your local broker
3. Map of Memfault projects to project keys
2. Start the service by running `python mqtt.py`

Can be built using `idf.py build` as usual.
### Device Setup

1. Make the following modifications to `main/app_memfault_transport_mqtt.c`:
1. Update `MEMFAULT_PROJECT` macro with your project's name
2. Update `s_mqtt_config` with your setup's IP address, and MQTT client username and password
2. Clean your existing build with `idf.py fullclean && rm sdkconfig`
3. Set your target: `idf.py set-target <esp32_platform_name>`
4. Build your image: `idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.mqtt" build`
5. Flash to your device using `idf.py flash`
15 changes: 14 additions & 1 deletion main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ list(APPEND
cmd_system.c
console_example_main.c
led.c
memfault/memfault_platform_device_info.c
)

if (CONFIG_APP_MEMFAULT_TRANSPORT_HTTP)
list(APPEND COMPONENT_SRCS app_memfault_transport_http.c)
elseif(CONFIG_APP_MEMFAULT_TRANSPORT_MQTT)
list(APPEND COMPONENT_SRCS app_memfault_transport_mqtt.c)
endif()

# the 'cmd_wifi.c' implementation is different for ESP-IDF v5+
if("${IDF_VERSION_MAJOR}" VERSION_GREATER_EQUAL 5)
list(APPEND
Expand All @@ -20,6 +25,14 @@ else()
)
endif()

# include settings.c only on idf >= 4
if("${IDF_VERSION_MAJOR}" VERSION_GREATER_EQUAL 4)
list(APPEND
COMPONENT_SRCS
settings.c
)
endif()

set(COMPONENT_ADD_INCLUDEDIRS
.
memfault
Expand Down
24 changes: 17 additions & 7 deletions main/Kconfig.projbuild
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,6 @@ config STORE_HISTORY
command history. If this option is enabled, initalizes a FAT filesystem
and uses it to store command history.

config MEMFAULT_ESP32_MAIN_FIRMWARE_VERSION
string "Main firmware version"
default "1.0.0-dev"
help
The version of the main firmware. Used when the device reports in as
well as for OTA checks.

config MEMFAULT_APP_OTA
bool "Enable automatic periodic check+update for OTA"
default y
Expand All @@ -28,6 +21,23 @@ config MEMFAULT_APP_WIFI_AUTOJOIN
help
Automatically join if credentials are configured.

choice APP_MEMFAULT_TRANSPORT
prompt "Protocol to send chunks over"
default APP_MEMFAULT_TRANSPORT_HTTP

config APP_MEMFAULT_TRANSPORT_HTTP
bool "HTTP"
config APP_MEMFAULT_TRANSPORT_MQTT
bool "MQTT"
select MQTT_PROTOCOL_5
endchoice

if APP_MEMFAULT_TRANSPORT_MQTT
config MEMFAULT_DEVICE_INFO_SOFTWARE_TYPE
string "Override default software version for the application"
default "esp32-main-mqtt"
endif

# These LED settings are taken from ESP-IDF:
# examples/get-started/blink/main/blink_example_main.c

Expand Down
26 changes: 26 additions & 0 deletions main/app_memfault_transport.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//! @file
//!
//! Copyright (c) Memfault, Inc.
//! See License.txt for details

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

//! Initializes any components needed for configured transport
//!
//! Transport is selected via CONFIG_APP_MEMFAULT_TRANSPORT choices
void app_memfault_transport_init(void);

//! Sends all available Memfault chunks over configured transport
//!
//! Transport is selected via CONFIG_APP_MEMFAULT_TRANSPORT choices
//!
//! @return 0 on success or non-zero on error
int app_memfault_transport_send_chunks(void);

#ifdef __cplusplus
}
#endif
13 changes: 13 additions & 0 deletions main/app_memfault_transport_http.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! @file
//!
//! Copyright (c) Memfault, Inc.
//! See License.txt for details

#include "app_memfault_transport.h"
#include "memfault/esp_port/http_client.h"

void app_memfault_transport_init(void) {}

int app_memfault_transport_send_chunks(void) {
return memfault_esp_port_http_client_post_data();
}
171 changes: 171 additions & 0 deletions main/app_memfault_transport_mqtt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
//! @file
//!
//! Copyright (c) Memfault, Inc.
//! See License.txt for details

#include <stddef.h>
#include <stdint.h>

#include "app_memfault_transport.h"
#include "esp_log.h"
#include "memfault/components.h"
#include "mqtt_client.h"

// TODO: Fill in with device's Memfault project
#define MEMFAULT_PROJECT "my_project"

static const char *TAG = "app_memfault_transport_mqtt";

// TODO: Fill in with broker connection configuration
static esp_mqtt_client_config_t s_mqtt_config = {
.broker.address.uri = "mqtt://192.168.50.88",
.credentials.username = "test",
.credentials.authentication.password = "test1234",
.session.protocol_ver = MQTT_PROTOCOL_V_5,
};
static SemaphoreHandle_t s_mqtt_connected = NULL;

static esp_mqtt_client_handle_t s_mqtt_client = NULL;
static esp_mqtt5_publish_property_config_t s_publish_property = {
.topic_alias = 1,
};
static char s_topic_string[128] = {0};

static uint8_t s_chunk_data[1024] = {0};

static void mqtt_event_handler(MEMFAULT_UNUSED void *handler_args,
MEMFAULT_UNUSED esp_event_base_t base,
MEMFAULT_UNUSED int32_t event_id, void *event_data) {
esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data;

switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "Connected to MQTT broker");
xSemaphoreGive(s_mqtt_connected);
break;
default:
ESP_LOGE(TAG, "Unknown MQTT event received[%d]", event->event_id);
break;
}
}

static void prv_close_client(void) {
int rv = esp_mqtt_client_disconnect(s_mqtt_client);
if (rv) {
ESP_LOGW(TAG, "Failed to disconnect[%d]", rv);
}

rv = esp_mqtt_client_destroy(s_mqtt_client);
if (rv) {
ESP_LOGW(TAG, "Failed to destroy client[%d]", rv);
}
memfault_metrics_heartbeat_timer_stop(MEMFAULT_METRICS_KEY(mqtt_conn_uptime));

s_mqtt_client = NULL;
}

static int prv_create_client(void) {
if (s_mqtt_client) {
return 0;
}

s_mqtt_client = esp_mqtt_client_init(&s_mqtt_config);
if (s_mqtt_client == NULL) {
ESP_LOGE(TAG, "MQTT client failed to initialize");
return -1;
}

int rv =
esp_mqtt_client_register_event(s_mqtt_client, MQTT_EVENT_CONNECTED, mqtt_event_handler, NULL);
if (rv) {
ESP_LOGE(TAG, "MQTT event handler registration failed[%d]", rv);
}

rv = esp_mqtt_client_start(s_mqtt_client);
if (rv) {
ESP_LOGE(TAG, "MQTT client start failed[%d]", rv);
return -1;
}

// Wait for successful connection
rv = xSemaphoreTake(s_mqtt_connected, (1000 * 10) / portTICK_PERIOD_MS);
if (rv != pdTRUE) {
ESP_LOGE(TAG, "MQTT client failed to connect[%d]", rv);
memfault_metrics_heartbeat_timer_start(MEMFAULT_METRICS_KEY(mqtt_conn_downtime));
prv_close_client();
return -1;
}

// Update connection metrics when connected
memfault_metrics_heartbeat_timer_stop(MEMFAULT_METRICS_KEY(mqtt_conn_downtime));
memfault_metrics_heartbeat_timer_start(MEMFAULT_METRICS_KEY(mqtt_conn_uptime));

// Set topic alias before publishing
rv = esp_mqtt5_client_set_publish_property(s_mqtt_client, &s_publish_property);
if (rv != 0) {
ESP_LOGW(TAG, "MQTT client could not set publish property [%d]", rv);
}
return 0;
}

static const char *prv_get_device_serial(void) {
sMemfaultDeviceInfo info = {0};
memfault_platform_get_device_info(&info);
return info.device_serial;
}

void prv_build_topic_string(void) {
// String already built
if (strlen(s_topic_string) > 0) {
return;
}

const char *device_serial = prv_get_device_serial();
snprintf(s_topic_string, MEMFAULT_ARRAY_SIZE(s_topic_string),
"memfault/" MEMFAULT_PROJECT "/%s/chunks", device_serial);
}

void app_memfault_transport_init(void) {
#if MEMFAULT_FREERTOS_PORT_USE_STATIC_ALLOCATION != 0
static StaticSemaphore_t s_mqtt_connected;
s_mqtt_connected = xSemaphoreCreateBinaryStatic(&s_mqtt_connected);
#else
s_mqtt_connected = xSemaphoreCreateBinary();
#endif
}

int app_memfault_transport_send_chunks(void) {
int rv = prv_create_client();

if (rv) {
return rv;
}

prv_build_topic_string();

ESP_LOGD(TAG, "Checking for packetizer data");
while (memfault_packetizer_data_available()) {
size_t chunk_size = MEMFAULT_ARRAY_SIZE(s_chunk_data);
bool chunk_filled = memfault_packetizer_get_chunk(s_chunk_data, &chunk_size);

if (!chunk_filled) {
ESP_LOGW(TAG, "No chunk data produced");
break;
}

rv = esp_mqtt_client_publish(s_mqtt_client, s_topic_string, (char *)s_chunk_data, chunk_size, 1,
0);
if (rv < 0) {
ESP_LOGE(TAG, "MQTT failed to publish[%d]", rv);
memfault_packetizer_abort();
break;
}

memfault_metrics_heartbeat_add(MEMFAULT_METRICS_KEY(mqtt_publish_bytes), chunk_size);
memfault_metrics_heartbeat_add(MEMFAULT_METRICS_KEY(mqtt_publish_count), 1);
ESP_LOGD(TAG, "chunk[%d], len[%zu] published to %s", rv, chunk_size, s_topic_string);
}

prv_close_client();
return rv;
}
Loading

0 comments on commit 26d2511

Please sign in to comment.