ESP32-PaxCounter/src/main.cpp

451 lines
15 KiB
C++
Raw Normal View History

2018-06-10 22:46:13 +02:00
/*
//////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\
2018-06-10 22:46:13 +02:00
Copyright 2018 Oliver Brandmueller <ob@sysadm.in>
Copyright 2018 Klaus Wilting <verkehrsrot@arcor.de>
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.
NOTICE:
Parts of the source files in this repository are made available under different
licenses. Refer to LICENSE.txt file in repository for more details.
2018-09-22 21:26:11 +02:00
//////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\
Uused tasks and timers:
Task Core Prio Purpose
====================================================================================
2018-09-27 14:01:23 +02:00
wifiloop 0 4 rotates wifi channels
ledloop 0 3 blinks LEDs
if482loop 1 3 serial feed of IF482 time telegrams
2018-10-03 16:24:45 +02:00
spiloop 0 2 reads/writes data on spi interface
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
2018-10-03 16:24:45 +02:00
looptask 1 1 arduino core -> runs the LMIC LoRa stack
irqhandler 1 1 executes tasks triggered by irq
2018-11-25 16:05:30 +01:00
gpsloop 1 2 reads data from GPS via serial or i2c
bmeloop 1 1 reads data from BME sensor via i2c
IDLE 1 0 ESP32 arduino scheduler
2018-09-22 21:26:11 +02:00
2018-12-22 21:40:43 +01:00
Low priority numbers denote low priority tasks.
Tasks using i2c bus all must have same priority, because using mutex semaphore
(irqhandler, bmeloop)
2018-09-22 21:26:11 +02:00
ESP32 hardware timers
================================
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
2018-09-22 21:26:11 +02:00
2018-06-10 22:46:13 +02:00
*/
// Basic Config
2018-07-17 11:53:43 +02:00
#include "main.h"
2018-06-10 22:46:13 +02:00
2018-09-15 18:59:20 +02:00
configData_t cfg; // struct holds current device configuration
2018-07-14 20:07:33 +02:00
char display_line6[16], display_line7[16]; // display buffers
2018-09-23 22:12:10 +02:00
uint8_t volatile channel = 0; // channel rotation counter
uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
2018-12-22 14:37:47 +01:00
batt_voltage = 0; // globals for display
2018-11-17 18:30:19 +01:00
hw_timer_t *channelSwitch = NULL, *sendCycle = NULL, *homeCycle = NULL,
*displaytimer = NULL; // irq tasks
TaskHandle_t irqHandlerTask, wifiSwitchTask;
SemaphoreHandle_t I2Caccess;
2018-08-12 00:17:57 +02:00
2018-12-22 14:37:47 +01:00
// 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;
2018-06-10 22:46:13 +02:00
// initialize payload encoder
2018-07-19 22:33:37 +02:00
PayloadConvert payload(PAYLOAD_BUFFER_SIZE);
2018-06-16 19:50:36 +02:00
// set Time Zone, fetch user setting from paxcounter.conf
TimeChangeRule myDST = DAYLIGHT_TIME;
TimeChangeRule mySTD = STANDARD_TIME;
Timezone myTZ(myDST, mySTD);
2018-06-10 22:46:13 +02:00
// local Tag for logging
static const char TAG[] = "main";
void setup() {
2018-06-17 11:40:52 +02:00
// disable the default wifi logging
esp_log_level_set("wifi", ESP_LOG_NONE);
2018-08-02 11:33:02 +02:00
char features[100] = "";
2018-06-10 22:46:13 +02:00
2019-02-02 10:35:20 +01:00
I2Caccess = xSemaphoreCreateMutex(); // for access management of i2c bus
if ((I2Caccess) != NULL)
xSemaphoreGive((I2Caccess)); // Flag the i2c bus available for use
2019-02-02 10:35:20 +01:00
// disable brownout detection
2018-06-10 22:46:13 +02:00
#ifdef DISABLE_BROWNOUT
// register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4
2018-09-23 22:12:10 +02:00
(*((uint32_t volatile *)ETS_UNCACHED_ADDR((DR_REG_RTCCNTL_BASE + 0xd4)))) = 0;
2018-06-10 22:46:13 +02:00
#endif
// setup debug output or silence device
#ifdef VERBOSE
Serial.begin(115200);
esp_log_level_set("*", ESP_LOG_VERBOSE);
#else
// mute logs completely by redirecting them to silence function
esp_log_level_set("*", ESP_LOG_NONE);
esp_log_set_vprintf(redirect_log);
#endif
2018-11-25 16:05:30 +01:00
ESP_LOGI(TAG, "Starting %s v%s", PRODUCTNAME, PROGVERSION);
// print chip information on startup if in verbose mode
#ifdef VERBOSE
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
ESP_LOGI(TAG,
"This is ESP32 chip with %d CPU cores, WiFi%s%s, silicon revision "
"%d, %dMB %s Flash",
chip_info.cores, (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "",
chip_info.revision, spi_flash_get_chip_size() / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded"
: "external");
2018-12-21 00:35:21 +01:00
ESP_LOGI(TAG, "Internal Total heap %d, internal Free Heap %d",
ESP.getHeapSize(), ESP.getFreeHeap());
2018-12-22 14:37:47 +01:00
#ifdef BOARD_HAS_PSRAM
2018-12-21 00:35:21 +01:00
ESP_LOGI(TAG, "SPIRam Total heap %d, SPIRam Free Heap %d", ESP.getPsramSize(),
ESP.getFreePsram());
2018-12-22 14:37:47 +01:00
#endif
2018-12-21 00:35:21 +01:00
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());
2018-12-09 13:48:03 +01:00
ESP_LOGI(TAG, "Wifi/BT software coexist version: %s", esp_coex_version_get());
2018-11-25 16:05:30 +01:00
#ifdef HAS_GPS
ESP_LOGI(TAG, "TinyGPS+ v%s", TinyGPSPlus::libraryVersion());
#endif
#endif // verbose
2018-09-22 21:26:11 +02:00
// read (and initialize on first run) runtime settings from NVRAM
loadConfig(); // includes initialize if necessary
2018-06-10 22:46:13 +02:00
2018-12-22 14:37:47 +01:00
#ifdef BOARD_HAS_PSRAM
2018-12-22 18:01:45 +01:00
assert(psramFound());
ESP_LOGI(TAG, "PSRAM found and initialized");
strcat_P(features, " PSRAM");
2018-12-22 14:37:47 +01:00
#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
2018-12-22 18:01:45 +01:00
// initialize leds
2018-09-22 21:26:11 +02:00
#if (HAS_LED != NOT_A_PIN)
pinMode(HAS_LED, OUTPUT);
strcat_P(features, " LED");
2018-10-24 18:07:41 +02:00
// switch on power LED if we have 2 LEDs, else use it for status
#ifdef HAS_RGB_LED
switch_LED(LED_ON);
2018-11-25 16:05:30 +01:00
strcat_P(features, " RGB");
rgb_set_color(COLOR_PINK);
2018-10-24 18:07:41 +02:00
#endif
2018-09-22 21:26:11 +02:00
#endif
2018-10-24 18:07:41 +02:00
2018-11-25 16:05:30 +01:00
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
// start led loop
2019-01-28 00:38:31 +01:00
ESP_LOGI(TAG, "Starting LED Controller...");
2018-11-25 16:05:30 +01:00
xTaskCreatePinnedToCore(ledLoop, // task function
"ledloop", // name of task
1024, // stack size of task
(void *)1, // parameter of the task
3, // priority of the task
&ledLoopTask, // task handle
0); // CPU core
2018-09-22 21:26:11 +02:00
#endif
2018-06-10 22:46:13 +02:00
2019-01-20 22:38:53 +01:00
// initialize wifi antenna
2018-09-22 21:26:11 +02:00
#ifdef HAS_ANTENNA_SWITCH
strcat_P(features, " ANT");
antenna_init();
antenna_select(cfg.wifiant);
#endif
2018-06-10 22:46:13 +02:00
2018-09-22 21:26:11 +02:00
// initialize battery status
#ifdef HAS_BATTERY_PROBE
strcat_P(features, " BATT");
calibrate_voltage();
batt_voltage = read_voltage();
#endif
2018-09-24 16:36:11 +02:00
#ifdef USE_OTA
strcat_P(features, " OTA");
2018-09-22 21:26:11 +02:00
// reboot to firmware update mode if ota trigger switch is set
if (cfg.runmode == 1) {
cfg.runmode = 0;
saveConfig();
start_ota_update();
}
2018-09-24 16:36:11 +02:00
#endif
2018-08-11 19:12:04 +02:00
2018-12-02 18:22:57 +01:00
// 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());
2018-12-02 18:22:57 +01:00
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
2018-12-02 18:22:57 +01:00
#endif
// initialize button
2018-09-22 21:26:11 +02:00
#ifdef HAS_BUTTON
strcat_P(features, " BTN_");
#ifdef BUTTON_PULLUP
strcat_P(features, "PU");
// install button interrupt (pullup mode)
pinMode(HAS_BUTTON, INPUT_PULLUP);
#else
strcat_P(features, "PD");
// install button interrupt (pulldown mode)
pinMode(HAS_BUTTON, INPUT_PULLDOWN);
#endif // BUTTON_PULLUP
#endif // HAS_BUTTON
// initialize gps
#ifdef HAS_GPS
strcat_P(features, " GPS");
2018-11-25 16:05:30 +01:00
if (gps_init()) {
2019-01-28 00:38:31 +01:00
ESP_LOGI(TAG, "Starting GPS Feed...");
2018-11-25 16:05:30 +01:00
xTaskCreatePinnedToCore(gps_loop, // task function
"gpsloop", // name of task
2048, // stack size of task
(void *)1, // parameter of the task
2, // priority of the task
&GpsTask, // task handle
1); // CPU core
}
2018-08-11 19:12:04 +02:00
#endif
2018-11-20 16:48:35 +01:00
// initialize sensors
#ifdef HAS_SENSORS
strcat_P(features, " SENS");
sensor_init();
#endif
2018-08-11 19:12:04 +02:00
// initialize LoRa
2018-08-03 23:50:04 +02:00
#ifdef HAS_LORA
2018-08-11 19:12:04 +02:00
strcat_P(features, " LORA");
2018-08-03 23:50:04 +02:00
#endif
2018-11-03 20:29:02 +01:00
assert(lora_stack_init() == ESP_OK);
2018-08-11 19:12:04 +02:00
// initialize SPI
2018-08-03 23:50:04 +02:00
#ifdef HAS_SPI
2018-08-11 19:12:04 +02:00
strcat_P(features, " SPI");
2018-08-03 23:50:04 +02:00
#endif
assert(spi_init() == ESP_OK);
2018-08-03 23:50:04 +02:00
2018-09-22 21:26:11 +02:00
#ifdef VENDORFILTER
strcat_P(features, " OUIFLT");
2018-06-10 22:46:13 +02:00
#endif
2018-07-23 13:20:06 +02:00
// initialize display
2018-06-10 22:46:13 +02:00
#ifdef HAS_DISPLAY
2018-06-17 11:40:52 +02:00
strcat_P(features, " OLED");
2018-06-10 22:46:13 +02:00
DisplayState = cfg.screenon;
2019-02-02 10:35:20 +01:00
init_display(PRODUCTNAME, PROGVERSION); // note: blocking call
// setup display refresh trigger IRQ using esp32 hardware timer
// https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/
// prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up
displaytimer = timerBegin(0, 80, true);
// interrupt handler DisplayIRQ, triggered by edge
timerAttachInterrupt(displaytimer, &DisplayIRQ, true);
// reload interrupt after each trigger of display refresh cycle
timerAlarmWrite(displaytimer, DISPLAYREFRESH_MS * 1000, true);
2018-06-10 22:46:13 +02:00
#endif
2018-07-14 23:13:25 +02:00
// setup send cycle trigger IRQ using esp32 hardware timer 2
sendCycle = timerBegin(2, 8000, true);
timerAttachInterrupt(sendCycle, &SendCycleIRQ, true);
timerAlarmWrite(sendCycle, cfg.sendcycle * 2 * 10000, true);
2018-07-22 20:27:58 +02:00
// setup house keeping cycle trigger IRQ using esp32 hardware timer 3
homeCycle = timerBegin(3, 8000, true);
timerAttachInterrupt(homeCycle, &homeCycleIRQ, true);
timerAlarmWrite(homeCycle, HOMECYCLE * 10000, true);
2018-09-21 12:25:52 +02:00
2018-09-27 14:01:23 +02:00
// setup channel rotation trigger IRQ using esp32 hardware timer 1
channelSwitch = timerBegin(1, 800, true);
timerAttachInterrupt(channelSwitch, &ChannelSwitchIRQ, true);
timerAlarmWrite(channelSwitch, cfg.wifichancycle * 1000, true);
2018-07-22 08:41:41 +02:00
2018-06-17 11:40:52 +02:00
// show payload encoder
#if PAYLOAD_ENCODER == 1
2018-08-02 11:33:02 +02:00
strcat_P(features, " PLAIN");
2018-06-17 11:40:52 +02:00
#elif PAYLOAD_ENCODER == 2
2018-08-02 11:33:02 +02:00
strcat_P(features, " PACKED");
2018-06-17 11:40:52 +02:00
#elif PAYLOAD_ENCODER == 3
2018-08-02 11:33:02 +02:00
strcat_P(features, " LPPDYN");
2018-07-22 16:12:46 +02:00
#elif PAYLOAD_ENCODER == 4
2018-08-02 11:33:02 +02:00
strcat_P(features, " LPPPKD");
2018-06-17 11:40:52 +02:00
#endif
2019-02-02 10:35:20 +01:00
// initialize RTC
#ifdef HAS_RTC
strcat_P(features, " RTC");
assert(rtc_init());
setSyncProvider(&get_rtctime);
if (timeStatus() != timeSet)
ESP_LOGI(TAG, "Unable to sync system time with RTC");
else
ESP_LOGI(TAG, "RTC has set the system time");
setSyncInterval(TIME_SYNC_INTERVAL_RTC * 60);
#endif // HAS_RTC
2018-06-10 22:46:13 +02:00
// show compiled features
2018-07-23 17:38:33 +02:00
ESP_LOGI(TAG, "Features:%s", features);
2018-06-10 22:46:13 +02:00
2018-07-14 20:07:33 +02:00
#ifdef HAS_LORA
2019-01-20 22:38:53 +01:00
// output LoRaWAN keys to console
2018-06-10 22:46:13 +02:00
#ifdef VERBOSE
2018-08-03 23:50:04 +02:00
showLoraKeys();
2018-06-10 22:46:13 +02:00
#endif
2018-07-15 14:28:05 +02:00
#endif
2018-06-10 22:46:13 +02:00
2018-07-15 14:28:05 +02:00
// start wifi in monitor mode and start channel rotation task on core 0
ESP_LOGI(TAG, "Starting Wifi...");
2018-07-15 14:28:05 +02:00
wifi_sniffer_init();
// initialize salt value using esp_random() called by random() in
2018-09-15 18:59:20 +02:00
// arduino-esp32 core. Note: do this *after* wifi has started, since
// function gets it's seed from RF noise
2018-09-23 22:12:10 +02:00
get_salt(); // get new 16bit for salting hashes
2018-06-10 22:46:13 +02:00
2018-09-21 18:23:34 +02:00
// start state machine
2019-01-28 00:38:31 +01:00
ESP_LOGI(TAG, "Starting Interrupt Handler...");
xTaskCreatePinnedToCore(irqHandler, // task function
"irqhandler", // name of task
2018-12-22 21:40:43 +01:00
4096, // stack size of task
(void *)1, // parameter of the task
1, // priority of the task
&irqHandlerTask, // task handle
1); // CPU core
2018-09-22 13:43:12 +02:00
// start wifi channel rotation task
ESP_LOGI(TAG, "Starting Wifi Channel rotation...");
2018-10-03 20:18:01 +02:00
xTaskCreatePinnedToCore(switchWifiChannel, // task function
"wifiloop", // name of task
2048, // stack size of task
NULL, // parameter of the task
4, // priority of the task
&wifiSwitchTask, // task handle
0); // CPU core
2019-01-20 22:38:53 +01:00
// initialize bme
2018-12-26 13:00:32 +01:00
#ifdef HAS_BME
strcat_P(features, " BME");
if (bme_init()) {
2019-01-28 00:38:31 +01:00
ESP_LOGI(TAG, "Starting Bluetooth sniffer...");
2018-12-26 13:00:32 +01:00
xTaskCreatePinnedToCore(bme_loop, // task function
"bmeloop", // name of task
2019-01-06 19:41:42 +01:00
2048, // stack size of task
2018-12-26 13:00:32 +01:00
(void *)1, // parameter of the task
1, // priority of the task
2018-12-26 13:00:32 +01:00
&BmeTask, // task handle
1); // CPU core
}
#endif
2019-01-28 00:38:31 +01:00
assert(irqHandlerTask != NULL); // has interrupt handler task started?
// start timer triggered interrupts
ESP_LOGI(TAG, "Starting Interrupts...");
#ifdef HAS_DISPLAY
timerAlarmEnable(displaytimer);
#endif
timerAlarmEnable(sendCycle);
timerAlarmEnable(homeCycle);
timerAlarmEnable(channelSwitch);
2019-01-20 22:38:53 +01:00
// start button interrupt
2018-10-04 22:59:02 +02:00
#ifdef HAS_BUTTON
#ifdef BUTTON_PULLUP
attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, RISING);
#else
attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, FALLING);
#endif
#endif // HAS_BUTTON
2019-01-28 23:59:52 +01:00
#ifdef HAS_GPS
setSyncProvider(&get_gpstime);
if (timeStatus() != timeSet)
ESP_LOGI(TAG, "Unable to sync system time with GPS");
2019-01-29 19:52:54 +01:00
else {
2019-01-28 23:59:52 +01:00
ESP_LOGI(TAG, "GPS has set the system time");
2019-01-29 19:52:54 +01:00
#ifdef HAS_RTC
2019-02-02 09:15:31 +01:00
if (!set_rtctime(now())) // epoch time
2019-01-29 19:52:54 +01:00
ESP_LOGE(TAG, "RTC set time failure");
#endif
}
setSyncInterval(TIME_SYNC_INTERVAL_GPS * 60);
2019-01-28 23:59:52 +01:00
#endif
2019-01-29 09:04:31 +01:00
#if defined HAS_IF482 && defined RTC_INT
2019-02-02 10:35:20 +01:00
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
2019-01-26 18:49:53 +01:00
// setup external interupt for active low RTC INT pin
2019-01-28 00:38:31 +01:00
assert(IF482Task != NULL); // has if482loop task started?
attachInterrupt(digitalPinToInterrupt(RTC_INT), IF482IRQ, FALLING);
2019-01-26 18:49:53 +01:00
#endif
} // setup()
void loop() {
2018-10-03 16:24:45 +02:00
while (1) {
2018-10-03 16:24:45 +02:00
#ifdef HAS_LORA
os_runloop_once(); // execute lmic scheduled jobs and events
#endif
2018-12-19 12:32:25 +01:00
delay(2); // yield to CPU
}
2018-10-03 16:24:45 +02:00
vTaskDelete(NULL); // shoud never be reached
2018-09-27 14:01:23 +02:00
}