TST-Library Documentation

Overview

The TST (TrueSmartTech) Library is a comprehensive communication framework designed to facilitate seamless device connectivity through multiple protocols. The library provides a structured approach for variable management, file operations, device monitoring, and firmware updates across different communication interfaces such as Serial, BLE, I2C, and WebSockets.

Core Components

The library is organized into several core components:

  • Device Management: Handles device registration, identification, and management
  • Interface Management: Supports multiple communication interfaces with configurable payload sizes
  • Variable Management: Provides mechanisms to get and set variables across devices
  • File System Operations: Enables file listing, upload, download, and deletion
  • Monitoring System: Facilitates device status monitoring and message exchange
  • Update System: Manages firmware update processes with sequence tracking and CRC validation

Key Features

  • Multi-protocol support: Compatible with Serial, I2C, BLE, and WebSocket protocols
  • Fragmentation handling: Automatically manages large data transmissions by fragmenting and reassembling messages
  • Memory management: Efficient memory allocation and deallocation through managed data structures
  • Error handling: Comprehensive error codes and status reporting
  • Data integrity: CRC32 validation for transmitted data
  • Structured communication: Well-defined message formats for different operations

Getting Started

Initialization

To use the TST Library, you need to initialize it with your device configuration:

#include "tst_library.h"
#include "tst_variables.h"

void setup() {
  // Initialize the TST library with device configuration
  tstInit(&TST_Device);
  
  // Register handlers for file system operations
  tstRegisterFsHandlers(handleFsList, handleFsUpload, handleFsDownload, handleFsDelete);
  
  // Register handler for update operations
  tstRegisterUpdateHandler(handleUpdate);
}

Communication Loop

The library requires continuous execution of receive and transmit functions to maintain communication. This should be placed in your main loop:

void loop() {
  // Process incoming/outgoing messages
  uint8_t buffer[TSTMAXSIZE];
  size_t size = 0;
  
  // Check for incoming data from your physical interface
  if (yourPhysicalInterface.available()) {
    size = yourPhysicalInterface.read(buffer, TSTMAXSIZE);
    
    // Pass received data to TST library
    if (size > 0) {
      tstRx(TSTNAME, "SERIAL", buffer, size);
    }
  }
  
  // Check for outgoing messages
  if (tstTx(TSTNAME, "SERIAL", buffer, &size) == TST_OK && size > 0) {
    yourPhysicalInterface.write(buffer, size);
  }
}

Variable Management

Defining Variables

Variables are defined in the tst_variables.h file:

typedef struct {
    uint16_t myVariable;
    uint16_t myLED;
} TST_Variables;

Getting and Setting Variables

The library provides functions to get and set variables:

// Get a variable value
tstVariablesGet(TSTNAME, "SERIAL", &TST_V.myVariable, sizeof(TST_V.myVariable));

// Set a variable value
uint16_t newValue = 100;
tstVariablesSet(TSTNAME, "SERIAL", &TST_V.myVariable, sizeof(TST_V.myVariable), &newValue);

File System Operations

Implementing File System Handlers

To handle file system operations, you need to implement and register handler functions:

uint8_t handleFsList(const char *path, uint8_t **outData, uint32_t *outSize) {
  // Implementation for listing files in a directory
  // Populate outData with the directory listing
  // Set outSize to the size of the listing
  return TST_OK;
}

uint8_t handleFsUpload(const char *path, const uint8_t *data, uint32_t offset, uint32_t size) {
  // Implementation for uploading/writing file data
  // Write 'data' of 'size' bytes at 'offset' to 'path'
  return TST_OK;
}

uint8_t handleFsDownload(const char *path, uint32_t offset, uint32_t requestedSize, 
                        uint8_t **outData, uint32_t *outSize) {
  // Implementation for downloading/reading file data
  // Read 'requestedSize' bytes from 'path' starting at 'offset'
  // Store the result in outData and the actual size in outSize
  return TST_OK;
}

uint8_t handleFsDelete(const char *path) {
  // Implementation for deleting a file
  return TST_OK;
}

Initiating File Operations

To perform file operations from the client side:

// List files in a directory
tstFsList(TSTNAME, "SERIAL", "/some/directory");

// Upload a file
uint8_t fileData[] = {0x01, 0x02, 0x03, 0x04};
tstFsUpload(TSTNAME, "SERIAL", "/path/to/file.bin", fileData, 0, sizeof(fileData));

// Download a file
tstFsDownload(TSTNAME, "SERIAL", "/path/to/file.bin", 0, 1024);

// Delete a file
tstFsDelete(TSTNAME, "SERIAL", "/path/to/file.bin");

Monitoring

To send monitoring messages:

// Send a monitoring message
tstMonitorSend(TSTNAME, "SERIAL", "System status: OK, Temperature: 25°C");

Firmware Updates

Implementing Update Handler

To handle firmware updates, implement and register an update handler:

uint8_t handleUpdate(const uint8_t *data, uint32_t size, uint32_t sequenceNumber, 
                    uint32_t crc, uint8_t operation) {
  switch (operation) {
    case START:
      // Begin update process
      // Initialize update state, prepare storage
      break;
    
    case DATA:
      // Process update data chunk
      // Write data to storage, validate CRC
      break;
    
    case END:
      // Complete update process
      // Finalize, validate entire update, prepare for reboot
      break;
  }
  return TST_OK;
}

Sending Update Responses

After processing update requests, send responses to acknowledge receipt:

// Send update response
// Parameters: device, interface, operation, status, sequence, crc
tstUpdateResponseSend(TSTNAME, "SERIAL", DATA, TST_OK, sequenceNumber, crc);

Error Handling

The library provides a set of error codes defined in tst_errors.h:

  • TST_OK: Operation completed successfully
  • TST_NO_STRUCT_FOUND: Requested structure not found
  • TST_INVALID_POINTER: Invalid pointer provided
  • TST_STRUCT_ALREADY_PRESENT: Structure already registered
  • TST_FAIL_ALLOCATE_MEMORY: Memory allocation failed
  • TST_FAIL: General failure
  • TST_FAIL_UPDATE: Update operation failed

Always check return values from TST library functions to handle errors appropriately.

Important Notes

  • Communication Continuity: Do not interrupt the loop where tstRx and tstTx are called, as this will break the communication flow.
  • Memory Management: The library handles memory internally, but you must properly free any memory allocated in your handlers.
  • Buffer Sizes: Configure appropriate buffer sizes based on your application needs through TSTMAXSIZE.
  • Device Identification: Each device should have a unique name to avoid conflicts in networks with multiple devices.
  • Fragmentation: The library automatically handles message fragmentation for large data transfers, but be aware of the limitations of your transport layer.

Advanced Usage

Multiple Interfaces

The library supports multiple communication interfaces. To use more than one interface:

// Define interfaces in tst_variables.h
static const TST_InterfaceConfig TST_Interfaces[] = {
    {.interface = "SERIAL", .maxSize = 1024},
    {.interface = "BLE", .maxSize = 512},
    {.interface = "I2C", .maxSize = 256}
};

// Update device config
static const TST_DeviceConfig TST_Device = {
    .name = "MyDevice",
    .pInterfaces = TST_Interfaces,
    .nInterfaces = sizeof(TST_Interfaces),
    .pStructs = &TST_Struct,
    .nStructs = sizeof(TST_Struct)
};

Multiple Structs

You can manage multiple variable structures:

typedef struct {
    uint16_t sensorValue;
} TST_SensorData;

typedef struct {
    uint8_t ledState;
} TST_OutputState;

// Define and initialize the structs
TST_SensorData TST_Sensors = {0};
TST_OutputState TST_Outputs = {0};

// Create struct configs
static const TST_StructConfig TST_Structs[] = {
    {.name = "Sensors", .structName = "TST_SensorData", .pStruct = &TST_Sensors, .sStruct = sizeof(TST_Sensors)},
    {.name = "Outputs", .structName = "TST_OutputState", .pStruct = &TST_Outputs, .sStruct = sizeof(TST_Outputs)}
};

// Update device config
static const TST_DeviceConfig TST_Device = {
    .name = "MyDevice",
    .pInterfaces = &TST_Interface,
    .nInterfaces = sizeof(TST_Interface),
    .pStructs = TST_Structs,
    .nStructs = sizeof(TST_Structs)
};

Best Practices

  • Error Handling: Always check return values from TST functions
  • Buffer Sizing: Choose appropriate buffer sizes for your application
  • Continuous Processing: Ensure tstRx and tstTx are called regularly
  • Memory Management: Be mindful of memory usage in callbacks
  • CRC Validation: Use CRC validation for critical data
  • Fragmentation Awareness: Consider message fragmentation when working with large data
  • Interface Selection: Choose the most appropriate interface for your use case

Troubleshooting

If you encounter communication issues:

  • Verify initialization: Ensure tstInit() is called correctly
  • Check continuity: Make sure the loop calling tstRx and tstTx is not blocked
  • Validate handlers: Verify all handlers are properly implemented and registered
  • Monitor errors: Check return values for error codes
  • Buffer overflow: Ensure buffer sizes are appropriate for your data
  • Interface configuration: Verify interface parameters match physical layer capabilities

Compilation and Integration

  • Include the following files in your build environment:
    • tst_library.c
    • tst_library.h
    • tst_variables.c
    • tst_variables.h
  • Set appropriate include paths.
  • Insert your variables in the struct inside the two markers /*TSTVARIABLESSTART*/ /*TSTVARIABLESEND*/
  • Set your device name #define TSTNAME "your_device_name"

Core Functions

FunctionDescriptionExample
tstInitInitializes the TST library with device configurationtstInit(&TST_Device);
tstRxProcesses received data from an interfacetstRx(TSTNAME, ”INTERFACE”, data, size);
tstTxTransmits data through the specified interfacetstTx(TSTNAME, ”INTERFACE”, buffer, &size);
tstVariablesGetRetrieves variable values from another devicetstVariablesGet(TSTNAME, ”INTERFACE”, &variable, sizeof(variable));
tstVariablesSetSets a variable value on another devicetstVariablesSet(TSTNAME, ”INTERFACE”, &variable, sizeof(variable), &value);
tstMonitorSendSends a text message to the monitortstMonitorSend(TSTNAME, ”INTERFACE”, message);
tstRegisterFsHandlersRegisters handlers for file system operationststRegisterFsHandlers(handleList, handleUpload, handleDownload, handleDelete);
tstRegisterUpdateHandlerRegisters handler for firmware updateststRegisterUpdateHandler(handleUpdate);

Supported Variable Types

TypeSize (bytes)Description
bool1Boolean value (true/false)
int8_t18-bit signed integer
uint8_t18-bit unsigned integer
int16_t216-bit signed integer
uint16_t216-bit unsigned integer
int32_t432-bit signed integer
uint32_t432-bit unsigned integer
int64_t864-bit signed integer
uint64_t864-bit unsigned integer
float4Single-precision floating-point
double8Double-precision floating-point
char[]VariableCharacter array (string)
structVariableCustom data structure (packed)
arrayVariableArray of any supported type

User Manual for the Application

This guide explains how to operate the application across its different communication interfaces. Follow these steps for a smooth experience.

General Overview

The application implements a protocol that lets users read and update internal variables of the device. The device supports several communication methods (I2C, BLE, Serial, and WebSocket). This manual explains how to interact with the device using these methods.

Example I2C (Master/Slave)

For the Master Device

  1. Connect the Device: Connect the master device to your computer or power supply.
  2. Power Up the Device: When the device starts, it registers itself and initializes communication with the slave device.
  3. Variable Update: The master periodically toggles an LED variable on the slave device and sends protocol messages via I2C.
  4. Monitoring Activity: Use a serial monitor (if available) to observe any debug output provided by the master.

For the Slave Device

  1. Setup and Listen: The slave device listens for incoming I2C messages. It receives messages that update an internal LED variable.
  2. Visual Feedback: The device”s LED will turn on or off based on the received variable value.
  3. Status Reporting: Consult the serial console for debugging information about message reception if required.
Tested with Arduino Nano esp32

Example BLE

  1. Turn on BLE: Power on the BLE device. It will automatically advertise its presence with the name provided by the application.
  2. Connect from a Client: Use a smartphone or BLE-compatible device to search for the advertised BLE device (e.g., "tst-device").
  3. Data Write and Notification:
    • Once connected, send binary messages to the device.
    • The device processes incoming messages and can update its internal variables.
    • When a variable changes, the device notifies the client via BLE characteristics.
  4. Visual Feedback: The onboard LED will reflect the state of a variable (on or off).
Tested with Arduino Nano esp32

Example Serial Communication (USB)

  1. Connect via USB/Serial Cable: Attach the device to your computer using a USB/serial cable.
  2. Start a Serial Monitor: Open your preferred serial monitor (set to 115200 baud) to view command and status messages.
  3. Sending Data: You can send text or binary data from the serial monitor. The device listens and processes incoming data according to the protocol.
  4. Variable Update: The device increments a primary variable continuously and toggles an LED based on an internal state. Messages are sent back automatically on the serial line.
Tested with Arduino Nano esp32

Example WebSocket (Wi‑Fi Based)

  1. Network Connection: Power on the Wi‑Fi device and ensure it connects to the preconfigured network. The device will print its local IP address on startup.
  2. Access the WebSocket: Use a WebSocket-enabled client (such as a web browser with a JavaScript client) to open a connection to ws://<device_ip>/ws.
  3. Sending Messages: Send binary messages from the client to control or query the device's status. The device processes these messages and updates its internal variables.
  4. Receiving Data: The device sends updated protocol messages via the WebSocket. Use your client to display these binary updates.
  5. LED Status: The device's LED reflects the current value of the relevant variable.
Tested with Arduino Nano esp32

Troubleshooting and Tips

  • Verify Wiring and Power: Ensure that all physical connections (for I2C and Serial) are secure and that the device is properly powered.
  • Communication Settings: Double-check the baud rate in Serial mode (115200) and the device address (e.g., 0x08 for I2C).
  • Network Credentials: For WebSocket mode, make sure the correct SSID and password are configured. If the device fails to connect, verify your network settings.
  • BLE Pairing: If your BLE client does not find the device, ensure that the device is broadcasting and that your client's BLE is enabled.
  • Client Software: Have a serial monitor, a BLE scanner, or a WebSocket client (for example, a browser-based tool) readily available during testing.

Changelog

VersionChanges
v4.0.4
  • New example
v4.0.3
  • Added attribute packed for better memory management
  • Update example improvements
v4.0.2
  • REMOVED THE ATTRIBUTE PACKED that fail the import of the file
  • New example
v4.0.1
  • New example
v4.0.0
  • Use the latest TST-Center (v4.0.0+)
  • New declaration
  • Update
  • File system
v3.0.0
  • Monitor function
  • New examples
v2.0.0
  • New Registration function for easier implementation (It combine the previous 3 functions)
  • Changed definition and declaration of structs in the .h file
v1.0.1
  • Added BLE example (between device and desktop)
  • Added I2C example (between two devices)
v1.0.0
  • Initial release

Contact Us

Have questions or need assistance? We'd love to hear from you.