Merge branch 'development' of https://github.com/cyberman54/ESP32-Paxcounter into development
This commit is contained in:
commit
014e6cd4a5
52
README.md
52
README.md
@ -44,6 +44,7 @@ LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-L
|
|||||||
|
|
||||||
- Pyom: WiPy
|
- Pyom: WiPy
|
||||||
- WeMos: LoLin32, LoLin32 Lite, WeMos D32, [Wemos32 Oled](https://www.instructables.com/id/ESP32-With-Integrated-OLED-WEMOSLolin-Getting-Star/)
|
- WeMos: LoLin32, LoLin32 Lite, WeMos D32, [Wemos32 Oled](https://www.instructables.com/id/ESP32-With-Integrated-OLED-WEMOSLolin-Getting-Star/)
|
||||||
|
- Crowdsupply: [TinyPICO](https://www.crowdsupply.com/unexpected-maker/tinypico)
|
||||||
- Generic ESP32
|
- Generic ESP32
|
||||||
|
|
||||||
Depending on board hardware following features are supported:
|
Depending on board hardware following features are supported:
|
||||||
@ -58,7 +59,7 @@ Depending on board hardware following features are supported:
|
|||||||
- Real Time Clock (Maxim DS3231 I2C)
|
- Real Time Clock (Maxim DS3231 I2C)
|
||||||
- IF482 (serial) and DCF77 (gpio) time telegram generator
|
- IF482 (serial) and DCF77 (gpio) time telegram generator
|
||||||
- Switch external power / battery
|
- Switch external power / battery
|
||||||
- 64x16 pixel LED Matrix display (similar to [this model](https://www.instructables.com/id/64x16-RED-LED-Marquee/), can be ordered on [Aliexpress](https://www.aliexpress.com/item/P3-75-dot-matrix-led-module-3-75mm-high-clear-top1-for-text-display-304-60mm/32616683948.html))
|
- LED Matrix display (similar to [this 64x16 model](https://www.instructables.com/id/64x16-RED-LED-Marquee/), can be ordered on [Aliexpress](https://www.aliexpress.com/item/P3-75-dot-matrix-led-module-3-75mm-high-clear-top1-for-text-display-304-60mm/32616683948.html))
|
||||||
|
|
||||||
Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).<br>
|
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>
|
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>
|
||||||
@ -155,9 +156,9 @@ If you're using a device with OLED display, or if you add such one to the I2C bu
|
|||||||
|
|
||||||
You can add up to 3 user defined sensors. Insert sensor's payload scheme in [*sensor.cpp*](src/sensor.cpp). Bosch BME280 / BME680 environment sensors are supported. Enable flag *lib_deps_sensors* for your board in [*platformio.ini*](src/platformio.ini) and configure BME in board's hal file before build. If you need Bosch's proprietary BSEC libraray (e.g. to get indoor air quality value from BME680) further enable *build_flags_sensors*, which comes on the price of reduced RAM and increased build size. RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, and to be configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options and for proper configuration of BME280/BME680.
|
You can add up to 3 user defined sensors. Insert sensor's payload scheme in [*sensor.cpp*](src/sensor.cpp). Bosch BME280 / BME680 environment sensors are supported. Enable flag *lib_deps_sensors* for your board in [*platformio.ini*](src/platformio.ini) and configure BME in board's hal file before build. If you need Bosch's proprietary BSEC libraray (e.g. to get indoor air quality value from BME680) further enable *build_flags_sensors*, which comes on the price of reduced RAM and increased build size. RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, and to be configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options and for proper configuration of BME280/BME680.
|
||||||
|
|
||||||
Output of user sensor data can be switched by user remote control command 0x13 sent to Port 2.
|
Output of user sensor data can be switched by user remote control command 0x14 sent to Port 2.
|
||||||
|
|
||||||
Output of sensor and peripheral data is internally switched by a bitmask register. Default mask (0xFF) can be tailored by editing *cfg.payloadmask* initialization value in [*configmanager.cpp*](src/configmanager.cpp) following this scheme:
|
Output of sensor and peripheral data is internally switched by a bitmask register. Default mask can be tailored by editing *cfg.payloadmask* initialization value in [*configmanager.cpp*](src/configmanager.cpp) following this scheme:
|
||||||
|
|
||||||
| Bit | Sensordata |
|
| Bit | Sensordata |
|
||||||
| --- | ------------- |
|
| --- | ------------- |
|
||||||
@ -168,7 +169,7 @@ Output of sensor and peripheral data is internally switched by a bitmask registe
|
|||||||
| 4 | User sensor 1 |
|
| 4 | User sensor 1 |
|
||||||
| 5 | User sensor 2 |
|
| 5 | User sensor 2 |
|
||||||
| 6 | User sensor 3 |
|
| 6 | User sensor 3 |
|
||||||
| 7 | reserved |
|
| 7 | Batterylevel |
|
||||||
|
|
||||||
|
|
||||||
# Time sync
|
# Time sync
|
||||||
@ -304,21 +305,36 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts.
|
|||||||
0 = display off
|
0 = display off
|
||||||
1 = display on [default]
|
1 = display on [default]
|
||||||
|
|
||||||
0x05 set LoRa spread factor
|
0x05 set LoRa datarate
|
||||||
|
|
||||||
7 ... 12 [default: 9]
|
0 ... 15 see LoRaWAN regional parameters for details [default: 5]
|
||||||
|
|
||||||
|
Example for EU868:
|
||||||
|
|
||||||
|
DataRate Configuration Bit/s
|
||||||
|
0 LoRa: SF12 / 125 kHz 250
|
||||||
|
1 LoRa: SF11 / 125 kHz 440
|
||||||
|
2 LoRa: SF10 / 125 kHz 980
|
||||||
|
3 LoRa: SF9 / 125 kHz 1760
|
||||||
|
4 LoRa: SF8 / 125 kHz 3125
|
||||||
|
5 LoRa: SF7 / 125 kHz 5470
|
||||||
|
6* LoRa: SF7 / 250 kHz 11000
|
||||||
|
7* FSK: 50 kbps 50000
|
||||||
|
8 .. 14 reserved for future use (RFU)
|
||||||
|
15 ignored (device keeps current setting)
|
||||||
|
|
||||||
|
*) not supported by TheThingsNetwork
|
||||||
|
|
||||||
0x06 set LoRa TXpower
|
0x06 set LoRa TXpower
|
||||||
|
|
||||||
2 ... 15 [default: 15]
|
0 ... 255 desired TX power in dBm [default: 14]
|
||||||
|
|
||||||
0x07 set LoRa Adaptive Data Rate mode
|
0x07 set LoRa Adaptive Data Rate mode
|
||||||
|
|
||||||
0 = ADR off
|
0 = ADR off
|
||||||
1 = ADR on [default]
|
1 = ADR on [default]
|
||||||
|
|
||||||
Note: set ADR to off, if device is moving, set to on, if not.
|
If ADR is set to off, SF value is shown inverted on display.
|
||||||
If ADR is set to on, SF value is shown inverted on display.
|
|
||||||
|
|
||||||
0x08 do nothing
|
0x08 do nothing
|
||||||
|
|
||||||
@ -382,6 +398,20 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts.
|
|||||||
byte 1 = user sensor number (1..3)
|
byte 1 = user sensor number (1..3)
|
||||||
byte 2 = sensor mode (0 = disabled / 1 = enabled [default])
|
byte 2 = sensor mode (0 = disabled / 1 = enabled [default])
|
||||||
|
|
||||||
|
0x14 set payload mask
|
||||||
|
|
||||||
|
byte 1 = sensor data payload mask (0..255, meaning of bits see above)
|
||||||
|
|
||||||
|
0x15 set BME data on/off
|
||||||
|
|
||||||
|
0 = BME data off
|
||||||
|
1 = BME data on, sends BME data on port 7 [default]
|
||||||
|
|
||||||
|
0x16 set battery data on/off
|
||||||
|
|
||||||
|
0 = battery data off [default]
|
||||||
|
1 = battery data on, sends voltage on port 8
|
||||||
|
|
||||||
0x80 get device configuration
|
0x80 get device configuration
|
||||||
|
|
||||||
Device answers with it's current configuration on Port 3.
|
Device answers with it's current configuration on Port 3.
|
||||||
@ -390,6 +420,10 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts.
|
|||||||
|
|
||||||
Device answers with it's current status on Port 2.
|
Device answers with it's current status on Port 2.
|
||||||
|
|
||||||
|
0x83 get battery status
|
||||||
|
|
||||||
|
Device answers with battery voltage on Port 8.
|
||||||
|
|
||||||
0x84 get device GPS status
|
0x84 get device GPS status
|
||||||
|
|
||||||
Device answers with it's current status on Port 4.
|
Device answers with it's current status on Port 4.
|
||||||
|
12
build.py
12
build.py
@ -7,15 +7,15 @@ import os.path
|
|||||||
import requests
|
import requests
|
||||||
from os.path import basename
|
from os.path import basename
|
||||||
from platformio import util
|
from platformio import util
|
||||||
|
from SCons.Script import DefaultEnvironment
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import configparser
|
import configparser
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import ConfigParser as configparser
|
import ConfigParser as configparser
|
||||||
|
|
||||||
Import("env")
|
|
||||||
|
|
||||||
# get platformio environment variables
|
# get platformio environment variables
|
||||||
|
env = DefaultEnvironment()
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
config.read("platformio.ini")
|
config.read("platformio.ini")
|
||||||
|
|
||||||
@ -66,7 +66,15 @@ myboard = mykeys["board"]
|
|||||||
myuploadspeed = mykeys["upload_speed"]
|
myuploadspeed = mykeys["upload_speed"]
|
||||||
env.Replace(BOARD=myboard)
|
env.Replace(BOARD=myboard)
|
||||||
env.Replace(UPLOAD_SPEED=myuploadspeed)
|
env.Replace(UPLOAD_SPEED=myuploadspeed)
|
||||||
|
|
||||||
|
# re-set partition table
|
||||||
|
mypartitiontable = config.get("env", "board_build.partitions")
|
||||||
|
board = env.BoardConfig(myboard)
|
||||||
|
board.manifest['build']['partitions'] = mypartitiontable
|
||||||
|
|
||||||
|
# display target
|
||||||
print('\033[94m' + "TARGET BOARD: " + myboard + " @ " + myuploadspeed + "bps" + '\033[0m')
|
print('\033[94m' + "TARGET BOARD: " + myboard + " @ " + myuploadspeed + "bps" + '\033[0m')
|
||||||
|
print('\033[94m' + "Partition table: " + mypartitiontable + '\033[0m')
|
||||||
|
|
||||||
# parse ota key file
|
# parse ota key file
|
||||||
with open(otakeyfile) as myfile:
|
with open(otakeyfile) as myfile:
|
||||||
|
@ -42,12 +42,16 @@
|
|||||||
#define SCREEN_MODE (0x80)
|
#define SCREEN_MODE (0x80)
|
||||||
|
|
||||||
// I2C bus access control
|
// I2C bus access control
|
||||||
#define I2C_MUTEX_LOCK() (xSemaphoreTake(I2Caccess, pdMS_TO_TICKS(10)) == pdTRUE)
|
#define I2C_MUTEX_LOCK() \
|
||||||
|
(xSemaphoreTake(I2Caccess, pdMS_TO_TICKS(10)) == pdTRUE)
|
||||||
#define I2C_MUTEX_UNLOCK() (xSemaphoreGive(I2Caccess))
|
#define I2C_MUTEX_UNLOCK() (xSemaphoreGive(I2Caccess))
|
||||||
|
|
||||||
|
enum sendprio_t { prio_low, prio_normal, prio_high };
|
||||||
|
enum timesource_t { _gps, _rtc, _lora, _unsynced };
|
||||||
|
|
||||||
// Struct holding devices's runtime configuration
|
// Struct holding devices's runtime configuration
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t lorasf; // 7-12, lora spreadfactor
|
uint8_t loradr; // 0-15, lora datarate
|
||||||
uint8_t txpower; // 2-15, lora tx power
|
uint8_t txpower; // 2-15, lora tx power
|
||||||
uint8_t adrmode; // 0=disabled, 1=enabled
|
uint8_t adrmode; // 0=disabled, 1=enabled
|
||||||
uint8_t screensaver; // 0=disabled, 1=enabled
|
uint8_t screensaver; // 0=disabled, 1=enabled
|
||||||
@ -73,6 +77,7 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t MessageSize;
|
uint8_t MessageSize;
|
||||||
uint8_t MessagePort;
|
uint8_t MessagePort;
|
||||||
|
sendprio_t MessagePrio;
|
||||||
uint8_t Message[PAYLOAD_BUFFER_SIZE];
|
uint8_t Message[PAYLOAD_BUFFER_SIZE];
|
||||||
} MessageBuffer_t;
|
} MessageBuffer_t;
|
||||||
|
|
||||||
@ -95,15 +100,12 @@ typedef struct {
|
|||||||
float gas; // raw gas sensor signal
|
float gas; // raw gas sensor signal
|
||||||
} bmeStatus_t;
|
} bmeStatus_t;
|
||||||
|
|
||||||
enum sendprio_t { prio_low, prio_normal, prio_high };
|
|
||||||
enum timesource_t { _gps, _rtc, _lora, _unsynced };
|
|
||||||
|
|
||||||
extern std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs;
|
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>::iterator it;
|
||||||
extern std::array<uint64_t, 0xff> beacons;
|
extern std::array<uint64_t, 0xff> beacons;
|
||||||
|
|
||||||
extern configData_t cfg; // current device configuration
|
extern configData_t cfg; // current device configuration
|
||||||
extern char display_line6[], display_line7[]; // screen buffers
|
extern char lmic_event_msg[]; // display buffer
|
||||||
extern uint8_t volatile channel; // wifi channel rotation counter
|
extern uint8_t volatile channel; // wifi channel rotation counter
|
||||||
extern uint16_t volatile macs_total, macs_wifi, macs_ble,
|
extern uint16_t volatile macs_total, macs_wifi, macs_ble,
|
||||||
batt_voltage; // display values
|
batt_voltage; // display values
|
||||||
@ -120,6 +122,7 @@ extern time_t userUTCTime;
|
|||||||
#include "led.h"
|
#include "led.h"
|
||||||
#include "payload.h"
|
#include "payload.h"
|
||||||
#include "blescan.h"
|
#include "blescan.h"
|
||||||
|
#include "power.h"
|
||||||
|
|
||||||
#if (HAS_GPS)
|
#if (HAS_GPS)
|
||||||
#include "gpsread.h"
|
#include "gpsread.h"
|
||||||
@ -141,10 +144,6 @@ extern time_t userUTCTime;
|
|||||||
#include "button.h"
|
#include "button.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BAT_MEASURE_ADC
|
|
||||||
#include "battery.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAS_ANTENNA_SWITCH
|
#ifdef HAS_ANTENNA_SWITCH
|
||||||
#include "antenna.h"
|
#include "antenna.h"
|
||||||
#endif
|
#endif
|
||||||
|
16
include/i2cscan.h
Normal file
16
include/i2cscan.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef _I2CSCAN_H
|
||||||
|
#define _I2CSCAN_H
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#define SSD1306_PRIMARY_ADDRESS (0x3D)
|
||||||
|
#define SSD1306_SECONDARY_ADDRESS (0x3C)
|
||||||
|
#define BME_PRIMARY_ADDRESS (0x77)
|
||||||
|
#define BME_SECONDARY_ADDRESS (0x76)
|
||||||
|
#define AXP192_PRIMARY_ADDRESS (0x34)
|
||||||
|
#define MCP_24AA02E64_PRIMARY_ADDRESS (0x50)
|
||||||
|
#define QUECTEL_GPS_PRIMARY_ADDRESS (0x10)
|
||||||
|
|
||||||
|
int i2c_scan(void);
|
||||||
|
|
||||||
|
#endif
|
@ -10,12 +10,14 @@
|
|||||||
#define UNMASK_IRQ 0x040
|
#define UNMASK_IRQ 0x040
|
||||||
#define BME_IRQ 0x080
|
#define BME_IRQ 0x080
|
||||||
#define MATRIX_DISPLAY_IRQ 0x100
|
#define MATRIX_DISPLAY_IRQ 0x100
|
||||||
|
#define PMU_IRQ 0x200
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "cyclic.h"
|
#include "cyclic.h"
|
||||||
#include "senddata.h"
|
#include "senddata.h"
|
||||||
#include "timekeeper.h"
|
#include "timekeeper.h"
|
||||||
#include "bmesensor.h"
|
#include "bmesensor.h"
|
||||||
|
#include "power.h"
|
||||||
|
|
||||||
void irqHandler(void *pvParameters);
|
void irqHandler(void *pvParameters);
|
||||||
void mask_user_IRQ();
|
void mask_user_IRQ();
|
||||||
@ -33,5 +35,9 @@ void IRAM_ATTR MatrixDisplayIRQ();
|
|||||||
void IRAM_ATTR ButtonIRQ();
|
void IRAM_ATTR ButtonIRQ();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAS_PMU
|
||||||
|
void IRAM_ATTR PMUIRQ();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -13,5 +13,6 @@ void refreshTheMatrixDisplay(bool nextPage = false);
|
|||||||
void DrawNumber(String strNum, uint8_t iDotPos = 0);
|
void DrawNumber(String strNum, uint8_t iDotPos = 0);
|
||||||
uint8_t GetCharFromFont(char cChar);
|
uint8_t GetCharFromFont(char cChar);
|
||||||
uint8_t GetCharWidth(char cChar);
|
uint8_t GetCharWidth(char cChar);
|
||||||
|
void ScrollLeft(uint8_t *buf, const uint16_t cols, const uint16_t rows);
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -4,7 +4,7 @@
|
|||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "rcommand.h"
|
#include "rcommand.h"
|
||||||
#include "timekeeper.h"
|
#include "timekeeper.h"
|
||||||
#if(TIME_SYNC_LORASERVER)
|
#if (TIME_SYNC_LORASERVER)
|
||||||
#include "timesync.h"
|
#include "timesync.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -21,7 +21,14 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern QueueHandle_t LoraSendQueue;
|
extern QueueHandle_t LoraSendQueue;
|
||||||
extern TaskHandle_t lmicTask;
|
extern TaskHandle_t lmicTask, lorasendTask;
|
||||||
|
|
||||||
|
// table of LORAWAN MAC commands
|
||||||
|
typedef struct {
|
||||||
|
const uint8_t opcode;
|
||||||
|
const char cmdname[20];
|
||||||
|
const uint8_t params;
|
||||||
|
} mac_t;
|
||||||
|
|
||||||
esp_err_t lora_stack_init();
|
esp_err_t lora_stack_init();
|
||||||
void lmictask(void *pvParameters);
|
void lmictask(void *pvParameters);
|
||||||
@ -33,10 +40,19 @@ void os_getDevKey(u1_t *buf);
|
|||||||
void os_getArtEui(u1_t *buf);
|
void os_getArtEui(u1_t *buf);
|
||||||
void os_getDevEui(u1_t *buf);
|
void os_getDevEui(u1_t *buf);
|
||||||
void showLoraKeys(void);
|
void showLoraKeys(void);
|
||||||
void switch_lora(uint8_t sf, uint8_t tx);
|
void lora_send(void *pvParameters);
|
||||||
void lora_send(osjob_t *job);
|
void lora_enqueuedata(MessageBuffer_t *message);
|
||||||
void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio);
|
|
||||||
void lora_queuereset(void);
|
void lora_queuereset(void);
|
||||||
|
void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
|
||||||
|
size_t nMsg);
|
||||||
|
void myTxCallback(void *pUserData, int fSuccess);
|
||||||
|
void mac_decode(const uint8_t cmd[], const uint8_t cmdlen, const mac_t table[],
|
||||||
|
const uint8_t tablesize);
|
||||||
|
uint8_t getBattLevel(void);
|
||||||
|
const char *getSfName(rps_t rps);
|
||||||
|
const char *getBwName(rps_t rps);
|
||||||
|
const char *getCrName(rps_t rps);
|
||||||
|
|
||||||
#if (TIME_SYNC_LORAWAN)
|
#if (TIME_SYNC_LORAWAN)
|
||||||
void user_request_network_time_callback(void *pVoidUserUTCTime,
|
void user_request_network_time_callback(void *pVoidUserUTCTime,
|
||||||
int flagSuccess);
|
int flagSuccess);
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include <esp_coexist.h> // needed for showing coex sw version
|
#include <esp_coexist.h> // needed for showing coex sw version
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
#include "power.h"
|
||||||
|
#include "i2cscan.h"
|
||||||
#include "blescan.h"
|
#include "blescan.h"
|
||||||
#include "wifiscan.h"
|
#include "wifiscan.h"
|
||||||
#include "configmanager.h"
|
#include "configmanager.h"
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#ifdef USE_OTA
|
#ifdef USE_OTA
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "battery.h"
|
|
||||||
#include <Update.h>
|
#include <Update.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <WiFiClientSecure.h>
|
#include <WiFiClientSecure.h>
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
#ifndef _BATTERY_H
|
#ifndef _POWER_H
|
||||||
#define _BATTERY_H
|
#define _POWER_H
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
#include <driver/adc.h>
|
#include <driver/adc.h>
|
||||||
#include <esp_adc_cal.h>
|
#include <esp_adc_cal.h>
|
||||||
|
#include "i2cscan.h"
|
||||||
|
|
||||||
#define DEFAULT_VREF 1100 // tbd: use adc2_vref_to_gpio() for better estimate
|
#define DEFAULT_VREF 1100 // tbd: use adc2_vref_to_gpio() for better estimate
|
||||||
#define NO_OF_SAMPLES 64 // we do some multisampling to get better values
|
#define NO_OF_SAMPLES 64 // we do some multisampling to get better values
|
||||||
@ -11,4 +13,12 @@ uint16_t read_voltage(void);
|
|||||||
void calibrate_voltage(void);
|
void calibrate_voltage(void);
|
||||||
bool batt_sufficient(void);
|
bool batt_sufficient(void);
|
||||||
|
|
||||||
|
#ifdef HAS_PMU
|
||||||
|
#include <axp20x.h>
|
||||||
|
void power_event_IRQ(void);
|
||||||
|
void AXP192_power(bool on);
|
||||||
|
void AXP192_init(void);
|
||||||
|
void AXP192_showstatus(void);
|
||||||
|
#endif // HAS_PMU
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -19,11 +19,11 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
const uint8_t opcode;
|
const uint8_t opcode;
|
||||||
void (*func)(uint8_t []);
|
void (*func)(uint8_t []);
|
||||||
uint8_t params;
|
const uint8_t params;
|
||||||
const bool store;
|
const bool store;
|
||||||
} cmd_t;
|
} cmd_t;
|
||||||
|
|
||||||
void rcommand(uint8_t cmd[], uint8_t cmdlength);
|
void rcommand(const uint8_t cmd[], const uint8_t cmdlength);
|
||||||
void do_reset();
|
void do_reset();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
extern Ticker sendcycler;
|
extern Ticker sendcycler;
|
||||||
|
|
||||||
void SendPayload(uint8_t port, sendprio_t prio);
|
void SendPayload(uint8_t port, sendprio_t prio);
|
||||||
void sendCounter(void);
|
void sendData(void);
|
||||||
void checkSendQueues(void);
|
void checkSendQueues(void);
|
||||||
void flushQueues();
|
void flushQueues();
|
||||||
void sendcycle(void);
|
void sendcycle(void);
|
||||||
|
@ -28,7 +28,7 @@ licenses. Refer to LICENSE.txt file in repository for more details.
|
|||||||
|
|
||||||
esp_err_t spi_init();
|
esp_err_t spi_init();
|
||||||
|
|
||||||
void spi_enqueuedata(MessageBuffer_t *message, sendprio_t prio);
|
void spi_enqueuedata(MessageBuffer_t *message);
|
||||||
void spi_queuereset();
|
void spi_queuereset();
|
||||||
|
|
||||||
#endif // _SPISLAVE_H
|
#endif // _SPISLAVE_H
|
||||||
|
@ -27,6 +27,7 @@ void timeSync(void);
|
|||||||
uint8_t timepulse_init(void);
|
uint8_t timepulse_init(void);
|
||||||
time_t timeIsValid(time_t const t);
|
time_t timeIsValid(time_t const t);
|
||||||
void calibrateTime(void);
|
void calibrateTime(void);
|
||||||
|
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, timesource_t mytimesource);
|
||||||
time_t compiledUTC(void);
|
time_t compiledUTC(void);
|
||||||
TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config,
|
TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config,
|
||||||
int8_t rxPin, int8_t txPins);
|
int8_t rxPin, int8_t txPins);
|
||||||
|
@ -12,9 +12,8 @@
|
|||||||
|
|
||||||
void timesync_init(void);
|
void timesync_init(void);
|
||||||
void send_timesync_req(void);
|
void send_timesync_req(void);
|
||||||
int recv_timesync_ans(uint8_t buf[], uint8_t buf_len);
|
int recv_timesync_ans(const uint8_t buf[], uint8_t buf_len);
|
||||||
void process_timesync_req(void *taskparameter);
|
void process_timesync_req(void *taskparameter);
|
||||||
void store_time_sync_req(uint32_t t_millisec);
|
void store_time_sync_req(uint32_t t_millisec);
|
||||||
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, timesource_t timesource);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -19,6 +19,7 @@ halfile = generic.h
|
|||||||
;halfile = ttgov21new.h
|
;halfile = ttgov21new.h
|
||||||
;halfile = ttgofox.h
|
;halfile = ttgofox.h
|
||||||
;halfile = ttgobeam.h
|
;halfile = ttgobeam.h
|
||||||
|
;halfile = ttgobeam10.h
|
||||||
;halfile = fipy.h
|
;halfile = fipy.h
|
||||||
;halfile = lopy.h
|
;halfile = lopy.h
|
||||||
;halfile = lopy4.h
|
;halfile = lopy4.h
|
||||||
@ -28,6 +29,8 @@ halfile = generic.h
|
|||||||
;halfile = wemos32oled.h
|
;halfile = wemos32oled.h
|
||||||
;halfile = wemos32matrix.h
|
;halfile = wemos32matrix.h
|
||||||
;halfile = octopus32.h
|
;halfile = octopus32.h
|
||||||
|
;halfile = tinypico.h
|
||||||
|
;halfile = tinypicomatrix.h
|
||||||
|
|
||||||
[platformio]
|
[platformio]
|
||||||
; upload firmware to board with usb cable
|
; upload firmware to board with usb cable
|
||||||
@ -40,7 +43,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I
|
|||||||
|
|
||||||
[common]
|
[common]
|
||||||
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
||||||
release_version = 1.7.841
|
release_version = 1.8.34
|
||||||
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
; 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
|
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
||||||
debug_level = 3
|
debug_level = 3
|
||||||
@ -52,14 +55,13 @@ platform_espressif32 = espressif32@1.9.0
|
|||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
upload_speed = 115200
|
upload_speed = 115200
|
||||||
lib_deps_lora =
|
lib_deps_lora =
|
||||||
;MCCI LoRaWAN LMIC library@2.3.2
|
MCCI LoRaWAN LMIC library@>=3.0.99
|
||||||
https://github.com/mcci-catena/arduino-lmic.git
|
|
||||||
lib_deps_display =
|
lib_deps_display =
|
||||||
U8g2@>=2.26.13
|
U8g2@>=2.26.13
|
||||||
lib_deps_matrix_display =
|
lib_deps_matrix_display =
|
||||||
https://github.com/Seeed-Studio/Ultrathin_LED_Matrix.git
|
https://github.com/Seeed-Studio/Ultrathin_LED_Matrix.git
|
||||||
lib_deps_rgbled =
|
lib_deps_rgbled =
|
||||||
SmartLeds@>=1.1.5
|
SmartLeds@>=1.1.6
|
||||||
lib_deps_gps =
|
lib_deps_gps =
|
||||||
1655@>=1.0.2 ;TinyGPSPlus by Mikal Hart
|
1655@>=1.0.2 ;TinyGPSPlus by Mikal Hart
|
||||||
lib_deps_sensors =
|
lib_deps_sensors =
|
||||||
@ -70,6 +72,8 @@ lib_deps_basic =
|
|||||||
76@>=1.2.2 ;Timezone by Jack Christensen
|
76@>=1.2.2 ;Timezone by Jack Christensen
|
||||||
274@>=2.3.3 ;RTC by Michael Miller
|
274@>=2.3.3 ;RTC by Michael Miller
|
||||||
SimpleButton
|
SimpleButton
|
||||||
|
;AXP202X_Library@^1.0.1
|
||||||
|
https://github.com/lewisxhe/AXP202X_Library.git#8045ddf
|
||||||
lib_deps_all =
|
lib_deps_all =
|
||||||
${common.lib_deps_basic}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
@ -115,6 +119,4 @@ upload_protocol = esptool
|
|||||||
upload_protocol = esptool
|
upload_protocol = esptool
|
||||||
build_type = debug
|
build_type = debug
|
||||||
platform = https://github.com/platformio/platform-espressif32.git#develop
|
platform = https://github.com/platformio/platform-espressif32.git#develop
|
||||||
platform_packages =
|
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git
|
||||||
; use upstream Git version
|
|
||||||
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git
|
|
||||||
|
@ -37,7 +37,7 @@ function Decoder(bytes, port) {
|
|||||||
|
|
||||||
if (port === 3) {
|
if (port === 3) {
|
||||||
// device config data
|
// device config data
|
||||||
return decode(bytes, [uint8, uint8, int16, uint8, uint8, uint8, uint8, bitmap1, bitmap2, version], ['lorasf', 'txpower', 'rssilimit', 'sendcycle', 'wifichancycle', 'blescantime', 'rgblum', 'flags', 'payloadmask', 'version']);
|
return decode(bytes, [uint8, uint8, int16, uint8, uint8, uint8, uint8, bitmap1, bitmap2, version], ['loradr', 'txpower', 'rssilimit', 'sendcycle', 'wifichancycle', 'blescantime', 'rgblum', 'flags', 'payloadmask', 'version']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (port === 4) {
|
if (port === 4) {
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
#include "globals.h"
|
|
||||||
|
|
||||||
// Local logging tag
|
|
||||||
static const char TAG[] = __FILE__;
|
|
||||||
|
|
||||||
#ifdef BAT_MEASURE_ADC
|
|
||||||
esp_adc_cal_characteristics_t *adc_characs =
|
|
||||||
(esp_adc_cal_characteristics_t *)calloc(
|
|
||||||
1, sizeof(esp_adc_cal_characteristics_t));
|
|
||||||
|
|
||||||
static const adc1_channel_t adc_channel = BAT_MEASURE_ADC;
|
|
||||||
static const adc_atten_t atten = ADC_ATTEN_DB_11;
|
|
||||||
static const adc_unit_t unit = ADC_UNIT_1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void calibrate_voltage(void) {
|
|
||||||
#ifdef BAT_MEASURE_ADC
|
|
||||||
// configure ADC
|
|
||||||
ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_12));
|
|
||||||
ESP_ERROR_CHECK(adc1_config_channel_atten(adc_channel, atten));
|
|
||||||
// calibrate ADC
|
|
||||||
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(
|
|
||||||
unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_characs);
|
|
||||||
// show ADC characterization base
|
|
||||||
if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
|
|
||||||
ESP_LOGI(TAG,
|
|
||||||
"ADC characterization based on Two Point values stored in eFuse");
|
|
||||||
} else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
|
|
||||||
ESP_LOGI(TAG,
|
|
||||||
"ADC characterization based on reference voltage stored in eFuse");
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG, "ADC characterization based on default reference voltage");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t read_voltage() {
|
|
||||||
#ifdef BAT_MEASURE_ADC
|
|
||||||
// multisample ADC
|
|
||||||
uint32_t adc_reading = 0;
|
|
||||||
for (int i = 0; i < NO_OF_SAMPLES; i++) {
|
|
||||||
adc_reading += adc1_get_raw(adc_channel);
|
|
||||||
}
|
|
||||||
adc_reading /= NO_OF_SAMPLES;
|
|
||||||
// Convert ADC reading to voltage in mV
|
|
||||||
uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, adc_characs);
|
|
||||||
#ifdef BAT_VOLTAGE_DIVIDER
|
|
||||||
voltage *= BAT_VOLTAGE_DIVIDER;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef BAT_MEASURE_EN // turn ext. power off
|
|
||||||
digitalWrite(EXT_POWER_SW, EXT_POWER_OFF);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return (uint16_t)voltage;
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool batt_sufficient() {
|
|
||||||
#ifdef BAT_MEASURE_ADC
|
|
||||||
uint16_t volts = read_voltage();
|
|
||||||
return ((volts < 1000) ||
|
|
||||||
(volts > OTA_MIN_BATT)); // no battery or battery sufficient
|
|
||||||
#else
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
@ -143,7 +143,9 @@ int checkIaqSensorStatus(void) {
|
|||||||
|
|
||||||
// store current BME sensor data in struct
|
// store current BME sensor data in struct
|
||||||
void bme_storedata(bmeStatus_t *bme_store) {
|
void bme_storedata(bmeStatus_t *bme_store) {
|
||||||
if (I2C_MUTEX_LOCK()) { // block i2c bus access
|
|
||||||
|
if ((cfg.payloadmask & MEMS_DATA) &&
|
||||||
|
(I2C_MUTEX_LOCK())) { // block i2c bus access
|
||||||
|
|
||||||
#ifdef HAS_BME680
|
#ifdef HAS_BME680
|
||||||
if (iaqSensor.run()) { // if new data is available
|
if (iaqSensor.run()) { // if new data is available
|
||||||
|
@ -8,10 +8,15 @@ static const char TAG[] = "flash";
|
|||||||
nvs_handle my_handle;
|
nvs_handle my_handle;
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
|
|
||||||
|
#define PAYLOADMASK \
|
||||||
|
((GPS_DATA | ALARM_DATA | MEMS_DATA | COUNT_DATA | SENSOR1_DATA | \
|
||||||
|
SENSOR2_DATA | SENSOR3_DATA) & \
|
||||||
|
~BATT_DATA)
|
||||||
|
|
||||||
// populate cfg vars with factory settings
|
// populate cfg vars with factory settings
|
||||||
void defaultConfig() {
|
void defaultConfig() {
|
||||||
cfg.lorasf = LORASFDEFAULT; // 7-12, initial lora sf, see pacounter.conf
|
cfg.loradr = LORADRDEFAULT; // 0-15, lora datarate, see pacounter.conf
|
||||||
cfg.txpower = 15; // 2-15, lora tx power
|
cfg.txpower = LORATXPOWDEFAULT; // 0-15, lora tx power
|
||||||
cfg.adrmode = 1; // 0=disabled, 1=enabled
|
cfg.adrmode = 1; // 0=disabled, 1=enabled
|
||||||
cfg.screensaver = 0; // 0=disabled, 1=enabled
|
cfg.screensaver = 0; // 0=disabled, 1=enabled
|
||||||
cfg.screenon = 1; // 0=disabled, 1=enabled
|
cfg.screenon = 1; // 0=disabled, 1=enabled
|
||||||
@ -29,7 +34,7 @@ void defaultConfig() {
|
|||||||
cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
|
cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
|
||||||
cfg.monitormode = 0; // 0=disabled, 1=enabled
|
cfg.monitormode = 0; // 0=disabled, 1=enabled
|
||||||
cfg.runmode = 0; // 0=normal, 1=update
|
cfg.runmode = 0; // 0=normal, 1=update
|
||||||
cfg.payloadmask = 0xFF; // all payload switched on
|
cfg.payloadmask = PAYLOADMASK; // all payload switched on
|
||||||
cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE] = {
|
cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE] = {
|
||||||
0}; // init BSEC state for BME680 sensor
|
0}; // init BSEC state for BME680 sensor
|
||||||
|
|
||||||
@ -92,9 +97,9 @@ void saveConfig() {
|
|||||||
strcmp(storedversion, cfg.version) != 0)
|
strcmp(storedversion, cfg.version) != 0)
|
||||||
nvs_set_str(my_handle, "version", cfg.version);
|
nvs_set_str(my_handle, "version", cfg.version);
|
||||||
|
|
||||||
if (nvs_get_i8(my_handle, "lorasf", &flash8) != ESP_OK ||
|
if (nvs_get_i8(my_handle, "loradr", &flash8) != ESP_OK ||
|
||||||
flash8 != cfg.lorasf)
|
flash8 != cfg.loradr)
|
||||||
nvs_set_i8(my_handle, "lorasf", cfg.lorasf);
|
nvs_set_i8(my_handle, "loradr", cfg.loradr);
|
||||||
|
|
||||||
if (nvs_get_i8(my_handle, "txpower", &flash8) != ESP_OK ||
|
if (nvs_get_i8(my_handle, "txpower", &flash8) != ESP_OK ||
|
||||||
flash8 != cfg.txpower)
|
flash8 != cfg.txpower)
|
||||||
@ -217,11 +222,11 @@ void loadConfig() {
|
|||||||
ESP_LOGI(TAG, "bsecstate = %d", cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE]);
|
ESP_LOGI(TAG, "bsecstate = %d", cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE]);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (nvs_get_i8(my_handle, "lorasf", &flash8) == ESP_OK) {
|
if (nvs_get_i8(my_handle, "loradr", &flash8) == ESP_OK) {
|
||||||
cfg.lorasf = flash8;
|
cfg.loradr = flash8;
|
||||||
ESP_LOGI(TAG, "lorasf = %d", flash8);
|
ESP_LOGI(TAG, "loradr = %d", flash8);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG, "lorasf set to default %d", cfg.lorasf);
|
ESP_LOGI(TAG, "loradr set to default %d", cfg.loradr);
|
||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,9 @@ void doHousekeeping() {
|
|||||||
#if (HAS_LORA)
|
#if (HAS_LORA)
|
||||||
ESP_LOGD(TAG, "LMiCtask %d bytes left | Taskstate = %d",
|
ESP_LOGD(TAG, "LMiCtask %d bytes left | Taskstate = %d",
|
||||||
uxTaskGetStackHighWaterMark(lmicTask), eTaskGetState(lmicTask));
|
uxTaskGetStackHighWaterMark(lmicTask), eTaskGetState(lmicTask));
|
||||||
|
ESP_LOGD(TAG, "Lorasendtask %d bytes left | Taskstate = %d",
|
||||||
|
uxTaskGetStackHighWaterMark(lorasendTask),
|
||||||
|
eTaskGetState(lorasendTask));
|
||||||
#endif
|
#endif
|
||||||
#if (HAS_GPS)
|
#if (HAS_GPS)
|
||||||
ESP_LOGD(TAG, "Gpsloop %d bytes left | Taskstate = %d",
|
ESP_LOGD(TAG, "Gpsloop %d bytes left | Taskstate = %d",
|
||||||
@ -52,9 +55,15 @@ void doHousekeeping() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// read battery voltage into global variable
|
// read battery voltage into global variable
|
||||||
#ifdef BAT_MEASURE_ADC
|
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
||||||
batt_voltage = read_voltage();
|
batt_voltage = read_voltage();
|
||||||
ESP_LOGI(TAG, "Voltage: %dmV", batt_voltage);
|
if (batt_voltage == 0xffff)
|
||||||
|
ESP_LOGI(TAG, "Battery: external power");
|
||||||
|
else
|
||||||
|
ESP_LOGI(TAG, "Battery: %dmV", batt_voltage);
|
||||||
|
#ifdef HAS_PMU
|
||||||
|
AXP192_showstatus();
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// display BME680/280 sensor data
|
// display BME680/280 sensor data
|
||||||
|
@ -10,15 +10,17 @@ Display-Mask (128 x 64 pixel):
|
|||||||
0|PAX:aabbccddee
|
0|PAX:aabbccddee
|
||||||
1|PAX:aabbccddee
|
1|PAX:aabbccddee
|
||||||
2|B:a.bcV Sats:ab
|
2|B:a.bcV Sats:ab
|
||||||
3|BLTH:abcde SF:ab
|
3|BLTH:abcde SFab
|
||||||
4|WIFI:abcde ch:ab
|
4|WIFI:abcde ch:ab
|
||||||
5|RLIM:abcd abcdKB
|
5|RLIM:abcd abcdKB
|
||||||
6|xxxxxxxxxxxxxxxx
|
|
||||||
6|20:27:00* 27.Feb
|
6|20:27:00* 27.Feb
|
||||||
7|yyyyyyyyyyyyyyab
|
7|yyyyyyyyyyyyyyab
|
||||||
|
|
||||||
line 6: x = Text for LORA status OR time/date
|
line 6: * = char {L|G|R|?} indicates time source,
|
||||||
line 7: y = Text for LMIC status; ab = payload queue
|
inverse = clock controller is active,
|
||||||
|
pulsed = pps input signal is active
|
||||||
|
|
||||||
|
line 7: y = LMIC event message; ab = payload queue length
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -30,19 +32,6 @@ line 7: y = Text for LMIC status; ab = payload queue
|
|||||||
|
|
||||||
HAS_DISPLAY u8x8(MY_OLED_RST, MY_OLED_SCL, MY_OLED_SDA);
|
HAS_DISPLAY u8x8(MY_OLED_RST, MY_OLED_SCL, MY_OLED_SDA);
|
||||||
|
|
||||||
// helper string for converting LoRa spread factor values
|
|
||||||
#if defined(CFG_eu868)
|
|
||||||
const char lora_datarate[] = {"1211100908077BFSNA"};
|
|
||||||
#elif defined(CFG_us915)
|
|
||||||
const char lora_datarate[] = {"100908078CNA121110090807"};
|
|
||||||
#elif defined(CFG_as923)
|
|
||||||
const char lora_datarate[] = {"1211100908077BFSNA"};
|
|
||||||
#elif defined(CFG_au921)
|
|
||||||
const char lora_datarate[] = {"1211100908078CNA1211109C8C7C"};
|
|
||||||
#elif defined(CFG_in866)
|
|
||||||
const char lora_datarate[] = {"121110090807FSNA"};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// helper arry for converting month values to text
|
// helper arry for converting month values to text
|
||||||
const char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
const char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||||
@ -188,8 +177,11 @@ void draw_page(time_t t, uint8_t page) {
|
|||||||
case 0:
|
case 0:
|
||||||
|
|
||||||
// update Battery status (line 2)
|
// update Battery status (line 2)
|
||||||
#ifdef BAT_MEASURE_ADC
|
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
||||||
u8x8.setCursor(0, 2);
|
u8x8.setCursor(0, 2);
|
||||||
|
if (batt_voltage == 0xffff)
|
||||||
|
u8x8.printf("B:USB ");
|
||||||
|
else
|
||||||
u8x8.printf("B:%.2fV", batt_voltage / 1000.0);
|
u8x8.printf("B:%.2fV", batt_voltage / 1000.0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -215,13 +207,11 @@ void draw_page(time_t t, uint8_t page) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (HAS_LORA)
|
#if (HAS_LORA)
|
||||||
u8x8.setCursor(11, 3);
|
u8x8.setCursor(12, 3);
|
||||||
u8x8.printf("SF:");
|
if (!cfg.adrmode) // if ADR=off then display SF value inverse
|
||||||
if (cfg.adrmode) // if ADR=on then display SF value inverse
|
|
||||||
u8x8.setInverseFont(1);
|
u8x8.setInverseFont(1);
|
||||||
u8x8.printf("%c%c", lora_datarate[LMIC.datarate * 2],
|
u8x8.printf("%-4s", getSfName(updr2rps(LMIC.datarate)));
|
||||||
lora_datarate[LMIC.datarate * 2 + 1]);
|
if (!cfg.adrmode) // switch off inverse if it was turned on
|
||||||
if (cfg.adrmode) // switch off inverse if it was turned on
|
|
||||||
u8x8.setInverseFont(0);
|
u8x8.setInverseFont(0);
|
||||||
#endif // HAS_LORA
|
#endif // HAS_LORA
|
||||||
|
|
||||||
@ -254,17 +244,13 @@ void draw_page(time_t t, uint8_t page) {
|
|||||||
#endif // HAS_DCF77 || HAS_IF482
|
#endif // HAS_DCF77 || HAS_IF482
|
||||||
if (timeSource != _unsynced)
|
if (timeSource != _unsynced)
|
||||||
u8x8.printf(" %2d.%3s", day(t), printmonth[month(t)]);
|
u8x8.printf(" %2d.%3s", day(t), printmonth[month(t)]);
|
||||||
#else // update LoRa status display
|
|
||||||
#if (HAS_LORA)
|
|
||||||
u8x8.printf("%-16s", display_line6);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // TIME_SYNC_INTERVAL
|
#endif // TIME_SYNC_INTERVAL
|
||||||
|
|
||||||
#if (HAS_LORA)
|
#if (HAS_LORA)
|
||||||
// line 7: update LMiC event display
|
// line 7: update LMiC event display
|
||||||
u8x8.setCursor(0, 7);
|
u8x8.setCursor(0, 7);
|
||||||
u8x8.printf("%-14s", display_line7);
|
u8x8.printf("%-14s", lmic_event_msg);
|
||||||
|
|
||||||
// update LoRa send queue display
|
// update LoRa send queue display
|
||||||
msgWaiting = uxQueueMessagesWaiting(LoraSendQueue);
|
msgWaiting = uxQueueMessagesWaiting(LoraSendQueue);
|
||||||
@ -274,7 +260,6 @@ void draw_page(time_t t, uint8_t page) {
|
|||||||
u8x8.printf("%-2s", msgWaiting == SEND_QUEUE_SIZE ? "<>" : buff);
|
u8x8.printf("%-2s", msgWaiting == SEND_QUEUE_SIZE ? "<>" : buff);
|
||||||
} else
|
} else
|
||||||
u8x8.printf(" ");
|
u8x8.printf(" ");
|
||||||
|
|
||||||
#endif // HAS_LORA
|
#endif // HAS_LORA
|
||||||
|
|
||||||
break; // page0
|
break; // page0
|
||||||
|
@ -90,13 +90,12 @@ time_t fetch_gpsTime(uint16_t *msec) {
|
|||||||
// poll NMEA $GPZDA sentence
|
// poll NMEA $GPZDA sentence
|
||||||
#ifdef GPS_SERIAL
|
#ifdef GPS_SERIAL
|
||||||
GPS_Serial.print(ZDA_Request);
|
GPS_Serial.print(ZDA_Request);
|
||||||
|
// wait for gps NMEA answer
|
||||||
|
vTaskDelay(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL));
|
||||||
#elif defined GPS_I2C
|
#elif defined GPS_I2C
|
||||||
Wire.print(ZDA_Request);
|
Wire.print(ZDA_Request);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// wait for gps NMEA answer
|
|
||||||
//vTaskDelay(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL));
|
|
||||||
|
|
||||||
// did we get a current time?
|
// did we get a current time?
|
||||||
if (gpstime.isUpdated() && gpstime.isValid()) {
|
if (gpstime.isUpdated() && gpstime.isValid()) {
|
||||||
|
|
||||||
@ -104,7 +103,8 @@ time_t fetch_gpsTime(uint16_t *msec) {
|
|||||||
|
|
||||||
String rawtime = gpstime.value();
|
String rawtime = gpstime.value();
|
||||||
uint32_t time_bcd = rawtime.toFloat() * 100;
|
uint32_t time_bcd = rawtime.toFloat() * 100;
|
||||||
uint32_t delay_ms = gpstime.age() + nmea_txDelay_ms + NMEA_COMPENSATION_FACTOR;
|
uint32_t delay_ms =
|
||||||
|
gpstime.age() + nmea_txDelay_ms + NMEA_COMPENSATION_FACTOR;
|
||||||
uint8_t year =
|
uint8_t year =
|
||||||
CalendarYrToTm(gps.date.year()); // year offset from 1970 in microTime.h
|
CalendarYrToTm(gps.date.year()); // year offset from 1970 in microTime.h
|
||||||
|
|
||||||
@ -138,7 +138,7 @@ void gps_loop(void *pvParameters) {
|
|||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
if (cfg.payloadmask && GPS_DATA) {
|
if (cfg.payloadmask & GPS_DATA) {
|
||||||
#ifdef GPS_SERIAL
|
#ifdef GPS_SERIAL
|
||||||
// feed GPS decoder with serial NMEA data from GPS device
|
// feed GPS decoder with serial NMEA data from GPS device
|
||||||
while (GPS_Serial.available()) {
|
while (GPS_Serial.available()) {
|
||||||
@ -153,10 +153,10 @@ void gps_loop(void *pvParameters) {
|
|||||||
#endif
|
#endif
|
||||||
} // if
|
} // if
|
||||||
|
|
||||||
// show NMEA data in verbose mode, useful for debugging GPS
|
// show NMEA data in verbose mode, useful for debugging GPS, bu tvery noisy
|
||||||
ESP_LOGV(TAG, "GPS NMEA data: passed %u / failed: %u / with fix: %u",
|
//ESP_LOGV(TAG, "GPS NMEA data: passed %u / failed: %u / with fix: %u",
|
||||||
gps.passedChecksum(), gps.failedChecksum(),
|
// gps.passedChecksum(), gps.failedChecksum(),
|
||||||
gps.sentencesWithFix());
|
// gps.sentencesWithFix());
|
||||||
|
|
||||||
delay(2); // yield to CPU
|
delay(2); // yield to CPU
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
#define CFG_sx1276_radio 1
|
#define CFG_sx1276_radio 1
|
||||||
|
|
||||||
#define HAS_LED (22) // Green LED on board
|
#define HAS_LED (22) // Green LED on board
|
||||||
#define HAS_RGB_LED (2) // WS2812B RGB LED on board
|
#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_2) // WS2812B RGB LED on board
|
||||||
#define HAS_BUTTON (0) // button "FLASH" on board
|
#define HAS_BUTTON (0) // button "FLASH" on board
|
||||||
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
#define CFG_sx1272_radio 1
|
#define CFG_sx1272_radio 1
|
||||||
#define HAS_LED NOT_A_PIN // FiPy has no on board LED, so we use RGB LED
|
#define HAS_LED NOT_A_PIN // FiPy has no on board LED, so we use RGB LED
|
||||||
#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0
|
#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_0) // WS2812B RGB LED on GPIO0
|
||||||
#define BOARD_HAS_PSRAM // use extra 4MB extern RAM
|
#define BOARD_HAS_PSRAM // use extra 4MB extern RAM
|
||||||
|
|
||||||
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
|
|
||||||
#define HAS_LED (21) // on board LED
|
#define HAS_LED (21) // on board LED
|
||||||
#define HAS_BUTTON (39) // on board button
|
#define HAS_BUTTON (39) // on board button
|
||||||
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0
|
#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_0) // WS2812B RGB LED on GPIO0
|
||||||
|
|
||||||
// GPS settings
|
// GPS settings
|
||||||
#define HAS_GPS 1 // use on board GPS
|
#define HAS_GPS 1 // use on board GPS
|
||||||
|
@ -20,8 +20,13 @@
|
|||||||
#define HAS_LED LED_BUILTIN // white LED on board
|
#define HAS_LED LED_BUILTIN // white LED on board
|
||||||
#define HAS_BUTTON KEY_BUILTIN // button "PROG" on board
|
#define HAS_BUTTON KEY_BUILTIN // button "PROG" on board
|
||||||
|
|
||||||
|
// caveat: activating ADC2 conflicts with Wifi in current arduino-esp32
|
||||||
|
// see https://github.com/espressif/arduino-esp32/issues/3222
|
||||||
|
// thus we must waiver of battery monitoring
|
||||||
//#define BAT_MEASURE_ADC ADC2_GPIO13_CHANNEL // battery probe GPIO pin
|
//#define BAT_MEASURE_ADC ADC2_GPIO13_CHANNEL // battery probe GPIO pin
|
||||||
//#define BAT_VOLTAGE_DIVIDER 4 // voltage divider 220k/100k on board
|
//#define BAT_MEASURE_ADC_UNIT 2 // ADC 2
|
||||||
|
//#define BAT_VOLTAGE_DIVIDER 2 // voltage divider 220k/100k on board
|
||||||
|
|
||||||
#define EXT_POWER_SW Vext // switches battery power, Vext control 0 = on / 1 = off
|
#define EXT_POWER_SW Vext // switches battery power, Vext control 0 = on / 1 = off
|
||||||
#define EXT_POWER_ON 0
|
#define EXT_POWER_ON 0
|
||||||
//#define EXT_POWER_OFF 1
|
//#define EXT_POWER_OFF 1
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
//#define DISPLAY_FLIP 1 // uncomment this for rotated display
|
//#define DISPLAY_FLIP 1 // uncomment this for rotated display
|
||||||
#define HAS_LED 22 // ESP32 GPIO12 (pin22) On Board LED
|
#define HAS_LED 22 // ESP32 GPIO12 (pin22) On Board LED
|
||||||
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
||||||
#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
|
#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_13) // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
|
||||||
#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield
|
#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield
|
||||||
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
#define HAS_LED NOT_A_PIN // Led os on same pin as Lora SS pin, to avoid problems, we don't use it
|
#define HAS_LED NOT_A_PIN // Led os on same pin as Lora SS pin, to avoid problems, we don't use it
|
||||||
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
||||||
// Anyway shield is on over the LoLin32 board, so we won't be able to see this LED
|
// Anyway shield is on over the LoLin32 board, so we won't be able to see this LED
|
||||||
#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
|
#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_13) // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
|
||||||
#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield
|
#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield
|
||||||
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define CFG_sx1272_radio 1
|
#define CFG_sx1272_radio 1
|
||||||
#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
|
#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
|
#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_0) // WS2812B RGB LED on GPIO0 (P2)
|
||||||
|
|
||||||
// Note: Pins for LORA chip SPI interface come from board file pins_arduino.h
|
// Note: Pins for LORA chip SPI interface come from board file pins_arduino.h
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
//#define SPI_CS GPIO_NUM_36
|
//#define SPI_CS GPIO_NUM_36
|
||||||
|
|
||||||
#define CFG_sx1276_radio 1
|
#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_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 HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_0) // WS2812B RGB LED on GPIO0 (P2)
|
||||||
#define BOARD_HAS_PSRAM // use extra 4MB extern RAM
|
#define BOARD_HAS_PSRAM // use extra 4MB extern RAM
|
||||||
|
|
||||||
// Note: Pins for LORA chip SPI interface come from board file pins_arduino.h
|
// Note: Pins for LORA chip SPI interface come from board file pins_arduino.h
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#define HAS_LED 13 // ESP32 GPIO12 (pin22) On Board LED
|
#define HAS_LED 13 // ESP32 GPIO12 (pin22) On Board LED
|
||||||
//#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
//#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
||||||
//#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
|
//#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_13) // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
|
||||||
//#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield
|
//#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield
|
||||||
//#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
//#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
||||||
|
|
||||||
|
24
src/hal/tinypico.h
Normal file
24
src/hal/tinypico.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// clang-format off
|
||||||
|
// upload_speed 921600
|
||||||
|
// board esp32dev
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _TINYPICO_H
|
||||||
|
#define _TINYPICO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Hardware related definitions for crowdsupply tinypico board
|
||||||
|
|
||||||
|
#define HAS_LED NOT_A_PIN // Green LED on board
|
||||||
|
#define HAS_RGB_LED Apa102 rgb_led(1, GPIO_NUM_12, GPIO_NUM_2) // APA102 RGB LED on board
|
||||||
|
|
||||||
|
//#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
||||||
|
#define BAT_MEASURE_ADC ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
|
||||||
|
#define BAT_VOLTAGE_DIVIDER 2.7625f // voltage divider 160k/442k on board
|
||||||
|
#define BOARD_HAS_PSRAM // use extra 4MB external RAM
|
||||||
|
#define LED_POWER_SW (13) // switches LED power
|
||||||
|
#define LED_POWER_ON 0 // switch on transistor for LED power
|
||||||
|
#define LED_POWER_OFF 1
|
||||||
|
|
||||||
|
#endif
|
46
src/hal/tinypicomatrix.h
Normal file
46
src/hal/tinypicomatrix.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// clang-format off
|
||||||
|
// upload_speed 921600
|
||||||
|
// board esp32dev
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _TINYPICO_H
|
||||||
|
#define _TINYPICO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Hardware related definitions for crowdsupply tinypico board
|
||||||
|
// for operating a 96x16 shift register LED matrix display
|
||||||
|
|
||||||
|
#define HAS_LED NOT_A_PIN // Green LED on board
|
||||||
|
#define HAS_RGB_LED Apa102 rgb_led(1, GPIO_NUM_12, GPIO_NUM_2) // APA102 RGB LED on board
|
||||||
|
|
||||||
|
//#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
||||||
|
#define BAT_MEASURE_ADC ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
|
||||||
|
#define BAT_VOLTAGE_DIVIDER 2.7625f // voltage divider 160k/442k on board
|
||||||
|
#define BOARD_HAS_PSRAM // use extra 4MB external RAM
|
||||||
|
#define LED_POWER_SW (13) // switches LED power
|
||||||
|
#define LED_POWER_ON 0 // switch on transistor for LED power
|
||||||
|
#define LED_POWER_OFF 1
|
||||||
|
|
||||||
|
// LED Matrix display settings
|
||||||
|
#define HAS_MATRIX_DISPLAY 1 // Uncomment to enable LED matrix display output
|
||||||
|
#define LED_MATRIX_WIDTH (32*2) // Width (cols) in pixels (LEDs) of your display, must be 32X
|
||||||
|
#define LED_MATRIX_HEIGHT (16*1) // Height (rows) in pixels (LEDs) of your display, must be 16X
|
||||||
|
|
||||||
|
// Explanation of pin signals see https://learn.adafruit.com/32x16-32x32-rgb-led-matrix/new-wiring
|
||||||
|
#define MATRIX_DISPLAY_SCAN_US 500 // Matrix display scan rate in microseconds (1ms is about 'acceptable')
|
||||||
|
#define LED_MATRIX_LATCHPIN 32 // LAT (or STB = Strobe)
|
||||||
|
#define LED_MATRIX_CLOCKPIN 33 // CLK
|
||||||
|
#define LED_MATRIX_EN_74138 21 // EN (or OE)
|
||||||
|
#define LED_MATRIX_LA_74138 23 // LA (or A)
|
||||||
|
#define LED_MATRIX_LB_74138 19 // LB (or B)
|
||||||
|
#define LED_MATRIX_LC_74138 18 // LC (or C)
|
||||||
|
#define LED_MATRIX_LD_74138 5 // LD (or D)
|
||||||
|
#define LED_MATRIX_DATA_R1 22 // R1 (or R0)
|
||||||
|
|
||||||
|
// CLK: The clock signal moves the data bits from pin R1 ("red") in the shift registers
|
||||||
|
// LAT: The latch signal enables LEDs according to the shift register's contents
|
||||||
|
// Line Selects: LA, LB, LC, LD select which rows of the display are currently lit (0 .. 15)
|
||||||
|
// OE: Output enable switches the LEDs on/off while transitioning from one row to the next
|
||||||
|
|
||||||
|
#endif
|
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||||
#define BOARD_HAS_PSRAM // use extra 4MB external RAM
|
|
||||||
#define HAS_BUTTON GPIO_NUM_39 // on board button (next to reset)
|
#define HAS_BUTTON GPIO_NUM_39 // on board button (next to reset)
|
||||||
#define BAT_MEASURE_ADC ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
|
#define BAT_MEASURE_ADC ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
|
||||||
#define BAT_VOLTAGE_DIVIDER 2 // voltage divider 100k/100k on board
|
#define BAT_VOLTAGE_DIVIDER 2 // voltage divider 100k/100k on board
|
||||||
@ -29,9 +28,9 @@
|
|||||||
|
|
||||||
// enable only if device has these sensors, otherwise comment these lines
|
// enable only if device has these sensors, otherwise comment these lines
|
||||||
// BME680 sensor on I2C bus
|
// BME680 sensor on I2C bus
|
||||||
//#define HAS_BME 1 // Enable BME sensors in general
|
#define HAS_BME 1 // Enable BME sensors in general
|
||||||
//#define HAS_BME680 SDA, SCL
|
#define HAS_BME680 SDA, SCL
|
||||||
//#define BME680_ADDR BME680_I2C_ADDR_PRIMARY // !! connect SDIO of BME680 to GND !!
|
#define BME680_ADDR BME680_I2C_ADDR_PRIMARY // !! connect SDIO of BME680 to GND !!
|
||||||
|
|
||||||
// display (if connected)
|
// display (if connected)
|
||||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||||
|
74
src/hal/ttgobeam10.h
Normal file
74
src/hal/ttgobeam10.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// clang-format off
|
||||||
|
// upload_speed 921600
|
||||||
|
// board ttgo-t-beam
|
||||||
|
|
||||||
|
#ifndef _TTGOBEAM_H
|
||||||
|
#define _TTGOBEAM_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Hardware related definitions for TTGO T-Beam board
|
||||||
|
(only) for newer T-Beam version T22_V10
|
||||||
|
pinouts taken from https://github.com/lewisxhe/TTGO-T-Beam
|
||||||
|
|
||||||
|
/// Button functions: ///
|
||||||
|
Power, short press -> set device on (toggles display while device is on)
|
||||||
|
Power, long press -> set device off
|
||||||
|
User, short press -> flip display page
|
||||||
|
User, long press -> send LORA message
|
||||||
|
Reset -> reset device
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||||
|
#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
|
||||||
|
|
||||||
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
|
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||||
|
#define HAS_BUTTON GPIO_NUM_38 // middle on board button
|
||||||
|
#define HAS_PMU 1 // AXP192 power management chip
|
||||||
|
#define PMU_INT GPIO_NUM_35 // AXP192 interrupt
|
||||||
|
|
||||||
|
#define HAS_LED NOT_A_PIN
|
||||||
|
|
||||||
|
// GPS settings
|
||||||
|
#define HAS_GPS 1 // use on board GPS
|
||||||
|
#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_34, GPIO_NUM_12 // UBlox NEO 6M
|
||||||
|
#define GPS_INT GPIO_NUM_37 // 30ns accurary timepulse generated by NEO 6M Pin #3
|
||||||
|
|
||||||
|
// enable only if device has these sensors, otherwise comment these lines
|
||||||
|
// BME680 sensor on I2C bus
|
||||||
|
//#define HAS_BME 1 // Enable BME sensors in general
|
||||||
|
//#define HAS_BME680 SDA, SCL
|
||||||
|
//#define BME680_ADDR BME680_I2C_ADDR_PRIMARY // !! connect SDIO of BME680 to GND !!
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
// T-Beam V10 has on board power management by AXP192 PMU chip:
|
||||||
|
//
|
||||||
|
// DCDC1 0.7-3.5V @ 1200mA -> OLED
|
||||||
|
// DCDC3 0.7-3.5V @ 700mA -> ESP32 (keep this on!)
|
||||||
|
// LDO1 30mA -> GPS Backup
|
||||||
|
// LDO2 200mA -> LORA
|
||||||
|
// LDO3 200mA -> GPS
|
||||||
|
|
||||||
|
// Wiring for I2C OLED display:
|
||||||
|
//
|
||||||
|
// Signal Header OLED
|
||||||
|
// 3V3 7 VCC
|
||||||
|
// GND 8 GND
|
||||||
|
// IO22(SCL) 9 SCL
|
||||||
|
// IO21(SDA) 10 SDA
|
||||||
|
//
|
||||||
|
|
||||||
|
*/
|
@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
// LED Matrix display settings
|
// LED Matrix display settings
|
||||||
#define HAS_MATRIX_DISPLAY 1 // Uncomment to enable LED matrix display output
|
#define HAS_MATRIX_DISPLAY 1 // Uncomment to enable LED matrix display output
|
||||||
#define LED_MATRIX_WIDTH 64 // Width in pixels (LEDs) of your display
|
#define LED_MATRIX_WIDTH (32*2) // Width (cols) in pixels (LEDs) of your display, must be 32X
|
||||||
#define LED_MATRIX_HEIGHT 16 // Height in pixels (LEDs ) of your display
|
#define LED_MATRIX_HEIGHT (16*1) // Height (rows) in pixels (LEDs) of your display, must be 16X
|
||||||
|
|
||||||
// Pin numbers work fine for Wemos Lolin32 board (all used pins are on 1 side of the board)
|
// Pin numbers work fine for Wemos Lolin32 board (all used pins are on 1 side of the board)
|
||||||
// Explanation of pin signals see https://learn.adafruit.com/32x16-32x32-rgb-led-matrix/new-wiring
|
// Explanation of pin signals see https://learn.adafruit.com/32x16-32x32-rgb-led-matrix/new-wiring
|
||||||
|
66
src/i2cscan.cpp
Normal file
66
src/i2cscan.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Basic config
|
||||||
|
#include "globals.h"
|
||||||
|
#include "i2cscan.h"
|
||||||
|
|
||||||
|
// Local logging tag
|
||||||
|
static const char TAG[] = __FILE__;
|
||||||
|
|
||||||
|
int i2c_scan(void) {
|
||||||
|
|
||||||
|
int i2c_ret, addr;
|
||||||
|
int devices = 0;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Starting I2C bus scan...");
|
||||||
|
|
||||||
|
// block i2c bus access
|
||||||
|
if (I2C_MUTEX_LOCK()) {
|
||||||
|
|
||||||
|
for (addr = 8; addr <= 119; addr++) {
|
||||||
|
|
||||||
|
// scan i2c bus with no more to 100KHz
|
||||||
|
Wire.beginTransmission(addr);
|
||||||
|
Wire.write(addr);
|
||||||
|
i2c_ret = Wire.endTransmission();
|
||||||
|
|
||||||
|
if (i2c_ret == 0) {
|
||||||
|
devices++;
|
||||||
|
|
||||||
|
switch (addr) {
|
||||||
|
|
||||||
|
case SSD1306_PRIMARY_ADDRESS:
|
||||||
|
case SSD1306_SECONDARY_ADDRESS:
|
||||||
|
ESP_LOGI(TAG, "0x%X: SSD1306 Display controller", addr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BME_PRIMARY_ADDRESS:
|
||||||
|
case BME_SECONDARY_ADDRESS:
|
||||||
|
ESP_LOGI(TAG, "0x%X: Bosch BME MEMS", addr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AXP192_PRIMARY_ADDRESS:
|
||||||
|
ESP_LOGI(TAG, "0x%X: AXP192 power management", addr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QUECTEL_GPS_PRIMARY_ADDRESS:
|
||||||
|
ESP_LOGI(TAG, "0x%X: Quectel GPS", addr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MCP_24AA02E64_PRIMARY_ADDRESS:
|
||||||
|
ESP_LOGI(TAG, "0x%X: 24AA02E64 serial EEPROM", addr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ESP_LOGI(TAG, "0x%X: Unknown device", addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} // switch
|
||||||
|
} // for loop
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "I2C scan done, %u devices found.", devices);
|
||||||
|
|
||||||
|
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||||
|
} else
|
||||||
|
ESP_LOGE(TAG, "I2c bus busy - scan error");
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
@ -63,9 +63,15 @@ void irqHandler(void *pvParameters) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// do we have a power event?
|
||||||
|
#if (HAS_PMU)
|
||||||
|
if (InterruptStatus & PMU_IRQ)
|
||||||
|
power_event_IRQ();
|
||||||
|
#endif
|
||||||
|
|
||||||
// is time to send the payload?
|
// is time to send the payload?
|
||||||
if (InterruptStatus & SENDCYCLE_IRQ)
|
if (InterruptStatus & SENDCYCLE_IRQ)
|
||||||
sendCounter();
|
sendData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,6 +112,18 @@ void IRAM_ATTR ButtonIRQ() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAS_PMU
|
||||||
|
void IRAM_ATTR PMUIRQ() {
|
||||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||||
|
|
||||||
|
xTaskNotifyFromISR(irqHandlerTask, PMU_IRQ, eSetBits,
|
||||||
|
&xHigherPriorityTaskWoken);
|
||||||
|
|
||||||
|
if (xHigherPriorityTaskWoken)
|
||||||
|
portYIELD_FROM_ISR();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void mask_user_IRQ() { xTaskNotify(irqHandlerTask, MASK_IRQ, eSetBits); }
|
void mask_user_IRQ() { xTaskNotify(irqHandlerTask, MASK_IRQ, eSetBits); }
|
||||||
|
|
||||||
void unmask_user_IRQ() { xTaskNotify(irqHandlerTask, UNMASK_IRQ, eSetBits); }
|
void unmask_user_IRQ() { xTaskNotify(irqHandlerTask, UNMASK_IRQ, eSetBits); }
|
@ -14,7 +14,7 @@ unsigned long LEDBlinkStarted = 0; // When (in millis() led blink started)
|
|||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
|
|
||||||
// RGB Led instance
|
// RGB Led instance
|
||||||
SmartLed rgb_led(LED_WS2812, 1, HAS_RGB_LED);
|
HAS_RGB_LED;
|
||||||
|
|
||||||
float rgb_CalcColor(float p, float q, float t) {
|
float rgb_CalcColor(float p, float q, float t) {
|
||||||
if (t < 0.0f)
|
if (t < 0.0f)
|
||||||
|
@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
|
||||||
#define NUMCHARS 5
|
|
||||||
#define MATRIX_DISPLAY_PAGES (2) // number of display pages
|
#define MATRIX_DISPLAY_PAGES (2) // number of display pages
|
||||||
|
#define LINE_DIAGRAM_DIVIDER (2) // scales pax numbers to led rows
|
||||||
|
|
||||||
// local Tag for logging
|
// local Tag for logging
|
||||||
static const char TAG[] = __FILE__;
|
static const char TAG[] = __FILE__;
|
||||||
|
|
||||||
uint8_t MatrixDisplayIsOn = 0;
|
uint8_t MatrixDisplayIsOn = 0;
|
||||||
|
static uint8_t displaybuf[LED_MATRIX_WIDTH * LED_MATRIX_HEIGHT / 8] = {0};
|
||||||
static unsigned long ulLastNumMacs = 0;
|
static unsigned long ulLastNumMacs = 0;
|
||||||
static time_t ulLastTime = myTZ.toLocal(now());
|
static time_t ulLastTime = myTZ.toLocal(now());
|
||||||
|
|
||||||
@ -16,9 +17,6 @@ LEDMatrix matrix(LED_MATRIX_LA_74138, LED_MATRIX_LB_74138, LED_MATRIX_LC_74138,
|
|||||||
LED_MATRIX_LD_74138, LED_MATRIX_EN_74138, LED_MATRIX_DATA_R1,
|
LED_MATRIX_LD_74138, LED_MATRIX_EN_74138, LED_MATRIX_DATA_R1,
|
||||||
LED_MATRIX_LATCHPIN, LED_MATRIX_CLOCKPIN);
|
LED_MATRIX_LATCHPIN, LED_MATRIX_CLOCKPIN);
|
||||||
|
|
||||||
// Display Buffer 128 = 64 * 16 / 8
|
|
||||||
uint8_t displaybuf[LED_MATRIX_WIDTH * LED_MATRIX_HEIGHT / NUMCHARS];
|
|
||||||
|
|
||||||
// --- SELECT YOUR FONT HERE ---
|
// --- SELECT YOUR FONT HERE ---
|
||||||
const FONT_INFO *ActiveFontInfo = &digital7_18ptFontInfo;
|
const FONT_INFO *ActiveFontInfo = &digital7_18ptFontInfo;
|
||||||
// const FONT_INFO *ActiveFontInfo = &arialNarrow_17ptFontInfo;
|
// const FONT_INFO *ActiveFontInfo = &arialNarrow_17ptFontInfo;
|
||||||
@ -31,14 +29,21 @@ const FONT_CHAR_INFO *ActiveFontCharInfo = ActiveFontInfo->Descriptors;
|
|||||||
void init_matrix_display(bool reverse) {
|
void init_matrix_display(bool reverse) {
|
||||||
ESP_LOGI(TAG, "Initializing LED Matrix display");
|
ESP_LOGI(TAG, "Initializing LED Matrix display");
|
||||||
matrix.begin(displaybuf, LED_MATRIX_WIDTH, LED_MATRIX_HEIGHT);
|
matrix.begin(displaybuf, LED_MATRIX_WIDTH, LED_MATRIX_HEIGHT);
|
||||||
|
|
||||||
|
if (MatrixDisplayIsOn)
|
||||||
|
matrix.on();
|
||||||
|
else
|
||||||
|
matrix.off();
|
||||||
|
|
||||||
if (reverse)
|
if (reverse)
|
||||||
matrix.reverse();
|
matrix.reverse();
|
||||||
matrix.clear();
|
matrix.clear();
|
||||||
DrawNumber(String("0"));
|
matrix.drawPoint(0, LED_MATRIX_HEIGHT - 1, 1);
|
||||||
} // init_display
|
} // init_display
|
||||||
|
|
||||||
void refreshTheMatrixDisplay(bool nextPage) {
|
void refreshTheMatrixDisplay(bool nextPage) {
|
||||||
static uint8_t DisplayPage = 0;
|
static uint8_t DisplayPage = 0, col = 0, row = 0;
|
||||||
|
uint8_t level;
|
||||||
char buff[16];
|
char buff[16];
|
||||||
|
|
||||||
// if Matrixdisplay is switched off we don't refresh it to relax cpu
|
// if Matrixdisplay is switched off we don't refresh it to relax cpu
|
||||||
@ -48,26 +53,61 @@ void refreshTheMatrixDisplay(bool nextPage) {
|
|||||||
// set display on/off according to current device configuration
|
// set display on/off according to current device configuration
|
||||||
if (MatrixDisplayIsOn != cfg.screenon) {
|
if (MatrixDisplayIsOn != cfg.screenon) {
|
||||||
MatrixDisplayIsOn = cfg.screenon;
|
MatrixDisplayIsOn = cfg.screenon;
|
||||||
|
if (MatrixDisplayIsOn)
|
||||||
|
matrix.on();
|
||||||
|
else
|
||||||
|
matrix.off();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextPage) {
|
if (nextPage) {
|
||||||
DisplayPage =
|
DisplayPage =
|
||||||
(DisplayPage >= MATRIX_DISPLAY_PAGES - 1) ? 0 : (DisplayPage + 1);
|
(DisplayPage >= MATRIX_DISPLAY_PAGES - 1) ? 0 : (DisplayPage + 1);
|
||||||
matrix.clear();
|
matrix.clear();
|
||||||
|
col = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (DisplayPage % MATRIX_DISPLAY_PAGES) {
|
switch (DisplayPage % MATRIX_DISPLAY_PAGES) {
|
||||||
|
|
||||||
// page 0: pax
|
// page 0: number of current pax OR footfall line diagram
|
||||||
// page 1: time
|
// page 1: time of day
|
||||||
|
|
||||||
case 0:
|
case 0:
|
||||||
|
|
||||||
|
if (cfg.countermode == 1)
|
||||||
|
|
||||||
|
{ // cumulative counter mode -> display total number of pax
|
||||||
if (ulLastNumMacs != macs.size()) {
|
if (ulLastNumMacs != macs.size()) {
|
||||||
ulLastNumMacs = macs.size();
|
ulLastNumMacs = macs.size();
|
||||||
matrix.clear();
|
matrix.clear();
|
||||||
DrawNumber(String(ulLastNumMacs));
|
DrawNumber(String(ulLastNumMacs));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else { // cyclic counter mode -> plot a line diagram
|
||||||
|
|
||||||
|
if (ulLastNumMacs != macs.size()) {
|
||||||
|
|
||||||
|
// next count cycle?
|
||||||
|
if (macs.size() == 0) {
|
||||||
|
|
||||||
|
// matrix full? then scroll left 1 dot, else increment column
|
||||||
|
if (col < (LED_MATRIX_WIDTH - 1))
|
||||||
|
col++;
|
||||||
|
else
|
||||||
|
ScrollLeft(displaybuf, LED_MATRIX_WIDTH, LED_MATRIX_HEIGHT);
|
||||||
|
|
||||||
|
} else
|
||||||
|
matrix.drawPoint(col, row, 0); // clear current dot
|
||||||
|
|
||||||
|
// scale and set new dot
|
||||||
|
ulLastNumMacs = macs.size();
|
||||||
|
level = ulLastNumMacs / LINE_DIAGRAM_DIVIDER;
|
||||||
|
row = level <= LED_MATRIX_HEIGHT
|
||||||
|
? LED_MATRIX_HEIGHT - 1 - level % LED_MATRIX_HEIGHT
|
||||||
|
: 0;
|
||||||
|
matrix.drawPoint(col, row, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
@ -164,4 +204,18 @@ uint8_t GetCharWidth(char cChar) {
|
|||||||
return CharDescriptor.width;
|
return CharDescriptor.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScrollLeft(uint8_t *buf, const uint16_t cols, const uint16_t rows) {
|
||||||
|
uint32_t i, k, idx;
|
||||||
|
const uint32_t x = cols / 8;
|
||||||
|
|
||||||
|
for (k = 0; k < rows; k++) {
|
||||||
|
// scroll a line with x bytes one dot to the left
|
||||||
|
for (i = 0; i < x - 1; ++i) {
|
||||||
|
idx = i + k * x;
|
||||||
|
buf[idx] = (buf[idx] << 1) | ((buf[idx + 1] >> 7) & 1);
|
||||||
|
}
|
||||||
|
buf[idx + 1] <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif // HAS_MATRIX_DISPLAY
|
#endif // HAS_MATRIX_DISPLAY
|
@ -2,40 +2,33 @@
|
|||||||
|
|
||||||
// COUNTRY SETTINGS
|
// COUNTRY SETTINGS
|
||||||
// --> please check with you local regulations for ISM band frequency use!
|
// --> please check with you local regulations for ISM band frequency use!
|
||||||
//
|
|
||||||
// CFG_eu868 EU 863-870 MHz
|
|
||||||
// CFG_us915 US 902-928 MHz
|
|
||||||
// CFG_au921 Australia 915-928 MHz
|
|
||||||
// CFG_as923 Asia 923 MHz
|
|
||||||
// CFG_in866 India 865-867 MHz
|
|
||||||
|
|
||||||
#define CFG_eu868 1
|
#define CFG_eu868 1 // Europe (high band)
|
||||||
//#define CFG_us915 1
|
//#define CFG_eu433 1 // Europe (low band)
|
||||||
//#define CFG_in866 1
|
//#define CFG_us915 1 // USA, Canada and South America
|
||||||
//#define CFG_au921 1
|
//#define CFG_in866 1 // India
|
||||||
//#define CFG_as923 1
|
//#define CFG_au921 1 // Australia
|
||||||
//#define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP /* for as923-JP */
|
//#define CFG_as923 1 // Asia
|
||||||
|
//#define CFG_cn783 1 // China (high band)
|
||||||
|
//#define CFG_cn490 1 // China (low band)
|
||||||
|
//#define CFG_kr920 1 // Korea
|
||||||
|
|
||||||
// LMIC LORAWAN STACK SETTINGS
|
// LMIC LORAWAN STACK SETTINGS
|
||||||
// --> adapt to your device only if necessary
|
// --> adapt to your device only if necessary
|
||||||
|
|
||||||
//#define LMIC_USE_INTERRUPTS 1
|
// use interrupts only if LORA_IRQ and LORA_DIO are connected to interrupt
|
||||||
|
// capable GPIO pins on your board, if not disable interrupts
|
||||||
|
#define LMIC_USE_INTERRUPTS 1
|
||||||
|
|
||||||
//time sync via LoRaWAN network, is not yet supported by TTN (LoRaWAN spec v1.0.3)
|
// time sync via LoRaWAN network, note: not supported by TTNv2
|
||||||
//#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
|
|
||||||
#define US_PER_OSTICK_EXPONENT 4
|
|
||||||
#define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT)
|
|
||||||
#define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK)
|
|
||||||
|
|
||||||
// This tells LMIC to make the receive windows bigger, in case your clock is
|
// This tells LMIC to make the receive windows bigger, in case your clock is
|
||||||
// faster or slower. This causes the transceiver to be earlier switched on,
|
// 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
|
// so consuming more power. You may sharpen (reduce) this value if you are
|
||||||
// limited on battery.
|
// limited on battery.
|
||||||
// ATTN: VALUES > 7 WILL CAUSE RECEPTION AND JOIN PROBLEMS WITH HIGH SF RATES
|
// ATTN: VALUES > 7 WILL CAUSE RECEPTION AND JOIN PROBLEMS WITH HIGH SF RATES
|
||||||
#define CLOCK_ERROR_PROCENTAGE 5
|
//#define CLOCK_ERROR_PROCENTAGE 7
|
||||||
|
|
||||||
// Set this to 1 to enable some basic debug output (using printf) about
|
// Set this to 1 to enable some basic debug output (using printf) about
|
||||||
// RF settings used during transmission and reception. Set to 2 to
|
// RF settings used during transmission and reception. Set to 2 to
|
||||||
@ -49,6 +42,11 @@
|
|||||||
// current implementation only works on AVR, though.
|
// current implementation only works on AVR, though.
|
||||||
//#define LMIC_PRINTF_TO Serial
|
//#define LMIC_PRINTF_TO Serial
|
||||||
|
|
||||||
|
// Change the SPI clock speed if you encounter errors
|
||||||
|
// communicating with the radio.
|
||||||
|
// The standard range is 125kHz-8MHz, but some boards can go faster.
|
||||||
|
//#define LMIC_SPI_FREQ 1E6
|
||||||
|
|
||||||
// Any runtime assertion failures are printed to this serial port (or
|
// Any runtime assertion failures are printed to this serial port (or
|
||||||
// any other Print object). If this is unset, any failures just silently
|
// any other Print object). If this is unset, any failures just silently
|
||||||
// halt execution.
|
// halt execution.
|
||||||
@ -90,7 +88,7 @@
|
|||||||
// implementation is optimized for speed on 32-bit processors using
|
// implementation is optimized for speed on 32-bit processors using
|
||||||
// fairly big lookup tables, but it takes up big amounts of flash on the
|
// fairly big lookup tables, but it takes up big amounts of flash on the
|
||||||
// AVR architecture.
|
// AVR architecture.
|
||||||
//#define USE_ORIGINAL_AES
|
#define USE_ORIGINAL_AES
|
||||||
//
|
//
|
||||||
// This selects the AES implementation written by Ideetroon for their
|
// This selects the AES implementation written by Ideetroon for their
|
||||||
// own LoRaWAN library. It also uses lookup tables, but smaller
|
// own LoRaWAN library. It also uses lookup tables, but smaller
|
||||||
@ -98,4 +96,4 @@
|
|||||||
// also about twice as slow as the original).
|
// also about twice as slow as the original).
|
||||||
// #define USE_IDEETRON_AES
|
// #define USE_IDEETRON_AES
|
||||||
//
|
//
|
||||||
#define USE_MBEDTLS_AES
|
//#define USE_MBEDTLS_AES
|
||||||
|
401
src/lorawan.cpp
401
src/lorawan.cpp
@ -18,9 +18,31 @@ static const char TAG[] = "lora";
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
osjob_t sendjob;
|
|
||||||
QueueHandle_t LoraSendQueue;
|
QueueHandle_t LoraSendQueue;
|
||||||
TaskHandle_t lmicTask = NULL;
|
TaskHandle_t lmicTask = NULL, lorasendTask = NULL;
|
||||||
|
|
||||||
|
// table of LORAWAN MAC messages sent by the network to the device
|
||||||
|
// format: opcode, cmdname (max 19 chars), #bytes params
|
||||||
|
// source: LoRaWAN 1.1 Specification (October 11, 2017)
|
||||||
|
static const mac_t MACdn_table[] = {
|
||||||
|
{0x01, "ResetConf", 1}, {0x02, "LinkCheckAns", 2},
|
||||||
|
{0x03, "LinkADRReq", 4}, {0x04, "DutyCycleReq", 1},
|
||||||
|
{0x05, "RXParamSetupReq", 4}, {0x06, "DevStatusReq", 0},
|
||||||
|
{0x07, "NewChannelReq", 5}, {0x08, "RxTimingSetupReq", 1},
|
||||||
|
{0x09, "TxParamSetupReq", 1}, {0x0A, "DlChannelReq", 4},
|
||||||
|
{0x0B, "RekeyConf", 1}, {0x0C, "ADRParamSetupReq", 1},
|
||||||
|
{0x0D, "DeviceTimeAns", 5}, {0x0E, "ForceRejoinReq", 2},
|
||||||
|
{0x0F, "RejoinParamSetupReq", 1}};
|
||||||
|
|
||||||
|
// table of LORAWAN MAC messages sent by the device to the network
|
||||||
|
static const mac_t MACup_table[] = {
|
||||||
|
{0x01, "ResetInd", 1}, {0x02, "LinkCheckReq", 0},
|
||||||
|
{0x03, "LinkADRAns", 1}, {0x04, "DutyCycleAns", 0},
|
||||||
|
{0x05, "RXParamSetupAns", 1}, {0x06, "DevStatusAns", 2},
|
||||||
|
{0x07, "NewChannelAns", 1}, {0x08, "RxTimingSetupAns", 0},
|
||||||
|
{0x09, "TxParamSetupAns", 0}, {0x0A, "DlChannelAns", 1},
|
||||||
|
{0x0B, "RekeyInd", 1}, {0x0C, "ADRParamSetupAns", 0},
|
||||||
|
{0x0D, "DeviceTimeReq", 0}, {0x0F, "RejoinParamSetupAns", 1}};
|
||||||
|
|
||||||
class MyHalConfig_t : public Arduino_LMIC::HalConfiguration_t {
|
class MyHalConfig_t : public Arduino_LMIC::HalConfiguration_t {
|
||||||
|
|
||||||
@ -135,7 +157,7 @@ void get_hard_deveui(uint8_t *pdeveui) {
|
|||||||
uint8_t i2c_ret;
|
uint8_t i2c_ret;
|
||||||
|
|
||||||
// Init this just in case, no more to 100KHz
|
// Init this just in case, no more to 100KHz
|
||||||
Wire.begin(MY_OLED_SDA, MY_OLED_SCL, 100000);
|
Wire.begin(SDA, SCL, 100000);
|
||||||
Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS);
|
Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS);
|
||||||
Wire.write(MCP_24AA02E64_MAC_ADDRESS);
|
Wire.write(MCP_24AA02E64_MAC_ADDRESS);
|
||||||
i2c_ret = Wire.endTransmission();
|
i2c_ret = Wire.endTransmission();
|
||||||
@ -209,19 +231,17 @@ void onEvent(ev_t ev) {
|
|||||||
|
|
||||||
case EV_JOINED:
|
case EV_JOINED:
|
||||||
strcpy_P(buff, PSTR("JOINED"));
|
strcpy_P(buff, PSTR("JOINED"));
|
||||||
sprintf(display_line6, " "); // clear previous lmic status
|
|
||||||
// set data rate adaptation according to saved setting
|
// set data rate adaptation according to saved setting
|
||||||
LMIC_setAdrMode(cfg.adrmode);
|
LMIC_setAdrMode(cfg.adrmode);
|
||||||
// set cyclic lmic link check to off if no ADR because is not supported by
|
// set data rate and transmit power to defaults only if we have no ADR
|
||||||
// ttn (but enabled by lmic after join)
|
if (!cfg.adrmode)
|
||||||
LMIC_setLinkCheckMode(cfg.adrmode);
|
LMIC_setDrTxpow(assertDR(cfg.loradr), cfg.txpower);
|
||||||
// Set data rate and transmit power (note: txpower seems to be ignored by
|
// show current devaddr
|
||||||
// the library)
|
|
||||||
switch_lora(cfg.lorasf, cfg.txpower);
|
|
||||||
// kickoff first send job
|
|
||||||
os_setCallback(&sendjob, lora_send);
|
|
||||||
// show effective LoRa parameters after join
|
|
||||||
ESP_LOGI(TAG, "DEVaddr=%08X", LMIC.devaddr);
|
ESP_LOGI(TAG, "DEVaddr=%08X", LMIC.devaddr);
|
||||||
|
ESP_LOGI(TAG, "Radio parameters %s / %s / %s",
|
||||||
|
getSfName(updr2rps(LMIC.datarate)),
|
||||||
|
getBwName(updr2rps(LMIC.datarate)),
|
||||||
|
getCrName(updr2rps(LMIC.datarate)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EV_RFU1:
|
case EV_RFU1:
|
||||||
@ -237,46 +257,7 @@ void onEvent(ev_t ev) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case EV_TXCOMPLETE:
|
case EV_TXCOMPLETE:
|
||||||
|
strcpy_P(buff, PSTR("TX COMPLETE"));
|
||||||
#if (TIME_SYNC_LORASERVER)
|
|
||||||
// if last packet sent was a timesync request, store TX timestamp
|
|
||||||
if (LMIC.pendTxPort == TIMEPORT)
|
|
||||||
store_time_sync_req(osticks2ms(LMIC.txend)); // milliseconds
|
|
||||||
#endif
|
|
||||||
|
|
||||||
strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED ACK")
|
|
||||||
: PSTR("TX COMPLETE"));
|
|
||||||
sprintf(display_line6, " "); // clear previous lmic status
|
|
||||||
|
|
||||||
if (LMIC.dataLen) { // did we receive payload data -> display info
|
|
||||||
ESP_LOGI(TAG, "Received %d bytes of payload, RSSI %d SNR %d",
|
|
||||||
LMIC.dataLen, LMIC.rssi, LMIC.snr / 4);
|
|
||||||
sprintf(display_line6, "RSSI %d SNR %d", LMIC.rssi, LMIC.snr / 4);
|
|
||||||
|
|
||||||
if (LMIC.txrxFlags & TXRX_PORT) { // FPort -> use to switch
|
|
||||||
|
|
||||||
switch (LMIC.frame[LMIC.dataBeg - 1]) {
|
|
||||||
|
|
||||||
case RCMDPORT: // opcode -> call rcommand interpreter
|
|
||||||
rcommand(LMIC.frame + LMIC.dataBeg, LMIC.dataLen);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
|
|
||||||
#if (TIME_SYNC_LORASERVER)
|
|
||||||
// timesync answer -> call timesync processor
|
|
||||||
if (LMIC.frame[LMIC.dataBeg - 1] == TIMEPORT) {
|
|
||||||
recv_timesync_ans(LMIC.frame + LMIC.dataBeg, LMIC.dataLen);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// unknown port -> display info
|
|
||||||
ESP_LOGI(TAG, "Received data on unsupported port #%d",
|
|
||||||
LMIC.frame[LMIC.dataBeg - 1]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EV_LOST_TSYNC:
|
case EV_LOST_TSYNC:
|
||||||
@ -306,12 +287,6 @@ void onEvent(ev_t ev) {
|
|||||||
|
|
||||||
case EV_TXSTART:
|
case EV_TXSTART:
|
||||||
if (!(LMIC.opmode & OP_JOINING)) {
|
if (!(LMIC.opmode & OP_JOINING)) {
|
||||||
#if (TIME_SYNC_LORASERVER)
|
|
||||||
// if last packet sent was a timesync request, store TX time
|
|
||||||
if (LMIC.pendTxPort == TIMEPORT)
|
|
||||||
strcpy_P(buff, PSTR("TX TIMESYNC"));
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
strcpy_P(buff, PSTR("TX START"));
|
strcpy_P(buff, PSTR("TX START"));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -336,80 +311,58 @@ void onEvent(ev_t ev) {
|
|||||||
// Log & Display if asked
|
// Log & Display if asked
|
||||||
if (*buff) {
|
if (*buff) {
|
||||||
ESP_LOGI(TAG, "%s", buff);
|
ESP_LOGI(TAG, "%s", buff);
|
||||||
sprintf(display_line7, buff);
|
sprintf(lmic_event_msg, buff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper function to assign LoRa datarates to numeric spreadfactor values
|
// LMIC send task
|
||||||
void switch_lora(uint8_t sf, uint8_t tx) {
|
void lora_send(void *pvParameters) {
|
||||||
if (tx > 20)
|
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
||||||
return;
|
|
||||||
cfg.txpower = tx;
|
|
||||||
switch (sf) {
|
|
||||||
case 7:
|
|
||||||
LMIC_setDrTxpow(DR_SF7, tx);
|
|
||||||
cfg.lorasf = sf;
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
LMIC_setDrTxpow(DR_SF8, tx);
|
|
||||||
cfg.lorasf = sf;
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
LMIC_setDrTxpow(DR_SF9, tx);
|
|
||||||
cfg.lorasf = sf;
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
LMIC_setDrTxpow(DR_SF10, tx);
|
|
||||||
cfg.lorasf = sf;
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
#if defined(CFG_us915)
|
|
||||||
LMIC_setDrTxpow(DR_SF11CR, tx);
|
|
||||||
cfg.lorasf = sf;
|
|
||||||
break;
|
|
||||||
#else
|
|
||||||
LMIC_setDrTxpow(DR_SF11, tx);
|
|
||||||
cfg.lorasf = sf;
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case 12:
|
|
||||||
#if defined(CFG_us915)
|
|
||||||
LMIC_setDrTxpow(DR_SF12CR, tx);
|
|
||||||
cfg.lorasf = sf;
|
|
||||||
break;
|
|
||||||
#else
|
|
||||||
LMIC_setDrTxpow(DR_SF12, tx);
|
|
||||||
cfg.lorasf = sf;
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void lora_send(osjob_t *job) {
|
|
||||||
MessageBuffer_t SendBuffer;
|
MessageBuffer_t SendBuffer;
|
||||||
// Check if there is a pending TX/RX job running, if yes don't eat data
|
|
||||||
// since it cannot be sent right now
|
while (1) {
|
||||||
if ((LMIC.opmode & (OP_JOINING | OP_REJOIN | OP_TXDATA | OP_POLL)) != 0) {
|
|
||||||
// waiting for LoRa getting ready
|
// postpone until we are joined if we are not
|
||||||
} else {
|
while (!LMIC.devaddr) {
|
||||||
if (xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
|
vTaskDelay(pdMS_TO_TICKS(500));
|
||||||
// SendBuffer now filled with next payload from queue
|
|
||||||
if (!LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
|
|
||||||
SendBuffer.MessageSize, (cfg.countermode & 0x02))) {
|
|
||||||
ESP_LOGI(TAG, "%d byte(s) sent to LoRa", SendBuffer.MessageSize);
|
|
||||||
} else {
|
|
||||||
ESP_LOGE(TAG, "could not send %d byte(s) to LoRa",
|
|
||||||
SendBuffer.MessageSize);
|
|
||||||
}
|
}
|
||||||
// sprintf(display_line7, "PACKET QUEUED");
|
|
||||||
|
// fetch next or wait for payload to send from queue
|
||||||
|
if (xQueueReceive(LoraSendQueue, &SendBuffer, portMAX_DELAY) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// attempt to transmit payload
|
||||||
|
else {
|
||||||
|
|
||||||
|
switch (LMIC_sendWithCallback_strict(
|
||||||
|
SendBuffer.MessagePort, SendBuffer.Message, SendBuffer.MessageSize,
|
||||||
|
(cfg.countermode & 0x02), myTxCallback, NULL)) {
|
||||||
|
|
||||||
|
case LMIC_ERROR_SUCCESS:
|
||||||
|
ESP_LOGI(TAG, "%d byte(s) sent to LORA", SendBuffer.MessageSize);
|
||||||
|
break;
|
||||||
|
case LMIC_ERROR_TX_BUSY: // LMIC already has a tx message pending
|
||||||
|
case LMIC_ERROR_TX_FAILED: // message was not sent
|
||||||
|
// ESP_LOGD(TAG, "LMIC busy, message re-enqueued"); // very noisy
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000 + random(500))); // wait a while
|
||||||
|
lora_enqueuedata(&SendBuffer); // re-enqueue the undelivered message
|
||||||
|
break;
|
||||||
|
case LMIC_ERROR_TX_TOO_LARGE: // message size exceeds LMIC buffer size
|
||||||
|
case LMIC_ERROR_TX_NOT_FEASIBLE: // message too large for current datarate
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"Message too large to send, message not sent and deleted");
|
||||||
|
// we need some kind of error handling here -> to be done
|
||||||
|
break;
|
||||||
|
default: // other LMIC return code
|
||||||
|
ESP_LOGE(TAG, "LMIC error, message not sent and deleted");
|
||||||
|
|
||||||
|
} // switch
|
||||||
|
}
|
||||||
|
delay(2); // yield to CPU
|
||||||
}
|
}
|
||||||
// reschedule job every 0,5 - 1 sec. including a bit of random to prevent
|
|
||||||
// systematic collisions
|
|
||||||
os_setTimedCallback(job, os_getTime() + 500 + ms2osticks(random(500)),
|
|
||||||
lora_send);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t lora_stack_init() {
|
esp_err_t lora_stack_init() {
|
||||||
@ -422,13 +375,13 @@ esp_err_t lora_stack_init() {
|
|||||||
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
|
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
|
||||||
SEND_QUEUE_SIZE * sizeof(MessageBuffer_t));
|
SEND_QUEUE_SIZE * sizeof(MessageBuffer_t));
|
||||||
|
|
||||||
// starting lorawan stack
|
// start lorawan stack
|
||||||
ESP_LOGI(TAG, "Starting LMIC...");
|
ESP_LOGI(TAG, "Starting LMIC...");
|
||||||
xTaskCreatePinnedToCore(lmictask, // task function
|
xTaskCreatePinnedToCore(lmictask, // task function
|
||||||
"lmictask", // name of task
|
"lmictask", // name of task
|
||||||
4096, // stack size of task
|
4096, // stack size of task
|
||||||
(void *)1, // parameter of the task
|
(void *)1, // parameter of the task
|
||||||
2, // priority of the task
|
5, // priority of the task
|
||||||
&lmicTask, // task handle
|
&lmicTask, // task handle
|
||||||
1); // CPU core
|
1); // CPU core
|
||||||
|
|
||||||
@ -436,18 +389,31 @@ esp_err_t lora_stack_init() {
|
|||||||
ESP_LOGI(TAG, "Already joined");
|
ESP_LOGI(TAG, "Already joined");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ESP_OK; // continue main program
|
// start lmic send task
|
||||||
|
xTaskCreatePinnedToCore(lora_send, // task function
|
||||||
|
"lorasendtask", // name of task
|
||||||
|
3072, // stack size of task
|
||||||
|
(void *)1, // parameter of the task
|
||||||
|
1, // priority of the task
|
||||||
|
&lorasendTask, // task handle
|
||||||
|
1); // CPU core
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) {
|
void lora_enqueuedata(MessageBuffer_t *message) {
|
||||||
// enqueue message in LORA send queue
|
// enqueue message in LORA send queue
|
||||||
BaseType_t ret;
|
BaseType_t ret = pdFALSE;
|
||||||
MessageBuffer_t DummyBuffer;
|
MessageBuffer_t DummyBuffer;
|
||||||
|
sendprio_t prio = message->MessagePrio;
|
||||||
|
|
||||||
switch (prio) {
|
switch (prio) {
|
||||||
case prio_high:
|
case prio_high:
|
||||||
// clear space in queue if full, then fallthrough to normal
|
// clear some space in queue if full, then fallthrough to prio_normal
|
||||||
if (uxQueueSpacesAvailable(LoraSendQueue) == 0)
|
if (uxQueueSpacesAvailable(LoraSendQueue) == 0) {
|
||||||
xQueueReceive(LoraSendQueue, &DummyBuffer, (TickType_t)0);
|
xQueueReceive(LoraSendQueue, &DummyBuffer, (TickType_t)0);
|
||||||
|
ESP_LOGW(TAG, "LORA sendqueue purged, data is lost");
|
||||||
|
}
|
||||||
case prio_normal:
|
case prio_normal:
|
||||||
ret = xQueueSendToFront(LoraSendQueue, (void *)message, (TickType_t)0);
|
ret = xQueueSendToFront(LoraSendQueue, (void *)message, (TickType_t)0);
|
||||||
break;
|
break;
|
||||||
@ -519,17 +485,17 @@ void lmictask(void *pvParameters) {
|
|||||||
os_init(); // initialize lmic run-time environment
|
os_init(); // initialize lmic run-time environment
|
||||||
LMIC_reset(); // initialize lmic MAC
|
LMIC_reset(); // initialize lmic MAC
|
||||||
LMIC_setLinkCheckMode(0);
|
LMIC_setLinkCheckMode(0);
|
||||||
// This tells LMIC to make the receive windows bigger, in case your clock is
|
|
||||||
// faster or slower. This causes the transceiver to be earlier switched on,
|
|
||||||
// so consuming more power. You may sharpen (reduce) CLOCK_ERROR_PERCENTAGE
|
|
||||||
// in src/lmic_config.h if you are limited on battery.
|
|
||||||
LMIC_setClockError(MAX_CLOCK_ERROR * CLOCK_ERROR_PROCENTAGE / 100);
|
|
||||||
// Set the data rate to Spreading Factor 7. This is the fastest supported
|
|
||||||
// rate for 125 kHz channels, and it minimizes air time and battery power.
|
|
||||||
// Set the transmission power to 14 dBi (25 mW).
|
|
||||||
LMIC_setDrTxpow(DR_SF7, 14);
|
|
||||||
|
|
||||||
#if defined(CFG_US915) || defined(CFG_au921)
|
// This tells LMIC to make the receive windows bigger, in case your clock is
|
||||||
|
// faster or slower. This causes the transceiver to be earlier switched on,
|
||||||
|
// so consuming more power. You may sharpen (reduce) CLOCK_ERROR_PERCENTAGE
|
||||||
|
// in src/lmic_config.h if you are limited on battery.
|
||||||
|
#ifdef CLOCK_ERROR_PROCENTAGE
|
||||||
|
LMIC_setClockError(MAX_CLOCK_ERROR * CLOCK_ERROR_PROCENTAGE / 100);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//#if defined(CFG_US915) || defined(CFG_au921)
|
||||||
|
#if CFG_LMIC_US_like
|
||||||
// in the US, with TTN, it saves join time if we start on subband 1
|
// in the US, with TTN, it saves join time if we start on subband 1
|
||||||
// (channels 8-15). This will get overridden after the join by parameters
|
// (channels 8-15). This will get overridden after the join by parameters
|
||||||
// from the network. If working with other networks or in other regions,
|
// from the network. If working with other networks or in other regions,
|
||||||
@ -537,10 +503,161 @@ void lmictask(void *pvParameters) {
|
|||||||
LMIC_selectSubBand(1);
|
LMIC_selectSubBand(1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Set the data rate to Spreading Factor 7. This is the fastest supported
|
||||||
|
// rate for 125 kHz channels, and it minimizes air time and battery power.
|
||||||
|
// Set the transmission power to 14 dBi (25 mW).
|
||||||
|
LMIC_setDrTxpow(DR_SF7, 14);
|
||||||
|
|
||||||
|
// register a callback for downlink messages. We aren't trying to write
|
||||||
|
// reentrant code, so pUserData is NULL.
|
||||||
|
LMIC_registerRxMessageCb(myRxCallback, NULL);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
os_runloop_once(); // execute lmic scheduled jobs and events
|
os_runloop_once(); // execute lmic scheduled jobs and events
|
||||||
delay(2); // yield to CPU
|
delay(2); // yield to CPU
|
||||||
}
|
}
|
||||||
} // lmictask
|
} // lmictask
|
||||||
|
|
||||||
|
// receive message handler
|
||||||
|
void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
|
||||||
|
size_t nMsg) {
|
||||||
|
|
||||||
|
// display type of received data
|
||||||
|
if (nMsg)
|
||||||
|
ESP_LOGI(TAG, "Received %u byte(s) of payload on port %u", nMsg, port);
|
||||||
|
else if (port)
|
||||||
|
ESP_LOGI(TAG, "Received empty message on port %u", port);
|
||||||
|
|
||||||
|
// list MAC messages, if any
|
||||||
|
uint8_t nMac = pMsg - &LMIC.frame[0];
|
||||||
|
if (port != MACPORT)
|
||||||
|
--nMac;
|
||||||
|
if (nMac) {
|
||||||
|
ESP_LOGI(TAG, "%u byte(s) downlink MAC commands", nMac);
|
||||||
|
// NOT WORKING YET
|
||||||
|
// whe need to unwrap the MAC command from LMIC.frame here
|
||||||
|
// mac_decode(LMIC.frame, nMac, MACdn_table, sizeof(MACdn_table) /
|
||||||
|
// sizeof(MACdn_table[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LMIC.pendMacLen) {
|
||||||
|
ESP_LOGI(TAG, "%u byte(s) uplink MAC commands", LMIC.pendMacLen);
|
||||||
|
mac_decode(LMIC.pendMacData, LMIC.pendMacLen, MACup_table,
|
||||||
|
sizeof(MACup_table) / sizeof(MACup_table[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (port) {
|
||||||
|
|
||||||
|
// ignore mac messages
|
||||||
|
case MACPORT:
|
||||||
|
break;
|
||||||
|
|
||||||
|
// rcommand received -> call interpreter
|
||||||
|
case RCMDPORT:
|
||||||
|
rcommand(pMsg, nMsg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
#if (TIME_SYNC_LORASERVER)
|
||||||
|
// valid timesync answer -> call timesync processor
|
||||||
|
if ((port >= TIMEANSWERPORT_MIN) && (port <= TIMEANSWERPORT_MAX)) {
|
||||||
|
recv_timesync_ans(port, pMsg, nMsg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// unknown port -> display info
|
||||||
|
ESP_LOGI(TAG, "Received data on unsupported port %u", port);
|
||||||
|
break;
|
||||||
|
} // switch
|
||||||
|
}
|
||||||
|
|
||||||
|
// transmit complete message handler
|
||||||
|
void myTxCallback(void *pUserData, int fSuccess) {
|
||||||
|
|
||||||
|
#if (TIME_SYNC_LORASERVER)
|
||||||
|
// if last packet sent was a timesync request, store TX timestamp
|
||||||
|
if (LMIC.pendTxPort == TIMEPORT)
|
||||||
|
store_time_sync_req(osticks2ms(LMIC.txend)); // milliseconds
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode LORAWAN MAC message
|
||||||
|
void mac_decode(const uint8_t cmd[], const uint8_t cmdlen, const mac_t table[],
|
||||||
|
const uint8_t tablesize) {
|
||||||
|
|
||||||
|
if (!cmdlen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint8_t foundcmd[cmdlen], cursor = 0;
|
||||||
|
|
||||||
|
while (cursor < cmdlen) {
|
||||||
|
|
||||||
|
int i = tablesize; // number of commands in table
|
||||||
|
|
||||||
|
while (i--) {
|
||||||
|
if (cmd[cursor] == table[i].opcode) { // lookup command in opcode table
|
||||||
|
cursor++; // strip 1 byte opcode
|
||||||
|
if ((cursor + table[i].params) <= cmdlen) {
|
||||||
|
memmove(foundcmd, cmd + cursor,
|
||||||
|
table[i].params); // strip opcode from cmd array
|
||||||
|
cursor += table[i].params;
|
||||||
|
ESP_LOGD(TAG, "MAC command %s", table[i].cmdname);
|
||||||
|
} else
|
||||||
|
ESP_LOGD(TAG, "MAC command 0x%02X with missing parameter(s)",
|
||||||
|
table[i].opcode);
|
||||||
|
break; // command found -> exit table lookup loop
|
||||||
|
} // end of command validation
|
||||||
|
} // end of command table lookup loop
|
||||||
|
if (i < 0) { // command not found -> skip it
|
||||||
|
ESP_LOGD(TAG, "Unknown MAC command 0x%02X", cmd[cursor]);
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
} // command parsing loop
|
||||||
|
|
||||||
|
} // mac_decode()
|
||||||
|
|
||||||
|
uint8_t getBattLevel() {
|
||||||
|
/*
|
||||||
|
return values:
|
||||||
|
MCMD_DEVS_EXT_POWER = 0x00, // external power supply
|
||||||
|
MCMD_DEVS_BATT_MIN = 0x01, // min battery value
|
||||||
|
MCMD_DEVS_BATT_MAX = 0xFE, // max battery value
|
||||||
|
MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level
|
||||||
|
*/
|
||||||
|
#if (defined HAS_PMU || defined BAT_MEASURE_ADC)
|
||||||
|
uint16_t voltage = read_voltage();
|
||||||
|
|
||||||
|
switch (voltage) {
|
||||||
|
case 0:
|
||||||
|
return MCMD_DEVS_BATT_NOINFO;
|
||||||
|
case 0xffff:
|
||||||
|
return MCMD_DEVS_EXT_POWER;
|
||||||
|
default:
|
||||||
|
return (voltage > OTA_MIN_BATT ? MCMD_DEVS_BATT_MAX : MCMD_DEVS_BATT_MIN);
|
||||||
|
}
|
||||||
|
#else // we don't have any info on battery level
|
||||||
|
return MCMD_DEVS_BATT_NOINFO;
|
||||||
|
#endif
|
||||||
|
} // getBattLevel()
|
||||||
|
|
||||||
|
// u1_t os_getBattLevel(void) { return getBattLevel(); };
|
||||||
|
|
||||||
|
const char *getSfName(rps_t rps) {
|
||||||
|
const char *const t[] = {"FSK", "SF7", "SF8", "SF9",
|
||||||
|
"SF10", "SF11", "SF12", "SF?"};
|
||||||
|
return t[getSf(rps)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getBwName(rps_t rps) {
|
||||||
|
const char *const t[] = {"BW125", "BW250", "BW500", "BW?"};
|
||||||
|
return t[getBw(rps)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getCrName(rps_t rps) {
|
||||||
|
const char *const t[] = {"CR 4/5", "CR 4/6", "CR 4/7", "CR 4/8"};
|
||||||
|
return t[getCr(rps)];
|
||||||
|
}
|
||||||
|
|
||||||
#endif // HAS_LORA
|
#endif // HAS_LORA
|
||||||
|
74
src/main.cpp
74
src/main.cpp
@ -31,12 +31,12 @@ ledloop 0 3 blinks LEDs
|
|||||||
spiloop 0 2 reads/writes data on spi interface
|
spiloop 0 2 reads/writes data on spi interface
|
||||||
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
|
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
|
||||||
|
|
||||||
|
lmictask 1 5 MCCI LMiC LORAWAN stack
|
||||||
clockloop 1 4 generates realtime telegrams for external clock
|
clockloop 1 4 generates realtime telegrams for external clock
|
||||||
timesync_req 1 3 processes realtime time sync requests
|
timesync_req 1 3 processes realtime time sync requests
|
||||||
lmictask 1 2 MCCI LMiC LORAWAN stack
|
|
||||||
irqhandler 1 1 display, timesync, gps, etc. triggered by timers
|
irqhandler 1 1 display, timesync, gps, etc. triggered by timers
|
||||||
gpsloop 1 1 reads data from GPS via serial or i2c
|
gpsloop 1 1 reads data from GPS via serial or i2c
|
||||||
looptask 1 1 arduino loop (unused)
|
lorasendtask 1 1 feed data from lora sendqueue to lmcic
|
||||||
IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator
|
IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator
|
||||||
|
|
||||||
Low priority numbers denote low priority tasks.
|
Low priority numbers denote low priority tasks.
|
||||||
@ -58,6 +58,7 @@ fired by hardware
|
|||||||
DisplayIRQ -> esp32 timer 0 -> irqHandlerTask (Core 1)
|
DisplayIRQ -> esp32 timer 0 -> irqHandlerTask (Core 1)
|
||||||
CLOCKIRQ -> esp32 timer 1 -> ClockTask (Core 1)
|
CLOCKIRQ -> esp32 timer 1 -> ClockTask (Core 1)
|
||||||
ButtonIRQ -> external gpio -> irqHandlerTask (Core 1)
|
ButtonIRQ -> external gpio -> irqHandlerTask (Core 1)
|
||||||
|
PMUIRQ -> PMU chip gpio -> irqHandlerTask (Core 1)
|
||||||
|
|
||||||
fired by software (Ticker.h)
|
fired by software (Ticker.h)
|
||||||
TIMESYNC_IRQ -> timeSync() -> irqHandlerTask (Core 1)
|
TIMESYNC_IRQ -> timeSync() -> irqHandlerTask (Core 1)
|
||||||
@ -76,7 +77,7 @@ triggers pps 1 sec impulse
|
|||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
|
||||||
configData_t cfg; // struct holds current device configuration
|
configData_t cfg; // struct holds current device configuration
|
||||||
char display_line6[16], display_line7[16]; // display buffers
|
char lmic_event_msg[16]; // display buffer for LMIC event message
|
||||||
uint8_t volatile channel = 0; // channel rotation counter
|
uint8_t volatile channel = 0; // channel rotation counter
|
||||||
uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
|
uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
|
||||||
batt_voltage = 0; // globals for display
|
batt_voltage = 0; // globals for display
|
||||||
@ -169,24 +170,45 @@ void setup() {
|
|||||||
ESP_LOGI(TAG, "TinyGPS+ version %s", TinyGPSPlus::libraryVersion());
|
ESP_LOGI(TAG, "TinyGPS+ version %s", TinyGPSPlus::libraryVersion());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// open i2c bus
|
||||||
|
#ifdef HAS_DISPLAY
|
||||||
|
Wire.begin(MY_OLED_SDA, MY_OLED_SCL, 100000);
|
||||||
|
#else
|
||||||
|
Wire.begin(SDA, SCL, 100000);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// setup power on boards with power management logic
|
||||||
|
#ifdef EXT_POWER_SW
|
||||||
|
pinMode(EXT_POWER_SW, OUTPUT);
|
||||||
|
digitalWrite(EXT_POWER_SW, EXT_POWER_ON);
|
||||||
|
strcat_P(features, " VEXT");
|
||||||
|
#endif
|
||||||
|
#ifdef HAS_PMU
|
||||||
|
AXP192_init();
|
||||||
|
strcat_P(features, " PMU");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// scan i2c bus for devices
|
||||||
|
i2c_scan();
|
||||||
|
|
||||||
#endif // verbose
|
#endif // verbose
|
||||||
|
|
||||||
// read (and initialize on first run) runtime settings from NVRAM
|
// read (and initialize on first run) runtime settings from NVRAM
|
||||||
loadConfig(); // includes initialize if necessary
|
loadConfig(); // includes initialize if necessary
|
||||||
|
|
||||||
|
// initialize display
|
||||||
|
#ifdef HAS_DISPLAY
|
||||||
|
strcat_P(features, " OLED");
|
||||||
|
DisplayIsOn = cfg.screenon;
|
||||||
|
init_display(PRODUCTNAME, PROGVERSION); // note: blocking call
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef BOARD_HAS_PSRAM
|
#ifdef BOARD_HAS_PSRAM
|
||||||
assert(psramFound());
|
assert(psramFound());
|
||||||
ESP_LOGI(TAG, "PSRAM found and initialized");
|
ESP_LOGI(TAG, "PSRAM found and initialized");
|
||||||
strcat_P(features, " PSRAM");
|
strcat_P(features, " PSRAM");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// set external power mode
|
|
||||||
#ifdef EXT_POWER_SW
|
|
||||||
pinMode(EXT_POWER_SW, OUTPUT);
|
|
||||||
digitalWrite(EXT_POWER_SW, EXT_POWER_ON);
|
|
||||||
strcat_P(features, " VEXT");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef BAT_MEASURE_EN
|
#ifdef BAT_MEASURE_EN
|
||||||
pinMode(BAT_MEASURE_EN, OUTPUT);
|
pinMode(BAT_MEASURE_EN, OUTPUT);
|
||||||
#endif
|
#endif
|
||||||
@ -195,17 +217,25 @@ void setup() {
|
|||||||
#if (HAS_LED != NOT_A_PIN)
|
#if (HAS_LED != NOT_A_PIN)
|
||||||
pinMode(HAS_LED, OUTPUT);
|
pinMode(HAS_LED, OUTPUT);
|
||||||
strcat_P(features, " LED");
|
strcat_P(features, " LED");
|
||||||
|
|
||||||
|
#ifdef LED_POWER_SW
|
||||||
|
pinMode(LED_POWER_SW, OUTPUT);
|
||||||
|
digitalWrite(LED_POWER_SW, LED_POWER_ON);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_TWO_LED
|
#ifdef HAS_TWO_LED
|
||||||
pinMode(HAS_TWO_LED, OUTPUT);
|
pinMode(HAS_TWO_LED, OUTPUT);
|
||||||
strcat_P(features, " LED1");
|
strcat_P(features, " LED1");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// use LED for power display if we have additional RGB LED, else for status
|
// use LED for power display if we have additional RGB LED, else for status
|
||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
switch_LED(LED_ON);
|
switch_LED(LED_ON);
|
||||||
strcat_P(features, " RGB");
|
strcat_P(features, " RGB");
|
||||||
rgb_set_color(COLOR_PINK);
|
rgb_set_color(COLOR_PINK);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
#endif // HAS_LED
|
||||||
|
|
||||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||||
// start led loop
|
// start led loop
|
||||||
@ -227,7 +257,7 @@ void setup() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// initialize battery status
|
// initialize battery status
|
||||||
#ifdef BAT_MEASURE_ADC
|
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
||||||
strcat_P(features, " BATT");
|
strcat_P(features, " BATT");
|
||||||
calibrate_voltage();
|
calibrate_voltage();
|
||||||
batt_voltage = read_voltage();
|
batt_voltage = read_voltage();
|
||||||
@ -297,13 +327,6 @@ void setup() {
|
|||||||
strcat_P(features, " FILTER");
|
strcat_P(features, " FILTER");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// initialize display
|
|
||||||
#ifdef HAS_DISPLAY
|
|
||||||
strcat_P(features, " OLED");
|
|
||||||
DisplayIsOn = cfg.screenon;
|
|
||||||
init_display(PRODUCTNAME, PROGVERSION); // note: blocking call
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// initialize matrix display
|
// initialize matrix display
|
||||||
#ifdef HAS_MATRIX_DISPLAY
|
#ifdef HAS_MATRIX_DISPLAY
|
||||||
strcat_P(features, " LED_MATRIX");
|
strcat_P(features, " LED_MATRIX");
|
||||||
@ -419,12 +442,10 @@ void setup() {
|
|||||||
#warning you did not specify a time source, time will not be synched
|
#warning you did not specify a time source, time will not be synched
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
// initialize gps time
|
// initialize gps time
|
||||||
#if (HAS_GPS)
|
#if (HAS_GPS)
|
||||||
fetch_gpsTime();
|
fetch_gpsTime();
|
||||||
#endif
|
#endif
|
||||||
*/
|
|
||||||
|
|
||||||
#if (defined HAS_IF482 || defined HAS_DCF77)
|
#if (defined HAS_IF482 || defined HAS_DCF77)
|
||||||
ESP_LOGI(TAG, "Starting Clock Controller...");
|
ESP_LOGI(TAG, "Starting Clock Controller...");
|
||||||
@ -450,13 +471,6 @@ void setup() {
|
|||||||
|
|
||||||
} // setup()
|
} // setup()
|
||||||
|
|
||||||
void loop() {
|
} // setup()
|
||||||
|
|
||||||
while (1) {
|
void loop() { vTaskDelete(NULL); }
|
||||||
#if (HAS_LORA)
|
|
||||||
os_runloop_once(); // execute lmic scheduled jobs and events
|
|
||||||
#else
|
|
||||||
delay(2); // yield to CPU
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// clang-format off
|
||||||
|
|
||||||
// ----- Paxcounter user config file ------
|
// ----- Paxcounter user config file ------
|
||||||
//
|
//
|
||||||
// --> adapt to your needs and use case <--
|
// --> adapt to your needs and use case <--
|
||||||
@ -46,7 +48,8 @@
|
|||||||
#define MEM_LOW 2048 // [Bytes] low memory threshold triggering a send cycle
|
#define MEM_LOW 2048 // [Bytes] low memory threshold triggering a send cycle
|
||||||
#define RETRANSMIT_RCMD 5 // [seconds] wait time before retransmitting rcommand results
|
#define RETRANSMIT_RCMD 5 // [seconds] wait time before retransmitting rcommand results
|
||||||
#define PAYLOAD_BUFFER_SIZE 51 // maximum size of payload block per transmit
|
#define PAYLOAD_BUFFER_SIZE 51 // maximum size of payload block per transmit
|
||||||
#define LORASFDEFAULT 9 // 7 ... 12 SF, according to LoRaWAN specs
|
#define LORADRDEFAULT 5 // 0 .. 15, LoRaWAN datarate, according to regional LoRaWAN specs [default = 5]
|
||||||
|
#define LORATXPOWDEFAULT 7 // 0 .. 255, LoRaWAN TX power in dBm [default = 14]
|
||||||
#define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy
|
#define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy
|
||||||
#define SEND_QUEUE_SIZE 10 // maximum number of messages in payload send queue [1 = no queue]
|
#define SEND_QUEUE_SIZE 10 // maximum number of messages in payload send queue [1 = no queue]
|
||||||
|
|
||||||
@ -72,7 +75,7 @@
|
|||||||
#define TIME_SYNC_INTERVAL_RETRY 10 // retry time sync after lost sync each .. minutes [default = 10], 0 means off
|
#define TIME_SYNC_INTERVAL_RETRY 10 // retry time sync after lost sync each .. minutes [default = 10], 0 means off
|
||||||
#define TIME_SYNC_COMPILEDATE 0 // set to 1 to use compile date to initialize RTC after power outage [default = 0]
|
#define TIME_SYNC_COMPILEDATE 0 // set to 1 to use compile date to initialize RTC after power outage [default = 0]
|
||||||
#define TIME_SYNC_LORAWAN 0 // set to 1 to use LORA network as time source, 0 means off [default = 0]
|
#define TIME_SYNC_LORAWAN 0 // set to 1 to use LORA network as time source, 0 means off [default = 0]
|
||||||
#define TIME_SYNC_LORASERVER 1 // set to 1 to use LORA timeserver as time source, 0 means off [default = 0]
|
#define TIME_SYNC_LORASERVER 0 // set to 1 to use LORA timeserver as time source, 0 means off [default = 0]
|
||||||
|
|
||||||
// settings for syncing time with timeserver applications
|
// settings for syncing time with timeserver applications
|
||||||
#define TIME_SYNC_SAMPLES 1 // number of time requests for averaging
|
#define TIME_SYNC_SAMPLES 1 // number of time requests for averaging
|
||||||
@ -85,6 +88,7 @@
|
|||||||
|
|
||||||
// Ports on which the device sends and listenes on LoRaWAN and SPI
|
// Ports on which the device sends and listenes on LoRaWAN and SPI
|
||||||
#define COUNTERPORT 1 // counts
|
#define COUNTERPORT 1 // counts
|
||||||
|
#define MACPORT 0 // network commands
|
||||||
#define RCMDPORT 2 // remote commands
|
#define RCMDPORT 2 // remote commands
|
||||||
#define STATUSPORT 2 // remote command results
|
#define STATUSPORT 2 // remote command results
|
||||||
#define CONFIGPORT 3 // config query results
|
#define CONFIGPORT 3 // config query results
|
||||||
|
@ -43,7 +43,7 @@ void PayloadConvert::addVoltage(uint16_t value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PayloadConvert::addConfig(configData_t value) {
|
void PayloadConvert::addConfig(configData_t value) {
|
||||||
buffer[cursor++] = value.lorasf;
|
buffer[cursor++] = value.loradr;
|
||||||
buffer[cursor++] = value.txpower;
|
buffer[cursor++] = value.txpower;
|
||||||
buffer[cursor++] = value.adrmode;
|
buffer[cursor++] = value.adrmode;
|
||||||
buffer[cursor++] = value.screensaver;
|
buffer[cursor++] = value.screensaver;
|
||||||
@ -164,7 +164,7 @@ void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) {
|
|||||||
void PayloadConvert::addVoltage(uint16_t value) { writeUint16(value); }
|
void PayloadConvert::addVoltage(uint16_t value) { writeUint16(value); }
|
||||||
|
|
||||||
void PayloadConvert::addConfig(configData_t value) {
|
void PayloadConvert::addConfig(configData_t value) {
|
||||||
writeUint8(value.lorasf);
|
writeUint8(value.loradr);
|
||||||
writeUint8(value.txpower);
|
writeUint8(value.txpower);
|
||||||
writeUint16(value.rssilimit);
|
writeUint16(value.rssilimit);
|
||||||
writeUint8(value.sendcycle);
|
writeUint8(value.sendcycle);
|
||||||
@ -378,7 +378,7 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float celsius,
|
|||||||
uint32_t mem, uint8_t reset1, uint8_t reset2) {
|
uint32_t mem, uint8_t reset1, uint8_t reset2) {
|
||||||
uint16_t temp = celsius * 10;
|
uint16_t temp = celsius * 10;
|
||||||
uint16_t volt = voltage / 10;
|
uint16_t volt = voltage / 10;
|
||||||
#ifdef BAT_MEASURE_ADC
|
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
||||||
#if (PAYLOAD_ENCODER == 3)
|
#if (PAYLOAD_ENCODER == 3)
|
||||||
buffer[cursor++] = LPP_BATT_CHANNEL;
|
buffer[cursor++] = LPP_BATT_CHANNEL;
|
||||||
#endif
|
#endif
|
||||||
|
231
src/power.cpp
Normal file
231
src/power.cpp
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
// Basic config
|
||||||
|
#include "globals.h"
|
||||||
|
#include "power.h"
|
||||||
|
|
||||||
|
// Local logging tag
|
||||||
|
static const char TAG[] = __FILE__;
|
||||||
|
|
||||||
|
#ifdef HAS_PMU
|
||||||
|
|
||||||
|
AXP20X_Class pmu;
|
||||||
|
|
||||||
|
void power_event_IRQ(void) {
|
||||||
|
|
||||||
|
if (!I2C_MUTEX_LOCK())
|
||||||
|
ESP_LOGW(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0);
|
||||||
|
else {
|
||||||
|
|
||||||
|
pmu.readIRQ();
|
||||||
|
// put your power event handler code here
|
||||||
|
|
||||||
|
if (pmu.isVbusOverVoltageIRQ())
|
||||||
|
ESP_LOGI(TAG, "USB voltage %.2fV too high.", pmu.getVbusVoltage() / 1000);
|
||||||
|
if (pmu.isVbusPlugInIRQ())
|
||||||
|
ESP_LOGI(TAG, "USB plugged, %.2fV @ %.0mA", pmu.getVbusVoltage() / 1000,
|
||||||
|
pmu.getVbusCurrent());
|
||||||
|
if (pmu.isVbusRemoveIRQ())
|
||||||
|
ESP_LOGI(TAG, "USB unplugged.");
|
||||||
|
|
||||||
|
if (pmu.isBattPlugInIRQ())
|
||||||
|
ESP_LOGI(TAG, "Battery is connected.");
|
||||||
|
if (pmu.isBattRemoveIRQ())
|
||||||
|
ESP_LOGI(TAG, "Battery was removed.");
|
||||||
|
if (pmu.isChargingIRQ())
|
||||||
|
ESP_LOGI(TAG, "Battery charging.");
|
||||||
|
if (pmu.isChargingDoneIRQ())
|
||||||
|
ESP_LOGI(TAG, "Battery charging done.");
|
||||||
|
if (pmu.isBattTempLowIRQ())
|
||||||
|
ESP_LOGI(TAG, "Battery high temperature.");
|
||||||
|
if (pmu.isBattTempHighIRQ())
|
||||||
|
ESP_LOGI(TAG, "Battery low temperature.");
|
||||||
|
|
||||||
|
// display on/off
|
||||||
|
if (pmu.isPEKShortPressIRQ()) {
|
||||||
|
cfg.screenon = !cfg.screenon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// shutdown power
|
||||||
|
if (pmu.isPEKLongtPressIRQ()) {
|
||||||
|
AXP192_power(false); // switch off Lora, GPS, display
|
||||||
|
pmu.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
pmu.clearIRQ();
|
||||||
|
I2C_MUTEX_UNLOCK();
|
||||||
|
} // mutex
|
||||||
|
|
||||||
|
// refresh stored voltage value
|
||||||
|
read_voltage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AXP192_power(bool on) {
|
||||||
|
if (on) {
|
||||||
|
pmu.setPowerOutPut(AXP192_LDO2, AXP202_ON); // Lora on T-Beam V1.0
|
||||||
|
pmu.setPowerOutPut(AXP192_LDO3, AXP202_ON); // Gps on T-Beam V1.0
|
||||||
|
pmu.setPowerOutPut(AXP192_DCDC1, AXP202_ON); // OLED on T-Beam v1.0
|
||||||
|
// pmu.setChgLEDMode(AXP20X_LED_LOW_LEVEL);
|
||||||
|
pmu.setChgLEDMode(AXP20X_LED_BLINK_1HZ);
|
||||||
|
} else {
|
||||||
|
pmu.setChgLEDMode(AXP20X_LED_OFF);
|
||||||
|
pmu.setPowerOutPut(AXP192_DCDC1, AXP202_OFF);
|
||||||
|
pmu.setPowerOutPut(AXP192_LDO3, AXP202_OFF);
|
||||||
|
pmu.setPowerOutPut(AXP192_LDO2, AXP202_OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AXP192_showstatus(void) {
|
||||||
|
|
||||||
|
if (!I2C_MUTEX_LOCK())
|
||||||
|
ESP_LOGW(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0);
|
||||||
|
else {
|
||||||
|
|
||||||
|
if (pmu.isBatteryConnect())
|
||||||
|
if (pmu.isChargeing())
|
||||||
|
ESP_LOGI(TAG, "Battery charging, %.2fV @ %.0fmAh",
|
||||||
|
pmu.getBattVoltage() / 1000, pmu.getBattChargeCurrent());
|
||||||
|
else
|
||||||
|
ESP_LOGI(TAG, "Battery not charging");
|
||||||
|
else
|
||||||
|
ESP_LOGI(TAG, "No Battery");
|
||||||
|
|
||||||
|
if (pmu.isVBUSPlug())
|
||||||
|
ESP_LOGI(TAG, "USB powered, %.0fmW",
|
||||||
|
pmu.getVbusVoltage() / 1000 * pmu.getVbusCurrent());
|
||||||
|
else
|
||||||
|
ESP_LOGI(TAG, "USB not present");
|
||||||
|
|
||||||
|
I2C_MUTEX_UNLOCK();
|
||||||
|
} // mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
void AXP192_init(void) {
|
||||||
|
|
||||||
|
// block i2c bus access
|
||||||
|
if (I2C_MUTEX_LOCK()) {
|
||||||
|
|
||||||
|
if (pmu.begin(Wire, AXP192_PRIMARY_ADDRESS))
|
||||||
|
ESP_LOGI(TAG, "AXP192 PMU initialization failed");
|
||||||
|
else {
|
||||||
|
|
||||||
|
// configure AXP192
|
||||||
|
pmu.setDCDC1Voltage(3300); // for external OLED display
|
||||||
|
pmu.setTimeOutShutdown(false); // no automatic shutdown
|
||||||
|
pmu.setTSmode(AXP_TS_PIN_MODE_DISABLE); // TS pin mode off to save power
|
||||||
|
|
||||||
|
// switch ADCs on
|
||||||
|
pmu.adc1Enable(AXP202_BATT_VOL_ADC1, true);
|
||||||
|
pmu.adc1Enable(AXP202_BATT_CUR_ADC1, true);
|
||||||
|
pmu.adc1Enable(AXP202_VBUS_VOL_ADC1, true);
|
||||||
|
pmu.adc1Enable(AXP202_VBUS_CUR_ADC1, true);
|
||||||
|
|
||||||
|
// switch power rails on
|
||||||
|
AXP192_power(true);
|
||||||
|
|
||||||
|
// I2C access of AXP202X library currently is not mutexable
|
||||||
|
// so we better should disable AXP interrupts... ?
|
||||||
|
#ifdef PMU_INT
|
||||||
|
pinMode(PMU_INT, INPUT_PULLUP);
|
||||||
|
attachInterrupt(digitalPinToInterrupt(PMU_INT), PMUIRQ, FALLING);
|
||||||
|
pmu.enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ |
|
||||||
|
AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ |
|
||||||
|
AXP202_CHARGING_FINISHED_IRQ,
|
||||||
|
1);
|
||||||
|
pmu.clearIRQ();
|
||||||
|
#endif // PMU_INT
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "AXP192 PMU initialized");
|
||||||
|
}
|
||||||
|
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||||
|
} else
|
||||||
|
ESP_LOGE(TAG, "I2c bus busy - PMU initialization error");
|
||||||
|
}
|
||||||
|
#endif // HAS_PMU
|
||||||
|
|
||||||
|
#ifdef BAT_MEASURE_ADC
|
||||||
|
esp_adc_cal_characteristics_t *adc_characs =
|
||||||
|
(esp_adc_cal_characteristics_t *)calloc(
|
||||||
|
1, sizeof(esp_adc_cal_characteristics_t));
|
||||||
|
|
||||||
|
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
|
||||||
|
static const adc1_channel_t adc_channel = BAT_MEASURE_ADC;
|
||||||
|
#else // ADC2
|
||||||
|
static const adc2_channel_t adc_channel = BAT_MEASURE_ADC;
|
||||||
|
#endif
|
||||||
|
static const adc_atten_t atten = ADC_ATTEN_DB_11;
|
||||||
|
static const adc_unit_t unit = ADC_UNIT_1;
|
||||||
|
|
||||||
|
#endif // BAT_MEASURE_ADC
|
||||||
|
|
||||||
|
void calibrate_voltage(void) {
|
||||||
|
#ifdef BAT_MEASURE_ADC
|
||||||
|
// configure ADC
|
||||||
|
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
|
||||||
|
ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_12));
|
||||||
|
ESP_ERROR_CHECK(adc1_config_channel_atten(adc_channel, atten));
|
||||||
|
#else // ADC2
|
||||||
|
// ESP_ERROR_CHECK(adc2_config_width(ADC_WIDTH_BIT_12));
|
||||||
|
ESP_ERROR_CHECK(adc2_config_channel_atten(adc_channel, atten));
|
||||||
|
#endif
|
||||||
|
// calibrate ADC
|
||||||
|
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(
|
||||||
|
unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_characs);
|
||||||
|
// show ADC characterization base
|
||||||
|
if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"ADC characterization based on Two Point values stored in eFuse");
|
||||||
|
} else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"ADC characterization based on reference voltage stored in eFuse");
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "ADC characterization based on default reference voltage");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool batt_sufficient() {
|
||||||
|
#if (defined HAS_PMU || defined BAT_MEASURE_ADC)
|
||||||
|
uint16_t volts = read_voltage();
|
||||||
|
return ((volts < 1000) ||
|
||||||
|
(volts > OTA_MIN_BATT)); // no battery or battery sufficient
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t read_voltage() {
|
||||||
|
uint16_t voltage = 0;
|
||||||
|
|
||||||
|
#ifdef HAS_PMU
|
||||||
|
if (!I2C_MUTEX_LOCK())
|
||||||
|
ESP_LOGW(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0);
|
||||||
|
else {
|
||||||
|
voltage = pmu.isVBUSPlug() ? 0xffff : pmu.getBattVoltage();
|
||||||
|
I2C_MUTEX_UNLOCK();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifdef BAT_MEASURE_ADC
|
||||||
|
// multisample ADC
|
||||||
|
uint32_t adc_reading = 0;
|
||||||
|
int adc_buf = 0;
|
||||||
|
for (int i = 0; i < NO_OF_SAMPLES; i++) {
|
||||||
|
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
|
||||||
|
adc_reading += adc1_get_raw(adc_channel);
|
||||||
|
#else // ADC2
|
||||||
|
ESP_ERROR_CHECK(adc2_get_raw(adc_channel, ADC_WIDTH_BIT_12, &adc_buf));
|
||||||
|
adc_reading += adc_buf;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
adc_reading /= NO_OF_SAMPLES;
|
||||||
|
// Convert ADC reading to voltage in mV
|
||||||
|
voltage = esp_adc_cal_raw_to_voltage(adc_reading, adc_characs);
|
||||||
|
#endif // BAT_MEASURE_ADC
|
||||||
|
|
||||||
|
#ifdef BAT_VOLTAGE_DIVIDER
|
||||||
|
voltage *= BAT_VOLTAGE_DIVIDER;
|
||||||
|
#endif // BAT_VOLTAGE_DIVIDER
|
||||||
|
|
||||||
|
#endif // HAS_PMU
|
||||||
|
|
||||||
|
return voltage;
|
||||||
|
}
|
@ -19,32 +19,25 @@ void do_reset() {
|
|||||||
void set_reset(uint8_t val[]) {
|
void set_reset(uint8_t val[]) {
|
||||||
switch (val[0]) {
|
switch (val[0]) {
|
||||||
case 0: // restart device
|
case 0: // restart device
|
||||||
sprintf(display_line6, "Reset pending");
|
|
||||||
do_reset();
|
do_reset();
|
||||||
break;
|
break;
|
||||||
case 1: // reset MAC counter
|
case 1: // reset MAC counter
|
||||||
ESP_LOGI(TAG, "Remote command: reset MAC counter");
|
ESP_LOGI(TAG, "Remote command: reset MAC counter");
|
||||||
reset_counters(); // clear macs
|
reset_counters(); // clear macs
|
||||||
get_salt(); // get new salt
|
get_salt(); // get new salt
|
||||||
sprintf(display_line6, "Reset counter");
|
|
||||||
break;
|
break;
|
||||||
case 2: // reset device to factory settings
|
case 2: // reset device to factory settings
|
||||||
ESP_LOGI(TAG, "Remote command: reset device to factory settings");
|
ESP_LOGI(TAG, "Remote command: reset device to factory settings");
|
||||||
sprintf(display_line6, "Factory reset");
|
|
||||||
eraseConfig();
|
eraseConfig();
|
||||||
break;
|
break;
|
||||||
case 3: // reset send queues
|
case 3: // reset send queues
|
||||||
ESP_LOGI(TAG, "Remote command: flush send queue");
|
ESP_LOGI(TAG, "Remote command: flush send queue");
|
||||||
sprintf(display_line6, "Queue reset");
|
|
||||||
flushQueues();
|
flushQueues();
|
||||||
break;
|
break;
|
||||||
case 9: // reset and ask for software update via Wifi OTA
|
case 9: // reset and ask for software update via Wifi OTA
|
||||||
ESP_LOGI(TAG, "Remote command: software update via Wifi");
|
ESP_LOGI(TAG, "Remote command: software update via Wifi");
|
||||||
#if (USE_OTA)
|
#if (USE_OTA)
|
||||||
sprintf(display_line6, "Software update");
|
|
||||||
cfg.runmode = 1;
|
cfg.runmode = 1;
|
||||||
#else
|
|
||||||
sprintf(display_line6, "Software update not implemented");
|
|
||||||
#endif // USE_OTA
|
#endif // USE_OTA
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -127,10 +120,34 @@ void set_gps(uint8_t val[]) {
|
|||||||
if (val[0]) {
|
if (val[0]) {
|
||||||
cfg.payloadmask |= (uint8_t)GPS_DATA; // set bit in mask
|
cfg.payloadmask |= (uint8_t)GPS_DATA; // set bit in mask
|
||||||
} else {
|
} else {
|
||||||
cfg.payloadmask &= ~(uint8_t)GPS_DATA; // clear bit in mask
|
cfg.payloadmask &= (uint8_t)~GPS_DATA; // clear bit in mask
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_bme(uint8_t val[]) {
|
||||||
|
ESP_LOGI(TAG, "Remote command: set BME mode to %s", val[0] ? "on" : "off");
|
||||||
|
if (val[0]) {
|
||||||
|
cfg.payloadmask |= (uint8_t)MEMS_DATA; // set bit in mask
|
||||||
|
} else {
|
||||||
|
cfg.payloadmask &= (uint8_t)~MEMS_DATA; // clear bit in mask
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_batt(uint8_t val[]) {
|
||||||
|
ESP_LOGI(TAG, "Remote command: set battery mode to %s",
|
||||||
|
val[0] ? "on" : "off");
|
||||||
|
if (val[0]) {
|
||||||
|
cfg.payloadmask |= (uint8_t)BATT_DATA; // set bit in mask
|
||||||
|
} else {
|
||||||
|
cfg.payloadmask &= (uint8_t)~BATT_DATA; // clear bit in mask
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_payloadmask(uint8_t val[]) {
|
||||||
|
ESP_LOGI(TAG, "Remote command: set payload mask to %X", val[0]);
|
||||||
|
cfg.payloadmask = val[0];
|
||||||
|
}
|
||||||
|
|
||||||
void set_sensor(uint8_t val[]) {
|
void set_sensor(uint8_t val[]) {
|
||||||
#if (HAS_SENSORS)
|
#if (HAS_SENSORS)
|
||||||
switch (val[0]) { // check if valid sensor number 1...4
|
switch (val[0]) { // check if valid sensor number 1...4
|
||||||
@ -169,10 +186,22 @@ void set_monitor(uint8_t val[]) {
|
|||||||
cfg.monitormode = val[0] ? 1 : 0;
|
cfg.monitormode = val[0] ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_lorasf(uint8_t val[]) {
|
void set_loradr(uint8_t val[]) {
|
||||||
#if (HAS_LORA)
|
#if (HAS_LORA)
|
||||||
ESP_LOGI(TAG, "Remote command: set LoRa SF to %d", val[0]);
|
if (validDR(val[0])) {
|
||||||
switch_lora(val[0], cfg.txpower);
|
cfg.loradr = val[0];
|
||||||
|
ESP_LOGI(TAG, "Remote command: set LoRa Datarate to %d", cfg.loradr);
|
||||||
|
LMIC_setDrTxpow(assertDR(cfg.loradr), cfg.txpower);
|
||||||
|
ESP_LOGI(TAG, "Radio parameters now %s / %s / %s",
|
||||||
|
getSfName(updr2rps(LMIC.datarate)),
|
||||||
|
getBwName(updr2rps(LMIC.datarate)),
|
||||||
|
getCrName(updr2rps(LMIC.datarate)));
|
||||||
|
|
||||||
|
} else
|
||||||
|
ESP_LOGI(
|
||||||
|
TAG,
|
||||||
|
"Remote command: set LoRa Datarate called with illegal datarate %d",
|
||||||
|
val[0]);
|
||||||
#else
|
#else
|
||||||
ESP_LOGW(TAG, "Remote command: LoRa not implemented");
|
ESP_LOGW(TAG, "Remote command: LoRa not implemented");
|
||||||
#endif // HAS_LORA
|
#endif // HAS_LORA
|
||||||
@ -223,8 +252,16 @@ void set_rgblum(uint8_t val[]) {
|
|||||||
|
|
||||||
void set_lorapower(uint8_t val[]) {
|
void set_lorapower(uint8_t val[]) {
|
||||||
#if (HAS_LORA)
|
#if (HAS_LORA)
|
||||||
ESP_LOGI(TAG, "Remote command: set LoRa TXPOWER to %d", val[0]);
|
// set data rate and transmit power only if we have no ADR
|
||||||
switch_lora(cfg.lorasf, val[0]);
|
if (!cfg.adrmode) {
|
||||||
|
cfg.txpower = val[0];
|
||||||
|
ESP_LOGI(TAG, "Remote command: set LoRa TXPOWER to %d", cfg.txpower);
|
||||||
|
LMIC_setDrTxpow(assertDR(cfg.loradr), cfg.txpower);
|
||||||
|
} else
|
||||||
|
ESP_LOGI(
|
||||||
|
TAG,
|
||||||
|
"Remote command: set LoRa TXPOWER, not executed because ADR is on");
|
||||||
|
|
||||||
#else
|
#else
|
||||||
ESP_LOGW(TAG, "Remote command: LoRa not implemented");
|
ESP_LOGW(TAG, "Remote command: LoRa not implemented");
|
||||||
#endif // HAS_LORA
|
#endif // HAS_LORA
|
||||||
@ -239,14 +276,10 @@ void get_config(uint8_t val[]) {
|
|||||||
|
|
||||||
void get_status(uint8_t val[]) {
|
void get_status(uint8_t val[]) {
|
||||||
ESP_LOGI(TAG, "Remote command: get device status");
|
ESP_LOGI(TAG, "Remote command: get device status");
|
||||||
#ifdef BAT_MEASURE_ADC
|
|
||||||
uint16_t voltage = read_voltage();
|
|
||||||
#else
|
|
||||||
uint16_t voltage = 0;
|
|
||||||
#endif
|
|
||||||
payload.reset();
|
payload.reset();
|
||||||
payload.addStatus(voltage, uptime() / 1000, temperatureRead(), getFreeRAM(),
|
payload.addStatus(read_voltage(), uptime() / 1000, temperatureRead(),
|
||||||
rtc_get_reset_reason(0), rtc_get_reset_reason(1));
|
getFreeRAM(), rtc_get_reset_reason(0),
|
||||||
|
rtc_get_reset_reason(1));
|
||||||
SendPayload(STATUSPORT, prio_high);
|
SendPayload(STATUSPORT, prio_high);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -274,6 +307,17 @@ void get_bme(uint8_t val[]) {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void get_batt(uint8_t val[]) {
|
||||||
|
ESP_LOGI(TAG, "Remote command: get battery voltage");
|
||||||
|
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
||||||
|
payload.reset();
|
||||||
|
payload.addVoltage(read_voltage());
|
||||||
|
SendPayload(BATTPORT, prio_normal);
|
||||||
|
#else
|
||||||
|
ESP_LOGW(TAG, "Battery voltage not supported");
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
void get_time(uint8_t val[]) {
|
void get_time(uint8_t val[]) {
|
||||||
ESP_LOGI(TAG, "Remote command: get time");
|
ESP_LOGI(TAG, "Remote command: get time");
|
||||||
payload.reset();
|
payload.reset();
|
||||||
@ -297,26 +341,28 @@ void set_flush(uint8_t val[]) {
|
|||||||
// format: opcode, function, #bytes params,
|
// format: opcode, function, #bytes params,
|
||||||
// flag (true = do make settings persistent / false = don't)
|
// flag (true = do make settings persistent / false = don't)
|
||||||
//
|
//
|
||||||
cmd_t table[] = {
|
static cmd_t table[] = {
|
||||||
{0x01, set_rssi, 1, true}, {0x02, set_countmode, 1, true},
|
{0x01, set_rssi, 1, true}, {0x02, set_countmode, 1, true},
|
||||||
{0x03, set_gps, 1, true}, {0x04, set_display, 1, true},
|
{0x03, set_gps, 1, true}, {0x04, set_display, 1, true},
|
||||||
{0x05, set_lorasf, 1, true}, {0x06, set_lorapower, 1, true},
|
{0x05, set_loradr, 1, true}, {0x06, set_lorapower, 1, true},
|
||||||
{0x07, set_loraadr, 1, true}, {0x08, set_screensaver, 1, true},
|
{0x07, set_loraadr, 1, true}, {0x08, set_screensaver, 1, true},
|
||||||
{0x09, set_reset, 1, true}, {0x0a, set_sendcycle, 1, true},
|
{0x09, set_reset, 1, true}, {0x0a, set_sendcycle, 1, true},
|
||||||
{0x0b, set_wifichancycle, 1, true}, {0x0c, set_blescantime, 1, true},
|
{0x0b, set_wifichancycle, 1, true}, {0x0c, set_blescantime, 1, true},
|
||||||
{0x0d, set_vendorfilter, 1, false}, {0x0e, set_blescan, 1, true},
|
{0x0d, set_vendorfilter, 1, false}, {0x0e, set_blescan, 1, true},
|
||||||
{0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true},
|
{0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true},
|
||||||
{0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false},
|
{0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false},
|
||||||
{0x13, set_sensor, 2, true}, {0x80, get_config, 0, false},
|
{0x13, set_sensor, 2, true}, {0x14, set_payloadmask, 1, true},
|
||||||
{0x81, get_status, 0, false}, {0x84, get_gps, 0, false},
|
{0x15, set_bme, 1, true}, {0x16, set_batt, 1, true},
|
||||||
|
{0x80, get_config, 0, false}, {0x81, get_status, 0, false},
|
||||||
|
{0x83, get_batt, 0, false}, {0x84, get_gps, 0, false},
|
||||||
{0x85, get_bme, 0, false}, {0x86, get_time, 0, false},
|
{0x85, get_bme, 0, false}, {0x86, get_time, 0, false},
|
||||||
{0x87, set_time, 0, false}, {0x99, set_flush, 0, false}};
|
{0x87, set_time, 0, false}, {0x99, set_flush, 0, false}};
|
||||||
|
|
||||||
const uint8_t cmdtablesize =
|
static const uint8_t cmdtablesize =
|
||||||
sizeof(table) / sizeof(table[0]); // number of commands in command table
|
sizeof(table) / sizeof(table[0]); // number of commands in command table
|
||||||
|
|
||||||
// check and execute remote command
|
// check and execute remote command
|
||||||
void rcommand(uint8_t cmd[], uint8_t cmdlength) {
|
void rcommand(const uint8_t cmd[], const uint8_t cmdlength) {
|
||||||
|
|
||||||
if (cmdlength == 0)
|
if (cmdlength == 0)
|
||||||
return;
|
return;
|
||||||
|
@ -10,9 +10,12 @@ void sendcycle() {
|
|||||||
// put data to send in RTos Queues used for transmit over channels Lora and SPI
|
// put data to send in RTos Queues used for transmit over channels Lora and SPI
|
||||||
void SendPayload(uint8_t port, sendprio_t prio) {
|
void SendPayload(uint8_t port, sendprio_t prio) {
|
||||||
|
|
||||||
MessageBuffer_t SendBuffer; // contains MessageSize, MessagePort, Message[]
|
MessageBuffer_t
|
||||||
|
SendBuffer; // contains MessageSize, MessagePort, MessagePrio, Message[]
|
||||||
|
|
||||||
SendBuffer.MessageSize = payload.getSize();
|
SendBuffer.MessageSize = payload.getSize();
|
||||||
|
SendBuffer.MessagePrio = prio;
|
||||||
|
|
||||||
switch (PAYLOAD_ENCODER) {
|
switch (PAYLOAD_ENCODER) {
|
||||||
case 1: // plain -> no mapping
|
case 1: // plain -> no mapping
|
||||||
case 2: // packed -> no mapping
|
case 2: // packed -> no mapping
|
||||||
@ -38,20 +41,20 @@ void SendPayload(uint8_t port, sendprio_t prio) {
|
|||||||
default:
|
default:
|
||||||
SendBuffer.MessagePort = port;
|
SendBuffer.MessagePort = port;
|
||||||
}
|
}
|
||||||
memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize());
|
memcpy(SendBuffer.Message, payload.getBuffer(), SendBuffer.MessageSize);
|
||||||
|
|
||||||
// enqueue message in device's send queues
|
// enqueue message in device's send queues
|
||||||
#if (HAS_LORA)
|
#if (HAS_LORA)
|
||||||
lora_enqueuedata(&SendBuffer, prio);
|
lora_enqueuedata(&SendBuffer);
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAS_SPI
|
#ifdef HAS_SPI
|
||||||
spi_enqueuedata(&SendBuffer, prio);
|
spi_enqueuedata(&SendBuffer);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // SendPayload
|
} // SendPayload
|
||||||
|
|
||||||
// interrupt triggered function to prepare payload to send
|
// interrupt triggered function to prepare payload to send
|
||||||
void sendCounter() {
|
void sendData() {
|
||||||
|
|
||||||
uint8_t bitmask = cfg.payloadmask;
|
uint8_t bitmask = cfg.payloadmask;
|
||||||
uint8_t mask = 1;
|
uint8_t mask = 1;
|
||||||
@ -115,7 +118,7 @@ void sendCounter() {
|
|||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BAT_MEASURE_ADC
|
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
||||||
case BATT_DATA:
|
case BATT_DATA:
|
||||||
payload.reset();
|
payload.reset();
|
||||||
payload.addVoltage(read_voltage());
|
payload.addVoltage(read_voltage());
|
||||||
@ -128,7 +131,7 @@ void sendCounter() {
|
|||||||
mask <<= 1;
|
mask <<= 1;
|
||||||
} // while (bitmask)
|
} // while (bitmask)
|
||||||
|
|
||||||
} // sendCounter()
|
} // sendData()
|
||||||
|
|
||||||
void flushQueues() {
|
void flushQueues() {
|
||||||
#if (HAS_LORA)
|
#if (HAS_LORA)
|
||||||
|
@ -56,7 +56,7 @@ void spi_slave_task(void *param) {
|
|||||||
memset(txbuf, 0, sizeof(txbuf));
|
memset(txbuf, 0, sizeof(txbuf));
|
||||||
memset(rxbuf, 0, sizeof(rxbuf));
|
memset(rxbuf, 0, sizeof(rxbuf));
|
||||||
|
|
||||||
// wait until data to send arrivey
|
// fetch next or wait for payload to send from queue
|
||||||
if (xQueueReceive(SPISendQueue, &msg, portMAX_DELAY) != pdTRUE) {
|
if (xQueueReceive(SPISendQueue, &msg, portMAX_DELAY) != pdTRUE) {
|
||||||
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
|
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
|
||||||
continue;
|
continue;
|
||||||
@ -147,10 +147,12 @@ esp_err_t spi_init() {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void spi_enqueuedata(MessageBuffer_t *message, sendprio_t prio) {
|
void spi_enqueuedata(MessageBuffer_t *message) {
|
||||||
// enqueue message in SPI send queue
|
// enqueue message in SPI send queue
|
||||||
BaseType_t ret;
|
BaseType_t ret;
|
||||||
MessageBuffer_t DummyBuffer;
|
MessageBuffer_t DummyBuffer;
|
||||||
|
sendprio_t prio = message->MessagePrio;
|
||||||
|
|
||||||
switch (prio) {
|
switch (prio) {
|
||||||
case prio_high:
|
case prio_high:
|
||||||
// clear space in queue if full, then fallthrough to normal
|
// clear space in queue if full, then fallthrough to normal
|
||||||
|
@ -54,12 +54,64 @@ void calibrateTime(void) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
goto finish;
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
|
|
||||||
setMyTime(t, t_msec, timeSource); // set time
|
setMyTime((uint32_t)t, t_msec, timeSource); // set time
|
||||||
|
|
||||||
} // calibrateTime()
|
} // calibrateTime()
|
||||||
|
|
||||||
|
// adjust system time, calibrate RTC and RTC_INT pps
|
||||||
|
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
|
||||||
|
timesource_t mytimesource) {
|
||||||
|
|
||||||
|
// called with invalid timesource?
|
||||||
|
if (mytimesource == _unsynced)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// increment t_sec only if t_msec > 1000
|
||||||
|
time_t time_to_set = (time_t)(t_sec + t_msec / 1000);
|
||||||
|
|
||||||
|
// do we have a valid time?
|
||||||
|
if (timeIsValid(time_to_set)) {
|
||||||
|
|
||||||
|
// if we have msec fraction, then wait until top of second with
|
||||||
|
// millisecond precision
|
||||||
|
if (t_msec % 1000) {
|
||||||
|
time_to_set++;
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "[%0.3f] UTC epoch time: %d.%03d sec", millis() / 1000.0,
|
||||||
|
time_to_set, t_msec % 1000);
|
||||||
|
|
||||||
|
// if we have got an external timesource, set RTC time and shift RTC_INT pulse
|
||||||
|
// to top of second
|
||||||
|
#ifdef HAS_RTC
|
||||||
|
if ((mytimesource == _gps) || (mytimesource == _lora))
|
||||||
|
set_rtctime(time_to_set);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// if we have a software pps timer, shift it to top of second
|
||||||
|
#if (!defined GPS_INT && !defined RTC_INT)
|
||||||
|
timerWrite(ppsIRQ, 0); // reset pps timer
|
||||||
|
CLOCKIRQ(); // fire clock pps, this advances time 1 sec
|
||||||
|
#endif
|
||||||
|
|
||||||
|
setTime(time_to_set); // set the time on top of second
|
||||||
|
|
||||||
|
timeSource = mytimesource; // set global variable
|
||||||
|
timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync);
|
||||||
|
ESP_LOGI(TAG, "[%0.3f] Timesync finished, time was set | source: %c",
|
||||||
|
millis() / 1000.0, timeSetSymbols[timeSource]);
|
||||||
|
} else {
|
||||||
|
timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, timeSync);
|
||||||
|
ESP_LOGI(TAG, "[%0.3f] Timesync failed, invalid time fetched | source: %c",
|
||||||
|
millis() / 1000.0, timeSetSymbols[timeSource]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// helper function to setup a pulse per second for time synchronisation
|
// helper function to setup a pulse per second for time synchronisation
|
||||||
uint8_t timepulse_init() {
|
uint8_t timepulse_init() {
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ void process_timesync_req(void *taskparameter) {
|
|||||||
payload.addByte(0x99);
|
payload.addByte(0x99);
|
||||||
SendPayload(RCMDPORT, prio_high);
|
SendPayload(RCMDPORT, prio_high);
|
||||||
// ...send a alive open a receive window for last time_sync_answer
|
// ...send a alive open a receive window for last time_sync_answer
|
||||||
// LMIC_sendAlive();
|
LMIC_sendAlive();
|
||||||
}
|
}
|
||||||
} // end of for loop to collect timestamp samples
|
} // end of for loop to collect timestamp samples
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) {
|
|||||||
// the 5th byte contains the fractional seconds in 2^-8 second steps
|
// the 5th byte contains the fractional seconds in 2^-8 second steps
|
||||||
// (= 1/250th sec), we convert this to ms
|
// (= 1/250th sec), we convert this to ms
|
||||||
uint16_t timestamp_msec = 4 * buf[4];
|
uint16_t timestamp_msec = 4 * buf[4];
|
||||||
// pointers to 4 bytes 4 bytes containing UTC seconds since unix epoch, msb
|
// pointers to 4 bytes containing UTC seconds since unix epoch, msb
|
||||||
uint32_t timestamp_sec, *timestamp_ptr;
|
uint32_t timestamp_sec, *timestamp_ptr;
|
||||||
|
|
||||||
// convert buffer to uint32_t, octet order is big endian
|
// convert buffer to uint32_t, octet order is big endian
|
||||||
@ -210,92 +210,6 @@ int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// adjust system time, calibrate RTC and RTC_INT pps
|
|
||||||
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
|
|
||||||
timesource_t mytimesource) {
|
|
||||||
|
|
||||||
t_sec ++;
|
|
||||||
time_t time_to_set = (time_t)(t_sec);
|
|
||||||
|
|
||||||
// increment t_sec only if t_msec > 1000
|
|
||||||
time_to_set = (time_t)(t_sec + t_msec / 1000);
|
|
||||||
|
|
||||||
// do we have a valid time?
|
|
||||||
if (timeIsValid(time_to_set)) {
|
|
||||||
|
|
||||||
// if we have msec fraction, then wait until top of second with
|
|
||||||
// millisecond precision
|
|
||||||
if (t_msec % 1000) {
|
|
||||||
time_to_set++;
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000));
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "[%0.3f] UTC epoch time: %d.%03d sec", millis() / 1000.0,
|
|
||||||
time_to_set, t_msec % 1000);
|
|
||||||
|
|
||||||
// if we have got an external timesource, set RTC time and shift RTC_INT pulse
|
|
||||||
// to top of second
|
|
||||||
#ifdef HAS_RTC
|
|
||||||
if ((mytimesource == _gps) || (mytimesource == _lora))
|
|
||||||
set_rtctime(time_to_set);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// if we have a software pps timer, shift it to top of second
|
|
||||||
#if (!defined GPS_INT && !defined RTC_INT)
|
|
||||||
timerWrite(ppsIRQ, 0); // reset pps timer
|
|
||||||
CLOCKIRQ(); // fire clock pps, this advances time 1 sec
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct timeval tv;
|
|
||||||
struct timezone tz;
|
|
||||||
if(gettimeofday(&tv, &tz) != 0) {
|
|
||||||
ESP_LOGE(TAG, "ERROR gettimeofday");
|
|
||||||
}
|
|
||||||
struct timeval beforeTime = tv;
|
|
||||||
|
|
||||||
struct timeval nowTime;
|
|
||||||
nowTime.tv_sec = t_sec;
|
|
||||||
nowTime.tv_usec = t_msec;
|
|
||||||
if(settimeofday(&nowTime, &tz) != 0) {
|
|
||||||
ESP_LOGE(TAG, "ERROR settimeofday");
|
|
||||||
}
|
|
||||||
|
|
||||||
struct timeval diff;
|
|
||||||
diff.tv_sec = nowTime.tv_sec-beforeTime.tv_sec;
|
|
||||||
diff.tv_usec = nowTime.tv_usec-beforeTime.tv_usec;
|
|
||||||
|
|
||||||
// sum up diff_s and diff_ms to one ms value
|
|
||||||
int32_t diff_s = diff.tv_sec;
|
|
||||||
int32_t diff_ms = diff.tv_usec/1000;
|
|
||||||
int32_t diff_ms_remain = diff_ms / 1000;
|
|
||||||
diff_s += diff_ms_remain;
|
|
||||||
diff_ms += -1000*diff_ms_remain;
|
|
||||||
if(diff_ms < 0) {
|
|
||||||
diff_s --;
|
|
||||||
diff_ms += 1000;
|
|
||||||
}
|
|
||||||
// cap diff at 24h (= 86,400s)
|
|
||||||
diff_s = diff_s % 86400;
|
|
||||||
int32_t timediff_ms = diff_s * 1000 + diff_ms;
|
|
||||||
|
|
||||||
// send diffTime
|
|
||||||
payload.reset();
|
|
||||||
payload.addTimeDiff(timediff_ms);
|
|
||||||
SendPayload(TIMEDIFFPORT, prio_high);
|
|
||||||
ESP_LOGI(TAG, "timediff_ms: %d", timediff_ms);
|
|
||||||
|
|
||||||
timeSource = mytimesource; // set global variable
|
|
||||||
timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync);
|
|
||||||
time_uart_send_start();
|
|
||||||
ESP_LOGI(TAG, "[%0.3f] Timesync finished, time was set | source: %c",
|
|
||||||
millis() / 1000.0, timeSetSymbols[timeSource]);
|
|
||||||
} else {
|
|
||||||
timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, timeSync);
|
|
||||||
ESP_LOGI(TAG, "[%0.3f] Timesync failed, invalid time fetched | source: %c",
|
|
||||||
millis() / 1000.0, timeSetSymbols[timeSource]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create task for timeserver handshake processing, called from main.cpp
|
// create task for timeserver handshake processing, called from main.cpp
|
||||||
void timesync_init() {
|
void timesync_init() {
|
||||||
xTaskCreatePinnedToCore(process_timesync_req, // task function
|
xTaskCreatePinnedToCore(process_timesync_req, // task function
|
||||||
|
@ -57,7 +57,7 @@ void wifi_sniffer_init(void) {
|
|||||||
wificfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM
|
wificfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM
|
||||||
wificfg.wifi_task_core_id = 0; // we want wifi task running on core 0
|
wificfg.wifi_task_core_id = 0; // we want wifi task running on core 0
|
||||||
|
|
||||||
//wifi_promiscuous_filter_t filter = {
|
// wifi_promiscuous_filter_t filter = {
|
||||||
// .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // only MGMT frames
|
// .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // only MGMT frames
|
||||||
// .filter_mask = WIFI_PROMIS_FILTER_MASK_ALL}; // we use all frames
|
// .filter_mask = WIFI_PROMIS_FILTER_MASK_ALL}; // we use all frames
|
||||||
|
|
||||||
@ -71,9 +71,9 @@ void wifi_sniffer_init(void) {
|
|||||||
ESP_ERROR_CHECK(
|
ESP_ERROR_CHECK(
|
||||||
esp_wifi_set_storage(WIFI_STORAGE_RAM)); // we don't need NVRAM
|
esp_wifi_set_storage(WIFI_STORAGE_RAM)); // we don't need NVRAM
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));
|
||||||
ESP_ERROR_CHECK(esp_wifi_stop());
|
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); // no modem power saving
|
||||||
ESP_ERROR_CHECK(
|
ESP_ERROR_CHECK(esp_wifi_start()); // must be started to be able to switch ch
|
||||||
esp_wifi_set_promiscuous_filter(&filter)); // set frame filter
|
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&filter)); // set frame filter
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler));
|
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler));
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); // now switch on monitor mode
|
ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); // now switch on monitor mode
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user