Merge branch 'development' into master
This commit is contained in:
commit
696e46ce20
@ -28,7 +28,7 @@ This can all be done with a single small and cheap ESP32 board for less than $20
|
||||
*LoRa & SPI*:
|
||||
|
||||
- Heltec: LoRa-32
|
||||
- TTGO: T3_v1, T3_v2, T3_v2.1, T-Beam
|
||||
- TTGO: T1, T2, T3, T-Beam, T-Fox
|
||||
- Pycom: LoPy, LoPy4, FiPy
|
||||
- WeMos: LoLin32 + [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora),
|
||||
LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora)
|
||||
@ -49,6 +49,8 @@ Depending on board hardware following features are supported:
|
||||
- Battery voltage monitoring
|
||||
- GPS (Generic serial NMEA, or Quectel L76 I2C)
|
||||
- Environmental sensor (Bosch BME680 I2C)
|
||||
- Real Time Clock (Maxim DS3231 I2C)
|
||||
- IF482 time telegram generator (serial port)
|
||||
|
||||
Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).<br>
|
||||
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>
|
||||
@ -78,6 +80,8 @@ If your device has a fixed DEVEUI enter this in your local loraconf.h file. Duri
|
||||
|
||||
If your device has silicon **Unique ID** which is stored in serial EEPROM Microchip 24AA02E64 you don't need to change anything. The Unique ID will be read during startup and DEVEUI will be generated from it, overriding settings in loraconf.h.
|
||||
|
||||
If your device has a **real time clock** date/time of rtc will be updated bei either LoRaWAN network or GPS time (if present).
|
||||
|
||||
# Building
|
||||
|
||||
Use <A HREF="https://platformio.org/">PlatformIO</A> with your preferred IDE for development and building this code. Make sure you have latest PlatformIO version.
|
||||
|
@ -11,6 +11,11 @@
|
||||
#include "bme680mems.h"
|
||||
#endif
|
||||
|
||||
// Needed for RTC time sync if RTC present on board
|
||||
#ifdef HAS_RTC
|
||||
#include "rtctime.h"
|
||||
#endif
|
||||
|
||||
void doHousekeeping(void);
|
||||
void do_timesync(void);
|
||||
uint64_t uptime(void);
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include "mallocator.h"
|
||||
//#include "inc/bsec_datatypes.h"
|
||||
#include "../lib/Bosch-BSEC/src/inc/bsec_datatypes.h"
|
||||
|
||||
// sniffing types
|
||||
@ -36,6 +35,10 @@
|
||||
#define BLE_MODE (0x40)
|
||||
#define SCREEN_MODE (0x80)
|
||||
|
||||
// I2C bus access control
|
||||
#define I2C_MUTEX_LOCK() xSemaphoreTake(I2Caccess, (DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) == pdTRUE
|
||||
#define I2C_MUTEX_UNLOCK() xSemaphoreGive(I2Caccess)
|
||||
|
||||
// Struct holding devices's runtime configuration
|
||||
typedef struct {
|
||||
uint8_t lorasf; // 7-12, lora spreadfactor
|
||||
@ -137,4 +140,8 @@ extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
||||
#include "bme680mems.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAS_IF482
|
||||
#include "if482.h"
|
||||
#endif
|
||||
|
||||
#endif
|
@ -2,7 +2,7 @@
|
||||
#define _GPSREAD_H
|
||||
|
||||
#include <TinyGPS++.h> // library for parsing NMEA data
|
||||
#include <TimeLib.h>
|
||||
#include <Time.h>
|
||||
|
||||
#ifdef GPS_I2C // Needed for reading from I2C Bus
|
||||
#include <Wire.h>
|
||||
|
13
include/if482.h
Normal file
13
include/if482.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef _IF482_H
|
||||
#define _IF482_H
|
||||
|
||||
#include "globals.h"
|
||||
#include "irqhandler.h"
|
||||
|
||||
extern TaskHandle_t IF482Task;
|
||||
|
||||
int if482_init(void);
|
||||
void if482_loop(void *pvParameters);
|
||||
void IRAM_ATTR IF482IRQ(void);
|
||||
|
||||
#endif
|
@ -17,6 +17,11 @@
|
||||
#include <Wire.h>
|
||||
#endif
|
||||
|
||||
// Needed for RTC time sync if RTC present on board
|
||||
#ifdef HAS_RTC
|
||||
#include "rtctime.h"
|
||||
#endif
|
||||
|
||||
extern QueueHandle_t LoraSendQueue;
|
||||
|
||||
void onEvent(ev_t ev);
|
||||
|
32
include/rtctime.h
Normal file
32
include/rtctime.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef _RTCTIME_H
|
||||
#define _RTCTIME_H
|
||||
|
||||
#include "globals.h"
|
||||
#include <Time.h>
|
||||
#include <Timezone.h>
|
||||
#include <Wire.h> // must be included here so that Arduino library object file references work
|
||||
#include <RtcDS3231.h>
|
||||
|
||||
#ifdef HAS_GPS
|
||||
#include "gpsread.h"
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
useless = 0, // waiting for good enough signal
|
||||
dirty = 1, // time data available but inconfident
|
||||
reserve = 2, // clock was once synced but now may deviate
|
||||
synced_LORA = 3, // clock driven by LORAWAN network
|
||||
synced_GPS = 4 // best possible quality, clock is driven by GPS
|
||||
} clock_state_t;
|
||||
|
||||
extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available
|
||||
extern Timezone myTZ; // make Timezone myTZ globally available
|
||||
|
||||
int rtc_init(void);
|
||||
int set_rtctime(uint32_t UTCTime);
|
||||
int set_rtctime(RtcDateTime now);
|
||||
void sync_rtctime(void);
|
||||
time_t get_rtctime(void);
|
||||
float get_rtctemp(void);
|
||||
|
||||
#endif // _RTCTIME_H
|
@ -30,7 +30,7 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng
|
||||
|
||||
[common]
|
||||
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
||||
release_version = 1.7.11
|
||||
release_version = 1.7.14
|
||||
; 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 = 3
|
||||
@ -45,11 +45,13 @@ monitor_speed = 115200
|
||||
lib_deps_lora =
|
||||
MCCI LoRaWAN LMIC library@^2.3.1
|
||||
lib_deps_display =
|
||||
U8g2@>=2.25.0
|
||||
U8g2@>=2.25.5
|
||||
lib_deps_rgbled =
|
||||
SmartLeds@>=1.1.3
|
||||
lib_deps_gps =
|
||||
TinyGPSPlus@>=1.0.2
|
||||
lib_deps_rtc =
|
||||
RTC@^2.3.0
|
||||
lib_deps_basic =
|
||||
ArduinoJson@^5.13.1
|
||||
Time@>=1.5
|
||||
@ -59,6 +61,7 @@ lib_deps_all =
|
||||
${common.lib_deps_display}
|
||||
${common.lib_deps_rgbled}
|
||||
${common.lib_deps_gps}
|
||||
${common.lib_deps_rtc}
|
||||
build_flags_basic =
|
||||
-include "src/hal/${PIOENV}.h"
|
||||
-include "src/paxcounter.conf"
|
||||
@ -212,6 +215,7 @@ lib_deps =
|
||||
${common.lib_deps_basic}
|
||||
${common.lib_deps_lora}
|
||||
${common.lib_deps_display}
|
||||
${common.lib_deps_rtc}
|
||||
build_flags =
|
||||
${common.build_flags_basic}
|
||||
upload_protocol = ${common.upload_protocol}
|
||||
|
@ -1,5 +1,3 @@
|
||||
#ifdef BLECOUNTER
|
||||
|
||||
/* code snippets taken from
|
||||
https://github.com/nkolban/esp32-snippets/tree/master/BLE/scanner
|
||||
*/
|
||||
@ -235,8 +233,6 @@ esp_err_t register_ble_callback(void) {
|
||||
|
||||
} // register_ble_callback
|
||||
|
||||
#endif // BLECOUNTER
|
||||
|
||||
void start_BLEscan(void) {
|
||||
#ifdef BLECOUNTER
|
||||
ESP_LOGI(TAG, "Initializing bluetooth scanner ...");
|
||||
|
@ -32,8 +32,7 @@ int bme_init(void) {
|
||||
// return = 0 -> error / return = 1 -> success
|
||||
|
||||
// block i2c bus access
|
||||
if (xSemaphoreTake(I2Caccess, (DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) ==
|
||||
pdTRUE) {
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
|
||||
Wire.begin(HAS_BME);
|
||||
iaqSensor.begin(BME_ADDR, Wire);
|
||||
@ -62,17 +61,16 @@ int bme_init(void) {
|
||||
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
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
return 1;
|
||||
|
||||
error:
|
||||
xSemaphoreGive(I2Caccess); // release i2c bus access
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
return 0;
|
||||
|
||||
} // bme_init()
|
||||
@ -106,24 +104,24 @@ void bme_loop(void *pvParameters) {
|
||||
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
||||
|
||||
#ifdef HAS_BME
|
||||
// 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();
|
||||
while (1) {
|
||||
// block i2c bus access
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
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();
|
||||
}
|
||||
I2C_MUTEX_UNLOCK();
|
||||
}
|
||||
xSemaphoreGive(I2Caccess); // release i2c bus access
|
||||
|
||||
} // while
|
||||
}
|
||||
#endif
|
||||
ESP_LOGE(TAG, "BME task ended");
|
||||
vTaskDelete(BmeTask); // should never be reached
|
||||
|
@ -123,25 +123,30 @@ void reset_counters() {
|
||||
void do_timesync() {
|
||||
#ifdef TIME_SYNC_INTERVAL
|
||||
|
||||
// sync time & date by GPS if we have valid gps time
|
||||
// set system time to time source GPS, if we have valid gps time
|
||||
#ifdef HAS_GPS
|
||||
if (gps.time.isValid()) {
|
||||
setTime(gps.time.hour(), gps.time.minute(), gps.time.second(),
|
||||
gps.date.day(), gps.date.month(), gps.date.year());
|
||||
ESP_LOGI(TAG, "Time synced by GPS to %02d:%02d:%02d", hour(), minute(),
|
||||
second());
|
||||
// set RTC time to time source GPS, if RTC is present
|
||||
#ifdef HAS_RTC
|
||||
if (!set_rtctime(RtcDateTime(now())))
|
||||
ESP_LOGE(TAG, "RTC set time failure");
|
||||
#endif
|
||||
time_t tt = myTZ.toLocal(now());
|
||||
ESP_LOGI(TAG, "GPS has set system time to %02d/%02d/%d %02d:%02d:%02d",
|
||||
month(tt), day(tt), year(tt), hour(tt), minute(tt), second(tt));
|
||||
return;
|
||||
} else {
|
||||
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
|
||||
// set system time to time source LoRa Network, if network supports DevTimeReq
|
||||
#elif defined LMIC_ENABLE_DeviceTimeReq
|
||||
// Schedule a network time sync request at the next possible time
|
||||
LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime);
|
||||
ESP_LOGI(TAG, "Network time request scheduled");
|
||||
#endif
|
||||
#endif // HAS_GPS
|
||||
|
||||
#endif // TIME_SYNC_INTERVAL
|
||||
} // do_timesync()
|
||||
|
@ -1,5 +1,27 @@
|
||||
#ifdef HAS_DISPLAY
|
||||
|
||||
/*
|
||||
|
||||
Display-Mask (128 x 64 pixel):
|
||||
|
||||
| 111111
|
||||
|0123456789012345
|
||||
------------------
|
||||
0|PAX:aabbccddee
|
||||
1|PAX:aabbccddee
|
||||
2|B:a.bcV Sats:ab
|
||||
3|BLTH:abcde SF:ab
|
||||
4|WIFI:abcde ch:ab
|
||||
5|RLIM:abcd abcdKB
|
||||
6|xxxxxxxxxxxxxxxx
|
||||
6|20:27:00* 27.Feb
|
||||
7|yyyyyyyyyyyyyyab
|
||||
|
||||
line 6: x = Text for LORA status OR time/date
|
||||
line 7: y = Text for LMIC status; ab = payload queue
|
||||
|
||||
*/
|
||||
|
||||
// Basic Config
|
||||
#include "globals.h"
|
||||
#include <esp_spi_flash.h> // needed for reading ESP32 chip attributes
|
||||
@ -19,6 +41,10 @@ const char lora_datarate[] = {"1211100908078CNA1211109C8C7C"};
|
||||
const char lora_datarate[] = {"121110090807FSNA"};
|
||||
#endif
|
||||
|
||||
// helper arry for converting month values to text
|
||||
char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||
|
||||
uint8_t volatile DisplayState = 0;
|
||||
|
||||
// helper function, prints a hex key on display
|
||||
@ -99,8 +125,7 @@ void init_display(const char *Productname, const char *Version) {
|
||||
void refreshtheDisplay() {
|
||||
|
||||
// block i2c bus access
|
||||
if (xSemaphoreTake(I2Caccess, (DISPLAYREFRESH_MS / portTICK_PERIOD_MS)) ==
|
||||
pdTRUE) {
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
|
||||
// set display on/off according to current device configuration
|
||||
if (DisplayState != cfg.screenon) {
|
||||
@ -126,26 +151,29 @@ void refreshtheDisplay() {
|
||||
// update Battery status (line 2)
|
||||
#ifdef HAS_BATTERY_PROBE
|
||||
u8x8.setCursor(0, 2);
|
||||
u8x8.printf("B:%.1fV", batt_voltage / 1000.0);
|
||||
u8x8.printf("B:%.2fV", batt_voltage / 1000.0);
|
||||
#endif
|
||||
|
||||
// update GPS status (line 2)
|
||||
#ifdef HAS_GPS
|
||||
u8x8.setCursor(9, 2);
|
||||
if (!gps.location.isValid()) // if no fix then display Sats value inverse
|
||||
{
|
||||
u8x8.setInverseFont(1);
|
||||
u8x8.printf("Sats:%.2d", gps.satellites.value());
|
||||
u8x8.setInverseFont(0);
|
||||
} else
|
||||
u8x8.printf("Sats:%.2d", gps.satellites.value());
|
||||
// have we ever got valid gps data?
|
||||
if (gps.passedChecksum() > 0) {
|
||||
u8x8.setCursor(9, 2);
|
||||
if (!gps.location.isValid()) // if no fix then display Sats value inverse
|
||||
{
|
||||
u8x8.setInverseFont(1);
|
||||
u8x8.printf("Sats:%.2d", gps.satellites.value());
|
||||
u8x8.setInverseFont(0);
|
||||
} else
|
||||
u8x8.printf("Sats:%.2d", gps.satellites.value());
|
||||
}
|
||||
#endif
|
||||
|
||||
// update bluetooth counter + LoRa SF (line 3)
|
||||
// update bluetooth counter + LoRa SF (line 3)
|
||||
#ifdef BLECOUNTER
|
||||
u8x8.setCursor(0, 3);
|
||||
if (cfg.blescan)
|
||||
u8x8.printf("BLTH:%-4d", macs_ble);
|
||||
u8x8.printf("BLTH:%-5d", macs_ble);
|
||||
else
|
||||
u8x8.printf("%s", "BLTH:off");
|
||||
#endif
|
||||
@ -163,7 +191,7 @@ void refreshtheDisplay() {
|
||||
|
||||
// update wifi counter + channel display (line 4)
|
||||
u8x8.setCursor(0, 4);
|
||||
u8x8.printf("WIFI:%-4d", macs_wifi);
|
||||
u8x8.printf("WIFI:%-5d", macs_wifi);
|
||||
u8x8.setCursor(11, 4);
|
||||
u8x8.printf("ch:%02d", channel);
|
||||
|
||||
@ -174,9 +202,18 @@ void refreshtheDisplay() {
|
||||
u8x8.printf("%4dKB", getFreeRAM() / 1024);
|
||||
|
||||
#ifdef HAS_LORA
|
||||
// update LoRa status display (line 6)
|
||||
|
||||
u8x8.setCursor(0, 6);
|
||||
#ifndef HAS_RTC
|
||||
// update LoRa status display (line 6)
|
||||
u8x8.printf("%-16s", display_line6);
|
||||
#else
|
||||
// update time/date display (line 6)
|
||||
time_t t = myTZ.toLocal(now());
|
||||
u8x8.printf("%02d:%02d:%02d%c %2d.%3s", hour(t), minute(t), second(t),
|
||||
timeStatus() == timeSet ? '*' : '?', day(t),
|
||||
printmonth[month(t)]);
|
||||
#endif
|
||||
|
||||
// update LMiC event display (line 7)
|
||||
u8x8.setCursor(0, 7);
|
||||
@ -193,7 +230,7 @@ void refreshtheDisplay() {
|
||||
|
||||
#endif // HAS_LORA
|
||||
|
||||
xSemaphoreGive(I2Caccess); // release i2c bus access
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
}
|
||||
|
||||
} // refreshDisplay()
|
||||
|
@ -10,7 +10,7 @@ gpsStatus_t gps_status;
|
||||
TaskHandle_t GpsTask;
|
||||
|
||||
#ifdef GPS_SERIAL
|
||||
HardwareSerial GPS_Serial(1);
|
||||
HardwareSerial GPS_Serial(1); // use UART #1
|
||||
#endif
|
||||
|
||||
// initialize and configure GPS
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
// Hardware related definitions for generic ESP32 boards
|
||||
// generic.h is kitchensink with all available options
|
||||
|
||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa or has no LoRa
|
||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||
@ -30,7 +31,7 @@
|
||||
|
||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||
//#define DISPLAY_FLIP 1 // use if display is rotated
|
||||
#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // uses GPIO7
|
||||
#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_LED (21) // on board LED
|
||||
@ -47,6 +48,9 @@
|
||||
#define MY_OLED_SCL (15)
|
||||
#define MY_OLED_RST (16)
|
||||
|
||||
// Pins for on board DS3231 RTC chip
|
||||
#define HAS_RTC MY_OLED_SDA, MY_OLED_SCL // SDA, SCL
|
||||
|
||||
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
||||
#define LORA_SCK (5)
|
||||
#define LORA_CS (18)
|
||||
|
@ -24,13 +24,13 @@
|
||||
|
||||
// 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
|
||||
//#define HAS_BME 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_SDA SDA
|
||||
//#define MY_OLED_SCL SCL
|
||||
//#define MY_OLED_RST U8X8_PIN_NONE
|
||||
//#define DISPLAY_FLIP 1 // use if display is rotated
|
||||
|
||||
|
@ -9,9 +9,9 @@
|
||||
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||
|
||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||
#define HAS_LED NOT_A_PIN // green on board LED
|
||||
//#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
||||
#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // uses GPIO7
|
||||
#define HAS_LED NOT_A_PIN // green on board LED is useless, is GPIO25, which switches power for Lora+Display
|
||||
#define HAS_LOWPOWER_SWITCH GPIO_NUM_25 // switches power for LoRa chip + display (0 = off / 1 = on)
|
||||
#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL
|
||||
#define BATT_FACTOR 2 // voltage divider 100k/100k on board
|
||||
#define HAS_BUTTON GPIO_NUM_36 // on board button (next to reset)
|
||||
|
||||
@ -20,6 +20,13 @@
|
||||
#define MY_OLED_SCL (22)
|
||||
#define MY_OLED_RST U8X8_PIN_NONE
|
||||
|
||||
// Pins for on board DS3231 RTC chip
|
||||
#define HAS_RTC MY_OLED_SDA, MY_OLED_SCL // SDA, SCL
|
||||
#define RTC_INT GPIO_NUM_34 // interrupt input from rtc
|
||||
|
||||
// Settings for IF482 interface
|
||||
//#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters
|
||||
|
||||
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
||||
#define LORA_SCK (5)
|
||||
#define LORA_CS (18)
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||
#define HAS_LED (25) // green on board LED
|
||||
#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // uses GPIO7
|
||||
#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
|
||||
#define BATT_FACTOR 2 // voltage divider 100k/100k on board
|
||||
|
||||
// Pins for I2C interface of OLED Display
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||
//#define DISPLAY_FLIP 1 // rotated display
|
||||
//#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // uses GPIO7
|
||||
//#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
|
||||
//#define BATT_FACTOR 2 // voltage divider 100k/100k on board
|
||||
|
||||
// Pins for I2C interface of OLED Display
|
||||
|
160
src/if482.cpp
Normal file
160
src/if482.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
#if defined HAS_IF482 && defined HAS_RTC
|
||||
|
||||
/*
|
||||
|
||||
IF482 Generator to control clocks with IF482 telegram input (e.g. BÜRK BU190)
|
||||
|
||||
Example IF482 telegram: "OAL160806F170400"
|
||||
|
||||
IF482 Specification:
|
||||
http://www.mobatime.com/fileadmin/user_upload/downloads/TE-112023.pdf
|
||||
|
||||
The IF 482 telegram is a time telegram, which sends the time and date
|
||||
information as ASCII characters through the serial interface RS 232 or RS 422.
|
||||
|
||||
Communication parameters:
|
||||
|
||||
Baud rate: 9600 Bit/s
|
||||
Data bits 7
|
||||
Parity: even
|
||||
Stop bit: 1
|
||||
Jitter: < 50ms
|
||||
|
||||
Interface : RS232 or RS422
|
||||
|
||||
Synchronization: Telegram ends at the beginning of the second
|
||||
specified in the telegram
|
||||
|
||||
Cycle: 1 second
|
||||
|
||||
Format of ASCII telegram string:
|
||||
|
||||
Byte Meaning ASCII Hex
|
||||
1 Start of telegram O 4F
|
||||
2 Monitoring* A 41
|
||||
3 Time-Season** W/S/U/L 57 or 53
|
||||
4 Year tens 0 .. 9 30 .. 39
|
||||
5 Year unit 0 .. 9 30 .. 39
|
||||
6 Month tens 0 or 1 30 or 31
|
||||
7 Month unit 0 .. 9 30 .. 39
|
||||
8 Day tens 0 .. 3 30 .. 33
|
||||
9 Day unit 0 .. 9 30 .. 39
|
||||
10 Day of week*** 1 .. 7 31 .. 37
|
||||
11 Hours tens 0 .. 2 30 .. 32
|
||||
12 Hours unit 0 .. 9 30 .. 39
|
||||
13 Minutes tens 0 .. 5 30 .. 35
|
||||
14 Minutes unit 0 .. 9 30 .. 39
|
||||
15 Seconds tens 0 .. 5 30 .. 35
|
||||
16 Seconds unit 0 .. 9 30 .. 39
|
||||
17 End of telegram CR 0D
|
||||
|
||||
*) Monitoring:
|
||||
With a correctly received time in the sender unit, the ASCII character 'A' is
|
||||
issued. If 'M' is issued, this indicates that the sender was unable to receive
|
||||
any time signal for over 12 hours (time is accepted with ‘A’ and ‘M’).
|
||||
|
||||
**) Season:
|
||||
W: Standard time,
|
||||
S: Season time,
|
||||
U: UTC time (not supported by all systems),
|
||||
L: Local Time
|
||||
|
||||
***) Day of week:
|
||||
not evaluated by model BU-190
|
||||
|
||||
*/
|
||||
|
||||
#include "if482.h"
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = "main";
|
||||
|
||||
TaskHandle_t IF482Task;
|
||||
|
||||
HardwareSerial IF482(2); // use UART #2 (note: #1 may be in use for serial GPS)
|
||||
|
||||
// initialize and configure GPS
|
||||
int if482_init(void) {
|
||||
|
||||
// open serial interface
|
||||
IF482.begin(HAS_IF482);
|
||||
|
||||
// use rtc 1Hz clock for triggering IF482 telegram send
|
||||
Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz);
|
||||
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock);
|
||||
pinMode(RTC_INT, INPUT_PULLUP);
|
||||
|
||||
ESP_LOGI(TAG, "IF482 generator initialized");
|
||||
|
||||
return 1;
|
||||
|
||||
} // if482_init
|
||||
|
||||
String if482Telegram(time_t t) {
|
||||
|
||||
char mon;
|
||||
char buf[14] = "000000F000000";
|
||||
char out[17];
|
||||
|
||||
switch (timeStatus()) { // indicates if time has been set and recently synced
|
||||
case timeSet: // time is set and is synced
|
||||
mon = 'A';
|
||||
break;
|
||||
case timeNeedsSync: // time had been set but sync attempt did not succeed
|
||||
mon = 'M';
|
||||
break;
|
||||
default: // time not set, no valid time
|
||||
mon = '?';
|
||||
break;
|
||||
} // switch
|
||||
|
||||
if (!timeNotSet) // do we have valid time?
|
||||
snprintf(buf, sizeof buf, "%02u%02u%02u%1u%02u%02u%02u", year(t) - 2000,
|
||||
month(t), day(t), weekday(t), hour(t), minute(t), second(t));
|
||||
|
||||
snprintf(out, sizeof out, "O%cL%s\r", mon, buf);
|
||||
return out;
|
||||
}
|
||||
|
||||
void if482_loop(void *pvParameters) {
|
||||
|
||||
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
||||
|
||||
TickType_t wakeTime;
|
||||
time_t t, tt;
|
||||
const TickType_t shotTime = pdMS_TO_TICKS(IF482_OFFSET);
|
||||
|
||||
// wait until begin of a new second
|
||||
t = tt = now();
|
||||
do {
|
||||
tt = now();
|
||||
} while (t == tt);
|
||||
|
||||
const TickType_t startOffset = xTaskGetTickCount();
|
||||
|
||||
// task remains in blocked state until it is notified by isr
|
||||
for (;;) {
|
||||
xTaskNotifyWait(
|
||||
0x00, // don't clear any bits on entry
|
||||
ULONG_MAX, // clear all bits on exit
|
||||
&wakeTime, // receives moment of call from isr
|
||||
portMAX_DELAY); // wait forever (missing error handling here...)
|
||||
|
||||
t = myTZ.toLocal(now());
|
||||
wakeTime -= startOffset;
|
||||
|
||||
// now we're synced to start of second t and wait
|
||||
// until it's time to start transmit telegram for t+1
|
||||
vTaskDelayUntil(&wakeTime, shotTime);
|
||||
IF482.print(if482Telegram(t+1));
|
||||
}
|
||||
vTaskDelete(IF482Task); // shoud never be reached
|
||||
} // if482_loop()
|
||||
|
||||
// interrupt service routine triggered by RTC 1Hz precise clock
|
||||
void IRAM_ATTR IF482IRQ() {
|
||||
xTaskNotifyFromISR(IF482Task, xTaskGetTickCountFromISR(), eSetBits, NULL);
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
||||
#endif // HAS_IF482
|
@ -61,7 +61,7 @@ void IRAM_ATTR SendCycleIRQ() {
|
||||
|
||||
#ifdef HAS_DISPLAY
|
||||
void IRAM_ATTR DisplayIRQ() {
|
||||
xTaskNotifyFromISR(irqHandlerTask, DISPLAY_IRQ, eSetBits, NULL);
|
||||
xTaskNotifyFromISR(irqHandlerTask, DISPLAY_IRQ, eSetBits, NULL);
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
#endif
|
||||
|
@ -455,6 +455,12 @@ void user_request_network_time_callback(void *pVoidUserUTCTime,
|
||||
|
||||
// Update system time with time read from the network
|
||||
setTime(*pUserUTCTime);
|
||||
ESP_LOGI(TAG, "Time synced by LoRa network to %02d:%02d:%02d", hour(),
|
||||
minute(), second());
|
||||
#ifdef HAS_RTC
|
||||
if (!set_rtctime(*pUserUTCTime))
|
||||
ESP_LOGE(TAG, "RTC set time failure");
|
||||
#endif
|
||||
time_t t = myTZ.toLocal(now());
|
||||
ESP_LOGI(TAG,
|
||||
"LORA Network has set system time to %02d/%02d/%d %02d:%02d:%02d",
|
||||
month(t), day(t), year(t), hour(t), minute(t), second(t));
|
||||
}
|
68
src/main.cpp
68
src/main.cpp
@ -29,6 +29,7 @@ Task Core Prio Purpose
|
||||
====================================================================================
|
||||
wifiloop 0 4 rotates wifi channels
|
||||
ledloop 0 3 blinks LEDs
|
||||
if482loop 1 3 serial feed of IF482 time telegrams
|
||||
spiloop 0 2 reads/writes data on spi interface
|
||||
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
|
||||
|
||||
@ -44,11 +45,15 @@ Tasks using i2c bus all must have same priority, because using mutex semaphore
|
||||
(irqhandler, bmeloop)
|
||||
|
||||
ESP32 hardware timers
|
||||
==========================
|
||||
0 Trigger display refresh
|
||||
1 Trigger Wifi channel switch
|
||||
2 Trigger send payload cycle
|
||||
3 Trigger housekeeping cycle
|
||||
================================
|
||||
0 triggers display refresh
|
||||
1 triggers Wifi channel switch
|
||||
2 triggers send payload cycle
|
||||
3 triggers housekeeping cycle
|
||||
|
||||
RTC hardware timer (if present)
|
||||
================================
|
||||
triggers IF482 clock generator
|
||||
|
||||
*/
|
||||
|
||||
@ -147,6 +152,13 @@ void setup() {
|
||||
strcat_P(features, " PSRAM");
|
||||
#endif
|
||||
|
||||
// set low power mode to off
|
||||
#ifdef HAS_LOWPOWER_SWITCH
|
||||
pinMode(HAS_LED, OUTPUT);
|
||||
digitalWrite(HAS_LOWPOWER_SWITCH, HIGH);
|
||||
strcat_P(features, " LPWR");
|
||||
#endif
|
||||
|
||||
// initialize leds
|
||||
#if (HAS_LED != NOT_A_PIN)
|
||||
pinMode(HAS_LED, OUTPUT);
|
||||
@ -161,7 +173,7 @@ void setup() {
|
||||
|
||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||
// start led loop
|
||||
ESP_LOGI(TAG, "Starting LEDloop...");
|
||||
ESP_LOGI(TAG, "Starting LED Controller...");
|
||||
xTaskCreatePinnedToCore(ledLoop, // task function
|
||||
"ledloop", // name of task
|
||||
1024, // stack size of task
|
||||
@ -171,7 +183,26 @@ void setup() {
|
||||
0); // CPU core
|
||||
#endif
|
||||
|
||||
// initialize wifi antenna
|
||||
// initialize RTC
|
||||
#ifdef HAS_RTC
|
||||
strcat_P(features, " RTC");
|
||||
assert(rtc_init());
|
||||
sync_rtctime();
|
||||
#ifdef HAS_IF482
|
||||
strcat_P(features, " IF482");
|
||||
assert(if482_init());
|
||||
ESP_LOGI(TAG, "Starting IF482 Generator...");
|
||||
xTaskCreatePinnedToCore(if482_loop, // task function
|
||||
"if482loop", // name of task
|
||||
2048, // stack size of task
|
||||
(void *)1, // parameter of the task
|
||||
3, // priority of the task
|
||||
&IF482Task, // task handle
|
||||
0); // CPU core
|
||||
#endif // HAS_IF482
|
||||
#endif // HAS_RTC
|
||||
|
||||
// initialize wifi antenna
|
||||
#ifdef HAS_ANTENNA_SWITCH
|
||||
strcat_P(features, " ANT");
|
||||
antenna_init();
|
||||
@ -233,7 +264,7 @@ void setup() {
|
||||
#ifdef HAS_GPS
|
||||
strcat_P(features, " GPS");
|
||||
if (gps_init()) {
|
||||
ESP_LOGI(TAG, "Starting GPSloop...");
|
||||
ESP_LOGI(TAG, "Starting GPS Feed...");
|
||||
xTaskCreatePinnedToCore(gps_loop, // task function
|
||||
"gpsloop", // name of task
|
||||
2048, // stack size of task
|
||||
@ -312,7 +343,7 @@ void setup() {
|
||||
ESP_LOGI(TAG, "Features:%s", features);
|
||||
|
||||
#ifdef HAS_LORA
|
||||
// output LoRaWAN keys to console
|
||||
// output LoRaWAN keys to console
|
||||
#ifdef VERBOSE
|
||||
showLoraKeys();
|
||||
#endif
|
||||
@ -327,7 +358,7 @@ void setup() {
|
||||
get_salt(); // get new 16bit for salting hashes
|
||||
|
||||
// start state machine
|
||||
ESP_LOGI(TAG, "Starting IRQ Handler...");
|
||||
ESP_LOGI(TAG, "Starting Interrupt Handler...");
|
||||
xTaskCreatePinnedToCore(irqHandler, // task function
|
||||
"irqhandler", // name of task
|
||||
4096, // stack size of task
|
||||
@ -346,11 +377,11 @@ void setup() {
|
||||
&wifiSwitchTask, // task handle
|
||||
0); // CPU core
|
||||
|
||||
// initialize bme
|
||||
// initialize bme
|
||||
#ifdef HAS_BME
|
||||
strcat_P(features, " BME");
|
||||
if (bme_init()) {
|
||||
ESP_LOGI(TAG, "Starting BMEloop...");
|
||||
ESP_LOGI(TAG, "Starting Bluetooth sniffer...");
|
||||
xTaskCreatePinnedToCore(bme_loop, // task function
|
||||
"bmeloop", // name of task
|
||||
2048, // stack size of task
|
||||
@ -361,7 +392,8 @@ void setup() {
|
||||
}
|
||||
#endif
|
||||
|
||||
// start timer triggered interrupts
|
||||
assert(irqHandlerTask != NULL); // has interrupt handler task started?
|
||||
// start timer triggered interrupts
|
||||
ESP_LOGI(TAG, "Starting Interrupts...");
|
||||
#ifdef HAS_DISPLAY
|
||||
timerAlarmEnable(displaytimer);
|
||||
@ -370,7 +402,7 @@ void setup() {
|
||||
timerAlarmEnable(homeCycle);
|
||||
timerAlarmEnable(channelSwitch);
|
||||
|
||||
// start button interrupt
|
||||
// start button interrupt
|
||||
#ifdef HAS_BUTTON
|
||||
#ifdef BUTTON_PULLUP
|
||||
attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, RISING);
|
||||
@ -379,6 +411,14 @@ void setup() {
|
||||
#endif
|
||||
#endif // HAS_BUTTON
|
||||
|
||||
// start RTC interrupt
|
||||
#if defined HAS_IF482 && defined HAS_RTC
|
||||
// setup external interupt for active low RTC INT pin
|
||||
assert(IF482Task != NULL); // has if482loop task started?
|
||||
ESP_LOGI(TAG, "Starting IF482 output...");
|
||||
attachInterrupt(digitalPinToInterrupt(RTC_INT), IF482IRQ, FALLING);
|
||||
#endif
|
||||
|
||||
} // setup()
|
||||
|
||||
void loop() {
|
||||
|
@ -81,8 +81,13 @@
|
||||
#define OTA_MIN_BATT 3600 // minimum battery level for OTA [millivolt]
|
||||
#define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds]
|
||||
|
||||
// setting for syncing time of node
|
||||
//#define TIME_SYNC_INTERVAL 60 // sync time each ... minutes [default = 60], comment out means off
|
||||
// settings for syncing time of node and external time sources
|
||||
#define TIME_SYNC_INTERVAL 60 // sync time each ... minutes with external source [default = 60], comment out means off
|
||||
#define TIME_SYNC_INTERVAL_RTC 5 // sync time each ... minutes with RTC [default = 5], comment out means off
|
||||
#define IF482_OFFSET 984 // 1sec minus IF482 serial transmit time [ms]: e.g. 9 bits * 17 bytes * 1/9600 bps = 16ms
|
||||
// time zone, see https://github.com/JChristensen/Timezone/blob/master/examples/WorldClock/WorldClock.ino
|
||||
#define DAYLIGHT_TIME {"CEST", Last, Sun, Mar, 2, 120} // Central European Summer Time
|
||||
#define STANDARD_TIME {"CET ", Last, Sun, Oct, 3, 60} // Central European Standard Time
|
||||
|
||||
// LMIC settings
|
||||
// moved to src/lmic_config.h
|
137
src/rtctime.cpp
Normal file
137
src/rtctime.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
#ifdef HAS_RTC
|
||||
|
||||
#include "rtctime.h"
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = "main";
|
||||
|
||||
RtcDS3231<TwoWire> Rtc(Wire); // RTC hardware i2c interface
|
||||
|
||||
// set Time Zone, fetch user setting from paxcounter.conf
|
||||
TimeChangeRule myDST = DAYLIGHT_TIME;
|
||||
TimeChangeRule mySTD = STANDARD_TIME;
|
||||
Timezone myTZ(myDST, mySTD);
|
||||
|
||||
// initialize RTC
|
||||
int rtc_init(void) {
|
||||
|
||||
// return = 0 -> error / return = 1 -> success
|
||||
|
||||
// block i2c bus access
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
|
||||
Wire.begin(HAS_RTC);
|
||||
Rtc.Begin();
|
||||
|
||||
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
|
||||
|
||||
if (!Rtc.IsDateTimeValid()) {
|
||||
ESP_LOGW(TAG,
|
||||
"RTC has no valid RTC date/time, setting to compilation date");
|
||||
Rtc.SetDateTime(compiled);
|
||||
}
|
||||
|
||||
if (!Rtc.GetIsRunning()) {
|
||||
ESP_LOGI(TAG, "RTC not running, starting now");
|
||||
Rtc.SetIsRunning(true);
|
||||
}
|
||||
|
||||
RtcDateTime now = Rtc.GetDateTime();
|
||||
|
||||
if (now < compiled) {
|
||||
ESP_LOGI(TAG, "RTC date/time is older than compilation date, updating");
|
||||
Rtc.SetDateTime(compiled);
|
||||
}
|
||||
|
||||
// configure RTC chip
|
||||
Rtc.Enable32kHzPin(false);
|
||||
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone);
|
||||
|
||||
} else {
|
||||
ESP_LOGE(TAG, "I2c bus busy - RTC initialization error");
|
||||
goto error;
|
||||
}
|
||||
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
ESP_LOGI(TAG, "RTC initialized");
|
||||
return 1;
|
||||
|
||||
error:
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
return 0;
|
||||
|
||||
} // rtc_init()
|
||||
|
||||
int set_rtctime(uint32_t UTCTime) {
|
||||
// return = 0 -> error / return = 1 -> success
|
||||
// block i2c bus access
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
Rtc.SetDateTime(RtcDateTime(UTCTime));
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
} // set_rtctime()
|
||||
|
||||
int set_rtctime(RtcDateTime t) {
|
||||
// return = 0 -> error / return = 1 -> success
|
||||
// block i2c bus access
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
Rtc.SetDateTime(t);
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
} // set_rtctime()
|
||||
|
||||
time_t get_rtctime(void) {
|
||||
// never call now() in this function, this would cause a recursion!
|
||||
time_t tt = 0;
|
||||
// block i2c bus access
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
if (!Rtc.IsDateTimeValid()) {
|
||||
ESP_LOGW(TAG, "RTC has no confident time");
|
||||
} else {
|
||||
RtcDateTime t = Rtc.GetDateTime();
|
||||
tt = t.Epoch32Time();
|
||||
}
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
return tt;
|
||||
}
|
||||
return tt;
|
||||
} // get_rtctime()
|
||||
|
||||
void sync_rtctime(void) {
|
||||
if (timeStatus() != timeSet) { // do we need time sync?
|
||||
time_t t = get_rtctime();
|
||||
if (t) { // have we got a valid time from RTC?
|
||||
setTime(t);
|
||||
time_t tt = myTZ.toLocal(t);
|
||||
ESP_LOGI(TAG, "RTC has set system time to %02d/%02d/%d %02d:%02d:%02d",
|
||||
month(tt), day(tt), year(tt), hour(tt), minute(tt), second(tt));
|
||||
} else
|
||||
ESP_LOGW(TAG, "System time was not synced");
|
||||
}
|
||||
|
||||
#ifdef TIME_SYNC_INTERVAL_RTC
|
||||
setSyncProvider(&get_rtctime); // does not sync if callback function returns 0
|
||||
if (timeStatus() != timeSet)
|
||||
ESP_LOGI("Unable to sync with the RTC");
|
||||
else
|
||||
ESP_LOGI("RTC has set the system time");
|
||||
setSyncInterval(TIME_SYNC_INTERVAL_RTC);
|
||||
#endif
|
||||
|
||||
} // sync_rtctime;
|
||||
|
||||
float get_rtctemp(void) {
|
||||
// block i2c bus access
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
RtcTemperature temp = Rtc.GetTemperature();
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
return temp.AsFloatDegC();
|
||||
} // while
|
||||
return 0;
|
||||
} // get_rtc()
|
||||
|
||||
#endif // HAS_RTC
|
Loading…
Reference in New Issue
Block a user