diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..109c4227bd21f49d63673d1d869b41c684e4f7dd --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +# +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(Projet) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) + +target_include_directories(app PRIVATE include) diff --git a/README.rst b/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..8b91fb432770ea9ed414eef1d1d3159145e83add --- /dev/null +++ b/README.rst @@ -0,0 +1,23 @@ +.. _bluetooth-bthome-sensor-template-sample: + +Bluetooth: BTHome sensor template +################################# + +Template for a `BTHome <https://bthome.io/>`_ sensor. + +Requirements +************ + +* A board with BLE support +* A BTHome compatible listener, for example `Home Assistant <https://www.home-assistant.io/>`_ with the BTHome integration running. + +Building and Running +******************** + +This sample can be found under :zephyr_file:`samples/bluetooth/bthome_sensor_template` in the Zephyr tree. + +See :ref:`bluetooth samples section <bluetooth-samples>` for details. + +When the sample is running, navigate to Devices & Services under settings in Home +Assistant. There you will be asked to configure the BTHome sensor if everything +went well. diff --git a/boards/adafruit_feather_m0_basic_proto.overlay b/boards/adafruit_feather_m0_basic_proto.overlay new file mode 100644 index 0000000000000000000000000000000000000000..ca73008cf8efb567f8a5c52038199f59a4d5b617 --- /dev/null +++ b/boards/adafruit_feather_m0_basic_proto.overlay @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2020 Jacek Ozimek <jacek.ozmk@gmail.com> + * + * SPDX-License-Identifier: Apache-2.0 + */ + + &feather_i2c { + bme280: bme280@76 { + /* 0x76 - SDO <-> GND */ + /* 0x77 - SDO <-> VCC */ + compatible = "bosch,bme280"; + reg = <0x76>; + }; +}; \ No newline at end of file diff --git a/boards/bbc_microbit_v2.overlay b/boards/bbc_microbit_v2.overlay new file mode 100644 index 0000000000000000000000000000000000000000..11d922039bc03427d0370d76650f267b8e98fb61 --- /dev/null +++ b/boards/bbc_microbit_v2.overlay @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 Centro de Inovacao EDGE + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + adc0 = &adc; + }; +}; + +&adc { + status="okay"; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>; + zephyr,input-positive = <NRF_SAADC_VDD>; + zephyr,oversampling = <8>; + }; +}; + + + + +//------------ + +&pinctrl { + spi1_default_alt: spi1_default_alt { + group1 { + psels = <NRF_PSEL(SPIM_SCK, 0, 17)>, // P13 : SCL -> SPI Clock (SCK) + <NRF_PSEL(SPIM_MOSI, 0, 13)>, // P15 : SDA -> SPI MOSI + <NRF_PSEL(SPIM_MISO, 0, 1)>; // P14: SDO -> SPI MISO + }; + }; + + spi1_sleep_alt: spi1_sleep_alt { + group1 { + psels = <NRF_PSEL(SPIM_SCK, 0, 17)>, // P13 : SCL -> SPI Clock (SCK) + <NRF_PSEL(SPIM_MOSI, 0, 13)>, // P15 : SDA -> SPI MOSI + <NRF_PSEL(SPIM_MISO, 0, 1)>; // P14 : SDO -> SPI MISO + low-power-enable; + }; + }; +}; + +&spi1 { + compatible = "nordic,nrf-spi"; + status = "okay"; + pinctrl-0 = <&spi1_default_alt>; + pinctrl-1 = <&spi1_sleep_alt>; + pinctrl-names = "default", "sleep"; + cs-gpios = <&gpio1 2 GPIO_ACTIVE_LOW>; /* CSB connecté à P16 */ + + bme280@0 { + compatible = "bosch,bme280"; + reg = <0>; + spi-max-frequency = <1000000>; /* conservatively set to 1MHz */ + status = "okay"; + }; +}; \ No newline at end of file diff --git a/boards/intel_adl_crb.overlay b/boards/intel_adl_crb.overlay new file mode 100644 index 0000000000000000000000000000000000000000..6c4edf6cfc66b72d99ef3f3d498d944a9a807a5a --- /dev/null +++ b/boards/intel_adl_crb.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + + &i2c0 { + bme280@76 { + compatible = "bosch,bme280"; + reg = <0x76>; + status = "okay"; + }; +}; \ No newline at end of file diff --git a/boards/intel_rpl_p_crb.overlay b/boards/intel_rpl_p_crb.overlay new file mode 100644 index 0000000000000000000000000000000000000000..cb3bf8239ccda988d6598d45afdfbcf21f094a26 --- /dev/null +++ b/boards/intel_rpl_p_crb.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + + &i2c1 { + bme280@76 { + compatible = "bosch,bme280"; + reg = <0x76>; + status = "okay"; + }; +}; \ No newline at end of file diff --git a/boards/rpi_pico.overlay b/boards/rpi_pico.overlay new file mode 100644 index 0000000000000000000000000000000000000000..baa67d6925016fc0d2d665d667dc2392d121b436 --- /dev/null +++ b/boards/rpi_pico.overlay @@ -0,0 +1,10 @@ +&spi0 { + status = "okay"; + cs-gpios = <&gpio0 17 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&spi0_default>; + bme280@0 { + compatible = "bosch,bme280"; + reg = <0>; + spi-max-frequency = <1000000>; /* conservatively set to 1MHz */ + }; +}; \ No newline at end of file diff --git a/core b/core new file mode 100644 index 0000000000000000000000000000000000000000..684467142d0835140df7ea2d5a2c07842fab7eee Binary files /dev/null and b/core differ diff --git a/prj.conf b/prj.conf new file mode 100644 index 0000000000000000000000000000000000000000..d52e027f03619481a3551ce88a5559e3d160cfdd --- /dev/null +++ b/prj.conf @@ -0,0 +1,17 @@ +# +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_DEVICE_NAME="BTHome" + +CONFIG_ADC=y + +CONFIG_SPI=y +CONFIG_SENSOR=y +CONFIG_BME280=y + +CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y +CONFIG_NEWLIB_LIBC=y \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000000000000000000000000000000000000..cf9f5ac05e397744344674b02fd71386c2b71fac --- /dev/null +++ b/src/main.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <zephyr/bluetooth/bluetooth.h> +#include <zephyr/bluetooth/uuid.h> +//################################################# +#include <zephyr/drivers/adc.h> +#include <zephyr/kernel.h> +#include <stdio.h> +#include <stdint.h> +//------------------------------------------------ +//------------------------------------------------ +#include <zephyr/device.h> +#include <zephyr/devicetree.h> +#include <zephyr/drivers/sensor.h> +//------------------------------------------------ +//------------------------------------------------ + + +/* ADC node from the devicetree. */ +#define ADC_NODE DT_ALIAS(adc0) + +/* Data of ADC device specified in devicetree. */ +static const struct device *adc = DEVICE_DT_GET(ADC_NODE); + +/* Configuration of the ADC channel */ +static const struct adc_channel_cfg channel_cfg = { + .gain = ADC_GAIN_1_6, + .reference = ADC_REF_INTERNAL, + .acquisition_time = ADC_ACQ_TIME_DEFAULT, + .channel_id = 0, + .input_positive = SAADC_CH_PSELP_PSELP_VDD, +}; + +#define ADC_RESOLUTION 12 +#define BUFFER_SIZE 1 /* We only need one sample */ + +static int16_t sample_buffer[BUFFER_SIZE]; + +//################################################# + +#define SERVICE_DATA_LEN 15 +#define SERVICE_UUID 0xfcd2 /* BTHome service UUID */ +#define IDX_TEMPL 4 /* Index of lo byte of temp in service data*/ +#define IDX_TEMPH 5 /* Index of hi byte of temp in service data*/ +#define IDX_HUML 7 /* Index of lo byte of humidity in service data */ +#define IDX_HUMH 8 /* Index of hi byte of humidity in service data */ +#define IDX_PRESL 10 /* Index of lo byte of pressure in service data (in hPa */ +#define IDX_PRESM 11 /* Index of hi byte of pressure in service data (in hPa)*/ +#define IDX_PRESH 12 /* Index of hi byte of pressure in service data (in hPa) */ +#define BAT 14 /* Index of hi byte of temp in service data*/ + + +#define ADV_PARAM BT_LE_ADV_PARAM(BT_LE_ADV_OPT_USE_IDENTITY, \ + BT_GAP_ADV_SLOW_INT_MIN, \ + BT_GAP_ADV_SLOW_INT_MAX, NULL) + + +static uint8_t service_data[SERVICE_DATA_LEN] = { + BT_UUID_16_ENCODE(SERVICE_UUID), + 0x40, + 0x02, /* Temperature */ + 0xc4, /* Low byte */ + 0x00, /* High byte */ + 0x03, /* Humidity */ + 0xbf, /* 50.55% low byte*/ + 0x13, /* 50.55% high byte*/ + 0x04, /* Pressure type identifier*/ + 0x00, /* Low byte of pressure */ + 0x00, /* Middle byte of pressure */ + 0x00, /* High byte of pressure */ + 0x01, /* BATTERY */ + 0x80, + +}; + +static struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR), + BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1), + BT_DATA(BT_DATA_SVC_DATA16, service_data, ARRAY_SIZE(service_data)) +}; + +static void bt_ready(int err) +{ + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return; + } + + printk("Bluetooth initialized\n"); + + /* Start advertising */ + err = bt_le_adv_start(ADV_PARAM, ad, ARRAY_SIZE(ad), NULL, 0); + if (err) { + printk("Advertising failed to start (err %d)\n", err); + return; + } +} + +//////////////////////////////////////////////////////////// +double get_Tension(int32_t adc_value) { + // Le gain est toujours égal à 1/6 + double gain = 1.0 / 6.0; + double vref = 0.6; + + // Calcul de la tension en utilisant l'équation donnée + double tension = ( (adc_value * vref) / ( 4095 * gain) ); + return tension; +} + +double calculer_capacite_restante_NiMH_2_cells(double tension_batterie) { + double capacite_restante; + + // La tension de la batterie est de 2.0V à 2.7V pour 2 cellules NiMH en série + if (tension_batterie >= 2.7) { + capacite_restante = (tension_batterie - 2.7) * (100 - 90) / (2.8 - 2.7) + 90; // Plage 2.7V - 2.8V + } else if (tension_batterie >= 2.6) { + capacite_restante = (tension_batterie - 2.6) * (90 - 80) / (2.7 - 2.6) + 80; // Plage 2.6V - 2.7V + } else if (tension_batterie >= 2.5) { + capacite_restante = (tension_batterie - 2.5) * (80 - 70) / (2.6 - 2.5) + 70; // Plage 2.5V - 2.6V + } else if (tension_batterie >= 2.4) { + capacite_restante = (tension_batterie - 2.4) * (70 - 60) / (2.5 - 2.4) + 60; // Plage 2.4V - 2.5V + } else if (tension_batterie >= 2.3) { + capacite_restante = (tension_batterie - 2.3) * (60 - 50) / (2.4 - 2.3) + 50; // Plage 2.3V - 2.4V + } else if (tension_batterie >= 2.2) { + capacite_restante = (tension_batterie - 2.2) * (50 - 40) / (2.3 - 2.2) + 40; // Plage 2.2V - 2.3V + } else if (tension_batterie >= 2.1) { + capacite_restante = (tension_batterie - 2.1) * (40 - 30) / (2.2 - 2.1) + 30; // Plage 2.1V - 2.2V + } else if (tension_batterie >= 2.0) { + capacite_restante = (tension_batterie - 2.0) * (30 - 20) / (2.1 - 2.0) + 20; // Plage 2.0V - 2.1V + } else { + capacite_restante = 0; // Moins de 10% de charge + } + + return capacite_restante; +} +//////////////////////////////////////////////////////////// +//-------------------------------------------------- +//-------------------------------------------------- +static const struct device *get_bme280_device(void) { + const struct device *const dev = DEVICE_DT_GET_ANY(bosch_bme280); + + if (dev == NULL) { + /* No such node, or the node does not have status "okay". */ + printk("\nError: no device found.\n"); + return NULL; + } + + if (!device_is_ready(dev)) { + printk("\nError: Device \"%s\" is not ready; " + "check the driver initialization logs for errors.\n", + dev->name); + return NULL; + } + + printk("Found device \"%s\", getting sensor data\n", dev->name); + return dev; +} +//-------------------------------------------------- +//-------------------------------------------------- + + +int main(void) +{ + int err; + const struct device *dev = get_bme280_device(); + struct sensor_value temp, humidity, press; + + //################################################# + printk("Starting BTHome sensor template\n"); + + /* Initialize the Bluetooth Subsystem */ + err = bt_enable(bt_ready); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return 0; + } + + + /* Configure the sampling sequence */ + struct adc_sequence sequence = { + .channels = BIT(channel_cfg.channel_id), + .buffer = sample_buffer, + .buffer_size = sizeof(sample_buffer), + .resolution = ADC_RESOLUTION, + }; + + //################################################# + + if (!device_is_ready(adc)) { + printf("ADC controller device %s not ready\n", adc->name); + return 0; + } + + err = adc_channel_setup(adc, &channel_cfg); + if (err < 0) { + printf("Could not setup ADC channel (%d)\n", err); + return 0; + } +//################################################# + + + for (;;) { +//################################################# + // Récupération des échantillons du capteur + if (sensor_sample_fetch(dev) < 0) { + printk("Erreur lors de la récupération des données du capteur\n"); + continue; + } + + err = adc_read(adc, &sequence); + if (err < 0) { + printf("Could not read (%d)\n", err); + continue; + } + + // Récupération des différentes valeurs + sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp); + sensor_channel_get(dev, SENSOR_CHAN_HUMIDITY, &humidity); + sensor_channel_get(dev, SENSOR_CHAN_PRESS, &press); + + + // Convertir la température en centi-degrés Celsius (°C * 100) en tenant + // compte de val2 + int temperature_centi = (temp.val1 * 100) + (temp.val2 / 10000); + int32_t val_mv = sample_buffer[0]; /* Read raw value */ + + // Mettre à jour les deux octets dans service_data pour la température + service_data[IDX_TEMPH] = (temperature_centi >> 8) & 0xff; // Partie haute + service_data[IDX_TEMPL] = temperature_centi & 0xff; // Partie basse + + // Convertir l'humidité en centi-pourcent (% * 100) en tenant compte de val2 + int humidity_centi = (humidity.val1 * 100) + (humidity.val2 / 10000); + + // Mettre à jour les deux octets dans service_data pour l'humidité + service_data[IDX_HUMH] = (humidity_centi >> 8) & 0xff; // Partie haute + service_data[IDX_HUML] = humidity_centi & 0xff; // Partie basse + + // Convertir la pression et la mettre à jour dans service_data + int pressure_hpa = + 100*((press.val1 * 10) + (press.val2 / 10000)); // Convertir en hPa + service_data[IDX_PRESH] = + (pressure_hpa) >> 16; // Haute partie de la pression + service_data[IDX_PRESM] = + (pressure_hpa >> 8) & 0xff; // Partie intermédiaire + service_data[IDX_PRESL] = + (pressure_hpa)&0xff; // Basse partie de la pression + + // Affichage des résultats avec les unités + printk("temp: %d.%06d°C; humidity: %d.%06d%%; press: %d hPa\n", temp.val1, + temp.val2, humidity.val1, humidity.val2, pressure_hpa); + + // Mettre à jour les données de la publicité + err = bt_le_adv_update_data(ad, ARRAY_SIZE(ad), NULL, 0); + if (err) { + printk("Failed to update advertising data (err %d)\n", err); + } + + err = adc_raw_to_millivolts(channel_cfg.reference, + channel_cfg.gain, + ADC_RESOLUTION, &val_mv); + + + /* Simulate temperature from 0C to 25C */ + double battery_voltage =(get_Tension(sample_buffer[0])); + + service_data[BAT] = calculer_capacite_restante_NiMH_2_cells(battery_voltage); + //service_data[BAT_H] = (battery_voltage >> 8) & 0xff; + //service_data[BAT_L] = battery_voltage & 0xff; + + + if (err < 0 || channel_cfg.reference == 0) { + printk("Raw value: %d (conversion to mV not available)\n", sample_buffer[0]); + } else { + printk("Raw ADC value: %d\n", service_data[BAT]); + + printk("Battery voltage: %f \n", battery_voltage); + } +//################################################# + + + + + err = bt_le_adv_update_data(ad, ARRAY_SIZE(ad), NULL, 0); + if (err) { + printk("Failed to update advertising data (err %d)\n", err); + } + k_sleep(K_MSEC(BT_GAP_ADV_SLOW_INT_MIN)); + } + return 0; +}