Skip to main content

Sensor Basics Utility Library


The

Sensor Basics utility library provides small validation and conversion helpers shared by the sensor drivers and the main application. These are basic features that could be used if required...required, Butbut was somethingwere made in "spare time".

Source Code Location

Files:

  • components/sensor_board/sensor_basics/sensor_basics.h - Function(function declarations and documentationdocumentation)
  • components/sensor_board/sensor_basics/sensor_basics.c - Implementation(implementation)

Dependencies:

  • result.h - Standard(standard result/error code definitions and the TRY macro)
  • stdint.h - Integer(integer type definitionsdefinitions)

pH Sensor FunctionsValidation

validate_ph_value()

Validates ifthat a pH value is within the acceptable range (0-14).

/**
 * @brief Validates if a pH value is within the acceptable range (0-14).
 * @param ph_value The pH value0 to validate.
 * @return RESULT_OK if the value is valid, RESULT_ERR_INVALID_DATA otherwise.
 */
result_t validate_ph_value(float ph_value);

Implementation:14).

result_t validate_ph_value(float ph_value) {
    if (ph_value >= 0.0f && ph_value <= 14.0f) {
        return RESULT_OK;
    }
    return RESULT_ERR_INVALID_DATA;
}

Parameters:Return values:

  • ph_value -RESULT_OK Floatwhen the value to validate (typical range: 0.0 to 14.0)

Return Values:

  • RESULT_OK - Value is within validrange, range
  • RESULT_ERR_INVALID_DATA - Value is outside 0-14 range

Usage Example:

float ph_reading = ph_sensor.ph_value;

if (validate_ph_value(ph_reading) == RESULT_OK) {
    LOG_INFO("pH sensor valid: %.2f", ph_reading);
    diagnostics.ph_sensor.state = SENSOR_OPERATING;
} else {
    LOG_ERROR("pH out of range: %.2f", ph_reading);
    diagnostics.ph_sensor.state = SENSOR_ERROR;
    diagnostics.ph_sensor.error_code = PHErrorCode_PH_INVALID_DATA;
}

Temperature Conversion Functions

celsius_to_fahrenheit()

Converts temperature from Celsius to Fahrenheit.

/**
 * @brief Converts temperature from Celsius to Fahrenheit.
 * @param celsius The temperature in Celsius.
 * @param fahrenheit Pointer to a float where the converted Fahrenheit temperature will be stored.
 * @return RESULT_OK on success, or RESULT_ERR_INVALID_ARG if fahrenheit is NULL.
 */
result_t celsius_to_fahrenheit(float celsius, float *fahrenheit);

Conversion Formula: °F = (°C × 9/5) + 32otherwise.

Parameters:Note:

  • celsius -ph_sensor_update() Inputalready temperatureclamps inits Celsius
  • fahrenheit - Pointeroutput to output0..14, variableso (mustthis notcheck beonly NULL)
fails

Returnfor Values:

values
  • RESULT_OKthat - Conversion successful
  • RESULT_ERR_INVALID_ARG - fahrenheit pointer is NULL

Status: Currently commented out in implementation

Usage Example:

float temp_celsius = 25.0f;
float temp_fahrenheit;

if (celsius_to_fahrenheit(temp_celsius, &temp_fahrenheit) == RESULT_OK) {
    LOG_INFO("Temperature: %.1f°C = %.1f°F", temp_celsius, temp_fahrenheit);
}

fahrenheit_to_celsius()

Converts temperature from Fahrenheit to Celsius.

/**
 * @brief Converts temperature from Fahrenheit to Celsius.
 * @param fahrenheit The temperature in Fahrenheit.
 * @param celsius Pointer to a float wherebypass the converted Celsius temperature will be stored.
 * @return RESULT_OK on success, or RESULT_ERR_INVALID_ARG if celsius is NULL.
 */
result_t fahrenheit_to_celsius(float fahrenheit, float *celsius);

Conversion Formula: °C =driver (°Ffor -example 32)raw ×values 5/9received over the network).

Status: Currently commented out in implementation


IMU (Accelerometer) FunctionsValidation

validate_accelerometer_value()

Validates ifone accelerometer axis. Valid range is -160.0 to +160.0 m/s², which corresponds to a single accelerometer value is within the typical range.

±16
/**g * @brief Validates if an accelerometer value is within the typicalsensor range.
 * @param accel_value The accelerometer value to validate.
 * @return RESULT_OK if the value is valid, RESULT_ERR_INVALID_DATA otherwise.
 */
result_t validate_accelerometer_value(float accel_value);

Implementation:

result_t validate_accelerometer_value(float accel_value) {
    if (accel_value >= -160.0f && accel_value <= 160.0f) {
        return RESULT_OK;
    }
    return RESULT_ERR_INVALID_DATA;
}

Parameters:

  • accel_value - Single axis acceleration value in m/s²

Valid Range: -160.0 to +160.0 m/s² (typical ±16g sensor range)

Return Values:

  • RESULT_OK - Value is within valid range
  • RESULT_ERR_INVALID_DATA - Value exceeds acceptable limits

Usage Example:

float accel_x = imu_data.accel[0];

if (validate_accelerometer_value(accel_x) == RESULT_OK) {
    LOG_DEBUG("Accel X valid: %.2f m/s²", accel_x);
} else {
    LOG_ERROR("Accel X out of range: %.2f m/s²", accel_x);
}

validate_imu_data()

Validates all three axes of accelerometer data simultaneously.

/**
 * @brief Validates all three axes ofat accelerometeronce data.using *the @paramTRY accel_x The acceleration valuemacro for theearly X-axis.return * @param accel_y The acceleration value foron the Y-first invalid axis.
 * @param accel_z The acceleration value for the Z-axis.
 * @return RESULT_OK if all values are valid, RESULT_ERR_INVALID_DATA otherwise.
 */
result_t validate_imu_data(float accel_x, float accel_y, float accel_z);

Implementation:

result_t validate_imu_data(float accel_x, float accel_y, float accel_z) {
    TRY(validate_accelerometer_value(accel_x));
    TRY(validate_accelerometer_value(accel_y));
    TRY(validate_accelerometer_value(accel_z));
    return RESULT_OK;
}

Parameters:Note: the IMU driver has its own richer validators (imu_validate_accelerometer_range, imu_validate_gyroscope_range, imu_validate_magnetometer_range) which the main loop uses. See the imu component.

  • accel_x - X-axis acceleration (m/s²)
  • accel_y - Y-axis acceleration (m/s²)
  • accel_z - Z-axis acceleration (m/s²)

Return Values:

  • RESULT_OK - All three axes within valid range
  • RESULT_ERR_INVALID_DATA - Any axis exceeds acceptable limits

Usage Example:

if (validate_imu_data(imu_data.accel[0], imu_data.accel[1], imu_data.accel[2]) == RESULT_OK) {
    LOG_INFO("IMU acceleration valid");
    diagnostics.imu_sensor.state = SENSOR_OPERATING;
} else {
    LOG_ERROR("IMU acceleration out of range");
    diagnostics.imu_sensor.state = SENSOR_ERROR;
}

Pressure Conversion Functions (declared, currently inactive)

bar_to_psi()

The

Convertsheader pressuredeclares fromfour barunit toconversion psihelpers. (poundsTheir perimplementations squareexist inch).in sensor_basics.c but are commented out, so linking against them fails until they are re-enabled.

/** Temperature: F = C * @brief9/5 Converts+ pressure32,  fromC = (F - 32) * 5/9 */
result_t celsius_to_fahrenheit(float celsius, float *fahrenheit);
result_t fahrenheit_to_celsius(float fahrenheit, float *celsius);

/* Pressure: psi = bar to psi.
 * @param14.5038,  bar The pressure in bar.
 * @param= psi Pointer/ to a float where the converted psi pressure will be stored.
 * @return RESULT_OK on success, or RESULT_ERR_INVALID_ARG if psi is NULL.14.5038 */
result_t bar_to_psi(float bar, float *psi);

Conversion Formula: psi = bar × 14.5038

Parameters:

  • bar - Pressure in bar
  • psi - Pointer to output variable (must not be NULL)

Return Values:

  • RESULT_OK - Conversion successful
  • RESULT_ERR_INVALID_ARG - psi pointer is NULL

Status: Currently commented out in implementation

Usage Example:

float pressure_bar = 5.0f;
float pressure_psi;

if (bar_to_psi(pressure_bar, &pressure_psi) == RESULT_OK) {
    LOG_INFO("Pressure: %.2f bar = %.2f psi", pressure_bar, pressure_psi);
}

psi_to_bar()

Converts pressure from psi to bar.

/**
 * @brief Converts pressure from psi to bar.
 * @param psi The pressure in psi.
 * @param bar Pointer to a float where the converted bar pressure will be stored.
 * @return RESULT_OK on success, or RESULT_ERR_INVALID_ARG if bar is NULL.
 */
result_t psi_to_bar(float psi, float *bar);

ConversionEach Formula:returns barRESULT_OK =on psisuccess ÷or 14.5038RESULT_ERR_INVALID_ARG when the output pointer is NULL.

Implementation Status

Status:Currently implemented (active):

  • validate_ph_value()
  • validate_accelerometer_value()
  • validate_imu_data()

Currently commented out in implementation


GPS Functions

validate_gps_latitude()

Validates GPS latitude value is within valid range.

/**
 * @brief Validates GPS latitude value.
 * @param latitude The latitude value to validate (-90 to +90 degrees).
 * @return RESULT_OK if the value is valid, RESULT_ERR_INVALID_DATA otherwise.
 */
result_t validate_gps_latitude(double latitude);

Implementation:

result_t validate_gps_latitude(double latitude) {
    if (latitude >= -90.0 && latitude <= 90.0) {
        return RESULT_OK;
    }
    return RESULT_ERR_INVALID_DATA;
}

Parameters:inactive):

  • latitude - Latitude in degrees (double precision)celsius_to_fahrenheit()
  • fahrenheit_to_celsius()
  • bar_to_psi()
  • psi_to_bar()

Valid Range:Note: -90.0earlier to +90.0 degrees

  • Negative = South
  • Positive = North
  • 0° = Equator

Return Values:

  • RESULT_OK - Value is within valid range
  • RESULT_ERR_INVALID_DATA - Value outside ±90 degrees

validate_gps_longitude()

Validates GPS longitude value is within valid range.

/**
 * @brief Validates GPS longitude value.
 * @param longitude The longitude value to validate (-180 to +180 degrees).
 * @return RESULT_OK if the value is valid, RESULT_ERR_INVALID_DATA otherwise.
 */
result_t validate_gps_longitude(double longitude);

Implementation:

result_t validate_gps_longitude(double longitude) {
    if (longitude >= -180.0 && longitude <= 180.0) {
        return RESULT_OK;
    }
    return RESULT_ERR_INVALID_DATA;
}

Parameters:

  • longitude - Longitude in degrees (double precision)

Valid Range: -180.0 to +180.0 degrees

  • Negative = West
  • Positive = East
  • 0° = Prime Meridian
  • ±180° = International Date Line

Return Values:

  • RESULT_OK - Value is within valid range
  • RESULT_ERR_INVALID_DATA - Value outside ±180 degrees

validate_gps_hdop()

Validates GPS Horizontal Dilutiondrafts of Precisionthis (HDOP)page value.

documented
/**
 * @brief Validates GPS HDOP value.
 * @param hdop The HDOP value to validate (0-50 typical range).
 * @return RESULT_OK if the value is valid, RESULT_ERR_INVALID_DATA otherwise.
 */
result_t validate_gps_hdop(float hdop);

Implementation:

result_t validate_gps_hdop(float hdop) {
    if (hdop >= 0.0f && hdop <= 50.0f) {
        return RESULT_OK;
    }
    return RESULT_ERR_INVALID_DATA;
}

Parameters:

  • hdop - Horizontal dilution of precision value

Valid Range: 0.0 to 50.0

HDOP Quality Interpretation:

  • <1 - Ideal
  • 1-2 - Excellent
  • 2-5 - Good
  • 5-10 - Moderate
  • 10-20 - Fair
  • >20 - Poor

Return Values:

  • RESULT_OK - Value is within valid range
  • RESULT_ERR_INVALID_DATA - Value exceeds acceptable limits

validate_gps_satellite_count()

Validates GPS satellite count is within valid range.

/**
 * @brief Validates GPS satellite count.
 * @param satellites The number of satellites to validate.
 * @return RESULT_OK if the value is valid, RESULT_ERR_INVALID_DATA otherwise.
 */
result_t validate_gps_satellite_count(int32_t satellites);

Implementation:

result_t validate_gps_satellite_count(int32_t satellites) {
    if (satellites >= 0 && satellites <= 30) {
        return RESULT_OK;
    }
    return RESULT_ERR_INVALID_DATA;
}

Parameters:

  • satellites - Number of satellites in view

Valid Range: 0 to 30 satellites

Satellite Count Guidance:

  • 0-3 - No/poor fix possible
  • 4-5 - 3D fix possible
  • 6-9 - Good coverage
  • 10+ - Excellent coverage

Return Values:

  • RESULT_OK - Value is within valid range
  • RESULT_ERR_INVALID_DATA - Negative value or exceeds 30

Usage Example (Full GPS Validation):

if (validate_gps_latitude(gps_data.latitude) == RESULT_OK &&
    validate_gps_longitude(gps_data.longitude) == RESULT_OK &&
    validate_gps_hdop(gps_data.hdop) == RESULT_OK &&
    validate_gps_satellite_count(gps_data.satellites) == RESULT_OK) {
    
    LOG_INFO("GPS fix valid: %.6f, %.6f (sats=%d)", 
             gps_data.latitude, gps_data.longitude, gps_data.satellites);
    diagnostics.gps_sensor_1.state = SENSOR_OPERATING;
} else {
    LOG_WARNING("GPS validation failed");helpers diagnostics.gps_sensor_1.state(latitude, =longitude, SENSOR_ERROR;HDOP, }
satellite
count). These functions do not exist in the current library since there is no GPS driver on the sensor board.

Error Handling Pattern

All validation functions follow athe consistentsame pattern:pattern used across the firmware:

// Check sensor data
if (validate_<sensor>_<field>(value)validate_ph_value(ph_value) == RESULT_OK) {
    // Data is valid - use it
    diagnostics.<sensor>.ph_sensor.state = SENSOR_OPERATING;SensorState_SENSOR_OPERATING;
    diagnostics.ph_sensor.error_code = PHErrorCode_PH_NO_ERROR;
} else {
    // Data is invalid - set error state
    diagnostics.<sensor>.ph_sensor.state = SENSOR_ERROR;SensorState_SENSOR_ERROR;
    diagnostics.<sensor>.ph_sensor.error_code = <ERROR_CODE>_INVALID_DATA;PHErrorCode_PH_INVALID_DATA;
}

TRY Macro Usage:

The implementation uses a TRY() macro for error propagation (from result.h):

// In validate_imu_data()
result_t validate_imu_data(float accel_x, float accel_y, float accel_z) {
    TRY(validate_accelerometer_value(accel_x));  // Return on error
    TRY(validate_accelerometer_value(accel_y));  // Return on error
    TRY(validate_accelerometer_value(accel_z));  // Return on error
    return RESULT_OK;
}

Implementation Status

Currently Implemented (Active):

  • ✓ validate_ph_value()
  • ✓ validate_accelerometer_value()
  • ✓ validate_imu_data()
  • ✓ validate_gps_latitude()
  • ✓ validate_gps_longitude()
  • ✓ validate_gps_hdop()
  • ✓ validate_gps_satellite_count()

Currently Commented Out (Inactive):

  • ⊘ celsius_to_fahrenheit() - Declared but not implemented
  • ⊘ fahrenheit_to_celsius() - Declared but not implemented
  • ⊘ bar_to_psi() - Declared but not implemented
  • ⊘ psi_to_bar() - Declared but not implemented

Note: Temperature and pressure conversions are stubbed out in the current implementation. They can be enabled by uncommenting the implementation in sensor_basics.c if needed for future features.


Testing

Test Suitesuite Location:location: test/sensor_board/test_sensor_basics/

Building Tests:

//# Run allonly the sensor_basics tests
pio test -e sensor_board -f test_sensor_basics

//# Run with verbose output
pio test -e sensor_board -f test_sensor_basics -v

TestCurrent Coverage:

coverage:
  • Boundaryaccelerometer valueboundary testingvalues (min/max ranges)
  • Edge cases (±160.0 values,accepted, extreme±160.1 values)
  • Validrejected) rangeand acceptance
  • Invalidmulti-axis rangecombination. rejection
  • NULLThe pointertemperature handlingand forpressure conversion functions
tests

Integrationexist in Mainthe Application

file

Typicalbut Usageare incommented main.c:out together with their implementations.

// After polling a sensor
result_t poll_result = poll_gps_sensor(&gps_data);

if (poll_result == RESULT_OK) {
    // Validate all GPS fields before using
    if (validate_gps_latitude(gps_data.latitude) == RESULT_OK &&
        validate_gps_longitude(gps_data.longitude) == RESULT_OK) {
        
        diagnostics.gps_sensor_1.state = SENSOR_OPERATING;
        // Safe to use: gps_data.latitude, gps_data.longitude
        
    } else {
        diagnostics.gps_sensor_1.state = SENSOR_ERROR;
        diagnostics.gps_sensor_1.error_code = GPS_INVALID_DATA;
    }
} else if (poll_result == RESULT_ERR_COMMS) {
    diagnostics.gps_sensor_1.state = SENSOR_ERROR;
    diagnostics.gps_sensor_1.error_code = GPS_COMMUNICATION_FAILURE;
}