How to monitor a beehive with Arduino Nano 33BLE (bluetooth)

You have a beehive and you want to optimise your interventions ? Checkout our project where we embed sensors in the beehive.

AdvancedFull instructions providedOver 4 days6,703
How to monitor a beehive with Arduino Nano 33BLE (bluetooth)

Things used in this project

Hardware components

Nano 33 BLE Sense
Arduino Nano 33 BLE Sense
×1
Breakout board Sigfox
×1
Hackeet Accu Li-Ion 3,7 V 1050 mAh
×1
Seeed Studio LiPo Rider Pro Board
×1
DHT22 Temperature and Humidity Sensor
DFRobot DHT22 Temperature and Humidity Sensor
×2
Adafruit Waterproof DS18B20 Digital temperature sensor
Adafruit Waterproof DS18B20 Digital temperature sensor
×2
Weight sensor H40A-C3-0150
×1
Seeed Studio Load Cell Amplifier - HX711
×1
Seeed Studio Module GPS Grove
×1
Adafruit Light sensor VEML7700
×1
Box G218C
×1
Solar panel SOL2W
×1
Grove Right Connector
×10
Seeed Studio Kit 5 Grove cables - 4 female contacts
×1
Interruptor DSRR2B
×1
LDR, 5 Mohm
LDR, 5 Mohm
×1

Software apps and online services

Arduino IDE
Arduino IDE
KiCad
KiCad
Android Studio
Android Studio
Microsoft Azure
Microsoft Azure
Ubidots
Ubidots

Hand tools and fabrication machines

Soldering Iron Kit, Weller XNT/THM Tips
Soldering Iron Kit, Weller XNT/THM Tips
Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Custom parts and enclosures

PCB

This zip file contains all the files needed to print a PCB. You can open the files with Kicad. The files in the Gerber folder are used to print the PCB. The other ones are used to design the PCB.

Schematics

Circuit schematic

Pinout

Code

Main

Arduino
It's the main program to launch
/*
 * Bibliothques utilises :
 * - ArduinoBLE by Arduino
 * - Adafruit Unified Sensor by Adafruit
 * - DHT sensor library by Adafruit
 * - HX711 Arduino library by Bogdan Necula
 * - MaximWire by xeno
 * - NanoBLEFlashPrefs by Dirk
 */
 
#include "UBeeZ_LED.h"
#include "UBeez_Bluetooth.h"
#include "UBeeZ_Temperature_humidity_sensor.h"
#include "UBeeZ_Sigfox.h"
#include "UBeeZ_Battery_weight_light.h"
#include "UBeeZ_Prefs.h"
#include "UBeeZ_GPS.h"

#define DISABLE_SIGFOX false
#define DISABLE_GPS false

using namespace std;

std::chrono::time_point<std::chrono::system_clock> last_gps_sync = std::chrono::system_clock::now()-std::chrono::seconds(getPrefs()->gpsSyncDelay);

void setup() {
  // put your setup code here, to run once:
  set_time(0);
  SerialUSB.begin(9600);
  digitalWrite(PIN_ENABLE_I2C_PULLUP, LOW);
  digitalWrite(PIN_ENABLE_SENSORS_3V3, LOW);

  setupLedRGB();
  setupPrefs();
  if(!DISABLE_SIGFOX){
    setupSigfox();
  }
  setupTemperatureHumidity();
  setupBatteryLight();
  setupWeight();
  if(!DISABLE_GPS){
    setupGPS();
  }
  
  setupBLE();
  if(BLE_ALWAYS_ACTIVE){
    startBLE();
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  if(!DISABLE_SIGFOX){
    sendPayloadCapteurs();
  }
  if(!DISABLE_GPS && std::chrono::system_clock::now() >= last_gps_sync + std::chrono::seconds(getPrefs()->gpsSyncDelay)){
      if(get_GPS_location()){
        sendPayloadGPS();
      }
      last_gps_sync = std::chrono::system_clock::now();
    }
    rtos::ThisThread::sleep_for(std::chrono::seconds(getPrefs()->sigfoxSyncDelay)); 
}

UBeeZ_Prefs.cpp

C/C++
#include "UBeeZ_Prefs.h"

NanoBLEFlashPrefs prefsManager;
FlashPrefs prefs;

void setupPrefs(){
  int rc = prefsManager.readPrefs(&prefs, sizeof(prefs));
  if(rc == FDS_SUCCESS){
    if(DEBUG_PREFS){
      SerialUSB.println("Preferences found !");
    }
  }
  else{
    if(DEBUG_PREFS){
      SerialUSB.print("No preferences found. Return code: ");
      SerialUSB.print(rc);
      SerialUSB.print(", ");
      SerialUSB.println(prefsManager.errorString(rc));
    }
  }

  if(DEBUG_PREFS){
    SerialUSB.println("Preferences values :");
    SerialUSB.print("sigfoxSyncDelay : ");
    SerialUSB.println(prefs.sigfoxSyncDelay);
    SerialUSB.print("gpsLatitude : ");
    SerialUSB.println(prefs.gpsLatitude);
    SerialUSB.print("gpsLongitude : ");
    SerialUSB.println(prefs.gpsLongitude);
    SerialUSB.print("gpsHeading : ");
    SerialUSB.println(prefs.gpsHeading);
  }
}

FlashPrefs* getPrefs(){
  return &prefs;
}

void writePrefs(){
  if(DEBUG_PREFS){
    SerialUSB.println("Writting preferences values :");
    SerialUSB.print("sigfoxSyncDelay : ");
    SerialUSB.println(prefs.sigfoxSyncDelay);
    SerialUSB.print("gpsLatitude : ");
    SerialUSB.println(prefs.gpsLatitude, 6);
    SerialUSB.print("gpsLongitude : ");
    SerialUSB.println(prefs.gpsLongitude, 6);
    SerialUSB.print("gpsHeading : ");
    SerialUSB.println(prefs.gpsHeading);
  }
  
  prefsManager.writePrefs(&prefs, sizeof(prefs));
  // Wait until completion
  while (!prefsManager.operationCompleted()) {
    rtos::ThisThread::sleep_for(std::chrono::milliseconds(200));
  }
}

UBeeZ_Prefs.h

C Header File
#ifndef UBEEZ_PREFS
#define UBEEZ_PREFS

#include <NanoBLEFlashPrefs.h>
#include <Arduino.h>
#include <Serial.h>
#include "rtos.h"

#define DEBUG_PREFS true

typedef struct FlashStruct {
  unsigned short sigfoxSyncDelay = 10*60;
  unsigned int gpsSyncDelay = 24 * 60 * 60; //Tous les jours
  double gpsLatitude = 0;
  double gpsLongitude = 0;
  float gpsHeading = 0;
} FlashPrefs;

void setupPrefs();
FlashPrefs* getPrefs();
void writePrefs();

#endif

UBeeZ_Sigfox.cpp

C/C++
#include "UBeeZ_Sigfox.h"
#include "UBeeZ_LED.h"
#include "UBeeZ_Temperature_humidity_sensor.h"
#include "UBeeZ_Battery_weight_light.h"
#include "UBeeZ_Prefs.h"
#include <Serial.h>
#include "mbed.h"
#include "rtos.h"

void setupSigfox(){
  pinMode(PIN_SIGFOX_RESET, OUTPUT);
  Serial1.begin(9600);
  digitalWrite(PIN_SIGFOX_RESET, HIGH);
}

void sendPayloadCapteurs(){

  //Resume Sigfox
  digitalWrite(PIN_SIGFOX_RESET, LOW);  
  rtos::ThisThread::sleep_for(std::chrono::milliseconds(500));
  digitalWrite(PIN_SIGFOX_RESET, HIGH);
  
  led_blink_R(100); 
  while(!Serial1.available()){
    Serial1.write("AT\r\n");
    rtos::ThisThread::sleep_for(std::chrono::milliseconds(500));
  }
  led_stop_R();
  digitalWrite(LEDR, HIGH);
  
  //SerialUSB.println("Sending Sigfox data");
  /* Payload :
   * Humidit : 6 bit
   * Temprature : 10 bit
   * Poid : 10 bit
   * Batterie : 8 bit
   */

  //SerialUSB.println("Valeurs converties :\n");

  updateTemperatureHumidity();
  //Range humidit : 0 - 100 ===>>> 0 - 64
  unsigned int convertedHumidite[2];
  float currentHumidity;
  for(int i=0;i<2;i++){
    currentHumidity = getHumiditeSensor(i);
    if(isnan(currentHumidity)){
      convertedHumidite[i] = 63;    //Humidity reading error
    }
    else if(currentHumidity < 0 || currentHumidity > 100){
      convertedHumidite[i] = 62;    //Humidity out of range
    }
    else{
      convertedHumidite[i] =  std::round(currentHumidity*60/100);
    }
    //SerialUSB.println("Humidite %d\t: %u\t%#03x\n", i, convertedHumidite[i], convertedHumidite[i]);
  }

  //Range temprature : -20.0 - 80.0 ===>>> 0 - 1000
  unsigned int convertedTemperature[4];
  //char strTemp[255];
  float currentTemp;
  for(int i=0;i<4;i++){
    currentTemp = getTemperatureSensor(i);
    if(isnan(currentTemp)){
      convertedTemperature[i] = 1023;   //Temperature reading error
    }
    else if(currentTemp>80 || currentTemp < -20){
      convertedTemperature[i] = 1022;   //Temperature out of range
    }
    else{
      convertedTemperature[i] = std::round((currentTemp+20)*10);
    }
    //sprintf(strTemp, "Temperature %d\t: %f=>%u\t%#05x\n", i, getTemperatureSensor(i), convertedTemperature[i], convertedTemperature[i]);
    //SerialUSB.print(strTemp);
  }

  //Range poid : 0.0 - 100.0 ===>>> 0 - 1000
  unsigned int convertedPoid;
  if(!updateWeight()){
    convertedPoid = 1023;   //Weight reading error
  }
  else if(getWeight() < 0){
    if(getWeight() > -0.5){
      convertedPoid = 0;
    }
    else{
      convertedPoid = 1022;
    }
  }
  else if(getWeight() > 100){
    convertedPoid = 1022;   //Weight out of range
  }
  else{
    convertedPoid = std::round(getWeight()*10);
  }
  //SerialUSB.println("Poid \t\t: %u\t%#05x\n", convertedPoid, convertedPoid);

  //Range batterie : 0 - 5 ===>>> 0 - 500
  unsigned int convertedBatterie;
  float batteryVoltage = getBatteryVoltage();
  if(batteryVoltage > 5 || batteryVoltage < 0){
    convertedBatterie = 511;
  }
  else{
    convertedBatterie = std::round(batteryVoltage*100);
  }
  /*char str[256];
  sprintf(str, "Batterie \t: %u\t%#04x\n\n", convertedBatterie, convertedBatterie);
  SerialUSB.print(str);*/

  //Range luminosite : 0-1000 ===>>> 0 - 1000
  unsigned int convertedLuminosite = getLight();
  if(convertedLuminosite > 1023){
    convertedLuminosite = 1023;
  }

  unsigned char payload[12];
  for(int i=0;i<12;i++){
    payload[i] = 0;
  }
  payload[0] = convertedHumidite[0] << 2 | convertedHumidite[1] >> 4;
  payload[1] = convertedHumidite[1] << 4 | convertedTemperature[0] >> 6;
  payload[2] = convertedTemperature[0] << 2 | convertedTemperature[1] >> 8;
  payload[3] = convertedTemperature[1] & 0xFF;
  payload[4] = convertedTemperature[2] >> 2;
  payload[5] = convertedTemperature[2] << 6 | convertedTemperature[3] >> 4;
  payload[6] = convertedTemperature[3] << 4 | convertedPoid >> 6;
  payload[7] = convertedPoid << 2 | convertedBatterie >> 7;
  payload[8] = convertedBatterie << 1 | convertedLuminosite >> 9;
  payload[9] = convertedLuminosite >> 1;
  payload[10] = convertedLuminosite << 7;

  //SerialUSB.print("AT$SF=");
  
  Serial1.print("AT$SF=");
  char strBuffer[3];
  for(int i=0;i<12;i++){
    sprintf(strBuffer, "%02X", payload[i]);
    //SerialUSB.print(StrBuffer);
    Serial1.print(strBuffer);
  }
  
  //SerialUSB.print("\r\n");
  Serial1.print("\r\n");
  rtos::ThisThread::sleep_for(std::chrono::seconds(10));
  Serial1.print("AT$P=2\r\n");
}

void sendPayloadGPS(){
  //Resume Sigfox
  digitalWrite(PIN_SIGFOX_RESET, LOW);  
  rtos::ThisThread::sleep_for(std::chrono::milliseconds(500));
  digitalWrite(PIN_SIGFOX_RESET, HIGH);
  
  led_blink_R(100); 
  while(!Serial1.available()){
    Serial1.write("AT\r\n");
    rtos::ThisThread::sleep_for(std::chrono::milliseconds(500));
  }
  led_stop_R();
  digitalWrite(LEDR, HIGH);

  unsigned int convertedLatitude = std::round((getPrefs()->gpsLatitude+90)*1000);
  //printf("Latitude \t: %u\t%#04x\n", convertedLatitude, convertedLatitude);

  unsigned int convertedLongitude = std::round((getPrefs()->gpsLongitude+180)*1000);
  //printf("Longitude \t: %u\t%#04x\n", convertedLongitude, convertedLongitude);

  unsigned int convertedOrientation = std::round(getPrefs()->gpsHeading);
  //printf("Orientation \t: %u\t%#04x\n\n", convertedOrientation, convertedOrientation);

  unsigned char payload[12];
  for(int i=0;i<12;i++){
    payload[i] = 0;
  }

  payload[0] = convertedLatitude >> 11;
  payload[1] = convertedLatitude >> 3;
  payload[2] = convertedLatitude << 5 | convertedLongitude >> 14;
  payload[3] = convertedLongitude >> 6;
  payload[4] = convertedLongitude << 2 | convertedOrientation >> 7;
  payload[5] = convertedOrientation << 1;

  payload[11] |= 0x1;

  //Sending data
  Serial1.print("AT$SF=");
  char strBuffer[3];
  for(int i=0;i<12;i++){
    sprintf(strBuffer, "%02X", payload[i]);
    //SerialUSB.print(StrBuffer);
    Serial1.print(strBuffer);
  }

  //SerialUSB.print("\r\n");
  Serial1.print("\r\n");
  rtos::ThisThread::sleep_for(std::chrono::seconds(10));
  Serial1.print("AT$P=2\r\n");
}

UBeeZ_Sigfox.h

C Header File
#ifndef UBEEZ_SIGFOX
#define UBEEZ_SIGFOX

#define PIN_SIGFOX_RESET 5

void setupSigfox();
void sendPayloadCapteurs();
void sendPayloadGPS();

#endif

UBeeZ_Temperature_humidity_sensor.cpp

C/C++
#include "UBeeZ_Temperature_humidity_sensor.h"
#include <DHT.h>
#include <MaximWire.h>
#include "rtos.h"

MaximWire::Bus temperature_bus(PIN_TEMPERATURE_BUS);
MaximWire::DS18B20 temperature_device_1(ADDR_TEMPERATURE_1);
MaximWire::DS18B20 temperature_device_2(ADDR_TEMPERATURE_2);

DHT dht22_1(DHT22_1_PIN, DHT11);
DHT dht22_2(DHT22_2_PIN, DHT22);

float temperatures[4] = {NAN, NAN, NAN, NAN};
float humidites[2] = {NAN, NAN};

void setupTemperatureHumidity(){
    dht22_1.begin();
    dht22_2.begin();
}

void updateTemperatureHumidity(){
  temperature_device_1.Update(temperature_bus);
  rtos::ThisThread::sleep_for(std::chrono::milliseconds(100));
  temperatures[1] = temperature_device_1.GetTemperature<float>(temperature_bus);
  rtos::ThisThread::sleep_for(std::chrono::milliseconds(100));
  temperature_device_2.Update(temperature_bus);
  rtos::ThisThread::sleep_for(std::chrono::milliseconds(100));
  temperatures[2] = temperature_device_2.GetTemperature<float>(temperature_bus);
  rtos::ThisThread::sleep_for(std::chrono::milliseconds(100));

  temperatures[0] = dht22_1.readTemperature();
  temperatures[3] = dht22_2.readTemperature();

  humidites[0] = dht22_1.readHumidity();
  humidites[1] = dht22_2.readHumidity();
}

float getTemperatureSensor(int index){  //Retourne temprature en C
  if(index>=0 && index <4){
    return temperatures[index];
  }
  return NAN;
}

float getHumiditeSensor(int index){ //Retourne humidit entre 0 et 100
  if(index>=0 && index <2){
    return humidites[index];
  }
  return NAN;
}

UBeeZ_Temperature_humidity_sensor.h

C Header File
#ifndef UBEEZ_TEMPERATURE_HUMIDITY_SENSOR
#define UBEEZ_TEMPERATURE_HUMIDITY_SENSOR

#define MAXIMWIRE_EXTERNAL_PULLUP

#define ADDR_TEMPERATURE_1 "280C8016A8013C93"
#define ADDR_TEMPERATURE_2 "2876BF56B5013C95"
#define PIN_TEMPERATURE_BUS 10

#define DHT22_1_PIN 9
#define DHT22_2_PIN 8


void setupTemperatureHumidity();
void updateTemperatureHumidity();

float getTemperatureSensor(int index);
float getHumiditeSensor(int index);

#endif

UBeeZ_Battery_weight_light.cpp

C/C++
#include "UBeeZ_Battery_weight_light.h"
#include "HX711.h"
#include <Arduino.h>
#include "rtos.h"

HX711 weightSensor;
double weight = NAN;

void setupBatteryLight(){
  pinMode(PIN_BATTERY, INPUT);
  pinMode(PIN_LIGHT_ENABLE, OUTPUT);
  pinMode(PIN_LIGHT_READ, INPUT);
  digitalWrite(PIN_LIGHT_ENABLE, LOW);
}

void setupWeight(){
  weightSensor.begin(PIN_WEIGHT_DOUT, PIN_WEIGHT_PSCK);
  weightSensor.power_down();
}

float getBatteryPercentage(){
  float batteryPercentage = ((getBatteryVoltage()-2.9) * 100)/1.3;
  if(batteryPercentage > 100){
    return 100;
  }
  else if(batteryPercentage <0){
    return 0;
  }
  return batteryPercentage;
}

float getBatteryVoltage(){
  return ((float)analogRead(PIN_BATTERY) * 6.6)/1023;
}

int getLight(){
  digitalWrite(PIN_LIGHT_ENABLE, HIGH);
  rtos::ThisThread::sleep_for(std::chrono::milliseconds(200));
  int a = analogRead(PIN_LIGHT_READ);
  digitalWrite(PIN_LIGHT_ENABLE, LOW);
  return a;
}

bool updateWeight(){
  weightSensor.power_up();
  if(weightSensor.wait_ready_timeout(1000)){
    weightSensor.set_offset(DEFAULT_WEIGHT_OFFSET);
    weightSensor.set_scale(DEFAULT_WEIGHT_SCALE);
    weight = weightSensor.get_units(10);
    weightSensor.power_down();
    return true;
  }
  else{
    weightSensor.power_down();
    return false;
  }
}

float getWeight(){  //Retourne poid en kg
  return weight;
}

UBeeZ_Battery_weight_light.h

C Header File
#ifndef UBEEZ_BATTERY_WEIGHT_LIGHT
#define UBEEZ_BATTERY_WEIGHT_LIGHT

#define PIN_BATTERY A1
#define PIN_LIGHT_ENABLE 4
#define PIN_LIGHT_READ A6

#define PIN_WEIGHT_DOUT 6
#define PIN_WEIGHT_PSCK 7

#define DEFAULT_WEIGHT_OFFSET 107975
#define DEFAULT_WEIGHT_SCALE 29763

void setupBatteryLight();
void setupWeight();
float getBatteryPercentage();
float getBatteryVoltage();
float getWeight();
bool updateWeight();
int getLight();

#endif

UBeeZ_Bluetooth.cpp

C/C++
#include "UBeeZ_Bluetooth.h"
#include <mbed.h>
#include "UBeeZ_LED.h"
#include "UBeeZ_Battery_weight_light.h"
#include "UBeeZ_Temperature_humidity_sensor.h"
#include "UBeeZ_Prefs.h"

int taskBLEPoll = 0;
int taskBLEDisableUnused = 0;
int taskBLEDisableConnected = 0;
int taskBLEUpdateValues = 0;
bool stopBLEPollThread = false;
bool stopBLEUpdateValuesThread = false;
bool BLEEnabling = false;

events::EventQueue *BLEEventQueue;
rtos::Thread BLEEventThread;

BLEService BLEServiceUBeeZ("181A"); // create service
BLEService BLEServiceUBeeZConfig("180A"); // create service

//Bluetooth temperature
BLEShortCharacteristic BLECharacteristicTemperature[] = {BLEShortCharacteristic("00002A6E-0000-1000-8000-00805F9B34FB", BLERead | BLENotify),
BLEShortCharacteristic("00002A6E-0000-1000-8000-00805F9B34FB", BLERead | BLENotify), BLEShortCharacteristic("00002A6E-0000-1000-8000-00805F9B34FB", BLERead | BLENotify),
BLEShortCharacteristic("00002A6E-0000-1000-8000-00805F9B34FB", BLERead | BLENotify)};
uint8_t descriptorTemperature[4][7] = {{0x0E, (uint8_t)(int8_t)-2, 0x2F, 0x27, 0x01, 0x01, 0x00}, {0x0E, (uint8_t)(int8_t)-2, 0x2F, 0x27, 0x01, 0x02, 0x00},
{0x0E, (uint8_t)(int8_t)-2, 0x2F, 0x27, 0x01, 0x03, 0x00}, {0x0E, (uint8_t)(int8_t)-2, 0x2F, 0x27, 0x01, 0x04, 0x00}};
BLEDescriptor BLEDescriptorTemperature[4] = {BLEDescriptor("2904", descriptorTemperature[0], 7), BLEDescriptor("2904", descriptorTemperature[1], 7),
BLEDescriptor("2904", descriptorTemperature[2], 7), BLEDescriptor("2904", descriptorTemperature[3], 7)};

//Bluetooth humidite
BLEUnsignedShortCharacteristic BLECharacteristicHumidite[] = {BLEUnsignedShortCharacteristic("00002A6F-0000-1000-8000-00805F9B34FB", BLERead | BLENotify),
BLEUnsignedShortCharacteristic("00002A6F-0000-1000-8000-00805F9B34FB", BLERead | BLENotify)};
uint8_t descriptorHumidite[2][7] = {{0x06, (uint8_t)(int8_t)-2, 0xAD, 0x27, 0x01, 0x0B, 0x01}, {0x06, (uint8_t)(int8_t)-2, 0xAD, 0x27, 0x01, 0x0C, 0x01}};
BLEDescriptor BLEDescriptorHumidite[2] = {BLEDescriptor("2904", descriptorHumidite[0], 7), BLEDescriptor("2904", descriptorHumidite[1], 7)};

//Bluetooth poid
BLEUnsignedShortCharacteristic BLECharacteristicPoid("00002A98-0000-1000-8000-00805F9B34FB", BLERead | BLENotify);
uint8_t descriptorPoid[7] = {0x06, (uint8_t)(int8_t)-2, 0x02, 0x27, 0x01, 0x00, 0x00};
BLEDescriptor BLEDescriptorPoid("2904", descriptorPoid, 7);

//Bluetooth batterie
BLEByteCharacteristic BLECharacteristicBatterie("00002A19-0000-1000-8000-00805F9B34FB", BLERead | BLENotify);
uint8_t descriptorBatterie[7] = {0x04, 0x00, 0xAD, 0x27, 0x01, 0x00, 0x00};
BLEDescriptor BLEDescriptorBatterie("2904", descriptorBatterie, 7);

//Bluetooth luminosite
BLECharacteristic BLECharacteristicLuminosite("00002AFB-0000-1000-8000-00805F9B34FB" , BLERead | BLENotify, 3);
uint8_t descriptorLuminosite[7] = {0x7, (uint8_t)(int8_t)-2, 0x31, 0x27, 0x01, 0x0C, 0x01};
BLEDescriptor BLEDescriptorLuminosite("2904", descriptorLuminosite, 7);

//Bluetooth syncDelay
BLEUnsignedShortCharacteristic BLECharacteristicSyncDelay("00002A21-0000-1000-8000-00805F9B34FB" , BLERead | BLEWrite);
uint8_t descriptorSyncDelay[7] = {0x6, 0x00, 0x03, 0x27, 0x1, 0x00, 0x00};
BLEDescriptor BLEDescriptorSyncDelay("2904", descriptorSyncDelay, 7);

//Bluetooth localisation
BLECharacteristic BLECharacteristicLocalisation("00002A67-0000-1000-8000-00805F9B34FB", BLERead | BLEWrite, 12);
uint8_t descriptorLocalisation[4][7] = {{0x1B, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00}, {0x10, (uint8_t)(int8_t)-7, 0x63, 0x27, 0x01, 0x00, 0x00},
{0x10, (uint8_t)(int8_t)-7, 0x63, 0x27, 0x01, 0x00, 0x00}, {0x06, (uint8_t)(int8_t)-2, 0x63, 0x27, 0x01, 0x00, 0x00}};
BLEDescriptor BLEDescriptorLocalisation[4] = {BLEDescriptor("2904", descriptorLocalisation[0], 7), BLEDescriptor("2904", descriptorLocalisation[1], 7),
BLEDescriptor("2904", descriptorLocalisation[2], 7), BLEDescriptor("2904", descriptorLocalisation[3], 7)};

void blePeripheralConnectHandler(BLEDevice central) {
  if(BLE_DEBUG){
    SerialUSB.println("blePeripheralConnectHandler");
  }
  led_stop_B();
  digitalWrite(LEDB, LOW);
  if(taskBLEDisableUnused !=0){
    BLEEventQueue->cancel(taskBLEDisableUnused);
    taskBLEDisableUnused = 0;
  }
  if(taskBLEDisableConnected == 0 && !BLE_ALWAYS_ACTIVE){
    taskBLEDisableConnected = BLEEventQueue->call_in(BLE_CONNECTED_TIMEOUT, (void(*)(void))disableBLE);
  }
  BLE.stopAdvertise();
}

void blePeripheralDisconnectHandler(BLEDevice central) {
  if(BLE_DEBUG){
    SerialUSB.println("blePeripheralDisconnectHandler");
  }
  digitalWrite(LEDB, HIGH);
  led_blink_B(1000);
  if(taskBLEDisableConnected !=0){
    BLEEventQueue->cancel(taskBLEDisableConnected);
    taskBLEDisableConnected = 0;
  }
  if(taskBLEDisableUnused == 0 && !BLE_ALWAYS_ACTIVE){
    taskBLEDisableUnused = BLEEventQueue->call_in(BLE_UNUSED_TIMEOUT, (void(*)(void))disableBLE);
  }
  BLE.advertise();
}

void setupBLE(){
  if(BLE_DEBUG){
    SerialUSB.println("setupBLE");
  }
  pinMode(BLE_BUTTON_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(BLE_BUTTON_PIN), startBLE, FALLING);
  
  for(int i=0;i<4;i++){
    BLECharacteristicTemperature[i].addDescriptor(BLEDescriptorTemperature[i]);
    BLEServiceUBeeZ.addCharacteristic(BLECharacteristicTemperature[i]);
  }
  for(int i=0;i<2;i++){
    BLECharacteristicHumidite[i].addDescriptor(BLEDescriptorHumidite[i]);
    BLEServiceUBeeZ.addCharacteristic(BLECharacteristicHumidite[i]);
  }
  BLECharacteristicPoid.addDescriptor(BLEDescriptorPoid);
  BLEServiceUBeeZ.addCharacteristic(BLECharacteristicPoid);

  BLECharacteristicBatterie.addDescriptor(BLEDescriptorBatterie);
  BLEServiceUBeeZ.addCharacteristic(BLECharacteristicBatterie);

  BLECharacteristicLuminosite.addDescriptor(BLEDescriptorLuminosite);
  BLEServiceUBeeZ.addCharacteristic(BLECharacteristicLuminosite);

  BLECharacteristicSyncDelay.addDescriptor(BLEDescriptorSyncDelay);
  BLECharacteristicSyncDelay.setEventHandler(BLEWritten, syncDelayCharacteristicWritten);
  BLEServiceUBeeZConfig.addCharacteristic(BLECharacteristicSyncDelay);

  for(int i=0;i<4;i++){
    BLECharacteristicLocalisation.addDescriptor(BLEDescriptorLocalisation[i]);
  }
  BLECharacteristicLocalisation.setEventHandler(BLEWritten, localisationCharacteristicWritten);
  BLEServiceUBeeZConfig.addCharacteristic(BLECharacteristicLocalisation);

  BLEEventQueue = new events::EventQueue(5 * EVENTS_EVENT_SIZE);
  BLEEventThread.start(callback(BLEEventQueue, &events::EventQueue::dispatch_forever));
}

void enableBLE(){
  if(taskBLEPoll!=0){
    BLE.disconnect();
    return;
  }
  if(BLE_DEBUG){
    SerialUSB.println("enableBLE");
  }
  taskBLEPoll = BLEEventQueue->call_in(BLE_POLL_INTERVAL, (void(*)(void))pollBLE);
  taskBLEUpdateValues = BLEEventQueue->call_in(BLE_UPDATE_VALUES_INTERVAL, (void(*)(void))updateBLEValues);
  if(!BLE_ALWAYS_ACTIVE){
    taskBLEDisableUnused = BLEEventQueue->call_in(BLE_UNUSED_TIMEOUT, (void(*)(void))disableBLE);
  }
  
  if(!BLE.begin()){
    int delay_ms = 500;
    led_blink_B(delay_ms);
    led_blink_R(delay_ms);
    disableBLE();
    return;
  }
  else{
    led_blink_B(1000);
    led_stop_R();
  }

  BLE.setLocalName("UBeeZ");
  BLE.setDeviceName("UBeeZ");
  BLE.setAppearance(0x0552);

  BLE.setAdvertisedService(BLEServiceUBeeZ);
  BLE.addService(BLEServiceUBeeZ);
  BLE.addService(BLEServiceUBeeZConfig);

  BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
  BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
  BLE.advertise();
  if(BLE_DEBUG){
    SerialUSB.println("enabled BLE");
  }
}

void disableBLE(){
  if(BLE_DEBUG){
    SerialUSB.println("disableBLE");
  }
  led_stop_B();
  led_stop_R();
  digitalWrite(LEDB, HIGH);
  digitalWrite(LEDR, HIGH);
  if(taskBLEPoll!=0){
    stopBLEPollThread = true;
  }
  if(taskBLEUpdateValues!=0){
    stopBLEUpdateValuesThread = true;
  }
  
  if(taskBLEDisableUnused !=0){
    BLEEventQueue->cancel(taskBLEDisableUnused);
    taskBLEDisableUnused = 0;
  }
  if(taskBLEDisableConnected !=0){
    BLEEventQueue->cancel(taskBLEDisableConnected);
    taskBLEDisableConnected = 0;
  }
  BLE.end();
}

void pollBLE(){
  /*if(BLE_DEBUG){
    SerialUSB.println("pollBLE");
  }*/
  if(stopBLEPollThread){
    taskBLEPoll = 0;
    stopBLEPollThread = false;
  }
  else{
    BLE.poll(200);
    taskBLEPoll = BLEEventQueue->call_in(BLE_POLL_INTERVAL, (void(*)(void))pollBLE);
  }
}

void startBLE(){
  BLEEventQueue->call((void(*)(void))enableBLE);
}

void updateBLEValues(){
  if(BLE_DEBUG){
    SerialUSB.println("updateBLEValues");
  }
  if(stopBLEUpdateValuesThread){
    taskBLEUpdateValues = 0;
    stopBLEUpdateValuesThread = false;
    return;
  }
  updateTemperatureHumidity();
  for(int i=0;i<4;i++){
    float temperature = getTemperatureSensor(i);
    if(isnan(temperature)){
      BLECharacteristicTemperature[i].writeValue(82.3*100);//Temperature reading error
    }
    else{
      BLECharacteristicTemperature[i].writeValue(temperature*100);
    }
  }

  for(int i=0;i<2;i++){
    float humidite = getHumiditeSensor(i);
    if(isnan(humidite)){
      BLECharacteristicHumidite[i].writeValue(105*100);//Humidite reading error
    }
    else{
      BLECharacteristicHumidite[i].writeValue(humidite*100);
    }
  }

  if(updateWeight()){
    BLECharacteristicPoid.writeValue(getWeight()*200);
  }
  else{
    BLECharacteristicPoid.writeValue(102.3*200);
  }
  
  BLECharacteristicBatterie.writeValue(getBatteryPercentage());

  float light = getLight();
  
  if(light<=0){//Erreur dconnect
    light=1023;
  }
  else if(light>1022){
    light=1022;
  }
  light = 11.019*exp(0.0069*light);
  
  BLECharacteristicLuminosite.writeValue((uint32_t)(light*100));

  BLECharacteristicSyncDelay.writeValue(getPrefs()->sigfoxSyncDelay);

  uint8_t localisation[12];
  for(int i=0;i<12;i++){
    localisation[i] = 0;
  }
  localisation[0] = 0x14;
  localisation[1] = 0x10;
  int latitude = 123.4567899*10000000;//getPrefs()->gpsLatitude;
  int longitude = -123.4567899*10000000;//getPrefs()->gpsLongitude;
  unsigned short heading = 69.69*100;//getPrefs()->gpsHeading;
  localisation[2] = latitude;
  localisation[3] = latitude >> 8;
  localisation[4] = latitude >> 16;
  localisation[5] = latitude >> 24;
  localisation[6] = longitude;
  localisation[7] = longitude >> 8;
  localisation[8] = longitude >> 16;
  localisation[9] = longitude >> 24;
  localisation[10] = heading;
  localisation[11] = heading >> 8;
  

  BLECharacteristicLocalisation.writeValue(localisation, 12, true);

  taskBLEUpdateValues = BLEEventQueue->call_in(BLE_UPDATE_VALUES_INTERVAL, (void(*)(void))updateBLEValues);
}

void syncDelayCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) {
  unsigned short newDelay = (characteristic.value()[1] << 8 | characteristic.value()[0]);
  if(BLE_DEBUG){
    SerialUSB.print("syncDelayCharacteristicWritten new value : ");
    SerialUSB.println(newDelay);
  }
  
  if(newDelay >= 600 || DEBUG_PREFS){
    getPrefs()->sigfoxSyncDelay = newDelay;
    writePrefs();
  }
  else{
    BLECharacteristicSyncDelay.writeValue(getPrefs()->sigfoxSyncDelay);
  }
}

void localisationCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) {
  double newLatitude = ((double)(characteristic.value()[2] | characteristic.value()[3] << 8 | characteristic.value()[4] << 16 | characteristic.value()[5] << 24)) / 10000000;
  double newLongitude = ((double)(characteristic.value()[6] | characteristic.value()[7] << 8 | characteristic.value()[8] << 16 | characteristic.value()[9] << 24)) / 10000000;
  float newHeading = ((float)(characteristic.value()[10] | characteristic.value()[11] << 8)) / 100;
  
  if(BLE_DEBUG){
    SerialUSB.println("localisationCharacteristicWritten new value : ");
    SerialUSB.print("Latitude : ");
    SerialUSB.println(newLatitude, 7);
    SerialUSB.print("Longitude : ");
    SerialUSB.println(newLongitude, 7);
    SerialUSB.print("Heading : ");
    SerialUSB.println(newHeading, 2);
  }  
}

UBeeZ_Bluetooth.h

C Header File
#ifndef UBEEZ_BLUETOOTH
#define UBEEZ_BLUETOOTH

#include <ArduinoBLE.h>

#define BLE_UNUSED_TIMEOUT std::chrono::seconds(30)
#define BLE_CONNECTED_TIMEOUT std::chrono::seconds(60*15)
#define BLE_POLL_INTERVAL std::chrono::milliseconds(10)
#define BLE_UPDATE_VALUES_INTERVAL std::chrono::seconds(1)
#define BLE_BUTTON_PIN 2  //Digital pin 2 (D2)
#define BLE_ALWAYS_ACTIVE false
#define BLE_DEBUG false

void blePeripheralConnectHandler(BLEDevice central);
void blePeripheralDisconnectHandler(BLEDevice central);
void setupBLE();
void enableBLE();
void disableBLE();
void pollBLE();
void startBLE();
void updateBLEValues();
void syncDelayCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic);
void localisationCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic);

#endif

UBeeZ_GPS.cpp

C/C++
#include "UBeeZ_GPS.h"
#include "UBeeZ_LED.h"
#include "UBeeZ_Prefs.h"
#include <Serial.h>
#include "mbed.h"
#include "rtos.h"
#include <TinyGPS++.h>
#include <chrono>

using namespace rtos;

TinyGPSPlus gps;

std::chrono::time_point<std::chrono::system_clock> end_time;

bool DISABLE_GPS = false;
bool gpsEnabled = false;

void setupGPS(){
  if(GPS_DEBUG){
    SerialUSB.println("setupGPS");
  }
  
  Serial2.begin(9600);
  
  pinMode(PIN_TPL_DELAY, OUTPUT);
  pinMode(PIN_TPL_DONE, OUTPUT);

  digitalWrite(PIN_TPL_DELAY, LOW);
  digitalWrite(PIN_TPL_DONE, LOW);
  rtos::ThisThread::sleep_for(std::chrono::milliseconds(500));
  digitalWrite(PIN_TPL_DELAY, HIGH);
  rtos::ThisThread::sleep_for(std::chrono::milliseconds(500));
  digitalWrite(PIN_TPL_DELAY, LOW);

  led_blink_G(100); 
  rtos::ThisThread::sleep_for(std::chrono::milliseconds(200));
  std::chrono::time_point<std::chrono::system_clock> time_out= std::chrono::system_clock::now() + std::chrono::seconds(5);
  while(!Serial2.available() && std::chrono::system_clock::now() < time_out){
    rtos::ThisThread::sleep_for(std::chrono::milliseconds(500));
  }

  gpsEnabled = std::chrono::system_clock::now() < time_out;
  
  led_stop_G();
  digitalWrite(LEDG, HIGH);
  digitalWrite(PIN_TPL_DONE, HIGH);
  rtos::ThisThread::sleep_for(std::chrono::seconds(1));
  digitalWrite(PIN_TPL_DONE, LOW);
  if(GPS_DEBUG){
    if(gpsEnabled){
      Serial.println("GPS setup completed !");
    }
    else{
      Serial.println("GPS setup failed !");
    }
  }
}

bool get_GPS_location(){
  if(GPS_DEBUG){
    SerialUSB.println("get_GPS_location start");
  }
  if(!isGPSEnabled()){
    if(GPS_DEBUG){
      SerialUSB.println("get_GPS_location failed (gps not enabled)");
    }
    return false;
  }
  digitalWrite(PIN_TPL_DELAY, HIGH);
  rtos::ThisThread::sleep_for(std::chrono::milliseconds(500));
  digitalWrite(PIN_TPL_DELAY, LOW);
  
  end_time = std::chrono::system_clock::now() + GPS_UPTIME;

  bool stop_GPS = false;
  bool got_GPS_signal = false;
  while(!stop_GPS){
    while(Serial2.available()){
      gps.encode(Serial2.read());
    }
    if(GPS_DEBUG){
      if(gps.satellitesAll.isUpdated()){
        SerialUSB.print("Sat all : ");
        SerialUSB.println(gps.satellitesAll.value());
      }
    }
    
    if(gps.location.isUpdated()){
      double newLat = gps.location.lat();
      double newLng = gps.location.lng();
      if(GPS_DEBUG){
        SerialUSB.println("GOT LOCATION !");
        SerialUSB.print("Lat : ");
        SerialUSB.println(newLat, 6);
        SerialUSB.print("Long : ");
        SerialUSB.println(newLng, 6);
        SerialUSB.print("Sat : ");
        SerialUSB.println(gps.satellites.value());
      }
      getPrefs()->gpsLatitude = newLat;
      getPrefs()->gpsLongitude = newLng;
      writePrefs();
      stop_GPS = true;
      got_GPS_signal = true;
    }
    else if((gps.satellitesAll.value() <= 0 && std::chrono::system_clock::now() >= end_time)
              || (gps.satellitesAll.value() > 0 && std::chrono::system_clock::now() >= end_time+GPS_ADDITIONNAL_UPTIME)){
      if(GPS_DEBUG){
        SerialUSB.println("GPS timeout (no fix) !");
      }
      stop_GPS = true;
    }
    rtos::ThisThread::sleep_for(GPS_TICK);
  }

  
  if(GPS_DEBUG){
    SerialUSB.println("stoping GPS");
  }
  digitalWrite(PIN_TPL_DONE, HIGH);
  rtos::ThisThread::sleep_for(std::chrono::milliseconds(500));
  digitalWrite(PIN_TPL_DONE, LOW);
  if(GPS_DEBUG){
    SerialUSB.println("get_GPS_location done");
  }

  return got_GPS_signal;
}

bool isGPSEnabled(){
  return gpsEnabled;
}

UBeeZ_GPS.h

C Header File
#ifndef UBEEZ_GPS
#define UBEEZ_GPS

#define PIN_TPL_DELAY 3
#define PIN_TPL_DONE 13

#define GPS_DEBUG true
#define GPS_UPTIME std::chrono::minutes(1)
#define GPS_ADDITIONNAL_UPTIME std::chrono::minutes(2)
#define GPS_TICK std::chrono::seconds(1)

void setupGPS();
bool get_GPS_location();

bool isGPSEnabled();

#endif

UBeeZ_LED.cpp

C/C++
#include "UBeeZ_LED.h"

using namespace rtos;

events::EventQueue *ledEventQueue;
Thread ledEventThread;

bool stop_led_R_flag = false;
bool stop_led_G_flag = false;
bool stop_led_B_flag = false;

int led_R_task = 0;
int led_G_task = 0;
int led_B_task = 0;

byte led_R_state = HIGH;
byte led_G_state = HIGH;
byte led_B_state = HIGH;

std::chrono::milliseconds led_R_delay_ms(1000);
std::chrono::milliseconds led_G_delay_ms(1000);
std::chrono::milliseconds led_B_delay_ms(1000);

void setupLedRGB(){
  pinMode(LEDB, OUTPUT);
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_PWR, OUTPUT);
  
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDG, HIGH);
  digitalWrite(LEDB, HIGH);
  digitalWrite(LED_BUILTIN, LOW);
  digitalWrite(LED_PWR, LOW);
  ledEventQueue = new events::EventQueue(3 * EVENTS_EVENT_SIZE);
  ledEventThread.start(callback(ledEventQueue, &events::EventQueue::dispatch_forever));
}

void led_blink_R(int delay_ms){
  led_R_delay_ms = std::chrono::milliseconds(delay_ms);
  stop_led_R_flag = false;
  if(led_R_task == 0){
    led_R_task = ledEventQueue->call_in(led_R_delay_ms, (void(*)(void))handler_led_blink_R);
  }
}

void led_blink_G(int delay_ms){
  led_G_delay_ms = std::chrono::milliseconds(delay_ms);
  stop_led_G_flag = false;
  if(led_G_task == 0){
    led_G_task = ledEventQueue->call_in(led_G_delay_ms, (void(*)(void))handler_led_blink_G);
  }
}

void led_blink_B(int delay_ms){
  led_B_delay_ms = std::chrono::milliseconds(delay_ms);
  stop_led_B_flag = false;
  if(led_B_task == 0){
    led_B_task = ledEventQueue->call_in(led_B_delay_ms, (void(*)(void))handler_led_blink_B);
  }
}

void led_stop_R(){
  stop_led_R_flag = true;
}

void led_stop_G(){
  stop_led_G_flag = true;
}

void led_stop_B(){
  stop_led_B_flag = true;
}

void handler_led_blink_R(){
  if(!stop_led_R_flag){
    led_R_task = ledEventQueue->call_in(led_R_delay_ms, (void(*)(void))handler_led_blink_R);
    led_R_state = !led_R_state;
    digitalWrite(LEDR, led_R_state);
  }
  else{
    stop_led_R_flag = false;
    led_R_task = 0;
  }
}

void handler_led_blink_G(){
  if(!stop_led_G_flag){
    led_G_task = ledEventQueue->call_in(led_G_delay_ms, (void(*)(void))handler_led_blink_G);
    led_G_state = !led_G_state;
    digitalWrite(LEDG, led_G_state);
  }
  else{
    stop_led_G_flag = false;
    led_G_task = 0;
  }
}

void handler_led_blink_B(){
  if(!stop_led_B_flag){
    led_B_task = ledEventQueue->call_in(led_B_delay_ms, (void(*)(void))handler_led_blink_B);
    led_B_state = !led_B_state;
    digitalWrite(LEDB, led_B_state);
  }
  else{
    stop_led_B_flag = false;
    led_B_task = 0;
  }
}

UBeeZ_LED.h

C Header File
#ifndef UBEEZ_LED
#define UBEEZ_LED

#include <mbed.h>
#include <rtos.h>

void setupLedRGB();

void led_blink_R(int delay_ms);
void led_blink_G(int delay_ms);
void led_blink_B(int delay_ms);

void led_stop_R();
void led_stop_G();
void led_stop_B();

void handler_led_blink_R();
void handler_led_blink_G();
void handler_led_blink_B();

#endif

UBeeZ code base

You can find every bit of code we used in this Github repository

Credits

Clément Chamayou

Clément Chamayou

1 project • 4 followers
Jade EVRARD

Jade EVRARD

1 project • 4 followers
Margaux Launois

Margaux Launois

1 project • 4 followers
Jeremy Royer

Jeremy Royer

1 project • 1 follower
Nicolas Stein

Nicolas Stein

2 projects • 6 followers

Comments