Post

CayenneLPP Library Optimization for LoRaWAN

Optimizing the CayenneLPP payload library for minimal footprint while maintaining Arduino compatibility and adding new sensor datatypes.

Grade: 8.5

View Repository

CayenneLPP Library Optimization for LoRaWAN

CayenneLPP Library Optimization for LoRaWAN

As part of the HAN IoT Lab project, our team tackled the challenge of optimizing the CayenneLPP payload library for LoRaWAN applications. Working alongside Tristan Bosveld and Klaasjan-Wagenaar, we successfully reduced the library’s memory footprint while enhancing its functionality and maintainability.

Prototype photo 1
Prototype photo 1
Prototype photo 1

Project Overview

The CayenneLPP (Low Power Payload) library is essential for efficient data transmission in LoRaWAN networks, particularly for IoT applications where every byte counts. Our optimization focused on creating a more efficient, maintainable solution that works seamlessly with Arduino platforms and TheThingsNetwork (TTN).

Cayenne Low Power Payload Format

Overview

The Cayenne LPP provides a standardized way to send sensor data over LPWAN networks like LoRaWAN. It’s designed to work within strict payload size restrictions (down to 11 bytes) while allowing multiple sensor readings in a single transmission.

Each sensor value is prefixed with two identification bytes:

  • Data Channel (1 byte): Uniquely identifies each sensor across frames (e.g., “indoor sensor”)
  • Data Type (1 byte): Specifies the data type being transmitted (e.g., “temperature”)

Payload Structure

1
2
| 1 Byte    | 1 Byte     | N Bytes | 1 Byte    | 1 Byte     | M Bytes | ... |
| Data1 Ch. | Data1 Type | Data1   | Data2 Ch. | Data2 Type | Data2   | ... |

Data Types

Data types follow IPSO Alliance Smart Objects Guidelines, with Object IDs converted to single bytes:

1
LPP_DATA_TYPE = IPSO_OBJECT_ID - 3200

Key Sensor Types:

  • Temperature (0x67): 2 bytes, 0.1°C resolution, signed
  • Humidity (0x68): 1 byte, 0.5% resolution, unsigned
  • Accelerometer (0x71): 6 bytes, 0.001G per axis, signed
  • GPS Location (0x88): 9 bytes (lat/lon: 0.0001°, altitude: 0.01m)
  • Illuminance (0x65): 2 bytes, 1 Lux resolution, unsigned

Payload Examples

Two Temperature Sensors:

1
2
3
Payload: 03 67 01 10 05 67 00 FF
Channel 3: Temperature = 27.2°C (0x0110 = 272)
Channel 5: Temperature = 25.5°C (0x00FF = 255)

Temperature and Accelerometer (separate frames):

1
2
Frame N:   01 67 FF D7           // -4.1°C
Frame N+1: 06 71 04 D2 FB 2E 00 00  // X:+1.234G, Y:-1.234G, Z:0G

Objectives

Our primary goals were ambitious yet focused:

  • Minimize Memory Footprint: Reduce both flash and RAM usage without sacrificing functionality
  • Ensure Arduino Compatibility: Maintain seamless integration with Arduino development environment
  • Preserve Core Functionality: Support both encoding (HAN-KISS-NODE) and decoding (TheThingsNetwork) operations
  • Expand Sensor Support: Add new sensor datatypes to broaden application possibilities

Technical Analysis & Constraints

Working with embedded systems meant navigating several critical constraints:

Hardware Limitations:

  • AVR instruction set restrictions
  • Limited flash and RAM memory
  • Arduino platform compatibility requirements

Design Considerations:

  • Memory footprint optimization (dynamic vs. static allocation)
  • Memory pool implementation possibilities
  • Application flexibility and scalability

Encoder Implementation

The encoder redesign focused on efficiency and type safety:

Key Features:

  • Public Wrapper Functions: Intuitive sensor-specific functions like AddTemperature(), AddGPS(), etc.
  • Template Variadic Functions: Runtime selection of appropriate addFieldImpl through function overloading
  • Code Cleanup: Removed unnecessary functions and dependencies
  • Strong Typing: Implemented enums for data types with strong typed references

Example Usage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "CayenneLPP.h"

#define MAX_SIZE 200  // depends on spreading factor and frequency

CayenneLPP payload(MAX_SIZE);

// Add sensor data with automatic size checking
payload.reset();
if (payload.addTemperature(0, -4.1) == 0) {
    // Buffer full - handle error
}

payload.addAccelerometer(1, 1.234, -1.234, 0);
payload.addRelativeHumidity(3, 30.0);
payload.addBarometricPressure(4, 1014.1);
payload.addGPS(5, 42.3519, -87.9094, 10.0);

// Send via LoRaWAN
LORA_SEND(payload.getBuffer(), payload.getSize());

Architecture Highlights:

  • Macro configuration system for build-time optimization
  • Debug message control
  • Device key management
  • Configurable buffer sizes
  • Payload version control (fPort)

Decoder Implementation

The decoder module provides robust uplink payload processing:

Core Features:

  • Modular Design: Function-based decoding system adaptable to different sensor data types
  • Comprehensive Error Handling: Proper handling of unknown payload versions and data types
  • Type Safety: Consistent with encoder implementation for reliable data processing

Testing Strategy

Our comprehensive testing approach ensured reliability across all use cases:

Unit Testing:

  • Encoder Testing: Unity Framework with AAA strategy (Arrange, Act, Assert)
  • Decoder Testing: Jest Framework for JavaScript components
  • Coverage: All major sensor types and edge cases

Integration Testing:

  • TTN Connectivity: Verified interoperability with TheThingsNetwork
  • Hex Payload Validation: Ensured proper byte array processing
  • JavaScript Integrity: Validated TTN decoder functionality

System Testing:

  • Live TTN Validation: Real-world testing through TTN Live Log
  • HTTP API Integration: Demo application with web connectivity
  • Serial Communication: Comprehensive logging and inspection

Results & Performance

The optimization delivered significant improvements:

Memory Savings:

  • Flash Memory: 11.4% reduction (3,288 bytes saved)
  • RAM Usage: 7% reduction
  • Total Footprint: From 25.2 kB to 24.8 kB flash, maintaining 1.2 kB RAM

Validation Results: All sensor integrations passed testing:

  • Temperature Data ✅
  • Humidity Data ✅
  • Luminosity Data ✅
  • Rotary Switch Data ✅
  • Accelerometer Data ✅
  • Board Voltage Data ✅
  • Presence Detection ✅

Real-World Application

The optimization was validated through live IoT deployment:

KISS NODE Integration:

  • Continuous data transmission during development
  • Real-time monitoring through TTN console
  • Application dashboard implementation
  • Stable performance in production environment

Technologies Used

  • Programming Languages: C++ with Arduino framework
  • Testing Frameworks: Unity (C++) and Jest (JavaScript)
  • IoT Platform: TheThingsNetwork (TTN)
  • Communication Protocol: LoRaWAN
  • Documentation: Doxygen for code documentation
  • Version Control: Git with feature branching

Key Achievements

  • Performance: 400 bytes saved in total system footprint
  • Maintainability: Improved code structure and documentation
  • Scalability: Enhanced support for future sensor types
  • Usability: Simplified API for common sensor operations
  • Reliability: Comprehensive test coverage with 100% pass rate

Impact & Future Work

This optimization provides a solid foundation for IoT applications requiring efficient data transmission. The reduced memory footprint enables deployment on more resource-constrained devices, while the improved architecture supports easier maintenance and feature additions.

The refactored library is now production-ready and serves as a reference implementation for similar optimization projects in the IoT domain.

Conclusion

Through careful analysis, systematic refactoring, and comprehensive testing, we successfully optimized the CayenneLPP library while maintaining full functionality. The project demonstrates that significant performance improvements are achievable through thoughtful software engineering practices, even in resource-constrained embedded environments.

The collaboration between team members and the structured approach to optimization resulted in a library that not only performs better but is also more maintainable and extensible for future IoT applications.


This project was completed as part of the Internet of Things Lab (EMBSYS01-IoT) course at HAN University of Applied Sciences, Embedded Systems Engineering program, S6 2023-2024.

This post is licensed under CC BY 4.0 by the author.