Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add c5 #58

Merged
merged 3 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions .github/workflows/sonar-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,14 @@ jobs:
- name: Checkout submodules
run: git submodule update --init --recursive

- name: Set up Sonar Scanner 4.40
run: |
export SONAR_SCANNER_VERSION=4.4.0.2170
export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux
curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip
unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/
echo "$SONAR_SCANNER_HOME/bin" >> $GITHUB_PATH
curl --create-dirs -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip
unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/
echo "$HOME/.sonar/build-wrapper-linux-x86" >> $GITHUB_PATH
# Setup java 17 to be default (sonar-scanner requirement as of 5.x)
- uses: actions/setup-java@v3
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'

- name: Install sonar-scanner and build-wrapper
uses: sonarsource/sonarcloud-github-c-cpp@v2

- name: Download Nordic SDK
run: |
Expand Down
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ if(IDF_VERSION_MAJOR GREATER_EQUAL 4)
SRCS "src/ruuvi_endpoint_5.h"
SRCS "src/ruuvi_endpoint_6.c"
SRCS "src/ruuvi_endpoint_6.h"
SRCS "src/ruuvi_endpoint_c5.c"
SRCS "src/ruuvi_endpoint_c5.h"
SRCS "src/ruuvi_endpoint_ca_uart.c"
SRCS "src/ruuvi_endpoint_ca_uart.h"
SRCS "src/ruuvi_endpoint_ibeacon.c"
Expand All @@ -20,6 +22,7 @@ elseif(CMAKE_PROJECT_NAME STREQUAL "ruuvi.node_nrf91.c")
target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/ruuvi_endpoint_3.c)
target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/ruuvi_endpoint_5.c)
target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/ruuvi_endpoint_6.c)
target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/ruuvi_endpoint_c5.c)
target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/ruuvi_endpoint_ca_uart.c)
target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/ruuvi_endpoint_ibeacon.c)
else()
Expand Down
203 changes: 203 additions & 0 deletions src/ruuvi_endpoint_c5.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#include "ruuvi_endpoint_c5.h"
#include "ruuvi_endpoints.h"
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>

#if RE_C5_ENABLED

#define RE_C5_ACC_RATIO (1000.0f)
#define RE_C5_HUMI_RATIO (400.0f)
#define RE_C5_TEMP_RATIO (200.0f)
#define RE_C5_PRES_RATIO (1.0f)
#define RE_C5_PRES_OFFSET (-50000.0f)
#define RE_C5_BATT_RATIO (1000.0f)
#define RE_C5_BATT_OFFSET (1600)
#define RE_C5_BATT_MIN (1.6f)

#define RE_C5_TXPWR_RATIO (2)
#define RE_C5_TXPWR_OFFSET (40)

#define RE_C5_MAC_MAX (281474976710655)
#define RE_C5_MAC_MIN (0)

#define RE_C5_BYTE_0_SHIFT (0U)
#define RE_C5_BYTE_1_SHIFT (8U)
#define RE_C5_BYTE_2_SHIFT (16U)
#define RE_C5_BYTE_3_SHIFT (24U)
#define RE_C5_BYTE_4_SHIFT (32U)
#define RE_C5_BYTE_5_SHIFT (40U)
#define RE_C5_BYTE_MASK (0xFFU)
#define RE_C5_BYTE_VOLTAGE_OFFSET (5U)
#define RE_C5_BYTE_VOLTAGE_MASK (0x7FFU)
#define RE_C5_BYTE_TX_POWER_OFFSET (0U)
#define RE_C5_BYTE_TX_POWER_MASK (0x1FU)

// Avoid mocking simple function
#ifdef TEST
void re_clip (re_float * const value, const re_float min, const re_float max)
{
if (*value > max)
{
*value = max;
}

if (*value < min)
{
*value = min;
}
}
#endif

static void re_c5_encode_set_address (uint8_t * const buffer, const re_c5_data_t * data)
{
// Address is 64 bits, skip 2 first bytes
uint8_t addr_offset = RE_C5_OFFSET_ADDR_MSB;
uint64_t mac = data->address;

if ( (RE_C5_MAC_MAX < data->address) || (RE_C5_MAC_MIN > data->address))
{
mac = RE_C5_INVALID_MAC;
}

buffer[addr_offset] = (mac >> RE_C5_BYTE_5_SHIFT) & RE_C5_BYTE_MASK;
addr_offset++;
buffer[addr_offset] = (mac >> RE_C5_BYTE_4_SHIFT) & RE_C5_BYTE_MASK;
addr_offset++;
buffer[addr_offset] = (mac >> RE_C5_BYTE_3_SHIFT) & RE_C5_BYTE_MASK;
addr_offset++;
buffer[addr_offset] = (mac >> RE_C5_BYTE_2_SHIFT) & RE_C5_BYTE_MASK;
addr_offset++;
buffer[addr_offset] = (mac >> RE_C5_BYTE_1_SHIFT) & RE_C5_BYTE_MASK;
addr_offset++;
buffer[addr_offset] = (mac >> 0) & RE_C5_BYTE_MASK;
}

static void re_c5_encode_humidity (uint8_t * const buffer, const re_c5_data_t * data)
{
uint16_t coded_humidity = RE_C5_INVALID_HUMIDITY;
re_float humidity = data->humidity_rh;

if (!isnan (humidity))
{
re_clip (&humidity, RE_C5_HUMI_MIN, RE_C5_HUMI_MAX);
coded_humidity = (uint16_t) roundf (humidity * RE_C5_HUMI_RATIO);
}

buffer[RE_C5_OFFSET_HUMI_MSB] = coded_humidity >> RE_C5_BYTE_1_SHIFT;
buffer[RE_C5_OFFSET_HUMI_LSB] = coded_humidity & RE_C5_BYTE_MASK;
}

static void re_c5_encode_temperature (uint8_t * const buffer, const re_c5_data_t * data)
{
uint16_t coded_temperature = RE_C5_INVALID_TEMPERATURE;
re_float temperature = data->temperature_c;

if (!isnan (temperature))
{
re_clip (&temperature, RE_C5_TEMP_MIN, RE_C5_TEMP_MAX);
int16_t rounded_temperature = (int16_t) roundf (temperature * RE_C5_TEMP_RATIO);
// Type cast adds 2^16 to a negative signed value, not changing bits.
coded_temperature = (uint16_t) rounded_temperature;
}

buffer[RE_C5_OFFSET_TEMP_MSB] = coded_temperature >> RE_C5_BYTE_1_SHIFT;
buffer[RE_C5_OFFSET_TEMP_LSB] = coded_temperature & RE_C5_BYTE_MASK;
}

static void re_c5_encode_pressure (uint8_t * const buffer, const re_c5_data_t * data)
{
uint16_t coded_pressure = RE_C5_INVALID_PRESSURE;
re_float pressure = data->pressure_pa;

if (!isnan (pressure))
{
re_clip (&pressure, RE_C5_PRES_MIN, RE_C5_PRES_MAX);
pressure += RE_C5_PRES_OFFSET;
coded_pressure = (uint16_t) roundf (pressure * RE_C5_PRES_RATIO);
}

buffer[RE_C5_OFFSET_PRES_MSB] = coded_pressure >> RE_C5_BYTE_1_SHIFT;
buffer[RE_C5_OFFSET_PRES_LSB] = coded_pressure & RE_C5_BYTE_MASK;
}


static void re_c5_encode_pwr (uint8_t * const buffer, const re_c5_data_t * data)
{
uint16_t coded_voltage = RE_C5_INVALID_VOLTAGE;
re_float voltage = data->battery_v;
uint16_t coded_tx_power = RE_C5_INVALID_POWER;
re_float tx_power = (re_float) data->tx_power;

if (!isnan (voltage))
{
re_clip (&voltage, RE_C5_VOLTAGE_MIN, RE_C5_VOLTAGE_MAX);
coded_voltage = (uint16_t) roundf ( (voltage * RE_C5_BATT_RATIO)
- RE_C5_BATT_OFFSET);
}

// Check against original int value
if (RE_C5_INVALID_POWER != data->tx_power)
{
re_clip (&tx_power, RE_C5_TXPWR_MIN, RE_C5_TXPWR_MAX);
coded_tx_power = (uint16_t) roundf ( (tx_power
+ RE_C5_TXPWR_OFFSET)
/ RE_C5_TXPWR_RATIO);
}

uint16_t power_info = ( (uint16_t) (coded_voltage << RE_C5_BYTE_VOLTAGE_OFFSET))
+ coded_tx_power;
buffer[RE_C5_OFFSET_POWER_MSB] = (power_info >> RE_C5_BYTE_1_SHIFT);
buffer[RE_C5_OFFSET_POWER_LSB] = (power_info & RE_C5_BYTE_MASK);
}

static void re_c5_encode_movement (uint8_t * const buffer, const re_c5_data_t * data)
{
uint8_t movement_count = RE_C5_INVALID_MOVEMENT;

if (RE_C5_MVTCTR_MAX >= data->movement_count)
{
movement_count = data->movement_count;
}

buffer[RE_C5_OFFSET_MVTCTR] = movement_count;
}

static void re_c5_encode_sequence (uint8_t * const buffer, const re_c5_data_t * data)
{
uint16_t measurement_seq = RE_C5_INVALID_SEQUENCE;

if (RE_C5_SEQCTR_MAX >= data->measurement_count)
{
measurement_seq = data->measurement_count;
}

buffer[RE_C5_OFFSET_SEQCTR_MSB] = (measurement_seq >> RE_C5_BYTE_1_SHIFT);
buffer[RE_C5_OFFSET_SEQCTR_LSB] = (measurement_seq & RE_C5_BYTE_MASK);
}

re_status_t re_c5_encode (uint8_t * const buffer, const re_c5_data_t * data)
{
re_status_t result = RE_SUCCESS;

if ( (NULL == buffer) || (NULL == data))
{
result |= RE_ERROR_NULL;
}
else
{
buffer[RE_C5_OFFSET_HEADER] = RE_C5_DESTINATION;
re_c5_encode_humidity (buffer, data);
re_c5_encode_temperature (buffer, data);
re_c5_encode_pressure (buffer, data);
re_c5_encode_movement (buffer, data);
re_c5_encode_sequence (buffer, data);
re_c5_encode_pwr (buffer, data);
re_c5_encode_set_address (buffer, data);
}

return result;
}

#endif
91 changes: 91 additions & 0 deletions src/ruuvi_endpoint_c5.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* Ruuvi Endpoint C5 helper.
* Defines necessary data for creating a Ruuvi data format C5 broadcast.
* Drops acceleration values to leave space for service UUID field
*
* License: BSD-3
* Author: Otso Jousimaa <[email protected]>
*/

#ifndef RUUVI_ENDPOINT_C5_H
#define RUUVI_ENDPOINT_C5_H
#include "ruuvi_endpoints.h"
#include <stdbool.h>

#define RE_C5_DESTINATION (0xC5U)
#define RE_C5_INVALID_TEMPERATURE (0x8000U)
#define RE_C5_INVALID_HUMIDITY (0xFFFFU)
#define RE_C5_INVALID_PRESSURE (0xFFFFU)
#define RE_C5_INVALID_ACCELERATION (0x8000U)
#define RE_C5_INVALID_SEQUENCE (0xFFFFU)
#define RE_C5_INVALID_MOVEMENT (0xFFU)
#define RE_C5_INVALID_VOLTAGE (0x07FFU)
#define RE_C5_INVALID_POWER (0x1FU)
#define RE_C5_INVALID_MAC (0xFFFFFFFFFFFFU)
#define RE_C5_DATA_LENGTH (18U)

#define RE_C5_TEMP_MAX (163.835f)
#define RE_C5_TEMP_MIN (-163.835f)
#define RE_C5_HUMI_MAX (163.835f)
#define RE_C5_HUMI_MIN (0.0f)
#define RE_C5_PRES_MAX (115534.0f)
#define RE_C5_PRES_MIN (50000.0f)
#define RE_C5_VOLTAGE_MAX (3.646f)
#define RE_C5_VOLTAGE_MIN (1.6f)
#define RE_C5_TXPWR_MAX (20)
#define RE_C5_TXPWR_MIN (-40)
#define RE_C5_MVTCTR_MAX (254)
#define RE_C5_MVTCTR_MIN (0)
#define RE_C5_SEQCTR_MAX (65534)
#define RE_C5_SEQCTR_MIN (0)

#define RE_C5_OFFSET_PAYLOAD (7U)

#define RE_C5_OFFSET_HEADER (0U)
#define RE_C5_OFFSET_TEMP_MSB (1U)
#define RE_C5_OFFSET_TEMP_LSB (2U)
#define RE_C5_OFFSET_HUMI_MSB (3U)
#define RE_C5_OFFSET_HUMI_LSB (4U)
#define RE_C5_OFFSET_PRES_MSB (5U)
#define RE_C5_OFFSET_PRES_LSB (6U)
#define RE_C5_OFFSET_POWER_MSB (7U)
#define RE_C5_OFFSET_POWER_LSB (8U)
#define RE_C5_OFFSET_MVTCTR (9U)
#define RE_C5_OFFSET_SEQCTR_MSB (10U)
#define RE_C5_OFFSET_SEQCTR_LSB (11U)
#define RE_C5_OFFSET_ADDR_MSB (12U)

/** @brief All data required for Ruuvi dataformat 5 package. */
typedef struct
{
re_float humidity_rh;
//!< Humidity in relative humidity percentage.
re_float pressure_pa;
//!< Pressure in pascals.
re_float temperature_c;
//!< Temperature in celcius.
re_float battery_v;
//!< Battery voltage, preferably under load such as radio TX.
uint16_t measurement_count;
//!< Running counter of measurement.
uint8_t movement_count;
//!< Number of detected movements.
uint64_t address;
//!< BLE address of device, most significant byte first.
int8_t tx_power;
//!< Transmission power of radio, in dBm.
} re_c5_data_t;

/**
* @brief Encode given data to given buffer in Ruuvi DF5.
*
* NAN can be used as a placeholder for invalid / not available values.
*
* @param[in] buffer uint8_t array with length of 24 bytes.
* @param[in] data Struct containing all necessary information
* for encoding the data into buffer.
* @retval RE_SUCCESS if data was encoded successfully.
*/
re_status_t re_c5_encode (uint8_t * const buffer, const re_c5_data_t * data);

#endif
5 changes: 4 additions & 1 deletion src/ruuvi_endpoints.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#if !defined(RE_8_ENABLED)
# define RE_8_ENABLED (1U)
#endif
#if !defined(RE_C5_ENABLED)
# define RE_C5_ENABLED (1U)
#endif
#if !defined(RE_CA_ENABLED)
# define RE_CA_ENABLED (1U)
#endif
Expand All @@ -29,7 +32,7 @@

#include <stdint.h>

#define RUUVI_ENDPOINTS_SEMVER "4.0.0" //!< SEMVER of endpoints.
#define RUUVI_ENDPOINTS_SEMVER "4.1.0" //!< SEMVER of endpoints.

#define RE_SUCCESS (0U) //!< Encoded successfully.
#define RE_ERROR_DATA_SIZE (1U << 3U) //!< Data size too large/small.
Expand Down
Loading
Loading