Skip to content

Commit

Permalink
Merge pull request #8 from BuzzVerse/feat/NODEZ-12-bq27441
Browse files Browse the repository at this point in the history
BQ27441 Implementation and other improvements
  • Loading branch information
bykowskiolaf authored Feb 14, 2025
2 parents ac4e5d7 + c52490c commit 43230fe
Show file tree
Hide file tree
Showing 12 changed files with 252 additions and 83 deletions.
2 changes: 2 additions & 0 deletions app/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ CONFIG_I2C=y
CONFIG_GPIO=y
CONFIG_SPI=y

CONFIG_BQ274XX_PM=y

CONFIG_SENSOR=y
CONFIG_SENSOR_ASYNC_API=y

Expand Down
71 changes: 24 additions & 47 deletions app/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,50 @@
#include "buzzverse/packet.pb.h"
#include "peripherals/lorawan_handler/lorawan_handler.hpp"
#include "sensors/bme280/bme280.hpp"
#include "sensors/bq27441/bq27441.hpp"
#include "utils/packet_handler.hpp"

LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);

#define DELAY K_SECONDS(10)

// TODO: Remove this when ready
void test_encode_decode();
#define DELAY_10_SEC K_SECONDS(10)
#define DELAY_20_SEC K_SECONDS(20)
#define DELAY_30_SEC K_SECONDS(30)

int main(void) {
const device* const bme280_dev = DEVICE_DT_GET_ANY(bosch_bme280);
const device* const bq27441_dev = DEVICE_DT_GET_ANY(ti_bq274xx);
constexpr uint8_t DEVICE_AMOUNT = 3;

BME280 bme280(bme280_dev);
LoRaWANHandler lorawan;
BQ27441 bq27441(bq27441_dev);

LoRaWANHandler lorawan(bq27441);

etl::vector<Peripheral*, 2> peripherals = {&bme280, &lorawan};
etl::vector<Peripheral*, DEVICE_AMOUNT> peripherals = {&bme280, &bq27441, &lorawan};

for (auto* peripheral : peripherals) {
if (!peripheral->init()) {
if (peripheral->init() != Peripheral::Status::OK) {
LOG_ERR("%s initialization failed", peripheral->get_name().c_str());
}
}

while (true) {
// Create and populate a BME280Data protobuf message
buzzverse_v1_BME280Data bme280_data = buzzverse_v1_BME280Data_init_zero;
buzzverse_v1_BQ27441Data bq27441_data = buzzverse_v1_BQ27441Data_init_zero;

if (bme280.is_ready()) {
bme280.read_data(&bme280_data);
} else {
LOG_ERR("BME280 not ready");
k_sleep(DELAY);
k_sleep(DELAY_10_SEC);
continue;
}

if (bq27441.is_ready()) {
bq27441.read_data(&bq27441_data);
} else {
LOG_ERR("BQ27441 not ready");
k_sleep(DELAY_10_SEC);
continue;
}

Expand All @@ -45,49 +58,13 @@ int main(void) {
packet.which_data = buzzverse_v1_Packet_bme280_tag;
packet.data.bme280 = bme280_data;

// TODO: Remove this when ready
test_encode_decode();

// Send the packet using LoRaWANHandler
if (!lorawan.is_ready() || !lorawan.send_packet(packet)) {
if (!lorawan.is_ready() || (lorawan.send_packet(packet) != LoRaWANHandler::Status::OK)) {
LOG_ERR("Failed to send packet");
}

k_sleep(DELAY);
k_sleep(DELAY_10_SEC);
}

return 0;
}

// TODO: Remove this when ready
void test_encode_decode() {
buzzverse_v1_Packet packet = buzzverse_v1_Packet_init_zero;
packet.which_data = buzzverse_v1_Packet_bme280_tag;
packet.data.bme280.temperature = 1;
packet.data.bme280.pressure = 1;
packet.data.bme280.humidity = 1;

uint8_t buffer[64];
size_t size = 0;

if (!PacketHandler::encode(packet, buffer, size)) {
LOG_ERR("Failed to encode packet");
return;
}

// print encoded packet bytes
LOG_INF("Encoded packet: ");
for (size_t i = 0; i < size; i++) {
LOG_INF("0x%02x", buffer[i]);
}

buzzverse_v1_Packet decoded_packet = buzzverse_v1_Packet_init_zero;
if (!PacketHandler::decode(buffer, size, decoded_packet)) {
LOG_ERR("Failed to decode packet");
return;
}

LOG_INF("Decoded packet: temperature=%d, pressure=%d, humidity=%d",
decoded_packet.data.bme280.temperature, decoded_packet.data.bme280.pressure,
decoded_packet.data.bme280.humidity);
}
62 changes: 49 additions & 13 deletions app/src/peripherals/lorawan_handler/lorawan_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@

LOG_MODULE_REGISTER(lorawan_handler, LOG_LEVEL_DBG);

LoRaWANHandler::LoRaWANHandler() {
Sensor<buzzverse_v1_BQ27441Data>* LoRaWANHandler::battery_sensor = nullptr;

LoRaWANHandler::LoRaWANHandler(Sensor<buzzverse_v1_BQ27441Data>& battery_sensor) {
LoRaWANHandler::battery_sensor = &battery_sensor;
const char* dev_eui_str = CONFIG_LORAWAN_DEV_EUI;
const char* join_eui_str = CONFIG_LORAWAN_JOIN_EUI;
const char* app_key_str = CONFIG_LORAWAN_APP_KEY;
Expand All @@ -30,20 +33,49 @@ LoRaWANHandler::LoRaWANHandler() {
}
}

bool LoRaWANHandler::init() {
uint8_t LoRaWANHandler::battery_level_callback() {
if (nullptr == battery_sensor) {
LOG_ERR("Battery sensor is not set. Returning 255.");
return 255;
}

if (!battery_sensor->is_ready()) {
LOG_ERR("Battery sensor is not ready, returning 255.");
return 255;
}

buzzverse_v1_BQ27441Data battery_data = buzzverse_v1_BQ27441Data_init_zero;
auto status = battery_sensor->read_data(&battery_data);
if (Sensor<buzzverse_v1_BQ27441Data>::Status::OK != status) {
LOG_ERR("Battery sensor read failed: %d", static_cast<int>(status));
return 255;
}

uint8_t soc = battery_data.state_of_charge;

if (0 == soc) {
LOG_WRN("Battery level is 0, possibly external power source.");
return 0;
} else if (soc > 100) {
LOG_ERR("Invalid battery percentage: %d", soc);
return 255;
}

uint8_t lorawan_battery_level = (soc * 254) / 100;
return lorawan_battery_level == 0 ? 1 : lorawan_battery_level;
}

Peripheral::Status LoRaWANHandler::init() {
const device* lora_dev = DEVICE_DT_GET(DT_ALIAS(lora0));
if (!device_is_ready(lora_dev)) {
LOG_WRN("%s: device not ready.", lora_dev->name);
return false;
return Peripheral::Status::INIT_ERR;
}

// lorawan_enable_adr(false);
// lorawan_set_class(LORAWAN_CLASS_A);

int ret = lorawan_start();
if (ret < 0) {
LOG_WRN("lorawan_start failed: %d", ret);
return false;
return Peripheral::Status::INIT_ERR;
}

struct lorawan_join_config join_cfg;
Expand All @@ -57,31 +89,35 @@ bool LoRaWANHandler::init() {
ret = lorawan_join(&join_cfg);
if (ret < 0) {
LOG_WRN("lorawan_join failed: %d", ret);
return false;
return Peripheral::Status::INIT_ERR;
}

LOG_INF("Successfully joined LoRaWAN network");

lorawan_register_battery_level_callback(&LoRaWANHandler::battery_level_callback);

LOG_INF("LoRaWAN battery level callback registered.");
connected = true;
return true;
return Peripheral::Status::OK;
}

bool LoRaWANHandler::send_packet(const buzzverse_v1_Packet& packet) const {
LoRaWANHandler::Status LoRaWANHandler::send_packet(const buzzverse_v1_Packet& packet) const {
uint8_t buffer[MAX_MSG_SIZE];
size_t size = 0;

// Encode the protobuf message
if (!PacketHandler::encode(packet, buffer, size)) {
LOG_ERR("Packet encoding failed");
return false;
return Status::ENCODE_ERR;
}

// Send the encoded message over LoRaWAN
int ret = lorawan_send(LORAWAN_PORT, buffer, size, LORAWAN_MSG_UNCONFIRMED);
if (ret < 0) {
LOG_ERR("LoRaWAN send failed: %d", ret);
return false;
return Status::SEND_ERR;
}

LOG_INF("Packet sent successfully (size: %zu bytes)", size);
return true;
return Status::OK;
}
20 changes: 17 additions & 3 deletions app/src/peripherals/lorawan_handler/lorawan_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,32 @@
#include <etl/array.h>
#include <etl/string.h>

#include "buzzverse/bq27441.pb.h"
#include "buzzverse/packet.pb.h"
#include "peripheral.hpp"
#include "sensors/sensor.hpp"

class LoRaWANHandler : public Peripheral {
public:
LoRaWANHandler();
explicit LoRaWANHandler(Sensor<buzzverse_v1_BQ27441Data>& battery_sensor);

static constexpr uint8_t LORAWAN_PORT = 2;

static constexpr size_t MAX_MSG_SIZE = 64;
static constexpr size_t EUI_SIZE = 8;
static constexpr size_t KEY_SIZE = 16;

bool init() override;
/**
* @brief LoRaWAN-specific status codes
*/
enum class Status {
OK = 0, /**< Operation successful */
JOIN_ERR = -3, /**< Failed to join the LoRaWAN network */
SEND_ERR = -4, /**< Failed to send a packet */
ENCODE_ERR = -5 /**< Failed to encode the message */
};

Peripheral::Status init() override;

bool is_ready() const override {
return connected;
Expand All @@ -27,13 +39,15 @@ class LoRaWANHandler : public Peripheral {
return "LoRaWAN";
}

bool send_packet(const buzzverse_v1_Packet& packet) const;
Status send_packet(const buzzverse_v1_Packet& packet) const;

private:
bool connected{false};
etl::array<uint8_t, EUI_SIZE> dev_eui;
etl::array<uint8_t, EUI_SIZE> join_eui;
etl::array<uint8_t, KEY_SIZE> app_key;
static Sensor<buzzverse_v1_BQ27441Data>* battery_sensor;
static uint8_t battery_level_callback();
};

#endif // LORAWAN_HANDLER_HPP
37 changes: 33 additions & 4 deletions app/src/peripherals/peripheral.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,44 @@ constexpr size_t PERIPHERAL_NAME_SIZE = 32;

class Peripheral {
public:
/**
* @brief Default destructor
*/
virtual ~Peripheral() = default;

// Initialize the peripheral
virtual bool init() = 0;
/**
* @brief Generic peripheral status codes
*/
enum class Status {
OK = 0, /**< Operation successful */
INIT_ERR = -1, /**< Initialization failed */
NOT_READY = -2 /**< Peripheral is not ready */
};

// Check if the peripheral is ready
/**
* @brief Initialize the peripheral
*
* @return Status
* @retval OK if the peripheral is successfully initialized
* @retval INIT_ERR if the peripheral initialization fails
* @retval NOT_READY if the peripheral is not ready
*/
virtual Status init() = 0;

/**
* @brief Check if the peripheral is ready
*
* @return bool
* @retval true if the peripheral is ready
* @retval false if the peripheral is not ready
*/
virtual bool is_ready() const = 0;

// Get the name of the peripheral
/**
* @brief Get the name of the peripheral
*
* @return etl::string<PERIPHERAL_NAME_SIZE>
*/
virtual etl::string<PERIPHERAL_NAME_SIZE> get_name() const = 0;
};

Expand Down
34 changes: 23 additions & 11 deletions app/src/sensors/bme280/bme280.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,49 @@ LOG_MODULE_REGISTER(bme280, LOG_LEVEL_DBG);

BME280::BME280(const device* dev) : bme280_dev(dev) {}

bool BME280::init() {
using Status = Sensor<buzzverse_v1_BME280Data>::Status;

Peripheral::Status BME280::init() {
if (!device_is_ready(bme280_dev)) {
LOG_WRN("BME280 device not ready");
return false;
return Peripheral::Status::NOT_READY;
}

LOG_INF("BME280 device ready");
ready = true;
return true;
return Peripheral::Status::OK;
}

void BME280::read_data(buzzverse_v1_BME280Data* data) const {
Status BME280::read_data(buzzverse_v1_BME280Data* data) const {
struct sensor_value temp, press, humidity;

if (nullptr == data) {
LOG_ERR("Invalid data pointer");
return;
return Status::READ_ERR;
}

if (sensor_sample_fetch(bme280_dev) != 0) {
LOG_ERR("Failed to fetch BME280 data");
return;
return Status::READ_ERR;
}

sensor_channel_get(bme280_dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
sensor_channel_get(bme280_dev, SENSOR_CHAN_PRESS, &press);
sensor_channel_get(bme280_dev, SENSOR_CHAN_HUMIDITY, &humidity);

// Convert sensor values to protobuf-compatible integers
data->temperature =
static_cast<uint32_t>((temp.val1 * 100) + (temp.val2 / 10000)); // centi-degrees
data->pressure = static_cast<uint32_t>((press.val1 * 10) + (press.val2 / 100000)); // hPa
data->humidity = static_cast<uint32_t>((humidity.val1 * 10) + (humidity.val2 / 100000)); // %
// Convert temperature to whole degrees (-128 to 127)
data->temperature = static_cast<int8_t>(temp.val1);

// Convert pressure as difference from 1000 hPa (-128 to 127)
int32_t raw_pressure = press.val1 * 10 + ((press.val2 * 10) / 100000);
data->pressure = static_cast<int8_t>(raw_pressure - 1000);

// Convert humidity to whole percentage (0-100%)
data->humidity = static_cast<uint8_t>(humidity.val1);

LOG_DBG("Temperature: %d C", data->temperature);
LOG_DBG("Pressure (Difference from 1000 hPa): %d hPa", data->pressure);
LOG_DBG("Humidity: %d %%", data->humidity);

return Status::OK;
}
Loading

0 comments on commit 43230fe

Please sign in to comment.