App.
/* Wirepas Oy licensed under Apache License, Version 2.0
*
* See file LICENSE for full license details.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <MQTTClient.h> // Include the Paho MQTT library
#include "test.h"
#include "wpc.h"
#define LOG_MODULE_NAME "TestApp"
#define MAX_LOG_LEVEL INFO_LOG_LEVEL
#include "logger.h"
// MQTT Configuration
#define MQTT_ADDRESS "tcp://demo.thingsboard.io:1883" // ThingsBoard Cloud MQTT broker
#define MQTT_CLIENTID "WirepasGateway"
#define MQTT_TOPIC "v1/devices/me/telemetry" // ThingsBoard telemetry topic
#define MQTT_QOS 1
#define MQTT_TIMEOUT 10000L
// Replace with your ThingsBoard device access token
#define ACCESS_TOKEN "YOUR_DEVICE_ACCESS_TOKEN"
// Default serial port
char *port_name = "/dev/ttyACM0";
MQTTClient mqtt_client;
// Function to publish MQTT messages
void publish_mqtt_message(const char *topic, const char *message)
{
MQTTClient_message pubmsg = MQTTClient_message_initializer;
MQTTClient_deliveryToken token;
pubmsg.payload = (void *)message;
pubmsg.payloadlen = strlen(message);
pubmsg.qos = MQTT_QOS;
pubmsg.retained = 0;
MQTTClient_publishMessage(mqtt_client, topic, &pubmsg, &token);
MQTTClient_waitForCompletion(mqtt_client, token, MQTT_TIMEOUT);
LOGI("Message published to topic %s: %s\n", topic, message);
}
int main(int argc, char *argv[])
{
unsigned long bitrate = DEFAULT_BITRATE;
if (argc > 1)
{
port_name = argv[1];
}
if (argc > 2)
{
bitrate = strtoul(argv[2], NULL, 0);
}
// Initialize Wirepas stack
if (WPC_initialize(port_name, bitrate) != APP_RES_OK)
{
LOGE("Failed to initialize Wirepas stack\n");
return -1;
}
// Initialize MQTT client
MQTTClient_create(&mqtt_client, MQTT_ADDRESS, MQTT_CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL);
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
conn_opts.username = ACCESS_TOKEN; // Use the ThingsBoard access token as the username
if (MQTTClient_connect(mqtt_client, &conn_opts) != MQTTCLIENT_SUCCESS)
{
LOGE("Failed to connect to MQTT broker\n");
return -1;
}
LOGI("Connected to MQTT broker at %s\n", MQTT_ADDRESS);
// Run Wirepas tests
Test_runAll();
// Publish test results to MQTT
char message[256];
snprintf(message, sizeof(message), "{\"passed\": %d, \"failed\": %d}",
GetPassedTestCount(), GetFailedTestCount());
publish_mqtt_message(MQTT_TOPIC, message);
// Run scratchpad tests
Test_scratchpad();
// Publish scratchpad test results to MQTT
snprintf(message, sizeof(message), "{\"scratchpad\": \"completed\"}");
publish_mqtt_message(MQTT_TOPIC, message);
// Clean up
WPC_close();
MQTTClient_disconnect(mqtt_client, 10000);
MQTTClient_destroy(&mqtt_client);
LOGI("Test summary: Passed: %d, Failed: %d\n", GetPassedTestCount(), GetFailedTestCount());
return GetFailedTestCount();
}
test.h
/* Wirepas Oy licensed under Apache License, Version 2.0
*
* See file LICENSE for full license details.
*
*/
/**
* \brief Run all the tests
*/
int Test_runAll();
/**
* \brief Test scratchpad functionality
*/
int Test_scratchpad();
/**
* \brief Returns number of passed test cases
*/
int GetPassedTestCount();
/**
* \brief Returns number of failed test cases
*/
int GetFailedTestCount();
test.c
/* Wirepas Oy licensed under Apache License, Version 2.0
*
* See file LICENSE for full license details.
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define LOG_MODULE_NAME "Test"
#define MAX_LOG_LEVEL INFO_LOG_LEVEL
#include "logger.h"
#include "wpc.h"
static int passedTestCount = 0;
static int failedTestCount = 0;
static bool setInitialState(app_role_t role,
app_addr_t id,
net_addr_t network_add,
net_channel_t network_channel,
bool start)
{
WPC_stop_stack();
// Wait for stack to stop
usleep(3 * 1000 * 1000);
if (WPC_set_node_address(id) != APP_RES_OK)
return false;
if (WPC_set_role(role) != APP_RES_OK)
return false;
if (WPC_set_network_address(network_add) != APP_RES_OK)
return false;
if (WPC_set_network_channel(network_channel) != APP_RES_OK)
return false;
if (start && (WPC_start_stack() == APP_RES_OK))
return false;
return true;
}
static bool testFactoryReset()
{
app_res_e res = WPC_stop_stack();
app_addr_t add;
if (res != APP_RES_STACK_ALREADY_STOPPED && res != APP_RES_OK)
{
LOGE("Cannot stop stack\n");
return false;
}
if (WPC_set_node_address(0x123456) != APP_RES_OK)
{
LOGE("Cannot set node address\n");
return false;
}
if (WPC_do_factory_reset() != APP_RES_OK)
{
LOGE("Cannot do factory reset\n");
return false;
}
// Check node address previously set is correctly removed
if (WPC_get_node_address(&add) != APP_RES_ATTRIBUTE_NOT_SET)
{
LOGE("Node address is still set\n");
return false;
}
return true;
}
// Macro to launch a test and check result
#define RUN_TEST(_test_func_, _expected_result_) \
do \
{ \
LOGI("### Starting test %s\n", #_test_func_); \
if (_test_func_() != _expected_result_) \
{ \
LOGE("### Test is FAIL\n\n"); \
failedTestCount++; \
} \
else \
{ \
LOGI("### Test is PASS\n\n"); \
passedTestCount++; \
} \
} while (0)
int GetPassedTestCount()
{
return passedTestCount;
}
int GetFailedTestCount()
{
return failedTestCount;
}
int Test_runAll()
{
RUN_TEST(testFactoryReset, true);
setInitialState(APP_ROLE_SINK, 1234, 0x654321, 5, false);
return 0;
}
int Test_scratchpad()
{
// Configure node as a sink
setInitialState(APP_ROLE_SINK, 1234, 0x654321, 5, false);
return 0;
}
Makefile
# Makefile for Wirepas C Mesh API test suite
# Toolchain
CC := gcc
AS := as
LD := ld
AR := ar
# Paths, including trailing path separator
SOURCEPREFIX := ./
BUILDPREFIX := build/
# This test suite needs the mesh lib
MESH_LIB_FOLDER := ../lib/
MESH_LIB := $(MESH_LIB_FOLDER)build/mesh_api_lib.a
# General compiler flags
CFLAGS := -std=gnu99 -Wall -Werror
# Targets definition
MAIN_APP := meshAPItest
TARGET_APP := $(BUILDPREFIX)$(MAIN_APP)
# Add API header
CFLAGS += -I$(MESH_LIB_FOLDER)api
# Add pthread lib as needed by Mesh Lib
LDFLAGS += -pthread
# Add Reentrant flag as using pthread
CFLAGS += -D_REENTRANT
# Test app needs some platform abstraction (It is a hack as not really part of lib API)
CFLAGS += -I$(MESH_LIB_FOLDER)platform
# Main app
SOURCES := $(SOURCEPREFIX)app.c
# Test cases
CFLAGS += -I$(SOURCEPREFIX)
SOURCES += $(SOURCEPREFIX)test.c
OBJECTS := $(patsubst $(SOURCEPREFIX)%, \
$(BUILDPREFIX)%, \
$(SOURCES:.c=.o))
# Functions
# Also create the target directory if it does not exist
define COMPILE
echo " CC $(2)"
mkdir -p $(dir $(1))
$(CC) $(CFLAGS) -c -o $(1) $(2)
endef
define LINK
echo " Linking $(1)"
$(CC) $(CFLAGS) $(LDFLAGS) -o $(1) $(2) $(MESH_LIB)
endef
define CLEAN
echo " Cleaning up"
$(RM) -r $(BUILDPREFIX)
endef
# Target rules
# Use dependency files automatically generated by GCC.
# First collect all C source files
AUTODEPS := $(patsubst $(SOURCEPREFIX)%.c, $(BUILDPREFIX)%.d, $(SOURCES))
ifeq ($(V),1)
# "V=1" on command line, print commands.
else
# Default, do not print commands.
.SILENT:
endif
.PHONY: all
all: app
app: $(TARGET_APP)
.PHONY: clean
clean:
$(call CLEAN)
make -C $(MESH_LIB_FOLDER) clean
$(MESH_LIB):
make -C $(MESH_LIB_FOLDER)
$(BUILDPREFIX)%.o: $(SOURCEPREFIX)%.c
$(call COMPILE,$@,$<)
$(BUILDPREFIX)$(MAIN_APP): $(OBJECTS) $(MESH_LIB)
$(call LINK,$@,$^)