Undo merge

This commit is contained in:
Oliver Brandmueller 2019-01-19 19:54:54 +01:00
parent d9404b1a32
commit 7bdff00161
60 changed files with 1911 additions and 1679 deletions

View File

@ -7,6 +7,7 @@
<img src="img/Paxcounter-ttgo.jpg">
<img src="img/Paxcounter-lolin.gif">
<img src="img/Paxcounter-Screen.png">
<img src="img/paxcounter-curves.jpg">
# Use case
@ -53,9 +54,11 @@ Target platform must be selected in [platformio.ini](https://github.com/cyberman
Hardware dependent settings (pinout etc.) are stored in board files in /hal directory. If you want to use a ESP32 board which is not yet supported, use hal file generic.h and tailor pin mappings to your needs. Pull requests for new boards welcome.<br>
<b>3D printable cases</b> can be found (and, if wanted so, ordered) on Thingiverse, see
<A HREF="https://www.thingiverse.com/thing:2670713">Heltec</A>, <A HREF="https://www.thingiverse.com/thing:2811127">TTGOv2</A>, <A HREF="https://www.thingiverse.com/thing:3005574">TTGOv2.1</A>, <A HREF="https://www.thingiverse.com/thing:3041339">T-BEAM</A> for example.<br>
<A HREF="https://www.thingiverse.com/thing:2670713">Heltec</A>, <A HREF="https://www.thingiverse.com/thing:2811127">TTGOv2</A>, <A HREF="https://www.thingiverse.com/thing:3005574">TTGOv2.1</A>, <A HREF="https://www.thingiverse.com/thing:3041339">T-BEAM</A>,
<A HREF="https://www.thingiverse.com/thing:3203177">T-BEAM parts</A> for example.<br>
<b>Power consumption</b> was metered at around 450 - 1000mW, depending on board and user settings in paxcounter.conf. If you are limited on battery, you may want to save around 30% power by disabling bluetooth (commenting out line *#define BLECOUNTER* in paxcounter.conf).
<b>Power consumption</b> was metered at around 450 - 1000mW, depending on board and user settings in paxcounter.conf.
By default bluetooth sniffing is disabled (line *#define BLECOUNTER* in paxcounter.conf is commented out). Enabling bluetooth costs 30% more power + 30% flash storage for the software stack. Proof of concept showed that for passenger flow metering wifi sniffing shows better results than bluetooth sniffing. If you enable bluetooth be aware that this goes on expense of wifi sniffing results, because then wifi and bt stack must share the 2,4 GHz RF ressources of ESP32. If you need to sniff wifi and bt in parallel and need best possible results, use two boards - one for wifi only and one for bt only - and add counted results.
# Preparing
@ -150,7 +153,8 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.
**Port #1:** Paxcount data
byte 1-2: Number of unique pax, first seen on Wifi
byte 3-4: Number of unique pax, first seen on Bluetooth [0 if BT disabled]
byte 3-4: Number of unique pax, first seen on Bluetooth [omited if BT disabled]
bytes 5-17: GPS data, if present, in same format as for Port #4
**Port #2:** Device status query result

BIN
img/paxcounter-curves.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -3,29 +3,54 @@
#include "globals.h"
#include <Wire.h>
#include "bsec_integration.h"
extern const uint8_t bsec_config_iaq[454];
#include "../lib/Bosch-BSEC/src/bsec.h"
extern bmeStatus_t
bme_status; // Make struct for storing gps data globally available
extern TaskHandle_t BmeTask;
// --- Bosch BSEC library configuration ---
// 3,3V supply voltage; 3s max time between sensor_control calls; 4 days
// calibration. Change this const if not applicable for your application (see
// BME680 datasheet)
const uint8_t bsec_config_iaq[454] = {
1, 7, 4, 1, 61, 0, 0, 0, 0, 0, 0, 0, 174, 1, 0,
0, 48, 0, 1, 0, 137, 65, 0, 63, 205, 204, 204, 62, 0, 0,
64, 63, 205, 204, 204, 62, 0, 0, 225, 68, 0, 192, 168, 71, 64,
49, 119, 76, 0, 0, 0, 0, 0, 80, 5, 95, 0, 0, 0, 0,
0, 0, 0, 0, 28, 0, 2, 0, 0, 244, 1, 225, 0, 25, 0,
0, 128, 64, 0, 0, 32, 65, 144, 1, 0, 0, 112, 65, 0, 0,
0, 63, 16, 0, 3, 0, 10, 215, 163, 60, 10, 215, 35, 59, 10,
215, 35, 59, 9, 0, 5, 0, 0, 0, 0, 0, 1, 88, 0, 9,
0, 229, 208, 34, 62, 0, 0, 0, 0, 0, 0, 0, 0, 218, 27,
156, 62, 225, 11, 67, 64, 0, 0, 160, 64, 0, 0, 0, 0, 0,
0, 0, 0, 94, 75, 72, 189, 93, 254, 159, 64, 66, 62, 160, 191,
0, 0, 0, 0, 0, 0, 0, 0, 33, 31, 180, 190, 138, 176, 97,
64, 65, 241, 99, 190, 0, 0, 0, 0, 0, 0, 0, 0, 167, 121,
71, 61, 165, 189, 41, 192, 184, 30, 189, 64, 12, 0, 10, 0, 0,
0, 0, 0, 0, 0, 0, 0, 229, 0, 254, 0, 2, 1, 5, 48,
117, 100, 0, 44, 1, 112, 23, 151, 7, 132, 3, 197, 0, 92, 4,
144, 1, 64, 1, 64, 1, 144, 1, 48, 117, 48, 117, 48, 117, 48,
117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117, 48, 117, 100, 0,
100, 0, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 100, 0, 48,
117, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117,
100, 0, 100, 0, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44,
1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1,
44, 1, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8,
7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7,
112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112,
23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 255, 255,
255, 255, 255, 255, 255, 255, 220, 5, 220, 5, 220, 5, 255, 255, 255,
255, 255, 255, 220, 5, 220, 5, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 44, 1, 0, 0, 0, 0,
239, 79, 0, 0};
// Helper functions declarations
int bme_init();
void bme_loop(void *pvParameters);
int8_t i2c_read(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data,
uint16_t len);
int8_t i2c_write(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data,
uint16_t len);
void output_ready(int64_t timestamp, float iaq, uint8_t iaq_accuracy,
float temperature, float humidity, float pressure,
float raw_temperature, float raw_humidity, float gas,
bsec_library_return_t bsec_status, float static_iaq,
float co2_equivalent, float breath_voc_equivalent);
uint32_t state_load(uint8_t *state_buffer, uint32_t n_buffer);
void state_save(const uint8_t *state_buffer, uint32_t length);
uint32_t config_load(uint8_t *config_buffer, uint32_t n_buffer);
void user_delay_ms(uint32_t period);
int64_t get_timestamp_us();
int checkIaqSensorStatus(void);
void loadState(void);
void updateState(void);
#endif

View File

@ -0,0 +1,77 @@
// Copyright 2018-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef __COEXIST_INTERNAL_H__
#define __COEXIST_INTERNAL_H__
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
COEX_PREFER_WIFI = 0,
COEX_PREFER_BT,
COEX_PREFER_BALANCE,
COEX_PREFER_NUM,
} coex_prefer_t;
/**
* @brief Init software coexist
* extern function for internal use.
*
* @return Init ok or failed.
*/
esp_err_t coex_init(void);
/**
* @brief De-init software coexist
* extern function for internal use.
*/
void coex_deinit(void);
/**
* @brief Pause software coexist
* extern function for internal use.
*/
void coex_pause(void);
/**
* @brief Resume software coexist
* extern function for internal use.
*/
void coex_resume(void);
/**
* @brief Get software coexist version string
* extern function for internal use.
* @return : version string
*/
const char *coex_version_get(void);
/**
* @brief Coexist performance preference set from libbt.a
* extern function for internal use.
*
* @param prefer : the prefer enumeration value
* @return : ESP_OK - success, other - failed
*/
esp_err_t coex_preference_set(coex_prefer_t prefer);
#ifdef __cplusplus
}
#endif
#endif /* __COEXIST_INTERNAL_H__ */

View File

@ -7,10 +7,15 @@
#include "spislave.h"
#include <lmic.h>
#ifdef HAS_BME
#include "bme680mems.h"
#endif
void doHousekeeping(void);
void do_timesync(void);
uint64_t uptime(void);
void reset_counters(void);
int redirect_log(const char *fmt, va_list args);
uint32_t getFreeRAM();
#endif

View File

@ -2,6 +2,7 @@
#define _DISPLAY_H
#include <U8x8lib.h>
#include "cyclic.h"
extern uint8_t volatile DisplayState;
extern HAS_DISPLAY u8x8;

View File

@ -8,6 +8,13 @@
#include <set>
#include <array>
#include <algorithm>
#include "mallocator.h"
//#include "inc/bsec_datatypes.h"
#include "../lib/Bosch-BSEC/src/inc/bsec_datatypes.h"
// sniffing types
#define MAC_SNIFF_WIFI 0
#define MAC_SNIFF_BLE 1
// bits in payloadmask for filtering payload data
#define GPS_DATA (0x01)
@ -49,6 +56,7 @@ typedef struct {
uint8_t runmode; // 0=normal, 1=update
uint8_t payloadmask; // bitswitches for payload data
char version[10]; // Firmware version
uint8_t bsecstate[BSEC_MAX_STATE_BLOB_SIZE + 1]; // BSEC state for BME680 sensor
} configData_t;
// Struct holding payload for data send queue
@ -83,25 +91,24 @@ extern char display_line6[], display_line7[]; // screen buffers
extern uint8_t volatile channel; // wifi channel rotation counter
extern uint16_t volatile macs_total, macs_wifi, macs_ble,
batt_voltage; // display values
extern std::set<uint16_t> macs; // temp storage for MACs
extern hw_timer_t *channelSwitch, *sendCycle;
extern hw_timer_t *channelSwitch, *sendCycle, *displaytimer;
extern SemaphoreHandle_t I2Caccess;
extern std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs;
extern std::array<uint64_t, 0xff>::iterator it;
extern std::array<uint64_t, 0xff> beacons;
extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
// application includes
#include "led.h"
#include "payload.h"
#include "blescan.h"
#ifdef HAS_GPS
#include "gpsread.h"
#endif
#ifdef HAS_BME
#include "bme680mems.h"
#endif
#ifdef HAS_LORA
#include "lorawan.h"
#endif
@ -114,10 +121,6 @@ extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
#include "button.h"
#endif
#ifdef BLECOUNTER
#include "blescan.h"
#endif
#ifdef HAS_BATTERY_PROBE
#include "battery.h"
#endif
@ -130,4 +133,8 @@ extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
#include "sensor.h"
#endif
#ifdef HAS_BME
#include "bme680mems.h"
#endif
#endif

View File

@ -6,8 +6,8 @@
// Hash function for scrambling MAC addresses
#include "hash.h"
#include "senddata.h"
#include "cyclic.h"
#define MAC_SNIFF_WIFI 0
#define MAC_SNIFF_BLE 1

View File

@ -4,6 +4,7 @@
#include <esp_spi_flash.h> // needed for reading ESP32 chip attributes
#include <esp_event_loop.h> // needed for Wifi event handler
#include <esp32-hal-timer.h> // needed for timers
#include <esp_coexist.h> // needed for showing coex sw version
#include "globals.h"
#include "wifiscan.h"

54
include/mallocator.h Normal file
View File

@ -0,0 +1,54 @@
/*
This Mallocator code was taken from:
CppCon2014 Presentations
STL Features And Implementation Techniques
Stephan T. Lavavej
https://github.com/CppCon/CppCon2014
*/
#ifndef _MALLOCATOR_H
#define _MALLOCATOR_H
#include <stdlib.h> // size_t, malloc, free
#include <new> // bad_alloc, bad_array_new_length
#include "esp32-hal-psram.h" // ps_malloc
template <class T> struct Mallocator {
typedef T value_type;
Mallocator() noexcept {} // default ctor not required
template <class U> Mallocator(const Mallocator<U> &) noexcept {}
template <class U> bool operator==(const Mallocator<U> &) const noexcept {
return true;
}
template <class U> bool operator!=(const Mallocator<U> &) const noexcept {
return false;
}
T *allocate(const size_t n) const {
if (n == 0) {
return nullptr;
}
if (n > static_cast<size_t>(-1) / sizeof(T)) {
throw std::bad_array_new_length();
}
#ifndef BOARD_HAS_PSRAM
void *const pv = malloc(n * sizeof(T));
#else
void *const pv = ps_malloc(n * sizeof(T));
#endif
if (!pv) {
throw std::bad_alloc();
}
return static_cast<T *>(pv);
}
void deallocate(T *const p, size_t) const noexcept { free(p); }
};
#endif

View File

@ -39,7 +39,7 @@ public:
void reset(void);
uint8_t getSize(void);
uint8_t *getBuffer(void);
void addCount(uint16_t value1, uint16_t value2);
void addCount(uint16_t value, uint8_t sniffytpe);
void addConfig(configData_t value);
void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem,
uint8_t reset1, uint8_t reset2);

View File

@ -7,6 +7,7 @@
#include "lorawan.h"
#include "macsniff.h"
#include <rom/rtc.h>
#include "cyclic.h"
// table of remote commands and assigned functions
typedef struct {

View File

@ -7,9 +7,6 @@
// Hash function for scrambling MAC addresses
#include "hash.h"
#define MAC_SNIFF_WIFI 0
#define MAC_SNIFF_BLE 1
void wifi_sniffer_init(void);
void IRAM_ATTR wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type);
void switchWifiChannel(void * parameter);

View File

@ -1,559 +0,0 @@
/*
* Copyright (C) 2017 Robert Bosch. All Rights Reserved.
*
* Disclaimer
*
* Common:
* Bosch Sensortec products are developed for the consumer goods industry. They may only be used
* within the parameters of the respective valid product data sheet. Bosch Sensortec products are
* provided with the express understanding that there is no warranty of fitness for a particular purpose.
* They are not fit for use in life-sustaining, safety or security sensitive systems or any system or device
* that may lead to bodily harm or property damage if the system or device malfunctions. In addition,
* Bosch Sensortec products are not fit for use in products which interact with motor vehicle systems.
* The resale and/or use of products are at the purchasers own risk and his own responsibility. The
* examination of fitness for the intended use is the sole responsibility of the Purchaser.
*
* The purchaser shall indemnify Bosch Sensortec from all third party claims, including any claims for
* incidental, or consequential damages, arising from any product use not covered by the parameters of
* the respective valid product data sheet or not approved by Bosch Sensortec and reimburse Bosch
* Sensortec for all costs in connection with such claims.
*
* The purchaser must monitor the market for the purchased products, particularly with regard to
* product safety and inform Bosch Sensortec without delay of all security relevant incidents.
*
* Engineering Samples are marked with an asterisk (*) or (e). Samples may vary from the valid
* technical specifications of the product series. They are therefore not intended or fit for resale to third
* parties or for use in end products. Their sole purpose is internal client testing. The testing of an
* engineering sample may in no way replace the testing of a product series. Bosch Sensortec
* assumes no liability for the use of engineering samples. By accepting the engineering samples, the
* Purchaser agrees to indemnify Bosch Sensortec from all claims arising from the use of engineering
* samples.
*
* Special:
* This software module (hereinafter called "Software") and any information on application-sheets
* (hereinafter called "Information") is provided free of charge for the sole purpose to support your
* application work. The Software and Information is subject to the following terms and conditions:
*
* The Software is specifically designed for the exclusive use for Bosch Sensortec products by
* personnel who have special experience and training. Do not use this Software if you do not have the
* proper experience or training.
*
* This Software package is provided `` as is `` and without any expressed or implied warranties,
* including without limitation, the implied warranties of merchantability and fitness for a particular
* purpose.
*
* Bosch Sensortec and their representatives and agents deny any liability for the functional impairment
* of this Software in terms of fitness, performance and safety. Bosch Sensortec and their
* representatives and agents shall not be liable for any direct or indirect damages or injury, except as
* otherwise stipulated in mandatory applicable law.
*
* The Information provided is believed to be accurate and reliable. Bosch Sensortec assumes no
* responsibility for the consequences of use of such Information nor for any infringement of patents or
* other rights of third parties which may result from its use. No license is granted by implication or
* otherwise under any patent or patent rights of Bosch. Specifications mentioned in the Information are
* subject to change without notice.
*
* It is not allowed to deliver the source code of the Software to any third party without permission of
* Bosch Sensortec.
*
*/
/*!
* @file bsec_integration.c
*
* @brief
* Private part of the example for using of BSEC library.
*/
/*!
* @addtogroup bsec_examples BSEC Examples
* @brief BSEC usage examples
* @{*/
/**********************************************************************************************************************/
/* header files */
/**********************************************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "bsec_integration.h"
/**********************************************************************************************************************/
/* local macro definitions */
/**********************************************************************************************************************/
#define NUM_USED_OUTPUTS 8
/**********************************************************************************************************************/
/* global variable declarations */
/**********************************************************************************************************************/
/* Global sensor APIs data structure */
static struct bme680_dev bme680_g;
/* Global temperature offset to be subtracted */
static float bme680_temperature_offset_g = 0.0f;
/**********************************************************************************************************************/
/* functions */
/**********************************************************************************************************************/
/*!
* @brief Virtual sensor subscription
* Please call this function before processing of data using bsec_do_steps function
*
* @param[in] sample_rate mode to be used (either BSEC_SAMPLE_RATE_ULP or BSEC_SAMPLE_RATE_LP)
*
* @return subscription result, zero when successful
*/
static bsec_library_return_t bme680_bsec_update_subscription(float sample_rate)
{
bsec_sensor_configuration_t requested_virtual_sensors[NUM_USED_OUTPUTS];
uint8_t n_requested_virtual_sensors = NUM_USED_OUTPUTS;
bsec_sensor_configuration_t required_sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
uint8_t n_required_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
bsec_library_return_t status = BSEC_OK;
/* note: Virtual sensors as desired to be added here */
requested_virtual_sensors[0].sensor_id = BSEC_OUTPUT_IAQ;
requested_virtual_sensors[0].sample_rate = sample_rate;
requested_virtual_sensors[1].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
requested_virtual_sensors[1].sample_rate = sample_rate;
requested_virtual_sensors[2].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
requested_virtual_sensors[2].sample_rate = sample_rate;
requested_virtual_sensors[3].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
requested_virtual_sensors[3].sample_rate = sample_rate;
requested_virtual_sensors[4].sensor_id = BSEC_OUTPUT_RAW_GAS;
requested_virtual_sensors[4].sample_rate = sample_rate;
requested_virtual_sensors[5].sensor_id = BSEC_OUTPUT_RAW_TEMPERATURE;
requested_virtual_sensors[5].sample_rate = sample_rate;
requested_virtual_sensors[6].sensor_id = BSEC_OUTPUT_RAW_HUMIDITY;
requested_virtual_sensors[6].sample_rate = sample_rate;
requested_virtual_sensors[7].sensor_id = BSEC_OUTPUT_STATIC_IAQ;
requested_virtual_sensors[7].sample_rate = sample_rate;
/* Call bsec_update_subscription() to enable/disable the requested virtual sensors */
status = bsec_update_subscription(requested_virtual_sensors, n_requested_virtual_sensors, required_sensor_settings,
&n_required_sensor_settings);
return status;
}
/*!
* @brief Initialize the BME680 sensor and the BSEC library
*
* @param[in] sample_rate mode to be used (either BSEC_SAMPLE_RATE_ULP or BSEC_SAMPLE_RATE_LP)
* @param[in] temperature_offset device-specific temperature offset (due to self-heating)
* @param[in] bus_write pointer to the bus writing function
* @param[in] bus_read pointer to the bus reading function
* @param[in] sleep pointer to the system specific sleep function
* @param[in] state_load pointer to the system-specific state load function
* @param[in] config_load pointer to the system-specific config load function
*
* @return zero if successful, negative otherwise
*/
return_values_init bsec_iot_init(float sample_rate, float temperature_offset, bme680_com_fptr_t bus_write,
bme680_com_fptr_t bus_read, sleep_fct sleep, state_load_fct state_load, config_load_fct config_load)
{
return_values_init ret = {BME680_OK, BSEC_OK};
bsec_library_return_t bsec_status = BSEC_OK;
uint8_t bsec_state[BSEC_MAX_PROPERTY_BLOB_SIZE] = {0};
uint8_t bsec_config[BSEC_MAX_PROPERTY_BLOB_SIZE] = {0};
uint8_t work_buffer[BSEC_MAX_PROPERTY_BLOB_SIZE] = {0};
int bsec_state_len, bsec_config_len;
/* Fixed I2C configuration */
bme680_g.dev_id = BME680_I2C_ADDR_PRIMARY;
bme680_g.intf = BME680_I2C_INTF;
/* User configurable I2C configuration */
bme680_g.write = bus_write;
bme680_g.read = bus_read;
bme680_g.delay_ms = sleep;
/* Initialize BME680 API */
ret.bme680_status = bme680_init(&bme680_g);
if (ret.bme680_status != BME680_OK)
{
return ret;
}
/* Initialize BSEC library */
ret.bsec_status = bsec_init();
if (ret.bsec_status != BSEC_OK)
{
return ret;
}
/* Load library config, if available */
bsec_config_len = config_load(bsec_config, sizeof(bsec_config));
if (bsec_config_len != 0)
{
ret.bsec_status = bsec_set_configuration(bsec_config, bsec_config_len, work_buffer, sizeof(work_buffer));
if (ret.bsec_status != BSEC_OK)
{
return ret;
}
}
/* Load previous library state, if available */
bsec_state_len = state_load(bsec_state, sizeof(bsec_state));
if (bsec_state_len != 0)
{
ret.bsec_status = bsec_set_state(bsec_state, bsec_state_len, work_buffer, sizeof(work_buffer));
if (ret.bsec_status != BSEC_OK)
{
return ret;
}
}
/* Set temperature offset */
bme680_temperature_offset_g = temperature_offset;
/* Call to the function which sets the library with subscription information */
ret.bsec_status = bme680_bsec_update_subscription(sample_rate);
if (ret.bsec_status != BSEC_OK)
{
return ret;
}
return ret;
}
/*!
* @brief Trigger the measurement based on sensor settings
*
* @param[in] sensor_settings settings of the BME680 sensor adopted by sensor control function
* @param[in] sleep pointer to the system specific sleep function
*
* @return none
*/
static void bme680_bsec_trigger_measurement(bsec_bme_settings_t *sensor_settings, sleep_fct sleep)
{
uint16_t meas_period;
uint8_t set_required_settings;
int8_t bme680_status = BME680_OK;
/* Check if a forced-mode measurement should be triggered now */
if (sensor_settings->trigger_measurement)
{
/* Set sensor configuration */
bme680_g.tph_sett.os_hum = sensor_settings->humidity_oversampling;
bme680_g.tph_sett.os_pres = sensor_settings->pressure_oversampling;
bme680_g.tph_sett.os_temp = sensor_settings->temperature_oversampling;
bme680_g.gas_sett.run_gas = sensor_settings->run_gas;
bme680_g.gas_sett.heatr_temp = sensor_settings->heater_temperature; /* degree Celsius */
bme680_g.gas_sett.heatr_dur = sensor_settings->heating_duration; /* milliseconds */
/* Select the power mode */
/* Must be set before writing the sensor configuration */
bme680_g.power_mode = BME680_FORCED_MODE;
/* Set the required sensor settings needed */
set_required_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL;
/* Set the desired sensor configuration */
bme680_status = bme680_set_sensor_settings(set_required_settings, &bme680_g);
/* Set power mode as forced mode and trigger forced mode measurement */
bme680_status = bme680_set_sensor_mode(&bme680_g);
/* Get the total measurement duration so as to sleep or wait till the measurement is complete */
bme680_get_profile_dur(&meas_period, &bme680_g);
/* Delay till the measurement is ready. Timestamp resolution in ms */
sleep((uint32_t)meas_period);
}
/* Call the API to get current operation mode of the sensor */
bme680_status = bme680_get_sensor_mode(&bme680_g);
/* When the measurement is completed and data is ready for reading, the sensor must be in BME680_SLEEP_MODE.
* Read operation mode to check whether measurement is completely done and wait until the sensor is no more
* in BME680_FORCED_MODE. */
while (bme680_g.power_mode == BME680_FORCED_MODE)
{
/* sleep for 5 ms */
sleep(5);
bme680_status = bme680_get_sensor_mode(&bme680_g);
}
}
/*!
* @brief Read the data from registers and populate the inputs structure to be passed to do_steps function
*
* @param[in] time_stamp_trigger settings of the sensor returned from sensor control function
* @param[in] inputs input structure containing the information on sensors to be passed to do_steps
* @param[in] num_bsec_inputs number of inputs to be passed to do_steps
* @param[in] bsec_process_data process data variable returned from sensor_control
*
* @return none
*/
static void bme680_bsec_read_data(int64_t time_stamp_trigger, bsec_input_t *inputs, uint8_t *num_bsec_inputs,
int32_t bsec_process_data)
{
static struct bme680_field_data data;
int8_t bme680_status = BME680_OK;
/* We only have to read data if the previous call the bsec_sensor_control() actually asked for it */
if (bsec_process_data)
{
bme680_status = bme680_get_sensor_data(&data, &bme680_g);
if (data.status & BME680_NEW_DATA_MSK)
{
/* Pressure to be processed by BSEC */
if (bsec_process_data & BSEC_PROCESS_PRESSURE)
{
/* Place presssure sample into input struct */
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_PRESSURE;
inputs[*num_bsec_inputs].signal = data.pressure;
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
}
/* Temperature to be processed by BSEC */
if (bsec_process_data & BSEC_PROCESS_TEMPERATURE)
{
/* Place temperature sample into input struct */
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
#ifdef BME680_FLOAT_POINT_COMPENSATION
inputs[*num_bsec_inputs].signal = data.temperature;
#else
inputs[*num_bsec_inputs].signal = data.temperature / 100.0f;
#endif
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
/* Also add optional heatsource input which will be subtracted from the temperature reading to
* compensate for device-specific self-heating (supported in BSEC IAQ solution)*/
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
inputs[*num_bsec_inputs].signal = bme680_temperature_offset_g;
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
}
/* Humidity to be processed by BSEC */
if (bsec_process_data & BSEC_PROCESS_HUMIDITY)
{
/* Place humidity sample into input struct */
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
#ifdef BME680_FLOAT_POINT_COMPENSATION
inputs[*num_bsec_inputs].signal = data.humidity;
#else
inputs[*num_bsec_inputs].signal = data.humidity / 1000.0f;
#endif
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
}
/* Gas to be processed by BSEC */
if (bsec_process_data & BSEC_PROCESS_GAS)
{
/* Check whether gas_valid flag is set */
if(data.status & BME680_GASM_VALID_MSK)
{
/* Place sample into input struct */
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
inputs[*num_bsec_inputs].signal = data.gas_resistance;
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
}
}
}
}
}
/*!
* @brief This function is written to process the sensor data for the requested virtual sensors
*
* @param[in] bsec_inputs input structure containing the information on sensors to be passed to do_steps
* @param[in] num_bsec_inputs number of inputs to be passed to do_steps
* @param[in] output_ready pointer to the function processing obtained BSEC outputs
*
* @return none
*/
static void bme680_bsec_process_data(bsec_input_t *bsec_inputs, uint8_t num_bsec_inputs, output_ready_fct output_ready)
{
/* Output buffer set to the maximum virtual sensor outputs supported */
bsec_output_t bsec_outputs[BSEC_NUMBER_OUTPUTS];
uint8_t num_bsec_outputs = 0;
uint8_t index = 0;
bsec_library_return_t bsec_status = BSEC_OK;
int64_t timestamp = 0;
float iaq = 0.0f;
uint8_t iaq_accuracy = 0;
float temp = 0.0f;
float raw_temp = 0.0f;
float raw_pressure = 0.0f;
float humidity = 0.0f;
float raw_humidity = 0.0f;
float raw_gas = 0.0f;
float static_iaq = 0.0f;
uint8_t static_iaq_accuracy = 0;
float co2_equivalent = 0.0f;
uint8_t co2_accuracy = 0;
float breath_voc_equivalent = 0.0f;
uint8_t breath_voc_accuracy = 0;
float comp_gas_value = 0.0f;
uint8_t comp_gas_accuracy = 0;
float gas_percentage = 0.0f;
uint8_t gas_percentage_acccuracy = 0;
/* Check if something should be processed by BSEC */
if (num_bsec_inputs > 0)
{
/* Set number of outputs to the size of the allocated buffer */
/* BSEC_NUMBER_OUTPUTS to be defined */
num_bsec_outputs = BSEC_NUMBER_OUTPUTS;
/* Perform processing of the data by BSEC
Note:
* The number of outputs you get depends on what you asked for during bsec_update_subscription(). This is
handled under bme680_bsec_update_subscription() function in this example file.
* The number of actual outputs that are returned is written to num_bsec_outputs. */
bsec_status = bsec_do_steps(bsec_inputs, num_bsec_inputs, bsec_outputs, &num_bsec_outputs);
/* Iterate through the outputs and extract the relevant ones. */
for (index = 0; index < num_bsec_outputs; index++)
{
switch (bsec_outputs[index].sensor_id)
{
case BSEC_OUTPUT_IAQ:
iaq = bsec_outputs[index].signal;
iaq_accuracy = bsec_outputs[index].accuracy;
break;
case BSEC_OUTPUT_STATIC_IAQ:
static_iaq = bsec_outputs[index].signal;
static_iaq_accuracy = bsec_outputs[index].accuracy;
break;
case BSEC_OUTPUT_CO2_EQUIVALENT:
co2_equivalent = bsec_outputs[index].signal;
co2_accuracy = bsec_outputs[index].accuracy;
break;
case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
breath_voc_equivalent = bsec_outputs[index].signal;
breath_voc_accuracy = bsec_outputs[index].accuracy;
break;
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
temp = bsec_outputs[index].signal;
break;
case BSEC_OUTPUT_RAW_PRESSURE:
raw_pressure = bsec_outputs[index].signal;
break;
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
humidity = bsec_outputs[index].signal;
break;
case BSEC_OUTPUT_RAW_GAS:
raw_gas = bsec_outputs[index].signal;
break;
case BSEC_OUTPUT_RAW_TEMPERATURE:
raw_temp = bsec_outputs[index].signal;
break;
case BSEC_OUTPUT_RAW_HUMIDITY:
raw_humidity = bsec_outputs[index].signal;
break;
case BSEC_OUTPUT_COMPENSATED_GAS:
comp_gas_value = bsec_outputs[index].signal;
comp_gas_accuracy = bsec_outputs[index].accuracy;
break;
case BSEC_OUTPUT_GAS_PERCENTAGE:
gas_percentage = bsec_outputs[index].signal;
gas_percentage_acccuracy = bsec_outputs[index].accuracy;
break;
default:
continue;
}
/* Assume that all the returned timestamps are the same */
timestamp = bsec_outputs[index].time_stamp;
}
/* Pass the extracted outputs to the user provided output_ready() function. */
output_ready(timestamp, iaq, iaq_accuracy, temp, humidity, raw_pressure, raw_temp,
raw_humidity, raw_gas, bsec_status, static_iaq, co2_equivalent, breath_voc_equivalent);
}
}
/*!
* @brief Runs the main (endless) loop that queries sensor settings, applies them, and processes the measured data
*
* @param[in] sleep pointer to the system specific sleep function
* @param[in] get_timestamp_us pointer to the system specific timestamp derivation function
* @param[in] output_ready pointer to the function processing obtained BSEC outputs
* @param[in] state_save pointer to the system-specific state save function
* @param[in] save_intvl interval at which BSEC state should be saved (in samples)
*
* @return none
*/
void bsec_iot_loop(sleep_fct sleep, get_timestamp_us_fct get_timestamp_us, output_ready_fct output_ready,
state_save_fct state_save, uint32_t save_intvl)
{
/* Timestamp variables */
int64_t time_stamp = 0;
int64_t time_stamp_interval_ms = 0;
/* Allocate enough memory for up to BSEC_MAX_PHYSICAL_SENSOR physical inputs*/
bsec_input_t bsec_inputs[BSEC_MAX_PHYSICAL_SENSOR];
/* Number of inputs to BSEC */
uint8_t num_bsec_inputs = 0;
/* BSEC sensor settings struct */
bsec_bme_settings_t sensor_settings;
/* Save state variables */
uint8_t bsec_state[BSEC_MAX_STATE_BLOB_SIZE];
uint8_t work_buffer[BSEC_MAX_STATE_BLOB_SIZE];
uint32_t bsec_state_len = 0;
uint32_t n_samples = 0;
bsec_library_return_t bsec_status = BSEC_OK;
while (1)
{
/* get the timestamp in nanoseconds before calling bsec_sensor_control() */
time_stamp = get_timestamp_us() * 1000;
/* Retrieve sensor settings to be used in this time instant by calling bsec_sensor_control */
bsec_sensor_control(time_stamp, &sensor_settings);
/* Trigger a measurement if necessary */
bme680_bsec_trigger_measurement(&sensor_settings, sleep);
/* Read data from last measurement */
num_bsec_inputs = 0;
bme680_bsec_read_data(time_stamp, bsec_inputs, &num_bsec_inputs, sensor_settings.process_data);
/* Time to invoke BSEC to perform the actual processing */
bme680_bsec_process_data(bsec_inputs, num_bsec_inputs, output_ready);
/* Increment sample counter */
n_samples++;
/* Retrieve and store state if the passed save_intvl */
if (n_samples >= save_intvl)
{
bsec_status = bsec_get_state(0, bsec_state, sizeof(bsec_state), work_buffer, sizeof(work_buffer), &bsec_state_len);
if (bsec_status == BSEC_OK)
{
state_save(bsec_state, bsec_state_len);
}
n_samples = 0;
}
/* Compute how long we can sleep until we need to call bsec_sensor_control() next */
/* Time_stamp is converted from microseconds to nanoseconds first and then the difference to milliseconds */
time_stamp_interval_ms = (sensor_settings.next_call - get_timestamp_us() * 1000) / 1000000;
if (time_stamp_interval_ms > 0)
{
sleep((uint32_t)time_stamp_interval_ms);
}
}
}
/*! @}*/

View File

@ -1,165 +0,0 @@
/*
* Copyright (C) 2017 Robert Bosch. All Rights Reserved.
*
* Disclaimer
*
* Common:
* Bosch Sensortec products are developed for the consumer goods industry. They may only be used
* within the parameters of the respective valid product data sheet. Bosch Sensortec products are
* provided with the express understanding that there is no warranty of fitness for a particular purpose.
* They are not fit for use in life-sustaining, safety or security sensitive systems or any system or device
* that may lead to bodily harm or property damage if the system or device malfunctions. In addition,
* Bosch Sensortec products are not fit for use in products which interact with motor vehicle systems.
* The resale and/or use of products are at the purchasers own risk and his own responsibility. The
* examination of fitness for the intended use is the sole responsibility of the Purchaser.
*
* The purchaser shall indemnify Bosch Sensortec from all third party claims, including any claims for
* incidental, or consequential damages, arising from any product use not covered by the parameters of
* the respective valid product data sheet or not approved by Bosch Sensortec and reimburse Bosch
* Sensortec for all costs in connection with such claims.
*
* The purchaser must monitor the market for the purchased products, particularly with regard to
* product safety and inform Bosch Sensortec without delay of all security relevant incidents.
*
* Engineering Samples are marked with an asterisk (*) or (e). Samples may vary from the valid
* technical specifications of the product series. They are therefore not intended or fit for resale to third
* parties or for use in end products. Their sole purpose is internal client testing. The testing of an
* engineering sample may in no way replace the testing of a product series. Bosch Sensortec
* assumes no liability for the use of engineering samples. By accepting the engineering samples, the
* Purchaser agrees to indemnify Bosch Sensortec from all claims arising from the use of engineering
* samples.
*
* Special:
* This software module (hereinafter called "Software") and any information on application-sheets
* (hereinafter called "Information") is provided free of charge for the sole purpose to support your
* application work. The Software and Information is subject to the following terms and conditions:
*
* The Software is specifically designed for the exclusive use for Bosch Sensortec products by
* personnel who have special experience and training. Do not use this Software if you do not have the
* proper experience or training.
*
* This Software package is provided `` as is `` and without any expressed or implied warranties,
* including without limitation, the implied warranties of merchantability and fitness for a particular
* purpose.
*
* Bosch Sensortec and their representatives and agents deny any liability for the functional impairment
* of this Software in terms of fitness, performance and safety. Bosch Sensortec and their
* representatives and agents shall not be liable for any direct or indirect damages or injury, except as
* otherwise stipulated in mandatory applicable law.
*
* The Information provided is believed to be accurate and reliable. Bosch Sensortec assumes no
* responsibility for the consequences of use of such Information nor for any infringement of patents or
* other rights of third parties which may result from its use. No license is granted by implication or
* otherwise under any patent or patent rights of Bosch. Specifications mentioned in the Information are
* subject to change without notice.
*
* It is not allowed to deliver the source code of the Software to any third party without permission of
* Bosch Sensortec.
*
*/
/*!
* @file bsec_integration.h
*
* @brief
* Contains BSEC integration API
*/
/*!
* @addtogroup bsec_examples BSEC Examples
* @brief BSEC usage examples
* @{*/
#ifndef __BSEC_INTEGRATION_H__
#define __BSEC_INTEGRATION_H__
#ifdef __cplusplus
extern "C"
{
#endif
/**********************************************************************************************************************/
/* header files */
/**********************************************************************************************************************/
/* Use the following bme680 driver: https://github.com/BoschSensortec/BME680_driver/releases/tag/bme680_v3.5.1 */
#include "bme680.h"
/* BSEC header files are available in the inc/ folder of the release package */
#include "bsec_interface.h"
#include "bsec_datatypes.h"
/**********************************************************************************************************************/
/* type definitions */
/**********************************************************************************************************************/
/* function pointer to the system specific sleep function */
typedef void (*sleep_fct)(uint32_t t_ms);
/* function pointer to the system specific timestamp derivation function */
typedef int64_t (*get_timestamp_us_fct)();
/* function pointer to the function processing obtained BSEC outputs */
typedef void (*output_ready_fct)(int64_t timestamp, float iaq, uint8_t iaq_accuracy, float temperature, float humidity,
float pressure, float raw_temperature, float raw_humidity, float gas, bsec_library_return_t bsec_status,
float static_iaq, float co2_equivalent, float breath_voc_equivalent);
/* function pointer to the function loading a previous BSEC state from NVM */
typedef uint32_t (*state_load_fct)(uint8_t *state_buffer, uint32_t n_buffer);
/* function pointer to the function saving BSEC state to NVM */
typedef void (*state_save_fct)(const uint8_t *state_buffer, uint32_t length);
/* function pointer to the function loading the BSEC configuration string from NVM */
typedef uint32_t (*config_load_fct)(uint8_t *state_buffer, uint32_t n_buffer);
/* structure definitions */
/* Structure with the return value from bsec_iot_init() */
typedef struct{
/*! Result of API execution status */
int8_t bme680_status;
/*! Result of BSEC library */
bsec_library_return_t bsec_status;
}return_values_init;
/**********************************************************************************************************************/
/* function declarations */
/**********************************************************************************************************************/
/*!
* @brief Initialize the BME680 sensor and the BSEC library
*
* @param[in] sample_rate mode to be used (either BSEC_SAMPLE_RATE_ULP or BSEC_SAMPLE_RATE_LP)
* @param[in] temperature_offset device-specific temperature offset (due to self-heating)
* @param[in] bus_write pointer to the bus writing function
* @param[in] bus_read pointer to the bus reading function
* @param[in] sleep pointer to the system-specific sleep function
* @param[in] state_load pointer to the system-specific state load function
*
* @return zero if successful, negative otherwise
*/
return_values_init bsec_iot_init(float sample_rate, float temperature_offset, bme680_com_fptr_t bus_write, bme680_com_fptr_t bus_read,
sleep_fct sleep, state_load_fct state_load, config_load_fct config_load);
/*!
* @brief Runs the main (endless) loop that queries sensor settings, applies them, and processes the measured data
*
* @param[in] sleep pointer to the system-specific sleep function
* @param[in] get_timestamp_us pointer to the system-specific timestamp derivation function
* @param[in] output_ready pointer to the function processing obtained BSEC outputs
* @param[in] state_save pointer to the system-specific state save function
* @param[in] save_intvl interval at which BSEC state should be saved (in samples)
*
* @return return_values_init struct with the result of the API and the BSEC library
*/
void bsec_iot_loop(sleep_fct sleep, get_timestamp_us_fct get_timestamp_us, output_ready_fct output_ready,
state_save_fct state_save, uint32_t save_intvl);
#ifdef __cplusplus
}
#endif
#endif /* __BSEC_INTEGRATION_H__ */
/*! @}*/

View File

@ -103,7 +103,7 @@
/** BME680 configuration macros */
/** Enable or un-comment the macro to provide floating point data output */
#ifndef BME680_FLOAT_POINT_COMPENSATION
/* #define BME680_FLOAT_POINT_COMPENSATION */
//#define BME680_FLOAT_POINT_COMPENSATION
#endif
/** BME680 General config */

498
lib/Bosch-BSEC/src/bsec.cpp Normal file
View File

@ -0,0 +1,498 @@
/**
* Copyright (C) 2017 - 2018 Bosch Sensortec GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of the copyright holder nor the names of the
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
*
* The information provided is believed to be accurate and reliable.
* The copyright holder assumes no responsibility
* for the consequences of use
* of such information nor for any infringement of patents or
* other rights of third parties which may result from its use.
* No license is granted by implication or otherwise under any patent or
* patent rights of the copyright holder.
*
* @file bsec.cpp
* @date 31 Jan 2018
* @version 1.0
*
*/
#include "bsec.h"
TwoWire *Bsec::wireObj = NULL;
SPIClass *Bsec::spiObj = NULL;
/**
* @brief Constructor
*/
Bsec::Bsec() {
nextCall = 0;
version.major = 0;
version.minor = 0;
version.major_bugfix = 0;
version.minor_bugfix = 0;
millisOverflowCounter = 0;
lastTime = 0;
bme680Status = BME680_OK;
outputTimestamp = 0;
_tempOffset = 0.0f;
status = BSEC_OK;
zeroOutputs();
}
/**
* @brief Function to initialize the BSEC library and the BME680 sensor
*/
void Bsec::begin(uint8_t devId, enum bme680_intf intf, bme680_com_fptr_t read,
bme680_com_fptr_t write, bme680_delay_fptr_t idleTask) {
_bme680.dev_id = devId;
_bme680.intf = intf;
_bme680.read = read;
_bme680.write = write;
_bme680.delay_ms = idleTask;
_bme680.amb_temp = 25;
_bme680.power_mode = BME680_FORCED_MODE;
beginCommon();
}
/**
* @brief Function to initialize the BSEC library and the BME680 sensor
*/
void Bsec::begin(uint8_t i2cAddr, TwoWire &i2c) {
_bme680.dev_id = i2cAddr;
_bme680.intf = BME680_I2C_INTF;
_bme680.read = Bsec::i2cRead;
_bme680.write = Bsec::i2cWrite;
_bme680.delay_ms = Bsec::delay_ms;
_bme680.amb_temp = 25;
_bme680.power_mode = BME680_FORCED_MODE;
Bsec::wireObj = &i2c;
Bsec::wireObj->begin();
beginCommon();
}
/**
* @brief Function to initialize the BSEC library and the BME680 sensor
*/
void Bsec::begin(uint8_t chipSelect, SPIClass &spi) {
_bme680.dev_id = chipSelect;
_bme680.intf = BME680_SPI_INTF;
_bme680.read = Bsec::spiTransfer;
_bme680.write = Bsec::spiTransfer;
_bme680.delay_ms = Bsec::delay_ms;
_bme680.amb_temp = 25;
_bme680.power_mode = BME680_FORCED_MODE;
pinMode(chipSelect, OUTPUT);
digitalWrite(chipSelect, HIGH);
Bsec::spiObj = &spi;
Bsec::spiObj->begin();
beginCommon();
}
/**
* @brief Common code for the begin function
*/
void Bsec::beginCommon(void) {
status = bsec_init();
getVersion();
bme680Status = bme680_init(&_bme680);
}
/**
* @brief Function that sets the desired sensors and the sample rates
*/
void Bsec::updateSubscription(bsec_virtual_sensor_t sensorList[],
uint8_t nSensors, float sampleRate) {
bsec_sensor_configuration_t virtualSensors[BSEC_NUMBER_OUTPUTS],
sensorSettings[BSEC_MAX_PHYSICAL_SENSOR];
uint8_t nVirtualSensors = 0, nSensorSettings = BSEC_MAX_PHYSICAL_SENSOR;
for (uint8_t i = 0; i < nSensors; i++) {
virtualSensors[nVirtualSensors].sensor_id = sensorList[i];
virtualSensors[nVirtualSensors].sample_rate = sampleRate;
nVirtualSensors++;
}
status = bsec_update_subscription(virtualSensors, nVirtualSensors,
sensorSettings, &nSensorSettings);
return;
}
/**
* @brief Callback from the user to trigger reading of data from the BME680,
* process and store outputs
*/
bool Bsec::run(void) {
bool newData = false;
/* Check if the time has arrived to call do_steps() */
int64_t callTimeMs = getTimeMs();
if (callTimeMs >= nextCall) {
bsec_bme_settings_t bme680Settings;
int64_t callTimeNs = callTimeMs * INT64_C(1000000);
status = bsec_sensor_control(callTimeNs, &bme680Settings);
if (status < BSEC_OK)
return false;
nextCall =
bme680Settings.next_call / INT64_C(1000000); // Convert from ns to ms
bme680Status = setBme680Config(bme680Settings);
if (bme680Status != BME680_OK) {
return false;
}
bme680Status = bme680_set_sensor_mode(&_bme680);
if (bme680Status != BME680_OK) {
return false;
}
/* Wait for measurement to complete */
uint16_t meas_dur = 0;
bme680_get_profile_dur(&meas_dur, &_bme680);
delay_ms(meas_dur);
newData = readProcessData(callTimeNs, bme680Settings);
}
return newData;
}
/**
* @brief Function to get the state of the algorithm to save to non-volatile
* memory
*/
void Bsec::getState(uint8_t *state) {
uint8_t workBuffer[BSEC_MAX_STATE_BLOB_SIZE];
uint32_t n_serialized_state = BSEC_MAX_STATE_BLOB_SIZE;
status = bsec_get_state(0, state, BSEC_MAX_STATE_BLOB_SIZE, workBuffer,
BSEC_MAX_STATE_BLOB_SIZE, &n_serialized_state);
}
/**
* @brief Function to set the state of the algorithm from non-volatile memory
*/
void Bsec::setState(uint8_t *state) {
uint8_t workBuffer[BSEC_MAX_STATE_BLOB_SIZE];
status = bsec_set_state(state, BSEC_MAX_STATE_BLOB_SIZE, workBuffer,
BSEC_MAX_STATE_BLOB_SIZE);
}
/**
* @brief Function to set the configuration of the algorithm from memory
*/
void Bsec::setConfig(const uint8_t *state) {
uint8_t workBuffer[BSEC_MAX_PROPERTY_BLOB_SIZE];
status = bsec_set_configuration(state, BSEC_MAX_PROPERTY_BLOB_SIZE,
workBuffer, sizeof(workBuffer));
}
/* Private functions */
/**
* @brief Get the version of the BSEC library
*/
void Bsec::getVersion(void) { bsec_get_version(&version); }
/**
* @brief Read data from the BME680 and process it
*/
bool Bsec::readProcessData(int64_t currTimeNs,
bsec_bme_settings_t bme680Settings) {
bme680Status = bme680_get_sensor_data(&_data, &_bme680);
if (bme680Status != BME680_OK) {
return false;
}
bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR]; // Temp, Pres, Hum & Gas
uint8_t nInputs = 0, nOutputs = 0;
if (_data.status & BME680_NEW_DATA_MSK) {
if (bme680Settings.process_data & BSEC_PROCESS_TEMPERATURE) {
inputs[nInputs].sensor_id = BSEC_INPUT_TEMPERATURE;
#ifdef BME680_FLOAT_POINT_COMPENSATION
inputs[nInputs].signal = _data.temperature;
#else
inputs[nInputs].signal = _data.temperature / 100.0f;
#endif
inputs[nInputs].time_stamp = currTimeNs;
nInputs++;
/* Temperature offset from the real temperature due to external heat
* sources */
inputs[nInputs].sensor_id = BSEC_INPUT_HEATSOURCE;
inputs[nInputs].signal = _tempOffset;
inputs[nInputs].time_stamp = currTimeNs;
nInputs++;
}
if (bme680Settings.process_data & BSEC_PROCESS_HUMIDITY) {
inputs[nInputs].sensor_id = BSEC_INPUT_HUMIDITY;
#ifdef BME680_FLOAT_POINT_COMPENSATION
inputs[nInputs].signal = _data.humidity;
#else
inputs[nInputs].signal = _data.humidity / 1000.0f;
#endif
inputs[nInputs].time_stamp = currTimeNs;
nInputs++;
}
if (bme680Settings.process_data & BSEC_PROCESS_PRESSURE) {
inputs[nInputs].sensor_id = BSEC_INPUT_PRESSURE;
inputs[nInputs].signal = _data.pressure;
inputs[nInputs].time_stamp = currTimeNs;
nInputs++;
}
if (bme680Settings.process_data & BSEC_PROCESS_GAS) {
inputs[nInputs].sensor_id = BSEC_INPUT_GASRESISTOR;
inputs[nInputs].signal = _data.gas_resistance;
inputs[nInputs].time_stamp = currTimeNs;
nInputs++;
}
}
if (nInputs > 0) {
nOutputs = BSEC_NUMBER_OUTPUTS;
bsec_output_t _outputs[BSEC_NUMBER_OUTPUTS];
status = bsec_do_steps(inputs, nInputs, _outputs, &nOutputs);
if (status != BSEC_OK)
return false;
zeroOutputs();
if (nOutputs > 0) {
outputTimestamp =
_outputs[0].time_stamp / 1000000; // Convert from ns to ms
for (uint8_t i = 0; i < nOutputs; i++) {
switch (_outputs[i].sensor_id) {
case BSEC_OUTPUT_IAQ:
iaqEstimate = _outputs[i].signal;
iaqAccuracy = _outputs[i].accuracy;
break;
case BSEC_OUTPUT_STATIC_IAQ:
staticIaq = _outputs[i].signal;
staticIaqAccuracy = _outputs[i].accuracy;
break;
case BSEC_OUTPUT_CO2_EQUIVALENT:
co2Equivalent = _outputs[i].signal;
co2Accuracy = _outputs[i].accuracy;
break;
case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
breathVocEquivalent = _outputs[i].signal;
breathVocAccuracy = _outputs[i].accuracy;
break;
case BSEC_OUTPUT_RAW_TEMPERATURE:
rawTemperature = _outputs[i].signal;
break;
case BSEC_OUTPUT_RAW_PRESSURE:
pressure = _outputs[i].signal;
break;
case BSEC_OUTPUT_RAW_HUMIDITY:
rawHumidity = _outputs[i].signal;
break;
case BSEC_OUTPUT_RAW_GAS:
gasResistance = _outputs[i].signal;
break;
case BSEC_OUTPUT_STABILIZATION_STATUS:
stabStatus = _outputs[i].signal;
break;
case BSEC_OUTPUT_RUN_IN_STATUS:
runInStatus = _outputs[i].signal;
break;
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
temperature = _outputs[i].signal;
break;
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
humidity = _outputs[i].signal;
break;
case BSEC_OUTPUT_COMPENSATED_GAS:
compGasValue = _outputs[i].signal;
compGasAccuracy = _outputs[i].accuracy;
break;
case BSEC_OUTPUT_GAS_PERCENTAGE:
gasPercentage = _outputs[i].signal;
gasPercentageAcccuracy = _outputs[i].accuracy;
break;
default:
break;
}
}
return true;
}
}
return false;
}
/**
* @brief Set the BME680 sensor's configuration
*/
int8_t Bsec::setBme680Config(bsec_bme_settings_t bme680Settings) {
_bme680.gas_sett.run_gas = bme680Settings.run_gas;
_bme680.tph_sett.os_hum = bme680Settings.humidity_oversampling;
_bme680.tph_sett.os_temp = bme680Settings.temperature_oversampling;
_bme680.tph_sett.os_pres = bme680Settings.pressure_oversampling;
_bme680.gas_sett.heatr_temp = bme680Settings.heater_temperature;
_bme680.gas_sett.heatr_dur = bme680Settings.heating_duration;
uint16_t desired_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL |
BME680_FILTER_SEL | BME680_GAS_SENSOR_SEL;
return bme680_set_sensor_settings(desired_settings, &_bme680);
}
/**
* @brief Function to zero the outputs
*/
void Bsec::zeroOutputs(void) {
temperature = 0.0f;
pressure = 0.0f;
humidity = 0.0f;
gasResistance = 0.0f;
rawTemperature = 0.0f;
rawHumidity = 0.0f;
stabStatus = 0.0f;
runInStatus = 0.0f;
iaqEstimate = 0.0f;
iaqAccuracy = 0;
staticIaq = 0.0f;
staticIaqAccuracy = 0;
co2Equivalent = 0.0f;
co2Accuracy = 0;
breathVocEquivalent = 0.0f;
breathVocAccuracy = 0;
compGasValue = 0.0f;
compGasAccuracy = 0;
gasPercentage = 0.0f;
gasPercentageAcccuracy = 0;
}
/**
* @brief Function to calculate an int64_t timestamp in milliseconds
*/
int64_t Bsec::getTimeMs(void) {
int64_t timeMs = millis();
if (lastTime > timeMs) { // An overflow occured
lastTime = timeMs;
millisOverflowCounter++;
}
return timeMs + (millisOverflowCounter * 0xFFFFFFFF);
}
/**
@brief Task that delays for a ms period of time
*/
void Bsec::delay_ms(uint32_t period) {
// Wait for a period amount of ms
// The system may simply idle, sleep or even perform background tasks
delay(period);
}
/**
@brief Callback function for reading registers over I2C
*/
int8_t Bsec::i2cRead(uint8_t devId, uint8_t regAddr, uint8_t *regData,
uint16_t length) {
uint16_t i;
int8_t rslt = 0;
if (Bsec::wireObj) {
Bsec::wireObj->beginTransmission(devId);
Bsec::wireObj->write(regAddr);
rslt = Bsec::wireObj->endTransmission();
Bsec::wireObj->requestFrom((int)devId, (int)length);
for (i = 0; (i < length) && Bsec::wireObj->available(); i++) {
regData[i] = Bsec::wireObj->read();
}
} else {
rslt = -1;
}
return rslt;
}
/**
* @brief Callback function for writing registers over I2C
*/
int8_t Bsec::i2cWrite(uint8_t devId, uint8_t regAddr, uint8_t *regData,
uint16_t length) {
uint16_t i;
int8_t rslt = 0;
if (Bsec::wireObj) {
Bsec::wireObj->beginTransmission(devId);
Bsec::wireObj->write(regAddr);
for (i = 0; i < length; i++) {
Bsec::wireObj->write(regData[i]);
}
rslt = Bsec::wireObj->endTransmission();
} else {
rslt = -1;
}
return rslt;
}
/**
* @brief Callback function for reading and writing registers over SPI
*/
int8_t Bsec::spiTransfer(uint8_t devId, uint8_t regAddr, uint8_t *regData,
uint16_t length) {
int8_t rslt = 0;
if (Bsec::spiObj) {
Bsec::spiObj->beginTransaction(
SPISettings(4000000, MSBFIRST, SPI_MODE0)); // Can be upto 10MHz
digitalWrite(devId, LOW);
Bsec::spiObj->transfer(
regAddr); // Write the register address, ignore the return
for (uint16_t i = 0; i < length; i++)
regData[i] = Bsec::spiObj->transfer(regData[i]);
digitalWrite(devId, HIGH);
Bsec::spiObj->endTransaction();
} else {
rslt = -1;
}
return rslt;
;
}

230
lib/Bosch-BSEC/src/bsec.h Normal file
View File

@ -0,0 +1,230 @@
/**
* Copyright (C) 2017 - 2018 Bosch Sensortec GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of the copyright holder nor the names of the
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
*
* The information provided is believed to be accurate and reliable.
* The copyright holder assumes no responsibility
* for the consequences of use
* of such information nor for any infringement of patents or
* other rights of third parties which may result from its use.
* No license is granted by implication or otherwise under any patent or
* patent rights of the copyright holder.
*
* @file bsec.h
* @date 31 Jan 2018
* @version 1.0
*
*/
#ifndef BSEC_CLASS_H
#define BSEC_CLASS_H
/* Includes */
#include "Arduino.h"
#include "Wire.h"
#include "SPI.h"
#include "inc/bsec_datatypes.h"
#include "inc/bsec_interface.h"
#include "bme680/bme680.h"
/* BSEC class definition */
class Bsec
{
public:
/* Public variables */
bsec_version_t version; // Stores the version of the BSEC algorithm
int64_t nextCall; // Stores the time when the algorithm has to be called next in ms
int8_t bme680Status; // Placeholder for the BME680 driver's error codes
bsec_library_return_t status;
float iaqEstimate, rawTemperature, pressure, rawHumidity, gasResistance, stabStatus, runInStatus, temperature, humidity,
staticIaq, co2Equivalent, breathVocEquivalent, compGasValue, gasPercentage;
uint8_t iaqAccuracy, staticIaqAccuracy, co2Accuracy, breathVocAccuracy, compGasAccuracy, gasPercentageAcccuracy;
int64_t outputTimestamp; // Timestamp in ms of the output
static TwoWire *wireObj;
static SPIClass *spiObj;
/* Public APIs */
/**
* @brief Constructor
*/
Bsec();
/**
* @brief Function to initialize the BSEC library and the BME680 sensor
* @param devId : Device identifier parameter for the read/write interface functions
* @param intf : Physical communication interface
* @param read : Pointer to the read function
* @param write : Pointer to the write function
* @param idleTask : Pointer to the idling task
*/
void begin(uint8_t devId, enum bme680_intf intf, bme680_com_fptr_t read, bme680_com_fptr_t write, bme680_delay_fptr_t idleTask);
/**
* @brief Function to initialize the BSEC library and the BME680 sensor
* @param i2cAddr : I2C address
* @param i2c : Pointer to the TwoWire object
*/
void begin(uint8_t i2cAddr, TwoWire &i2c);
/**
* @brief Function to initialize the BSEC library and the BME680 sensor
* @param chipSelect : SPI chip select
* @param spi : Pointer to the SPIClass object
*/
void begin(uint8_t chipSelect, SPIClass &spi);
/**
* @brief Function that sets the desired sensors and the sample rates
* @param sensorList : The list of output sensors
* @param nSensors : Number of outputs requested
* @param sampleRate : The sample rate of requested sensors
*/
void updateSubscription(bsec_virtual_sensor_t sensorList[], uint8_t nSensors, float sampleRate = BSEC_SAMPLE_RATE_ULP);
/**
* @brief Callback from the user to trigger reading of data from the BME680, process and store outputs
* @return true if there are new outputs. false otherwise
*/
bool run(void);
/**
* @brief Function to get the state of the algorithm to save to non-volatile memory
* @param state : Pointer to a memory location that contains the state
*/
void getState(uint8_t *state);
/**
* @brief Function to set the state of the algorithm from non-volatile memory
* @param state : Pointer to a memory location that contains the state
*/
void setState(uint8_t *state);
/**
* @brief Function to set the configuration of the algorithm from memory
* @param state : Pointer to a memory location that contains the configuration
*/
void setConfig(const uint8_t *config);
/**
* @brief Function to set the temperature offset
* @param tempOffset : Temperature offset in degree Celsius
*/
void setTemperatureOffset(float tempOffset)
{
_tempOffset = tempOffset;
}
/**
* @brief Function to calculate an int64_t timestamp in milliseconds
*/
int64_t getTimeMs(void);
/**
* @brief Task that delays for a ms period of time
* @param period : Period of time in ms
*/
static void delay_ms(uint32_t period);
/**
* @brief Callback function for reading registers over I2C
* @param devId : Library agnostic parameter to identify the device to communicate with
* @param regAddr : Register address
* @param regData : Pointer to the array containing the data to be read
* @param length : Length of the array of data
* @return Zero for success, non-zero otherwise
*/
static int8_t i2cRead(uint8_t devId, uint8_t regAddr, uint8_t *regData, uint16_t length);
/**
* @brief Callback function for writing registers over I2C
* @param devId : Library agnostic parameter to identify the device to communicate with
* @param regAddr : Register address
* @param regData : Pointer to the array containing the data to be written
* @param length : Length of the array of data
* @return Zero for success, non-zero otherwise
*/
static int8_t i2cWrite(uint8_t devId, uint8_t regAddr, uint8_t *regData, uint16_t length);
/**
* @brief Callback function for reading and writing registers over SPI
* @param devId : Library agnostic parameter to identify the device to communicate with
* @param regAddr : Register address
* @param regData : Pointer to the array containing the data to be read or written
* @param length : Length of the array of data
* @return Zero for success, non-zero otherwise
*/
static int8_t spiTransfer(uint8_t devId, uint8_t regAddr, uint8_t *regData, uint16_t length);
private:
/* Private variables */
struct bme680_dev _bme680;
struct bme680_field_data _data;
float _tempOffset;
// Global variables to help create a millisecond timestamp that doesn't overflow every 51 days.
// If it overflows, it will have a negative value. Something that should never happen.
uint32_t millisOverflowCounter;
uint32_t lastTime;
/* Private APIs */
/**
* @brief Get the version of the BSEC library
*/
void getVersion(void);
/**
* @brief Read data from the BME680 and process it
* @param currTimeNs: Current time in ns
* @param bme680Settings: BME680 sensor's settings
* @return true if there are new outputs. false otherwise
*/
bool readProcessData(int64_t currTimeNs, bsec_bme_settings_t bme680Settings);
/**
* @brief Set the BME680 sensor's configuration
* @param bme680Settings: Settings to configure the BME680
* @return BME680 return code. BME680_OK for success, failure otherwise
*/
int8_t setBme680Config(bsec_bme_settings_t bme680Settings);
/**
* @brief Common code for the begin function
*/
void beginCommon(void);
/**
* @brief Function to zero the outputs
*/
void zeroOutputs(void);
};
#endif

View File

@ -15,8 +15,7 @@ env_default = generic
;env_default = ttgov2
;env_default = ttgov21old
;env_default = ttgov21new
;env_default = ttgobeam_old
;env_default = ttgobeam_new
;env_default = ttgobeam
;env_default = lopy
;env_default = lopy4
;env_default = fipy
@ -24,13 +23,13 @@ env_default = generic
;env_default = lolin32lora
;env_default = lolin32lite
;env_default = octopus32
;env_default = ebox, eboxtube, heltec, ttgobeam_old, ttgobeam_new, lopy4, lopy, ttgov21old, ttgov21new
;env_default = ebox, eboxtube, heltec, ttgobeam, lopy4, lopy, ttgov21old, ttgov21new
;
description = Paxcounter is a proof-of-concept ESP32 device for metering passenger flows in realtime. It counts how many mobile devices are around.
[common]
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
release_version = 1.6.972
release_version = 1.7.11
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
debug_level = 0
@ -39,12 +38,12 @@ upload_protocol = esptool
;upload_protocol = custom
extra_scripts = pre:build.py
keyfile = ota.conf
platform_espressif32 = espressif32@1.5.0
;platform_espressif32 = https://github.com/platformio/platform-espressif32.git#a7b1fe6
platform_espressif32 = espressif32@1.6.0
;platform_espressif32 = https://github.com/platformio/platform-espressif32.git#feature/stage
board_build.partitions = min_spiffs.csv
monitor_speed = 115200
lib_deps_lora =
MCCI LoRaWAN LMIC library@2.3.0
MCCI LoRaWAN LMIC library@^2.3.1
lib_deps_display =
U8g2@>=2.25.0
lib_deps_rgbled =
@ -70,8 +69,8 @@ build_flags_basic =
'-DBINTRAY_PACKAGE="${PIOENV}"'
'-DPROGVERSION="${common.release_version}"'
build_flags_sensors =
-Llib/Bosch-BSEC
-llibalgobsec.a
-Llib/Bosch-BSEC/src/esp32/
-lalgobsec
build_flags_all =
${common.build_flags_basic}
${common.build_flags_sensors}
@ -203,33 +202,17 @@ upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:ttgobeam_old]
[env:ttgobeam]
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
lib_deps =
${common.lib_deps_basic}
${common.lib_deps_lora}
${common.lib_deps_gps}
build_flags =
${common.build_flags_all}
-mfix-esp32-psram-cache-issue
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
[env:ttgobeam_new]
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board = ttgo-t-beam
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
lib_deps =
${common.lib_deps_basic}
${common.lib_deps_lora}
${common.lib_deps_gps}
; ${common.lib_deps_display}
build_flags =
${common.build_flags_all}
-mfix-esp32-psram-cache-issue
@ -256,7 +239,7 @@ monitor_speed = ${common.monitor_speed}
[env:lopy]
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board = lopy
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
lib_deps =
@ -273,7 +256,7 @@ monitor_speed = ${common.monitor_speed}
[env:lopy4]
platform = ${common.platform_espressif32}
framework = arduino
board = esp32dev
board = lopy4
board_build.partitions = ${common.board_build.partitions}
upload_speed = 921600
lib_deps =

View File

@ -4,15 +4,22 @@
function Converter(decoded, port) {
var converted = decoded;
var pax = 0;
if (port === 1) {
converted.pax = converted.ble + converted.wifi;
if('wifi' in converted){
pax += converted.wifi
}
if('ble' in converted){
pax += converted.ble
}
converted.pax = pax;
}
if (port === 2) {
converted.voltage /= 1000;
}
return converted;
}

View File

@ -5,12 +5,24 @@ function Decoder(bytes, port) {
var decoded = {};
if (bytes.length === 0) {
return {};
}
if (port === 1) {
// only counter data, no gps
// only wifi counter data, no gps
if (bytes.length === 2) {
return decode(bytes, [uint16], ['wifi']);
}
// wifi + ble counter data, no gps
if (bytes.length === 4) {
return decode(bytes, [uint16, uint16], ['wifi', 'ble']);
}
// combined counter and gps data
// combined wifi counter and gps data
if (bytes.length === 15) {
return decode(bytes, [uint16, latLng, latLng, uint8, hdop, uint16], ['wifi', 'latitude', 'longitude', 'sats', 'hdop', 'altitude']);
}
// combined wifi + ble counter and gps data
if (bytes.length === 17) {
return decode(bytes, [uint16, uint16, latLng, latLng, uint8, hdop, uint16], ['wifi', 'ble', 'latitude', 'longitude', 'sats', 'hdop', 'altitude']);
}
@ -46,6 +58,11 @@ function Decoder(bytes, port) {
return decode(bytes, [float, uint16, ufloat, ufloat], ['temperature', 'pressure', 'humidity', 'air']);
}
if (port === 8) {
// battery voltage
return decode(bytes, [uint16], ['voltage']);
}
}

View File

@ -7,8 +7,11 @@ function Decoder(bytes, port) {
if (port === 1) {
var i = 0;
decoded.wifi = (bytes[i++] << 8) | bytes[i++];
decoded.ble = (bytes[i++] << 8) | bytes[i++];
if (bytes.length >= 2) {
decoded.wifi = (bytes[i++] << 8) | bytes[i++];}
if (bytes.length >= 4) {
decoded.ble = (bytes[i++] << 8) | bytes[i++];}
if (bytes.length > 4) {
decoded.latitude = ((bytes[i++] << 24) | (bytes[i++] << 16) | (bytes[i++] << 8) | bytes[i++]);

View File

@ -13,6 +13,7 @@ https://github.com/nkolban/esp32-snippets/tree/master/BLE/scanner
#include <esp_bt_main.h>
#include <esp_gap_ble_api.h>
#include <esp_blufi_api.h> // needed for BLE_ADDR types, do not remove
#include <esp_coexist.h>
#define BT_BD_ADDR_HEX(addr) \
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]
@ -243,9 +244,16 @@ esp_err_t register_ble_callback(void) {
} // register_ble_callback
#endif // BLECOUNTER
void start_BLEscan(void) {
#ifdef BLECOUNTER
ESP_LOGI(TAG, "Initializing bluetooth scanner ...");
ESP_ERROR_CHECK(esp_coex_preference_set(
(esp_coex_prefer_t)
ESP_COEX_PREFER_BALANCE)); // configure Wifi/BT coexist lib
// Initialize BT controller to allocate task and other resource.
btStart();
ESP_ERROR_CHECK(esp_bluedroid_init());
@ -255,15 +263,18 @@ void start_BLEscan(void) {
ESP_ERROR_CHECK(register_ble_callback());
ESP_LOGI(TAG, "Bluetooth scanner started");
#endif // BLECOUNTER
} // start_BLEscan
void stop_BLEscan(void) {
#ifdef BLECOUNTER
ESP_LOGI(TAG, "Shutting down bluetooth scanner ...");
ESP_ERROR_CHECK(esp_ble_gap_register_callback(NULL));
ESP_ERROR_CHECK(esp_bluedroid_disable());
ESP_ERROR_CHECK(esp_bluedroid_deinit());
btStop(); // disable & deinit bt_controller
btStop(); // disable bt_controller
ESP_ERROR_CHECK(esp_coex_preference_set((
esp_coex_prefer_t)ESP_COEX_PREFER_WIFI)); // configure Wifi/BT coexist lib
ESP_LOGI(TAG, "Bluetooth scanner stopped");
} // stop_BLEscan
#endif // BLECOUNTER
} // stop_BLEscan

View File

@ -8,173 +8,166 @@ static const char TAG[] = "main";
bmeStatus_t bme_status;
TaskHandle_t BmeTask;
float bme_offset = (float) BME_TEMP_OFFSET;
bsec_virtual_sensor_t sensorList[10] = {
BSEC_OUTPUT_RAW_TEMPERATURE,
BSEC_OUTPUT_RAW_PRESSURE,
BSEC_OUTPUT_RAW_HUMIDITY,
BSEC_OUTPUT_RAW_GAS,
BSEC_OUTPUT_IAQ,
BSEC_OUTPUT_STATIC_IAQ,
BSEC_OUTPUT_CO2_EQUIVALENT,
BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
};
// --- Bosch BSEC library configuration ---
// 3,3V supply voltage; 3s max time between sensor_control calls; 4 days
// calibration. Change this const if not applicable for your application (see
// BME680 datasheet)
const uint8_t bsec_config_iaq[454] = {
1, 7, 4, 1, 61, 0, 0, 0, 0, 0, 0, 0, 174, 1, 0,
0, 48, 0, 1, 0, 137, 65, 0, 63, 205, 204, 204, 62, 0, 0,
64, 63, 205, 204, 204, 62, 0, 0, 225, 68, 0, 192, 168, 71, 64,
49, 119, 76, 0, 0, 0, 0, 0, 80, 5, 95, 0, 0, 0, 0,
0, 0, 0, 0, 28, 0, 2, 0, 0, 244, 1, 225, 0, 25, 0,
0, 128, 64, 0, 0, 32, 65, 144, 1, 0, 0, 112, 65, 0, 0,
0, 63, 16, 0, 3, 0, 10, 215, 163, 60, 10, 215, 35, 59, 10,
215, 35, 59, 9, 0, 5, 0, 0, 0, 0, 0, 1, 88, 0, 9,
0, 229, 208, 34, 62, 0, 0, 0, 0, 0, 0, 0, 0, 218, 27,
156, 62, 225, 11, 67, 64, 0, 0, 160, 64, 0, 0, 0, 0, 0,
0, 0, 0, 94, 75, 72, 189, 93, 254, 159, 64, 66, 62, 160, 191,
0, 0, 0, 0, 0, 0, 0, 0, 33, 31, 180, 190, 138, 176, 97,
64, 65, 241, 99, 190, 0, 0, 0, 0, 0, 0, 0, 0, 167, 121,
71, 61, 165, 189, 41, 192, 184, 30, 189, 64, 12, 0, 10, 0, 0,
0, 0, 0, 0, 0, 0, 0, 229, 0, 254, 0, 2, 1, 5, 48,
117, 100, 0, 44, 1, 112, 23, 151, 7, 132, 3, 197, 0, 92, 4,
144, 1, 64, 1, 64, 1, 144, 1, 48, 117, 48, 117, 48, 117, 48,
117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117, 48, 117, 100, 0,
100, 0, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 100, 0, 48,
117, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117,
100, 0, 100, 0, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44,
1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1,
44, 1, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8,
7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7,
112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112,
23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 255, 255,
255, 255, 255, 255, 255, 255, 220, 5, 220, 5, 220, 5, 255, 255, 255,
255, 255, 255, 220, 5, 220, 5, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 44, 1, 0, 0, 0, 0,
239, 79, 0, 0};
uint8_t bsecstate_buffer[BSEC_MAX_STATE_BLOB_SIZE] = {0};
uint16_t stateUpdateCounter = 0;
Bsec iaqSensor;
// initialize BME680 sensor
int bme_init(void) {
// struct bme680_dev gas_sensor;
Wire.begin(HAS_BME, 400000); // I2C connect to BME680 sensor with 400 KHz
// return = 0 -> error / return = 1 -> success
// Call to the function which initializes the BSEC library
// Switch on low-power mode and provide no temperature offset
// block i2c bus access
if (xSemaphoreTake(I2Caccess, (DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) ==
pdTRUE) {
return_values_init ret =
bsec_iot_init(BSEC_SAMPLE_RATE_LP, bme_offset, i2c_write, i2c_read,
user_delay_ms, state_load, config_load);
Wire.begin(HAS_BME);
iaqSensor.begin(BME_ADDR, Wire);
if ((int)ret.bme680_status) {
ESP_LOGE(TAG, "Could not initialize BME680, error %d",
(int)ret.bme680_status);
} else if ((int)ret.bsec_status) {
ESP_LOGE(TAG, "Could not initialize BSEC library, error %d",
(int)ret.bsec_status);
} else {
ESP_LOGI(TAG, "BSEC v%d.%d.%d.%d", iaqSensor.version.major,
iaqSensor.version.minor, iaqSensor.version.major_bugfix,
iaqSensor.version.minor_bugfix);
iaqSensor.setConfig(bsec_config_iaq);
if (checkIaqSensorStatus())
ESP_LOGI(TAG, "BME680 sensor found and initialized");
else {
ESP_LOGE(TAG, "BME680 sensor not found");
goto error;
}
loadState();
iaqSensor.setTemperatureOffset((float)BME_TEMP_OFFSET);
iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP);
if (checkIaqSensorStatus())
ESP_LOGI(TAG, "BSEC subscription succesful");
else {
ESP_LOGE(TAG, "BSEC subscription error");
goto error;
}
} else {
ESP_LOGE(TAG, "I2c bus busy - BME680 initialization error");
goto error;
}
xSemaphoreGive(I2Caccess); // release i2c bus access
return 1;
}
error:
xSemaphoreGive(I2Caccess); // release i2c bus access
return 0;
} // bme_init()
// Helper function definitions
int checkIaqSensorStatus(void) {
int rslt = 1; // true = 1 = no error, false = 0 = error
if (iaqSensor.status != BSEC_OK) {
rslt = 0;
if (iaqSensor.status < BSEC_OK)
ESP_LOGE(TAG, "BSEC error %d", iaqSensor.status);
else
ESP_LOGW(TAG, "BSEC warning %d", iaqSensor.status);
}
void output_ready(int64_t timestamp, float iaq, uint8_t iaq_accuracy,
float temperature, float humidity, float pressure,
float raw_temperature, float raw_humidity, float gas,
bsec_library_return_t bsec_status, float static_iaq,
float co2_equivalent, float breath_voc_equivalent) {
bme_status.temperature = temperature;
bme_status.humidity = humidity;
bme_status.pressure = (pressure / 100.0); // conversion Pa -> hPa
bme_status.iaq = iaq;
if (iaqSensor.bme680Status != BME680_OK) {
rslt = 0;
if (iaqSensor.bme680Status < BME680_OK)
ESP_LOGE(TAG, "BME680 error %d", iaqSensor.bme680Status);
else
ESP_LOGW(TAG, "BME680 warning %d", iaqSensor.bme680Status);
}
return rslt;
} // checkIaqSensorStatus()
// loop function which reads and processes data based on sensor settings
void bme_loop(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
#ifdef HAS_BME
// State is saved every 10.000 samples, which means every 10.000 * 3 secs =
// 500 minutes
bsec_iot_loop(user_delay_ms, get_timestamp_us, output_ready, state_save,
10000);
// block i2c bus access
while (xSemaphoreTake(I2Caccess, portMAX_DELAY) == pdTRUE) {
if (iaqSensor.run()) { // If new data is available
bme_status.raw_temperature = iaqSensor.rawTemperature;
bme_status.raw_humidity = iaqSensor.rawHumidity;
bme_status.temperature = iaqSensor.temperature;
bme_status.humidity = iaqSensor.humidity;
bme_status.pressure =
(iaqSensor.pressure / 100.0); // conversion Pa -> hPa
bme_status.iaq = iaqSensor.iaqEstimate;
bme_status.iaq_accuracy = iaqSensor.iaqAccuracy;
bme_status.gas = iaqSensor.gasResistance;
updateState();
}
xSemaphoreGive(I2Caccess); // release i2c bus access
} // while
#endif
ESP_LOGE(TAG, "BME task ended");
vTaskDelete(BmeTask); // should never be reached
} // bme_loop()
int8_t i2c_read(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data,
uint16_t len) {
int8_t rslt = 0; /* Return 0 for Success, non-zero for failure */
uint16_t i;
Wire.beginTransmission(dev_id);
Wire.write(reg_addr);
rslt = Wire.endTransmission();
Wire.requestFrom((int)dev_id, (int)len);
for (i = 0; (i < len) && Wire.available(); i++) {
reg_data[i] = Wire.read();
void loadState(void) {
if (cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE] == BSEC_MAX_STATE_BLOB_SIZE) {
// Existing state in NVS stored
ESP_LOGI(TAG, "restoring BSEC state from NVRAM");
memcpy(bsecstate_buffer, cfg.bsecstate, BSEC_MAX_STATE_BLOB_SIZE);
iaqSensor.setState(bsecstate_buffer);
checkIaqSensorStatus();
} else // no state stored
ESP_LOGI(TAG,
"no BSEC state stored in NVRAM, starting sensor with defaults");
}
return rslt;
void updateState(void) {
bool update = false;
if (stateUpdateCounter == 0) {
// first state update when IAQ accuracy is >= 1
if (iaqSensor.iaqAccuracy >= 1) {
update = true;
stateUpdateCounter++;
}
} else {
/* Update every STATE_SAVE_PERIOD minutes */
if ((stateUpdateCounter * STATE_SAVE_PERIOD) < millis()) {
update = true;
stateUpdateCounter++;
}
}
int8_t i2c_write(uint8_t dev_id, uint8_t reg_addr, uint8_t *reg_data,
uint16_t len) {
int8_t rslt = 0; /* Return 0 for Success, non-zero for failure */
uint16_t i;
Wire.beginTransmission(dev_id);
Wire.write(reg_addr);
for (i = 0; i < len; i++) {
Wire.write(reg_data[i]);
if (update) {
iaqSensor.getState(bsecstate_buffer);
checkIaqSensorStatus();
memcpy(cfg.bsecstate, bsecstate_buffer, BSEC_MAX_STATE_BLOB_SIZE);
cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE] = BSEC_MAX_STATE_BLOB_SIZE;
ESP_LOGI(TAG, "saving BSEC state to NVRAM");
saveConfig();
}
rslt = Wire.endTransmission();
return rslt;
}
/*!
* @brief Load previous library state from non-volatile memory
*
* @param[in,out] state_buffer buffer to hold the loaded state string
* @param[in] n_buffer size of the allocated state buffer
*
* @return number of bytes copied to state_buffer
*/
uint32_t state_load(uint8_t *state_buffer, uint32_t n_buffer) {
// ...
// Load a previous library state from non-volatile memory, if available.
//
// Return zero if loading was unsuccessful or no state was available,
// otherwise return length of loaded state string.
// ...
return 0;
}
/*!
* @brief Save library state to non-volatile memory
*
* @param[in] state_buffer buffer holding the state to be stored
* @param[in] length length of the state string to be stored
*
* @return none
*/
void state_save(const uint8_t *state_buffer, uint32_t length) {
// ...
// Save the string some form of non-volatile memory, if possible.
// ...
}
uint32_t config_load(uint8_t *config_buffer, uint32_t n_buffer) {
// Load a library config from non-volatile memory, if available.
// Return zero if loading was unsuccessful or no config was available,
// otherwise return length of loaded config string.
memcpy(config_buffer, bsec_config_iaq, sizeof(bsec_config_iaq));
return sizeof(bsec_config_iaq);
}
void user_delay_ms(uint32_t period) { vTaskDelay(period / portTICK_PERIOD_MS); }
int64_t get_timestamp_us() { return (int64_t)millis() * 1000; }
#endif // HAS_BME

View File

@ -25,13 +25,15 @@ void defaultConfig() {
cfg.blescantime =
BLESCANINTERVAL /
10; // BT channel scan cycle [seconds/100], default 1 (= 10ms)
cfg.blescan = 1; // 0=disabled, 1=enabled
cfg.blescan = 0; // 0=disabled, 1=enabled
cfg.wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4)
cfg.vendorfilter = 1; // 0=disabled, 1=enabled
cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
cfg.monitormode = 0; // 0=disabled, 1=enabled
cfg.runmode = 0; // 0=normal, 1=update
cfg.payloadmask = 0xFF; // all payload switched on
cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE] = {
0}; // init BSEC state for BME680 sensor
strncpy(cfg.version, PROGVERSION, sizeof(cfg.version) - 1);
}
@ -77,8 +79,15 @@ void saveConfig() {
int8_t flash8 = 0;
int16_t flash16 = 0;
size_t required_size;
uint8_t bsecstate_buffer[BSEC_MAX_STATE_BLOB_SIZE + 1];
char storedversion[10];
if (nvs_get_blob(my_handle, "bsecstate", bsecstate_buffer,
&required_size) != ESP_OK ||
memcmp(bsecstate_buffer, cfg.bsecstate, BSEC_MAX_STATE_BLOB_SIZE + 1) != 0)
nvs_set_blob(my_handle, "bsecstate", cfg.bsecstate,
BSEC_MAX_STATE_BLOB_SIZE + 1);
if (nvs_get_str(my_handle, "version", storedversion, &required_size) !=
ESP_OK ||
strcmp(storedversion, cfg.version) != 0)
@ -202,7 +211,14 @@ void loadConfig() {
migrateVersion();
}
// overwrite defaults with valid values from NVRAM
// populate pre set defaults with current values from NVRAM
if (nvs_get_blob(my_handle, "bsecstate", NULL, &required_size) == ESP_OK) {
nvs_get_blob(my_handle, "bsecstate", cfg.bsecstate, &required_size);
ESP_LOGI(TAG, "bsecstate = %d",
cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE]);
};
if (nvs_get_i8(my_handle, "lorasf", &flash8) == ESP_OK) {
cfg.lorasf = flash8;
ESP_LOGI(TAG, "lorasf = %d", flash8);
@ -317,9 +333,9 @@ void loadConfig() {
if (nvs_get_i8(my_handle, "payloadmask", &flash8) == ESP_OK) {
cfg.payloadmask = flash8;
ESP_LOGI(TAG, "payloadmask = %u", flash8);
ESP_LOGI(TAG, "payloadmask = %d", flash8);
} else {
ESP_LOGI(TAG, "payloadmask set to default %u", cfg.payloadmask);
ESP_LOGI(TAG, "payloadmask set to default %d", cfg.payloadmask);
saveConfig();
}

View File

@ -26,48 +26,73 @@ void doHousekeeping() {
// time sync once per TIME_SYNC_INTERVAL
#ifdef TIME_SYNC_INTERVAL
if (millis() >= nextTimeSync) {
nextTimeSync = millis() + TIME_SYNC_INTERVAL *
60000; // set up next time sync period
nextTimeSync =
millis() + TIME_SYNC_INTERVAL * 60000; // set up next time sync period
do_timesync();
}
#endif
// task storage debugging //
ESP_LOGD(TAG, "Wifiloop %d bytes left",
uxTaskGetStackHighWaterMark(wifiSwitchTask));
ESP_LOGD(TAG, "IRQhandler %d bytes left",
uxTaskGetStackHighWaterMark(irqHandlerTask));
ESP_LOGD(TAG, "Wifiloop %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(wifiSwitchTask),
eTaskGetState(wifiSwitchTask));
ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(irqHandlerTask),
eTaskGetState(irqHandlerTask));
#ifdef HAS_GPS
ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask));
ESP_LOGD(TAG, "Gpsloop %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(GpsTask), eTaskGetState(GpsTask));
#endif
#ifdef HAS_BME
ESP_LOGD(TAG, "Bmeloop %d bytes left", uxTaskGetStackHighWaterMark(BmeTask));
ESP_LOGD(TAG, "Bmeloop %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(BmeTask), eTaskGetState(BmeTask));
#endif
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
ESP_LOGD(TAG, "LEDloop %d bytes left",
uxTaskGetStackHighWaterMark(ledLoopTask));
ESP_LOGD(TAG, "LEDloop %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(ledLoopTask),
eTaskGetState(ledLoopTask));
#endif
// read battery voltage into global variable
#ifdef HAS_BATTERY_PROBE
batt_voltage = read_voltage();
ESP_LOGI(TAG, "Measured Voltage: %dmV", batt_voltage);
ESP_LOGI(TAG, "Voltage: %dmV", batt_voltage);
#endif
// check free memory
if (esp_get_minimum_free_heap_size() <= MEM_LOW) {
// display BME sensor data
#ifdef HAS_BME
ESP_LOGI(TAG, "BME680 Temp: %.2f°C | IAQ: %.2f | IAQacc: %d",
bme_status.temperature, bme_status.iaq, bme_status.iaq_accuracy);
#endif
// check free heap memory
if (ESP.getMinFreeHeap() <= MEM_LOW) {
ESP_LOGI(TAG,
"Memory full, counter cleared (heap low water mark = %d Bytes / "
"free heap = %d bytes)",
esp_get_minimum_free_heap_size(), ESP.getFreeHeap());
ESP.getMinFreeHeap(), ESP.getFreeHeap());
SendPayload(COUNTERPORT); // send data before clearing counters
reset_counters(); // clear macs container and reset all counters
get_salt(); // get new salt for salting hashes
if (esp_get_minimum_free_heap_size() <= MEM_LOW) // check again
if (ESP.getMinFreeHeap() <= MEM_LOW) // check again
do_reset(); // memory leak, reset device
}
// check free PSRAM memory
#ifdef BOARD_HAS_PSRAM
if (ESP.getMinFreePsram() <= MEM_LOW) {
ESP_LOGI(TAG, "PSRAM full, counter cleared");
SendPayload(COUNTERPORT); // send data before clearing counters
reset_counters(); // clear macs container and reset all counters
get_salt(); // get new salt for salting hashes
if (ESP.getMinFreePsram() <= MEM_LOW) // check again
do_reset(); // memory leak, reset device
}
#endif
} // doHousekeeping()
// uptime counter 64bit to prevent millis() rollover after 49 days
@ -80,6 +105,14 @@ uint64_t uptime() {
return (uint64_t)high32 << 32 | low32;
}
uint32_t getFreeRAM() {
#ifndef BOARD_HAS_PSRAM
return ESP.getFreeHeap();
#else
return ESP.getFreePsram();
#endif
}
void reset_counters() {
macs.clear(); // clear all macs container
macs_total = 0; // reset all counters
@ -89,7 +122,8 @@ void reset_counters() {
void do_timesync() {
#ifdef TIME_SYNC_INTERVAL
// sync time & date if we have valid gps time
// sync time & date by GPS if we have valid gps time
#ifdef HAS_GPS
if (gps.time.isValid()) {
setTime(gps.time.hour(), gps.time.minute(), gps.time.second(),
@ -101,9 +135,14 @@ void do_timesync() {
ESP_LOGI(TAG, "No valid GPS time");
}
#endif // HAS_GPS
// sync time by LoRa Network if network supports DevTimeReq
#ifdef LMIC_ENABLE_DeviceTimeReq
// Schedule a network time request at the next possible time
LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime);
ESP_LOGI(TAG, "Network time request scheduled");
#endif
#endif // TIME_SYNC_INTERVAL
} // do_timesync()

View File

@ -43,14 +43,14 @@ void init_display(const char *Productname, const char *Version) {
u8x8.draw2x2String(0, 0, Productname);
u8x8.setInverseFont(0);
u8x8.draw2x2String(2, 2, Productname);
vTaskDelay(1500 / portTICK_PERIOD_MS);
delay(1500);
u8x8.clear();
u8x8.setFlipMode(1);
u8x8.setInverseFont(1);
u8x8.draw2x2String(0, 0, Productname);
u8x8.setInverseFont(0);
u8x8.draw2x2String(2, 2, Productname);
vTaskDelay(1500 / portTICK_PERIOD_MS);
delay(1500);
u8x8.setFlipMode(0);
u8x8.clear();
@ -81,7 +81,7 @@ void init_display(const char *Productname, const char *Version) {
DisplayKey(buf, 8, true);
#endif // HAS_LORA
vTaskDelay(3000 / portTICK_PERIOD_MS);
delay(3000);
u8x8.clear();
u8x8.setPowerSave(!cfg.screenon); // set display off if disabled
u8x8.draw2x2String(0, 0, "PAX:0");
@ -98,6 +98,10 @@ void init_display(const char *Productname, const char *Version) {
void refreshtheDisplay() {
// block i2c bus access
if (xSemaphoreTake(I2Caccess, (DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) ==
pdTRUE) {
// set display on/off according to current device configuration
if (DisplayState != cfg.screenon) {
DisplayState = cfg.screenon;
@ -114,15 +118,15 @@ void refreshtheDisplay() {
// update counter (lines 0-1)
snprintf(
buff, sizeof(buff), "PAX:%-4d",
(int)macs.size()); // convert 16-bit MAC counter to decimal counter value
(int)
macs.size()); // convert 16-bit MAC counter to decimal counter value
u8x8.draw2x2String(0, 0,
buff); // display number on unique macs total Wifi + BLE
// update Battery status (line 2)
#ifdef HAS_BATTERY_PROBE
u8x8.setCursor(0, 2);
u8x8.printf(batt_voltage > 4000 ? "B:USB " : "B:%.1fV",
batt_voltage / 1000.0);
u8x8.printf("B:%.1fV", batt_voltage / 1000.0);
#endif
// update GPS status (line 2)
@ -167,7 +171,7 @@ void refreshtheDisplay() {
u8x8.setCursor(0, 5);
u8x8.printf(!cfg.rssilimit ? "RLIM:off " : "RLIM:%-4d", cfg.rssilimit);
u8x8.setCursor(10, 5);
u8x8.printf("%4dKB", ESP.getFreeHeap() / 1024);
u8x8.printf("%4dKB", getFreeRAM() / 1024);
#ifdef HAS_LORA
// update LoRa status display (line 6)
@ -183,14 +187,15 @@ void refreshtheDisplay() {
if (msgWaiting) {
sprintf(buff, "%2d", msgWaiting);
u8x8.setCursor(14, 7);
u8x8.setInverseFont(1);
u8x8.printf("%-2s", msgWaiting == SEND_QUEUE_SIZE ? "<>" : buff);
u8x8.setInverseFont(0);
} else
u8x8.printf(" ");
#endif // HAS_LORA
xSemaphoreGive(I2Caccess); // release i2c bus access
}
} // refreshDisplay()
#endif // HAS_DISPLAY

View File

@ -70,12 +70,12 @@ void gps_loop(void *pvParameters) {
Wire.requestFrom(GPS_ADDR, 32); // caution: this is a blocking call
while (Wire.available()) {
gps.encode(Wire.read());
vTaskDelay(2 / portTICK_PERIOD_MS); // 2ms delay according L76 datasheet
delay(2); // 2ms delay according L76 datasheet
}
#endif
} // if
vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU
delay(2); // yield to CPU
} // end of infinite loop

View File

@ -17,8 +17,8 @@
// enable only if device has these sensors, otherwise comment these lines
// BME680 sensor on I2C bus
// don't forget to connect SDIO of BME680 to GND for selecting i2c addr 0x76
#define HAS_BME GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL
#define BME_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND
// user defined sensors
//#define HAS_SENSORS 1 // comment out if device has user defined sensors

View File

@ -12,24 +12,17 @@
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define CFG_sx1276_radio 1
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
#define HAS_LED (25) // white LED on board
#define HAS_BUTTON (0) // button "PROG" on board
#define HAS_LED LED_BUILTIN // white LED on board
#define HAS_BUTTON KEY_BUILTIN // button "PROG" on board
// Pins for I2C interface of OLED Display
#define MY_OLED_SDA (4)
#define MY_OLED_SCL (15)
#define MY_OLED_RST (16)
#define MY_OLED_SDA OLED_SDA
#define MY_OLED_SCL OLED_SCL
#define MY_OLED_RST OLED_RST
// Pins for LORA chip SPI interface come from board file, we need some
// additional definitions for LMIC
#define LORA_IO1 (33)
#define LORA_IO2 LMIC_UNUSED_PIN
#define LORA_SCK (5)
#define LORA_MOSI (27)
#define LORA_MISO (19)
#define LORA_CS (18)
#define LORA_RST (14)
#define LORA_IRQ (26)
#endif

View File

@ -14,23 +14,17 @@
#define CFG_sx1276_radio 1
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
#define HAS_LED (25) // white LED on board
#define HAS_BUTTON (0) // button "PROG" on board
#define HAS_LED LED_BUILTIN // white LED on board
#define HAS_BUTTON KEY_BUILTIN // button "PROG" on board
// Pins for I2C interface of OLED Display
#define MY_OLED_SDA (4)
#define MY_OLED_SCL (15)
#define MY_OLED_RST (16)
#define MY_OLED_SDA OLED_SDA
#define MY_OLED_SCL OLED_SCL
#define MY_OLED_RST OLED_RST
// Pins for LORA chip SPI interface come from board file, we need some
// additional definitions for LMIC
#define LORA_IO1 (35)
#define LORA_IO2 (34)
#define LORA_SCK (5)
#define LORA_MOSI (27)
#define LORA_MISO (19)
#define LORA_CS (18)
#define LORA_RST (14)
#define LORA_IRQ (26)
#endif

View File

@ -8,8 +8,7 @@
// Hardware related definitions for lolin32lite (without LoRa shield)
#define CFG_sx1272_radio 1 // dummy
#define HAS_LED (5) // on board LED on GPIO5
#define HAS_LED LED_BUILTIN // on board LED on GPIO5
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
#endif

View File

@ -12,18 +12,7 @@
#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0
// Pins for LORA chip SPI interface come from board file, we need some
// additional definitions for LMIC
#define LORA_SCK 5 // GPIO5 - SX1276 SCK
#define LORA_MISO 19 // GPIO19 - SX1276 MISO
#define LORA_MOSI 27 // GPIO27 - SX1276 MOSI
#define LORA_CS 17 // GPIO17 - SX1276 CS
#define LORA_RST 18 // GPIO18 - SX1276 RST
#define LORA_IRQ 23 // GPIO23 - SX1276 IO0
#define LORA_IO0 LORA_IRQ // alias
#define LORA_IO1 LORA_IRQ // tied by diode to IO0
#define LORA_IO2 LORA_IRQ // tied by diode to IO0
// Note: Pins for LORA chip SPI interface come from board file pins_arduino.h
// select WIFI antenna (internal = onboard / external = u.fl socket)
#define HAS_ANTENNA_SWITCH (16) // pin for switching wifi antenna

View File

@ -9,29 +9,19 @@
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define HAS_SPI 1 // comment out if device shall not send data via SPI
//#defin HAS_SPI 1 // comment out if device shall not send data via SPI
// pin definitions for local wired SPI slave interface
#define SPI_MOSI GPIO_NUM_22
#define SPI_MISO GPIO_NUM_33
#define SPI_SCLK GPIO_NUM_26
#define SPI_CS GPIO_NUM_36
//#define SPI_MOSI GPIO_NUM_22
//#define SPI_MISO GPIO_NUM_33
//#define SPI_SCLK GPIO_NUM_26
//#define SPI_CS GPIO_NUM_36
#define CFG_sx1276_radio 1
//#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0 (P2)
#define BOARD_HAS_PSRAM // use extra 4MB extern RAM
// Pins for LORA chip SPI interface come from board file, we need some
// additional definitions for LMIC
#define LORA_SCK 5 // GPIO5 - SX1276 SCK
#define LORA_MISO 19 // GPIO19 - SX1276 MISO
#define LORA_MOSI 27 // GPIO27 - SX1276 MOSI
#define LORA_CS 18 // GPIO18 - SX1276 CS
#define LORA_IRQ 23 // GPIO23 - SX1276 IO0
#define LORA_IO0 LORA_IRQ // alias
#define LORA_IO1 LORA_IRQ // tied by diode to IO0
#define LORA_IO2 LORA_IRQ // tied by diode to IO0
#define LORA_RST NOT_A_PIN
// Note: Pins for LORA chip SPI interface come from board file pins_arduino.h
// select WIFI antenna (internal = onboard / external = u.fl socket)
#define HAS_ANTENNA_SWITCH (21) // pin for switching wifi antenna (P12)

View File

@ -11,11 +11,9 @@
// disable brownout detection (avoid unexpected reset on some boards)
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
// enable only if device has these sensors, otherwise comment these lines
// BME680 sensor on I2C bus
// Octopus32 has a pre-populated BME680 on i2c addr 0x76
#define HAS_BME GPIO_NUM_23, GPIO_NUM_22 // SDA, SCL
//#define HAS_BME 0x76
#define BME_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND
// user defined sensors
//#define HAS_SENSORS 1 // comment out if device has user defined sensors

View File

@ -6,41 +6,37 @@
#include <stdint.h>
// Hardware related definitions for TTGO T-Beam board
// (only) for older T-Beam version T22_V05 eternal wiring LORA_IO1 to GPIO33 is needed!
//
// pinouts taken from http://tinymicros.com/wiki/TTGO_T-Beam
//
// enable only if device has these sensors, otherwise comment these lines
// BME680 sensor on I2C bus
// don't forget to connect SDIO of BME680 to GND for selecting i2c addr 0x76
//
//#define HAS_BME GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL
#define HAS_LED GPIO_NUM_14 // on board green LED
// user defined sensors
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
#define HAS_LED GPIO_NUM_14 // on board green LED, only new version TTGO-BEAM V07
//#define HAS_LED GPIO_NUM_21 // on board green LED, only old verison TTGO-BEAM V05
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
#define BOARD_HAS_PSRAM // use extra 4MB external RAM
//#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
#define HAS_BUTTON GPIO_NUM_39 // on board button "SW5"
#define HAS_BUTTON GPIO_NUM_39 // on board button (next to reset)
#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
#define BATT_FACTOR 2 // voltage divider 100k/100k on board
#define HAS_GPS 1 // use on board GPS
#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // UBlox NEO 6M or 7M with default configuration
// Pins for LORA chip SPI interface, reset line and interrupt lines
#define LORA_SCK (5)
#define LORA_CS (18)
#define LORA_MISO (19)
#define LORA_MOSI (27)
#define LORA_RST (23)
#define LORA_IRQ (26)
#define LORA_IO1 (33) // for T-Beam T22_V05 and T22_V07, for other versions use ttgobeam_old.h
#define LORA_IO2 (32)
// enable only if device has these sensors, otherwise comment these lines
// BME680 sensor on I2C bus
//#define HAS_BME GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL
//#define BME_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND
// display (if connected)
//#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
//#define MY_OLED_SDA (21)
//#define MY_OLED_SCL (22)
//#define MY_OLED_RST U8X8_PIN_NONE
//#define DISPLAY_FLIP 1 // use if display is rotated
// user defined sensors (if connected)
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
//#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
#endif

View File

@ -1,45 +0,0 @@
// clang-format off
#ifndef _TTGOBEAM_H
#define _TTGOBEAM_H
#include <stdint.h>
// Hardware related definitions for TTGO T-Beam board
// enable only if device has these sensors, otherwise comment these lines
// BME680 sensor on I2C bus
// attention: Pin21 is also LED! set HAS_LED to NOT_A_PIN if using BME280
// don't forget to connect SDIO of BME680 to GND for selecting i2c addr 0x76
//#define HAS_BME GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL
//#define HAS_LED NOT_A_PIN // on board green LED
#define HAS_LED GPIO_NUM_21 // on board green LED
// user defined sensors
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
#define BOARD_HAS_PSRAM // use extra 4MB external RAM
//#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
#define HAS_BUTTON GPIO_NUM_39 // on board button "BOOT" (next to reset button)
#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
#define BATT_FACTOR 2 // voltage divider 100k/100k on board
#define HAS_GPS 1 // use on board GPS
#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // UBlox NEO 6M or 7M with default configuration
// Pins for LORA chip SPI interface, reset line and interrupt lines
#define LORA_SCK (5)
#define LORA_CS (18)
#define LORA_MISO (19)
#define LORA_MOSI (27)
#define LORA_RST LMIC_UNUSED_PIN
#define LORA_IRQ (26)
#define LORA_IO1 (32) // !! NEEDS EXTERNAL WIRING !!
#define LORA_IO2 LMIC_UNUSED_PIN
#endif

View File

@ -12,22 +12,17 @@
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
//#define DISPLAY_FLIP 1 // uncomment this for rotated display
#define HAS_LED (2) // white LED on board
#define HAS_LED LED_BUILTIN
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
#define HAS_BUTTON (0) // button "PRG" on board
#define HAS_BUTTON KEY_BUILTIN
// Pins for I2C interface of OLED Display
#define MY_OLED_SDA (4)
#define MY_OLED_SCL (15)
#define MY_OLED_RST (16)
#define MY_OLED_SDA OLED_SDA
#define MY_OLED_SCL OLED_SCL
#define MY_OLED_RST OLED_RST
// Pins for LORA chip SPI interface, reset line and interrupt lines
#define LORA_SCK (5)
#define LORA_CS (18)
#define LORA_MISO (19)
#define LORA_MOSI (27)
#define LORA_RST (14)
#define LORA_IRQ (26)
// Pins for LORA chip SPI interface come from board file, we need some
// additional definitions for LMIC
#define LORA_IO1 (33)
#define LORA_IO2 LMIC_UNUSED_PIN

View File

@ -18,17 +18,13 @@
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
// Pins for I2C interface of OLED Display
#define MY_OLED_SDA (21)
#define MY_OLED_SCL (22)
#define MY_OLED_SDA OLED_SDA
#define MY_OLED_SCL OLED_SCL
#define MY_OLED_RST U8X8_PIN_NONE
// Pins for LORA chip SPI interface, reset line and interrupt lines
#define LORA_SCK (5)
#define LORA_CS (18)
#define LORA_MISO (19)
#define LORA_MOSI (27)
// Pins for LORA chip SPI interface come from board file, we need some
// additional definitions for LMIC
#define LORA_RST LMIC_UNUSED_PIN
#define LORA_IRQ (26)
#define LORA_IO1 (33)
#define LORA_IO2 LMIC_UNUSED_PIN

View File

@ -190,7 +190,7 @@ void ledLoop(void *parameter) {
previousLEDState = LEDState;
}
// give yield to CPU
vTaskDelay(2 / portTICK_PERIOD_MS);
delay(2);
} // while(1)
vTaskDelete(ledLoopTask); // shoud never be reached
}; // ledloop()

View File

@ -22,7 +22,7 @@
//#define LMIC_USE_INTERRUPTS
//time sync via LoRaWAN network, is not yet supported by TTN (LoRaWAN spec v1.0.3)
#define LMIC_ENABLE_DeviceTimeReq 1
//#define LMIC_ENABLE_DeviceTimeReq 1
// 16 μs per tick
// LMIC requires ticks to be 15.5μs - 100 μs long
@ -34,7 +34,7 @@
// faster or slower. This causes the transceiver to be earlier switched on,
// so consuming more power. You may sharpen (reduce) this value if you are
// limited on battery.
#define CLOCK_ERROR_PROCENTAGE 20
#define CLOCK_ERROR_PROCENTAGE 3
// Set this to 1 to enable some basic debug output (using printf) about
// RF settings used during transmission and reception. Set to 2 to
@ -89,10 +89,12 @@
// implementation is optimized for speed on 32-bit processors using
// fairly big lookup tables, but it takes up big amounts of flash on the
// AVR architecture.
#define USE_ORIGINAL_AES
//#define USE_ORIGINAL_AES
//
// This selects the AES implementation written by Ideetroon for their
// own LoRaWAN library. It also uses lookup tables, but smaller
// byte-oriented ones, making it use a lot less flash space (but it is
// also about twice as slow as the original).
// #define USE_IDEETRON_AES
//
#define USE_MBEDTLS_AES

View File

@ -222,13 +222,6 @@ void onEvent(ev_t ev) {
: PSTR("TX_COMPLETE"));
sprintf(display_line6, " "); // clear previous lmic status
// if (LMIC.dataLen) {
// ESP_LOGI(TAG, "Received %d bytes of payload, RSSI %d SNR %d",
// LMIC.dataLen, (signed char)LMIC.rssi, (signed
// char)LMIC.snr);
// sprintf(display_line6, "RSSI %d SNR %d", (signed char)LMIC.rssi,
// (signed char)LMIC.snr);
if (LMIC.dataLen) {
ESP_LOGI(TAG, "Received %d bytes of payload, RSSI -%d SNR %d",
LMIC.dataLen, LMIC.rssi, LMIC.snr / 4);

View File

@ -119,7 +119,8 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
"%d Bytes left",
added ? "new " : "known",
sniff_type == MAC_SNIFF_WIFI ? "WiFi" : "BLTH", rssi, buff,
hashedmac, macs_wifi, macs_ble, ESP.getFreeHeap());
hashedmac, macs_wifi, macs_ble, getFreeRAM());
#ifdef VENDORFILTER
} else {
// Very noisy

View File

@ -35,9 +35,14 @@ IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
looptask 1 1 arduino core -> runs the LMIC LoRa stack
irqhandler 1 1 executes tasks triggered by irq
gpsloop 1 2 reads data from GPS via serial or i2c
bmeloop 1 2 reads data from BME sensor via i2c
bmeloop 1 1 reads data from BME sensor via i2c
IDLE 1 0 ESP32 arduino scheduler
Low priority numbers denote low priority tasks.
Tasks using i2c bus all must have same priority, because using mutex semaphore
(irqhandler, bmeloop)
ESP32 hardware timers
==========================
0 Trigger display refresh
@ -55,12 +60,14 @@ char display_line6[16], display_line7[16]; // display buffers
uint8_t volatile channel = 0; // channel rotation counter
uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
batt_voltage = 0; // globals for display
hw_timer_t *channelSwitch = NULL, *sendCycle = NULL, *homeCycle = NULL,
*displaytimer = NULL; // irq tasks
TaskHandle_t irqHandlerTask, wifiSwitchTask;
SemaphoreHandle_t I2Caccess;
std::set<uint16_t> macs; // container holding unique MAC adress hashes
// container holding unique MAC address hashes with Memory Alloctor using PSRAM,
// if present
std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs;
// initialize payload encoder
PayloadConvert payload(PAYLOAD_BUFFER_SIZE);
@ -75,6 +82,14 @@ void setup() {
char features[100] = "";
if (I2Caccess == NULL) // Check that semaphore has not already been created
{
I2Caccess = xSemaphoreCreateMutex(); // Create a mutex semaphore we will use
// to manage the i2c bus
if ((I2Caccess) != NULL)
xSemaphoreGive((I2Caccess)); // Flag the i2c bus available for use
}
// disable brownout detection
#ifdef DISABLE_BROWNOUT
// register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4
@ -105,8 +120,17 @@ void setup() {
chip_info.revision, spi_flash_get_chip_size() / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded"
: "external");
ESP_LOGI(TAG, "ESP32 SDK: %s", ESP.getSdkVersion());
ESP_LOGI(TAG, "Free RAM: %d bytes", ESP.getFreeHeap());
ESP_LOGI(TAG, "Internal Total heap %d, internal Free Heap %d",
ESP.getHeapSize(), ESP.getFreeHeap());
#ifdef BOARD_HAS_PSRAM
ESP_LOGI(TAG, "SPIRam Total heap %d, SPIRam Free Heap %d", ESP.getPsramSize(),
ESP.getFreePsram());
#endif
ESP_LOGI(TAG, "ChipRevision %d, Cpu Freq %d, SDK Version %s",
ESP.getChipRevision(), ESP.getCpuFreqMHz(), ESP.getSdkVersion());
ESP_LOGI(TAG, "Flash Size %d, Flash Speed %d", ESP.getFlashChipSize(),
ESP.getFlashChipSpeed());
ESP_LOGI(TAG, "Wifi/BT software coexist version: %s", esp_coex_version_get());
#ifdef HAS_GPS
ESP_LOGI(TAG, "TinyGPS+ v%s", TinyGPSPlus::libraryVersion());
@ -117,6 +141,12 @@ void setup() {
// read (and initialize on first run) runtime settings from NVRAM
loadConfig(); // includes initialize if necessary
#ifdef BOARD_HAS_PSRAM
assert(psramFound());
ESP_LOGI(TAG, "PSRAM found and initialized");
strcat_P(features, " PSRAM");
#endif
// initialize leds
#if (HAS_LED != NOT_A_PIN)
pinMode(HAS_LED, OUTPUT);
@ -148,13 +178,6 @@ void setup() {
antenna_select(cfg.wifiant);
#endif
// switch off bluetooth, if not compiled
#ifdef BLECOUNTER
strcat_P(features, " BLE");
#else
bool btstop = btStop();
#endif
// initialize battery status
#ifdef HAS_BATTERY_PROBE
strcat_P(features, " BATT");
@ -172,6 +195,26 @@ void setup() {
}
#endif
// start BLE scan callback if BLE function is enabled in NVRAM configuration
// or switch off bluetooth, if not compiled
#ifdef BLECOUNTER
strcat_P(features, " BLE");
if (cfg.blescan) {
ESP_LOGI(TAG, "Starting Bluetooth...");
start_BLEscan();
} else
btStop();
#else
// remove bluetooth stack to gain more free memory
ESP_ERROR_CHECK(esp_bluedroid_disable());
ESP_ERROR_CHECK(esp_bluedroid_deinit());
btStop();
ESP_ERROR_CHECK(esp_bt_controller_deinit());
ESP_ERROR_CHECK(esp_bt_mem_release(ESP_BT_MODE_BTDM));
ESP_ERROR_CHECK(esp_coex_preference_set((
esp_coex_prefer_t)ESP_COEX_PREFER_WIFI)); // configure Wifi/BT coexist lib
#endif
// initialize button
#ifdef HAS_BUTTON
strcat_P(features, " BTN_");
@ -201,21 +244,6 @@ void setup() {
}
#endif
// initialize bme
#ifdef HAS_BME
strcat_P(features, " BME");
if (bme_init()) {
ESP_LOGI(TAG, "Starting BMEloop...");
xTaskCreatePinnedToCore(bme_loop, // task function
"bmeloop", // name of task
4096, // stack size of task
(void *)1, // parameter of the task
2, // priority of the task
&BmeTask, // task handle
1); // CPU core
}
#endif
// initialize sensors
#ifdef HAS_SENSORS
strcat_P(features, " SENS");
@ -290,14 +318,6 @@ void setup() {
#endif
#endif
// start BLE scan callback if BLE function is enabled in NVRAM configuration
#ifdef BLECOUNTER
if (cfg.blescan) {
ESP_LOGI(TAG, "Starting Bluetooth...");
start_BLEscan();
}
#endif
// start wifi in monitor mode and start channel rotation task on core 0
ESP_LOGI(TAG, "Starting Wifi...");
wifi_sniffer_init();
@ -310,7 +330,7 @@ void setup() {
ESP_LOGI(TAG, "Starting IRQ Handler...");
xTaskCreatePinnedToCore(irqHandler, // task function
"irqhandler", // name of task
2048, // stack size of task
4096, // stack size of task
(void *)1, // parameter of the task
1, // priority of the task
&irqHandlerTask, // task handle
@ -326,6 +346,21 @@ void setup() {
&wifiSwitchTask, // task handle
0); // CPU core
// initialize bme
#ifdef HAS_BME
strcat_P(features, " BME");
if (bme_init()) {
ESP_LOGI(TAG, "Starting BMEloop...");
xTaskCreatePinnedToCore(bme_loop, // task function
"bmeloop", // name of task
2048, // stack size of task
(void *)1, // parameter of the task
1, // priority of the task
&BmeTask, // task handle
1); // CPU core
}
#endif
// start timer triggered interrupts
ESP_LOGI(TAG, "Starting Interrupts...");
#ifdef HAS_DISPLAY
@ -352,7 +387,7 @@ void loop() {
#ifdef HAS_LORA
os_runloop_once(); // execute lmic scheduled jobs and events
#endif
vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU
delay(2); // yield to CPU
}
vTaskDelete(NULL); // shoud never be reached

28
src/mbedtls_aes.c Normal file
View File

@ -0,0 +1,28 @@
/*******************************************************************************
*
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
*
* Copyright (c) 2018 Manuel Bleichenbacher
*
* Licensed under MIT License
* https://opensource.org/licenses/MIT
*
* AES encryption using ESP32's hardware AES unit.
*******************************************************************************/
#include "mbedtls/aes.h"
#include "lmic/oslmic.h"
#if defined(USE_MBEDTLS_AES)
void lmic_aes_encrypt(u1_t *data, u1_t *key)
{
mbedtls_aes_context ctx;
mbedtls_aes_init(&ctx);
mbedtls_aes_setkey_enc(&ctx, key, 128);
mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, data, data);
mbedtls_aes_free(&ctx);
}
#endif

View File

@ -77,7 +77,7 @@ void start_ota_update() {
while (i--) {
ESP_LOGI(TAG, "Trying to connect to %s, attempt %u of %u", WIFI_SSID,
WIFI_MAX_TRY - i, WIFI_MAX_TRY);
vTaskDelay(10000 / portTICK_PERIOD_MS); // wait for stable connect
delay(10000); // wait for stable connect
if (WiFi.status() == WL_CONNECTED) {
// we now have wifi connection and try to do an OTA over wifi update
ESP_LOGI(TAG, "Connected to %s", WIFI_SSID);
@ -98,13 +98,13 @@ void start_ota_update() {
// wifi did not connect
ESP_LOGI(TAG, "Could not connect to %s", WIFI_SSID);
display(1, " E", "no WiFi connect");
vTaskDelay(5000 / portTICK_PERIOD_MS);
delay(5000);
end:
switch_LED(LED_OFF);
ESP_LOGI(TAG, "Rebooting to %s firmware", (ret == 0) ? "new" : "current");
display(5, "**", ""); // mark line rebooting
vTaskDelay(5000 / portTICK_PERIOD_MS);
delay(5000);
ESP.restart();
} // start_ota_update

View File

@ -15,7 +15,7 @@
// Set this to include BLE counting and vendor filter functions
#define VENDORFILTER 1 // comment out if you want to count things, not people
#define BLECOUNTER 1 // comment out if you don't want BLE count, saves power & memory
#define BLECOUNTER 1 // activate if you want to use BLE count, at expense of memory
// BLE scan parameters
#define BLESCANTIME 0 // [seconds] scan duration, 0 means infinite [default], see note below
@ -72,9 +72,10 @@
// Settings for BME680 environmental sensor (if present)
#define BME_TEMP_OFFSET 5.0f // Offset sensor on chip temp <-> ambient temp [default = 5°C]
#define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // update every 360 minutes = 4 times a day
// OTA settings
//#define USE_OTA 1 // Comment out to disable OTA update
#define USE_OTA 1 // Comment out to disable OTA update
#define WIFI_MAX_TRY 5 // maximum number of wifi connect attempts for OTA update [default = 20]
#define OTA_MAX_TRY 5 // maximum number of attempts for OTA download and write to flash [default = 3]
#define OTA_MIN_BATT 3600 // minimum battery level for OTA [millivolt]

View File

@ -18,11 +18,9 @@ uint8_t *PayloadConvert::getBuffer(void) { return buffer; }
#if PAYLOAD_ENCODER == 1
void PayloadConvert::addCount(uint16_t value1, uint16_t value2) {
buffer[cursor++] = highByte(value1);
buffer[cursor++] = lowByte(value1);
buffer[cursor++] = highByte(value2);
buffer[cursor++] = lowByte(value2);
void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
buffer[cursor++] = highByte(value);
buffer[cursor++] = lowByte(value);
}
void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) {
@ -135,10 +133,7 @@ void PayloadConvert::addButton(uint8_t value) {
#elif PAYLOAD_ENCODER == 2
void PayloadConvert::addCount(uint16_t value1, uint16_t value2) {
writeUint16(value1);
writeUint16(value2);
}
void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) { writeUint16(value); }
void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) {
writeUint8(rssi);
@ -286,21 +281,27 @@ void PayloadConvert::writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f,
#elif (PAYLOAD_ENCODER == 3 || PAYLOAD_ENCODER == 4)
void PayloadConvert::addCount(uint16_t value1, uint16_t value2) {
void PayloadConvert::addCount(uint16_t value, uint8_t snifftype) {
switch(snifftype) {
case MAC_SNIFF_WIFI:
#if (PAYLOAD_ENCODER == 3)
buffer[cursor++] = LPP_COUNT_WIFI_CHANNEL;
#endif
buffer[cursor++] =
LPP_LUMINOSITY; // workaround since cayenne has no data type meter
buffer[cursor++] = highByte(value1);
buffer[cursor++] = lowByte(value1);
buffer[cursor++] = highByte(value);
buffer[cursor++] = lowByte(value);
break;
case MAC_SNIFF_BLE:
#if (PAYLOAD_ENCODER == 3)
buffer[cursor++] = LPP_COUNT_BLE_CHANNEL;
#endif
buffer[cursor++] =
LPP_LUMINOSITY; // workaround since cayenne has no data type meter
buffer[cursor++] = highByte(value2);
buffer[cursor++] = lowByte(value2);
buffer[cursor++] = highByte(value);
buffer[cursor++] = lowByte(value);
break;
}
}
void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) {

View File

@ -9,7 +9,7 @@ static const char TAG[] = "main";
void do_reset() {
ESP_LOGI(TAG, "Remote command: restart device");
LMIC_shutdown();
vTaskDelay(3000 / portTICK_PERIOD_MS);
delay(3000);
esp_restart();
}
@ -79,13 +79,11 @@ void set_blescantime(uint8_t val[]) {
cfg.blescantime = val[0];
ESP_LOGI(TAG, "Remote command: set BLE scan time to %.1f seconds",
cfg.blescantime / float(100));
#ifdef BLECOUNTER
// stop & restart BLE scan task to apply new parameter
if (cfg.blescan) {
stop_BLEscan();
start_BLEscan();
}
#endif
}
void set_countmode(uint8_t val[]) {
@ -193,14 +191,12 @@ void set_loraadr(uint8_t val[]) {
void set_blescan(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set BLE scanner to %s", val[0] ? "on" : "off");
cfg.blescan = val[0] ? 1 : 0;
#ifdef BLECOUNTER
if (cfg.blescan)
start_BLEscan();
else {
macs_ble = 0; // clear BLE counter
stop_BLEscan();
}
#endif
}
void set_wifiant(uint8_t val[]) {
@ -249,7 +245,7 @@ void get_status(uint8_t val[]) {
#endif
payload.reset();
payload.addStatus(voltage, uptime() / 1000, temperatureRead(),
ESP.getFreeHeap(), rtc_get_reset_reason(0),
getFreeRAM(), rtc_get_reset_reason(0),
rtc_get_reset_reason(1));
SendPayload(STATUSPORT);
};

View File

@ -40,7 +40,21 @@ void sendCounter() {
case COUNT_DATA:
payload.reset();
payload.addCount(macs_wifi, cfg.blescan ? macs_ble : 0);
payload.addCount(macs_wifi, MAC_SNIFF_WIFI);
if (cfg.blescan)
payload.addCount(macs_ble, MAC_SNIFF_BLE);
#ifdef HAS_GPS
if (gps.location.isValid()) { // send GPS position only if we have a fix
gps_read();
payload.addGPS(gps_status);
} else {
ESP_LOGD(
TAG,
"No valid GPS position. GPS data not appended to counter data.");
}
#endif
SendPayload(COUNTERPORT);
// clear counter if not in cumulative counter mode
if (cfg.countermode != 1) {

View File

@ -31,6 +31,8 @@ uint8_t sensor_mask(uint8_t sensor_no) {
return (uint8_t)MEMS_DATA;
case 7:
return (uint8_t)ALARM_DATA;
default:
return 0;
}
}

View File

@ -87,8 +87,7 @@ void spi_slave_task(void *param) {
// wait until spi master clocks out the data, and read results in rx buffer
ESP_LOGI(TAG, "Prepared SPI transaction for %zu byte(s)", transaction_size);
ESP_LOG_BUFFER_HEXDUMP(TAG, txbuf, transaction_size, ESP_LOG_DEBUG);
esp_err_t ret =
spi_slave_transmit(HSPI_HOST, &spi_transaction, portMAX_DELAY);
ESP_ERROR_CHECK_WITHOUT_ABORT(spi_slave_transmit(HSPI_HOST, &spi_transaction, portMAX_DELAY));
ESP_LOG_BUFFER_HEXDUMP(TAG, rxbuf, transaction_size, ESP_LOG_DEBUG);
ESP_LOGI(TAG, "Transaction finished with size %zu bits",
spi_transaction.trans_len);

View File

@ -1,6 +1,8 @@
// Basic Config
#include "globals.h"
#include "wifiscan.h"
#include <esp_coexist.h>
#include "coexist_internal.h"
// Local logging tag
static const char TAG[] = "wifi";
@ -49,6 +51,9 @@ void wifi_sniffer_init(void) {
// .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // only MGMT frames
.filter_mask = WIFI_PROMIS_FILTER_MASK_ALL}; // we use all frames
ESP_ERROR_CHECK(esp_coex_preference_set(
ESP_COEX_PREFER_BALANCE)); // configure Wifi/BT coexist lib
ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // configure Wifi with cfg
ESP_ERROR_CHECK(
esp_wifi_set_country(&wifi_country)); // set locales for RF and channels
@ -69,7 +74,7 @@ void switchWifiChannel(void *parameter) {
channel =
(channel % WIFI_CHANNEL_MAX) + 1; // rotate channel 1..WIFI_CHANNEL_MAX
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
ESP_LOGD(TAG, "Wifi set channel %d", channel);
//ESP_LOGD(TAG, "Wifi set channel %d", channel);
}
vTaskDelete(NULL); // shoud never be reached
}