Merge pull request #412 from cyberman54/development

v1.7.93
This commit is contained in:
Verkehrsrot 2019-08-18 17:53:04 +02:00 committed by GitHub
commit 737df716b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 667 additions and 549 deletions

View File

@ -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>
@ -405,11 +406,11 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts.
bytes 1..4 = time/date in UTC epoch seconds (LSB) bytes 1..4 = time/date in UTC epoch seconds (LSB)
byte 5 = time source & status, see below byte 5 = time source & status, see below
bits 0..3 time source bits 0..3 last seen time source
0x00 = GPS 0x00 = GPS
0x01 = RTC 0x01 = RTC
0x02 = LORA 0x02 = LORA
0x03 = unsynched 0x03 = unsynched (never synched)
bits 4..7 time status bits 4..7 time status
0x00 = timeNotSet (never synched) 0x00 = timeNotSet (never synched)

View File

@ -11,14 +11,16 @@
#include <Adafruit_BME280.h> #include <Adafruit_BME280.h>
#endif #endif
extern Ticker bmecycler;
extern bmeStatus_t extern bmeStatus_t
bme_status; // Make struct for storing gps data globally available bme_status; // Make struct for storing gps data globally available
extern TaskHandle_t BmeTask;
// --- Bosch BSEC v1.4.7.4 library configuration --- // --- Bosch BSEC v1.4.7.4 library configuration ---
// 3,3V supply voltage; 3s max time between sensor_control calls; 4 days // 3,3V supply voltage; 3s max time between sensor_control calls; 4 days
// calibration. Change this const if not applicable for your application (see // calibration. Change this const if not applicable for your application (see
// BME680 datasheet) // BME680 datasheet)
// Note: 3s max time not exceed BMECYCLE frequency set in paxcounter.conf!
const uint8_t bsec_config_iaq[454] = { const uint8_t bsec_config_iaq[454] = {
4, 7, 4, 1, 61, 0, 0, 0, 0, 0, 0, 0, 174, 1, 0, 4, 7, 4, 1, 61, 0, 0, 0, 0, 0, 0, 0, 174, 1, 0,
0, 48, 0, 1, 0, 0, 192, 168, 71, 64, 49, 119, 76, 0, 0, 0, 48, 0, 1, 0, 0, 192, 168, 71, 64, 49, 119, 76, 0, 0,
@ -54,7 +56,8 @@ const uint8_t bsec_config_iaq[454] = {
// Helper functions declarations // Helper functions declarations
int bme_init(); int bme_init();
void bme_loop(void *pvParameters); void bmecycle(void);
void bme_storedata(bmeStatus_t *bme_store);
int checkIaqSensorStatus(void); int checkIaqSensorStatus(void);
void loadState(void); void loadState(void);
void updateState(void); void updateState(void);

View File

@ -82,8 +82,6 @@ typedef struct {
uint8_t satellites; uint8_t satellites;
uint16_t hdop; uint16_t hdop;
int16_t altitude; int16_t altitude;
uint32_t time_age;
tmElements_t timedate;
} gpsStatus_t; } gpsStatus_t;
typedef struct { typedef struct {
@ -99,7 +97,6 @@ typedef struct {
enum sendprio_t { prio_low, prio_normal, prio_high }; enum sendprio_t { prio_low, prio_normal, prio_high };
enum timesource_t { _gps, _rtc, _lora, _unsynced }; enum timesource_t { _gps, _rtc, _lora, _unsynced };
enum mutexselect_t { no_mutex, do_mutex };
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;
@ -112,13 +109,12 @@ extern uint16_t volatile macs_total, macs_wifi, macs_ble,
batt_voltage; // display values batt_voltage; // display values
extern bool volatile TimePulseTick; // 1sec pps flag set by GPS or RTC extern bool volatile TimePulseTick; // 1sec pps flag set by GPS or RTC
extern timesource_t timeSource; extern timesource_t timeSource;
extern hw_timer_t *displayIRQ, *matrixDisplayIRQ, *ppsIRQ, *gpsIRQ; extern hw_timer_t *displayIRQ, *matrixDisplayIRQ, *ppsIRQ;
extern SemaphoreHandle_t I2Caccess; extern SemaphoreHandle_t I2Caccess;
extern TaskHandle_t irqHandlerTask, ClockTask; extern TaskHandle_t irqHandlerTask, ClockTask;
extern TimerHandle_t WifiChanTimer; extern TimerHandle_t WifiChanTimer;
extern Timezone myTZ; extern Timezone myTZ;
extern time_t userUTCTime; extern time_t userUTCTime;
extern time_t volatile gps_pps_time;
// application includes // application includes
#include "led.h" #include "led.h"

View File

@ -10,18 +10,16 @@
#endif #endif
#define NMEA_FRAME_SIZE 82 // NEMA has a maxium of 82 bytes per record #define NMEA_FRAME_SIZE 82 // NEMA has a maxium of 82 bytes per record
#define NMEA_BUFFERTIME 50 // 50ms safety time regardless #define NMEA_COMPENSATION_FACTOR 480 // empiric for Ublox Neo 6M
extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe
extern gpsStatus_t
gps_status; // Make struct for storing gps data globally available
extern TaskHandle_t GpsTask; extern TaskHandle_t GpsTask;
int gps_init(void); int gps_init(void);
void IRAM_ATTR gps_storetime(gpsStatus_t *gps_store); int gps_config();
void gps_storelocation(gpsStatus_t *gps_store); void gps_storelocation(gpsStatus_t *gps_store);
void gps_loop(void *pvParameters); void gps_loop(void *pvParameters);
time_t fetch_gpsTime(gpsStatus_t value); time_t fetch_gpsTime(uint16_t *msec);
int gps_config(); time_t fetch_gpsTime(void);
#endif #endif

View File

@ -8,13 +8,14 @@
#define TIMESYNC_IRQ 0x010 #define TIMESYNC_IRQ 0x010
#define MASK_IRQ 0x020 #define MASK_IRQ 0x020
#define UNMASK_IRQ 0x040 #define UNMASK_IRQ 0x040
#define GPS_IRQ 0x080 #define BME_IRQ 0x080
#define MATRIX_DISPLAY_IRQ 0x100 #define MATRIX_DISPLAY_IRQ 0x100
#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"
void irqHandler(void *pvParameters); void irqHandler(void *pvParameters);
void mask_user_IRQ(); void mask_user_IRQ();
@ -32,8 +33,5 @@ void IRAM_ATTR MatrixDisplayIRQ();
void IRAM_ATTR ButtonIRQ(); void IRAM_ATTR ButtonIRQ();
#endif #endif
#if (HAS_GPS)
void IRAM_ATTR GpsIRQ();
#endif
#endif #endif

View File

@ -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 ShiftLeft(uint8_t *arr, uint32_t len);
#endif #endif

View File

@ -21,8 +21,10 @@
#endif #endif
extern QueueHandle_t LoraSendQueue; extern QueueHandle_t LoraSendQueue;
extern TaskHandle_t lmicTask;
esp_err_t lora_stack_init(); esp_err_t lora_stack_init();
void lmictask(void *pvParameters);
void onEvent(ev_t ev); void onEvent(ev_t ev);
void gen_lora_deveui(uint8_t *pdeveui); void gen_lora_deveui(uint8_t *pdeveui);
void RevBytes(unsigned char *b, size_t c); void RevBytes(unsigned char *b, size_t c);
@ -35,7 +37,6 @@ void switch_lora(uint8_t sf, uint8_t tx);
void lora_send(osjob_t *job); void lora_send(osjob_t *job);
void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio); void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio);
void lora_queuereset(void); void lora_queuereset(void);
void lora_housekeeping(void);
#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);

View File

@ -9,7 +9,7 @@
extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available
uint8_t rtc_init(void); uint8_t rtc_init(void);
uint8_t set_rtctime(time_t t, mutexselect_t mutex); uint8_t set_rtctime(time_t t);
void sync_rtctime(void); void sync_rtctime(void);
time_t get_rtctime(void); time_t get_rtctime(void);
float get_rtctemp(void); float get_rtctemp(void);

View File

@ -31,6 +31,4 @@ esp_err_t spi_init();
void spi_enqueuedata(MessageBuffer_t *message, sendprio_t prio); void spi_enqueuedata(MessageBuffer_t *message, sendprio_t prio);
void spi_queuereset(); void spi_queuereset();
void spi_housekeeping();
#endif // _SPISLAVE_H #endif // _SPISLAVE_H

View File

@ -26,7 +26,8 @@ void timepulse_start(void);
void timeSync(void); 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);
time_t timeProvider(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);

View File

@ -15,6 +15,5 @@ void send_timesync_req(void);
int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len); int recv_timesync_ans(uint8_t seq_no, 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);
#endif #endif

View File

@ -1,233 +1,243 @@
std::array<uint32_t, 1617> vendors = { std::array<uint32_t, 1683> vendors = {
0x38f23e, 0x807abf, 0x90e7c4, 0x7c6193, 0x485073, 0x74e28c, 0x8463d6, 0xa44519, 0x2446c8, 0xd0d003, 0x8cb84a, 0x608b0e, 0x88299c, 0x7c8956,
0xd48f33, 0x2c8a72, 0x980d2e, 0xa826d9, 0xd4206d, 0x00155d, 0x806c1b, 0x201742, 0xd09c7a, 0xc4e39f, 0x2479f3, 0x8c79f5, 0xccd281, 0x88b291,
0xa470d6, 0x985fd3, 0x1c69a5, 0x382de8, 0xd087e2, 0x205531, 0x5440ad, 0xc42ad0, 0xd81edd, 0xacf6f7, 0xc02e25, 0x64c2de, 0x58c6f0, 0x70bc10,
0x842e27, 0x50f0d3, 0x84119e, 0x08eca9, 0x10d38a, 0x382dd1, 0xe0cbee, 0x2ca9f0, 0x586b14, 0xbcb863, 0x1040f3, 0x44e66e, 0xc0e862, 0xf40616,
0x64b853, 0xf4428f, 0x188331, 0x8455a5, 0xa87c01, 0xc01173, 0xbce63f, 0x601d91, 0xd4c94b, 0x70bbe9, 0xf83880, 0x4c569d, 0x38539c, 0x402619,
0xb857d8, 0x94b10a, 0xe458b8, 0x088c2c, 0xb86ce8, 0x9c65b0, 0xc8a823, 0x6ce85c, 0xe4b2fb, 0xd003df, 0xb4cb57, 0xfc039f, 0x18d717, 0x0057c1,
0xc44202, 0xd059e4, 0x64b310, 0x9476b7, 0x8c1abf, 0xb47443, 0x30cbf8, 0xa4d990, 0x20a60c, 0x08c5e1, 0x3c576c, 0xe4b021, 0x28ff3c, 0xf099b6,
0x182195, 0xa88195, 0x88add2, 0xd0fccc, 0x14c913, 0x4c6641, 0x3cbdd8, 0x1094bb, 0x88e9fe, 0x94bf2d, 0xf86fc1, 0x38892c, 0x749eaf, 0x8035c1,
0x38256b, 0x849866, 0xe89309, 0x0016db, 0x5c3c27, 0x10d542, 0xa0821f, 0x2047da, 0xe4c483, 0xc048e6, 0x785dc8, 0x40cbc0, 0xc4618b, 0x08e689,
0xc45006, 0x88329b, 0xbc8ccd, 0x400e85, 0xec9bf3, 0xf8042e, 0x843838, 0xdc56e7, 0x38e60a, 0xdcbfe9, 0xcc6ea4, 0xa46cf1, 0x08aed6, 0xa816d0,
0x54880e, 0xbc79ad, 0x30d6c9, 0xb0df3a, 0x805719, 0x78a873, 0x041bba, 0x3408bc, 0x1c36bb, 0x3c2eff, 0xf06e0b, 0x24f677, 0xb0ca68, 0xc83c85,
0x08fd0e, 0x08d42b, 0x00e3b2, 0xc81479, 0xf0728c, 0x94350a, 0x001fcd, 0x5433cb, 0x149f3c, 0x9ce063, 0xd03169, 0x7c6456, 0x084acf, 0xb8634d,
0xd0dfc7, 0x1c62b8, 0x18e2c2, 0x001a8a, 0x002567, 0xa8f274, 0x001599, 0xa4e975, 0x3035ad, 0x844167, 0x9800c6, 0xac1f74, 0xa85c2c, 0x00db70,
0x0012fb, 0x7cf854, 0x8cc8cd, 0xe81132, 0xa02195, 0x8c71f8, 0x04180f, 0x4c49e3, 0x389af6, 0xe0aa96, 0x507705, 0xa89675, 0x00ec0a, 0xc8d7b0,
0x9463d1, 0x0cdfa4, 0xcc051b, 0x68ebae, 0x60d0a9, 0x60a10a, 0xa07591, 0x9097f3, 0x7c1c68, 0xc087eb, 0x388c50, 0xd4503f, 0x245ba7, 0x70f087,
0x001fcc, 0xec107b, 0xa01081, 0xf4f524, 0xbc8385, 0x900628, 0xd4ae05, 0xf0d7aa, 0x3096fb, 0x4827ea, 0x7c787e, 0x28395e, 0x38295a, 0x8cf5a3,
0x3c0518, 0xe8bba8, 0xbc3aea, 0x8c0ee3, 0x6c5c14, 0x78abbb, 0x1816c9, 0xd47ae2, 0xd8e0e1, 0xe44790, 0xb0702d, 0x6c19c0, 0xf01dbc, 0xf83f51,
0xfc8f90, 0x244b03, 0x988389, 0x14bb6e, 0x1c3ade, 0xf83f51, 0xd8e0e1, 0x404e36, 0xdc0b34, 0x60a4d0, 0x008701, 0x5c9960, 0x2c3361, 0x101dc0,
0xecf342, 0x5092b9, 0xb4bff6, 0xc8d7b0, 0x982d68, 0xd80831, 0xdc5583, 0x78471d, 0xa07591, 0x0cdfa4, 0x68ebae, 0x444e1a, 0x4844f7, 0x001377,
0x2c54cf, 0x001fe3, 0x0026e2, 0x001e75, 0x6cd68a, 0x2021a5, 0x0c4885, 0x002454, 0xe81132, 0x50b7c3, 0x1c5a3e, 0xa02195, 0xf4d9fb, 0x3c6200,
0xdc0b34, 0xac0d1b, 0x60e3ac, 0xf895c7, 0xc4438f, 0xa816b2, 0xe892a4, 0x00e3b2, 0x301966, 0x94350a, 0x0017d5, 0x001e7d, 0x001df6, 0xc06599,
0x700514, 0x88c9d0, 0x2c598a, 0xec8350, 0x4cdd31, 0x705aac, 0xfc643a, 0xbc79ad, 0xf0e77e, 0xf008f1, 0xe47cf9, 0x58c38b, 0x001d25, 0x08fd0e,
0xd4e6b7, 0x2802d8, 0x48605f, 0xf0766f, 0x40cbc0, 0x4098ad, 0x6c4d73, 0x041bba, 0x889b39, 0xe432cb, 0x10d542, 0xa0821f, 0xf06bca, 0xac3613,
0xc48466, 0xb8634d, 0x503237, 0xd4619d, 0xb0481a, 0x989e63, 0xdca904, 0xbc8ccd, 0xd022be, 0xec9bf3, 0xf409d8, 0x4c3c16, 0x0073e0, 0x343111,
0x48a195, 0x6cab31, 0x7c5049, 0xe42b34, 0x1c36bb, 0x3c2eff, 0x6c96cf, 0x4849c7, 0x849866, 0x9476b7, 0xc4438f, 0xa09169, 0x9893cc, 0x3ccd93,
0x3035ad, 0xa8be27, 0x70a2b3, 0x4c57ca, 0x68fb7e, 0x90c1c6, 0xa4f1e8, 0x2021a5, 0x6cd68a, 0x002483, 0x001fe3, 0x2c54cf, 0x485929, 0x58a2b5,
0xac61ea, 0x38b54d, 0x00cdfe, 0x18af61, 0xcc4463, 0x34159e, 0x58b035, 0x10f96f, 0x4c6641, 0xac3743, 0x28e31f, 0xecadb8, 0x9801a7, 0xb8bbaf,
0xf0b479, 0x109add, 0x40a6d9, 0x7cf05f, 0xa4b197, 0x0c74c2, 0x403004, 0x60c5ad, 0x24f094, 0x086d41, 0xe4faed, 0x288335, 0xdccf96, 0xccb11a,
0x4860bc, 0xd02b20, 0x9ce33f, 0xf0989d, 0xace4b5, 0x6c72e7, 0x60fec5, 0xac61ea, 0x38b54d, 0x1c5cf2, 0xe0c767, 0x80ed2c, 0xa80600, 0xf05a09,
0x00a040, 0x000d93, 0xacbc32, 0x30d9d9, 0x6030d4, 0x94bf2d, 0xc49880, 0x503275, 0x08fc88, 0xf8d0bd, 0x94b10a, 0x3cbbfd, 0xa48431, 0xa0b4a5,
0xe0338e, 0x68fef7, 0xbce143, 0x645aed, 0xc0b658, 0x881908, 0xfc2a9c, 0xe4f8ef, 0x78595e, 0x0c1420, 0x24f5aa, 0x988389, 0x84a466, 0xc4576e,
0x44d884, 0xec852f, 0x286aba, 0x705681, 0x7cd1c3, 0xf0dce2, 0xb065bd, 0x508569, 0x489d24, 0xb07994, 0xa470d6, 0xc8f230, 0x8c0ee3, 0x28ed6a,
0xa82066, 0xbc6778, 0x68967b, 0x848506, 0x54ae27, 0x6476ba, 0x84b153, 0x38f23e, 0x902155, 0xd8b377, 0xa09347, 0xe8bba8, 0xb0aa36, 0xa4d18c,
0x783a84, 0x2cbe08, 0x24e314, 0x68d93c, 0x2cf0ee, 0x84788b, 0x6c94f8, 0x241eeb, 0xcc25ef, 0x28cfe9, 0x00a040, 0x003065, 0x3cd0f8, 0x680927,
0x703eac, 0xb4f0ab, 0x10ddb1, 0x04f7e4, 0x34c059, 0xf0d1a9, 0xbc3baf, 0x1093e9, 0x442a60, 0xc82a14, 0x3c0754, 0xa4b197, 0xa4d1d2, 0x28cfda,
0x786c1c, 0x041552, 0x38484c, 0x701124, 0xc86f1d, 0x685b35, 0x380f4a, 0x6cc26b, 0x44d884, 0x64200c, 0x001451, 0x001e52, 0x0021e9, 0x002608,
0x3010e4, 0x04db56, 0x881fa1, 0x04e536, 0xf82793, 0xacfdec, 0xd0e140, 0x0026b0, 0x0026bb, 0xd49a20, 0xf81edf, 0xcc08e0, 0xf0b479, 0x045453,
0x8c7c92, 0x7831c1, 0xf437b7, 0x50ead6, 0x28e02c, 0x60c547, 0x7c11be, 0xe88d28, 0x949426, 0x207d74, 0xf4f15a, 0xc86f1d, 0x18af8f, 0xc8b5b7,
0x003ee1, 0xc01ada, 0x34363b, 0xc81ee7, 0x9cfc01, 0xccc760, 0x087402, 0x90b21f, 0xb8e856, 0xd89695, 0x64a3cb, 0x30f7c5, 0x40b395, 0x44fb42,
0x285aeb, 0x28f076, 0x70700d, 0x9cf48e, 0xfcd848, 0x001cb3, 0x64b9e8, 0x98fe94, 0xd8004d, 0x542696, 0xc8334b, 0x64e682, 0x9c207b, 0xb065bd,
0xf8e94e, 0xf40616, 0xbcb863, 0x885a06, 0xc08c71, 0x108ee0, 0x68e7c2, 0x8c2daa, 0x848506, 0xf4f951, 0xc06394, 0x3090ab, 0x1499e2, 0xfce998,
0x3c576c, 0x0ce0dc, 0x702ad5, 0x889f6f, 0x1c427d, 0x5029f5, 0x4c569d, 0x0cbc9f, 0x34363b, 0xd0a637, 0x789f70, 0xe0accb, 0xa0999b, 0x24240e,
0x14c213, 0x38539c, 0x58e6ba, 0xb831b5, 0x90633b, 0x782327, 0xf8a45f, 0x903c92, 0xd81d72, 0xd8bb2c, 0xd04f7e, 0x9cf387, 0xa85b78, 0xb418d1,
0x8cbebe, 0x640980, 0x98fae3, 0x185936, 0x9c99a0, 0xc40bcb, 0xecd09f, 0xf0dbf8, 0x48746e, 0x341298, 0x70e72c, 0x70ece4, 0x54ae27, 0xc8f650,
0xf4f5db, 0xe446da, 0x18f0e4, 0x9c2ea1, 0x50a009, 0x20a60c, 0x20326c, 0x68ae20, 0xac87a3, 0xa88e24, 0x2078f0, 0x70480f, 0xcc20e8, 0xf0b0e7,
0xa8346a, 0xb06fe0, 0x98460a, 0xac88fd, 0x149d99, 0x846fce, 0xe0dcff, 0x0469f8, 0x205531, 0xe8b4c8, 0xd087e2, 0xf05b7b, 0xb047bf, 0x7c0bc6,
0x60ab67, 0x188796, 0x002376, 0x84100d, 0x04c23e, 0x5c5188, 0xe89120, 0x80c5e6, 0x5440ad, 0x804e81, 0xfc8f90, 0xe89120, 0x4480eb, 0x9cd35b,
0x9c6c15, 0x4886e8, 0x2c2997, 0x102f6b, 0x00eebd, 0x281878, 0x6045bd, 0xa89fba, 0x4886e8, 0x30d587, 0xec59e7, 0x2c2997, 0xd059e4, 0x14a364,
0x7ced8d, 0xe85b5b, 0x000d3a, 0xe09861, 0xf4f1e1, 0x60beb5, 0xb4e1c4, 0x7ced8d, 0x002538, 0x0022a1, 0x7cf31b, 0x0c2fb0, 0xf4d620, 0x8cf112,
0x70aab2, 0x0026ff, 0x406f2a, 0x002557, 0xf05a09, 0x503275, 0x28cc01, 0x44aeab, 0xa4f05e, 0x28167f, 0xf887f1, 0x305714, 0xc8b1cd, 0x1460cb,
0xb46293, 0x04fe31, 0x845181, 0xd831cf, 0xf8d0bd, 0xfcc734, 0xe4b021, 0xb8f12a, 0x04c807, 0x20f478, 0x90735a, 0x00fa21, 0x7c2302, 0xd49dc0,
0xb0ec71, 0x3cbbfd, 0x2cae2b, 0xc488e5, 0x7c9122, 0xe8b4c8, 0x18895b, 0xe0dcff, 0x846fce, 0x804a14, 0x703c69, 0x489dd1, 0xb06fe0, 0xc08c71,
0xe0db10, 0xe09971, 0x6077e2, 0x680571, 0x6c2f2c, 0x300d43, 0x6c2779, 0x60d0a9, 0x48fda3, 0xa45046, 0x007c2d, 0xb831b5, 0x14c213, 0xa4d931,
0x607edd, 0x9c2a83, 0xe45d75, 0xe4faed, 0xc83f26, 0x54f201, 0xa06090, 0xbcfed9, 0x808223, 0x5029f5, 0x007204, 0xbca58b, 0x80ceb9, 0xe06267,
0xac3743, 0x141f78, 0x006f64, 0xdc6672, 0x001e7d, 0x3c6200, 0x0024e9, 0x082525, 0xf460e2, 0x1cc3eb, 0x00c3f4, 0x7836cc, 0x48605f, 0x2816a8,
0x002399, 0xe4e0c5, 0xe8039a, 0xc4731e, 0x8c7712, 0x2013e0, 0x0007ab, 0x9c2ea1, 0xbce143, 0x647033, 0x846878, 0xc8d083, 0x6030d4, 0xf895ea,
0x0021d2, 0xbc4760, 0xd0176a, 0x2cbaba, 0x24920e, 0x40d3ae, 0xf01dbc, 0x18f1d8, 0x30d9d9, 0x0cf346, 0x80ad16, 0xfc643a, 0xa8515b, 0xb4f7a1,
0x24dbed, 0xac3613, 0x1449e0, 0xc0bdd1, 0xe8508b, 0xf025b7, 0xc8ba94, 0x88bd45, 0x54fcf0, 0x306a85, 0x4cdd31, 0xd0817a, 0x98ca33, 0x68ab1e,
0xec1f72, 0x9852b1, 0x1489fd, 0xccfe3c, 0x789ed0, 0xe440e2, 0x1caf05, 0x70ef00, 0xbcffeb, 0x0c1539, 0x88b4a6, 0x2c8a72, 0xd88f76, 0x409c28,
0xe492fb, 0x0073e0, 0xbc4486, 0x380b40, 0x002490, 0x0023d7, 0xfca13e, 0xd0b128, 0xbc5451, 0xec51bc, 0xf079e8, 0x58e28f, 0x787b8a, 0x88365f,
0xa00798, 0x945103, 0xc819f7, 0x2c4401, 0xec51bc, 0xf079e8, 0x887598, 0x2c4053, 0x3cf591, 0x602101, 0xacafb9, 0x041b6d, 0xecf342, 0x503da1,
0xd0b128, 0xd00401, 0xf06d78, 0x10683f, 0x74a722, 0x58a2b5, 0x64899a, 0x4c1a3d, 0x503237, 0xb0481a, 0xb49cdf, 0x48bf6b, 0x3c0518, 0x900628,
0x88074b, 0x64bc0c, 0xa039f7, 0x041b6d, 0x001f6b, 0x30b4b8, 0x503cea, 0x9c84bf, 0x00b362, 0xe4e4ab, 0x60334b, 0xf4f524, 0xcc2d83, 0xfcd848,
0x54fcf0, 0x08aed6, 0xa816d0, 0x88bd45, 0x641cb0, 0x3cdcbc, 0xf47190, 0xec107b, 0x1c232c, 0x0021d1, 0x001fcc, 0xec8892, 0x60a10a, 0x8c71f8,
0x587a6a, 0xe4c483, 0xfcaab6, 0xc0bdc8, 0xa887b3, 0x742344, 0xd832e3, 0xcc051b, 0x8c7712, 0x9463d1, 0x0021d2, 0x5c497d, 0x7825ad, 0xece09b,
0xe06267, 0x482ca0, 0x1801f1, 0x70bbe9, 0xf0b429, 0x0c9838, 0x0c1daf, 0xbc20a4, 0x08d42b, 0x789ed0, 0xb0c4e7, 0xa00798, 0x001fcd, 0x38ece4,
0x28e31f, 0x14f65a, 0xd4c94b, 0x703a51, 0xdc080f, 0xf82d7c, 0x9c648b, 0x945103, 0x002490, 0x0023d7, 0x549b12, 0xfca13e, 0x24c696, 0x94d771,
0x14d00d, 0xd467d3, 0x58c6f0, 0xc02e25, 0x804a14, 0xb85d0a, 0x941625, 0xe84e84, 0x001632, 0xe4e0c5, 0xc81479, 0x1caf05, 0x0016db, 0x001ee2,
0x34a8eb, 0x4883b4, 0xa8db03, 0x2479f3, 0x98b8ba, 0xd49dc0, 0x8cf5a3, 0x20d5bf, 0x5ce8eb, 0xc0bdd1, 0xb479a7, 0xb0df3a, 0x805719, 0x34be00,
0x14568e, 0x8058f8, 0xf0d7aa, 0xc49ded, 0xb0aa36, 0x2c5bb8, 0x1c48ce, 0x78521a, 0x38256b, 0x205ef7, 0x141f78, 0xb47443, 0x30766f, 0xa8922c,
0x24f5aa, 0xf877b8, 0x682737, 0x5056bf, 0x9097f3, 0x58c5cb, 0xacafb9, 0xf80cf3, 0xc49a02, 0x001f6b, 0x0026e2, 0xa860b6, 0xc4b301, 0xe05f45,
0x30074d, 0x5c5181, 0x389af6, 0xe0aa96, 0x507705, 0x2c4053, 0x084acf, 0x483b38, 0x1c9148, 0x5c70a3, 0x64cc2e, 0xf877b8, 0x182195, 0x44783e,
0x1cddea, 0x08152f, 0xb8c111, 0x3408bc, 0x844167, 0xb4f61c, 0x68ab1e, 0x30636b, 0xa4f1e8, 0x14bb6e, 0xe498d1, 0x6c2779, 0x28cc01, 0x6cf373,
0x2c61f6, 0xe49adc, 0xd0817a, 0xc4618b, 0x3451c9, 0xe0b9ba, 0xd023db, 0x9c3aaf, 0x781fdb, 0x4ca56d, 0xb86ce8, 0x0cb319, 0x183f47, 0xb46293,
0xb88d12, 0xb817c2, 0x68a86d, 0x78a3e4, 0x680927, 0x60facd, 0x1caba7, 0x50a4c8, 0x1867b0, 0x6c8336, 0x002557, 0x001ccc, 0xb4e1c4, 0xe0757d,
0x784f43, 0x404d7f, 0x7c04d0, 0xbc9fef, 0x8866a5, 0x88e87f, 0xb853ac, 0x34bb26, 0x806c1b, 0x2082c0, 0xdc6dcd, 0x440010, 0x0056cd, 0x00cdfe,
0x2c3361, 0xa860b6, 0x24f094, 0x90b0ed, 0xc4b301, 0xe05f45, 0x483b38, 0xe498d6, 0xf8db7f, 0x64a769, 0xe899c4, 0xbccfcc, 0xf431c3, 0x64a5c3,
0xe0c767, 0x1c9e46, 0x0cd746, 0x440010, 0xe498d6, 0x606944, 0x0452f3, 0xbc3aea, 0x7c1dd9, 0xa086c6, 0x9c99a0, 0x584498, 0x002332, 0x00236c,
0x241eeb, 0xf431c3, 0x64a5c3, 0xbc926b, 0x0050e4, 0x003065, 0x000a27, 0x0023df, 0x002500, 0x0025bc, 0x0019e3, 0x001b63, 0x001ec2, 0x001ff3,
0x001451, 0x8c7b9d, 0x88c663, 0xc82a14, 0x9803d8, 0x8c5877, 0x0019e3, 0x0010fa, 0x0050e4, 0x000d93, 0x7cfadf, 0x78a3e4, 0x148fc6, 0x286ab8,
0x002312, 0x002332, 0x002436, 0x00254b, 0x0026bb, 0x70f087, 0x886b6e, 0x28e02c, 0xe0b9ba, 0x00c610, 0xb8f6b1, 0x8cfaba, 0x7cd1c3, 0xf0dce2,
0x4c74bf, 0xe80688, 0xcc08e0, 0x5855ca, 0x5c0947, 0x38892c, 0x40831d, 0x24ab81, 0xe0f847, 0x28e7cf, 0xe4ce8f, 0xa82066, 0xbc52b7, 0x5c5948,
0x50bc96, 0x985aeb, 0x2078f0, 0x78d75f, 0xe0accb, 0x98e0d9, 0xc0cecd, 0xc8bcc8, 0xe8040b, 0x145a05, 0x1caba7, 0xc0847a, 0x34159e, 0x58b035,
0x70e72c, 0xd03311, 0x5cadcf, 0x006d52, 0x48437c, 0x34a395, 0x9cf387, 0xdc86d8, 0x90b931, 0xd0e140, 0x24a2e1, 0x80ea96, 0x600308, 0x04f13e,
0xa85b78, 0x908d6c, 0x0c1539, 0xbc4cc4, 0x0cbc9f, 0xa45e60, 0x544e90, 0x98f0ab, 0x7831c1, 0x783a84, 0x5c8d4e, 0x8863df, 0x881fa1, 0xc8e0eb,
0x9ce65e, 0x90dd5d, 0x08f69c, 0xd461da, 0xc8d083, 0x88e9fe, 0x88ae07, 0x98b8e3, 0x885395, 0x786c1c, 0x4c8d79, 0x1ce62b, 0x0c3021, 0x0c3e9f,
0x18af8f, 0xc8b5b7, 0xa8bbcf, 0x90b21f, 0xb8e856, 0x1499e2, 0xb418d1, 0xfcfc48, 0x9c293f, 0x087402, 0x94f6a3, 0x98e0d9, 0xcc29f5, 0x285aeb,
0x80006e, 0x60d9c7, 0xc8f650, 0x1c1ac0, 0xe06678, 0x5c8d4e, 0xc0f2fb, 0xf02475, 0x2c1f23, 0x549f13, 0xf0dbe2, 0x748114, 0x18f643, 0xa45e60,
0x00f76f, 0xac87a3, 0x542696, 0xd8d1cb, 0x64a3cb, 0x44fb42, 0xf41ba1, 0xa01828, 0xd0034b, 0x10417f, 0xa8667f, 0xd02598, 0x80be05, 0x24a074,
0x3ce072, 0xe88d28, 0xcc785f, 0xac3c0b, 0x88cb87, 0xec3586, 0xf0c1f1, 0x84788b, 0x587f57, 0x006d52, 0xacee9e, 0xb857d8, 0xb844d9, 0x1c56fe,
0xf4f951, 0x8cfaba, 0x5c95ae, 0xe0c97a, 0xbc52b7, 0x14109f, 0x00c3f4, 0xac5a14, 0x08eca9, 0xf8cfc5, 0x7840e4, 0xe09971, 0x10d38a, 0x206274,
0x74eb80, 0xa82bb9, 0x7c6b9c, 0x1cc3eb, 0xbca58b, 0x70fd46, 0xd07fa0, 0xd48f33, 0x20a99b, 0x6077e2, 0xfc1910, 0x3ca10d, 0x646cb2, 0x680571,
0x9caa1b, 0x18d717, 0xb4cb57, 0x74b587, 0xd81c79, 0x8cfe57, 0xc0a600, 0x14b484, 0x3059b7, 0xa43d78, 0x00eebd, 0x502e5c, 0x980d2e, 0xd4206d,
0xa823fe, 0x00092d, 0xf8db7f, 0xe899c4, 0x24da9b, 0x1c56fe, 0xe4907e, 0x7c1e52, 0xdcb4c4, 0x7c6f06, 0x749ef5, 0x68bfc4, 0x04b1a1, 0xcc464e,
0x80c5e6, 0x800184, 0xf8cfc5, 0xc808e9, 0x206274, 0x30d587, 0xc0eefb, 0x507ac5, 0x6cd71f, 0x4c6be8, 0x8c861e, 0x542b8d, 0x8ce5c0, 0xf08a76,
0x502e5c, 0x847a88, 0x0025ae, 0x002538, 0x0022a1, 0x00125a, 0x9cd917, 0xecaa25, 0x9078b2, 0xbc7fa4, 0x687d6b, 0x485169, 0xa8db03, 0x60ab67,
0x9068c3, 0x408805, 0xf8f1b6, 0x001ccc, 0x94ebcd, 0xa4e4b8, 0x389496, 0x4418fd, 0x005b94, 0xe0897e, 0xa8346a, 0x3c20f6, 0x7c38ad, 0x885a06,
0x0cb319, 0x08ee8b, 0xa89fba, 0xfc1910, 0x083d88, 0x5c2e59, 0x646cb2, 0x2c5d34, 0x7c035e, 0xd467d3, 0xa41232, 0x64c753, 0x38f9d3, 0xfc183c,
0xf884f2, 0x14b484, 0x608f5c, 0x4cbca5, 0x78595e, 0xb0d09c, 0x4ca56d, 0xf47def, 0x7c8bb5, 0x3830f9, 0x90e17b, 0xd81c79, 0x58e6ba, 0x1801f1,
0xa48431, 0xe4f8ef, 0x1432d1, 0xe458e7, 0x8cbfa6, 0x7840e4, 0x9000db, 0x9c0cdf, 0x14c697, 0x7c03ab, 0x00b5d0, 0x1496e5, 0xd07fa0, 0x7c6b9c,
0x183a2d, 0x08373d, 0x50f520, 0xa4ebd3, 0x28987b, 0xf40e22, 0x9c3aaf, 0x108ee0, 0xfca621, 0xd832e3, 0x9487e0, 0x4466fc, 0x50a009, 0x50a67f,
0x0821ef, 0xa0cbfd, 0x34145f, 0x6c8fb5, 0xac5f3e, 0x509ea7, 0xdccf96, 0xd461da, 0xb841a4, 0x9ce65e, 0xc49880, 0xe0338e, 0xf01898, 0x881908,
0x6c2483, 0xc09727, 0xd85b2a, 0xacc33a, 0x88797e, 0x00e091, 0x6cd032, 0x5c0947, 0x14205e, 0x08f69c, 0x641cae, 0x003de8, 0x48c796, 0xf4c248,
0xc041f6, 0x0017d5, 0x001247, 0xe4121d, 0x684898, 0xf409d8, 0xb479a7, 0xf47190, 0x2802d8, 0x24181d, 0x641cb0, 0x54b802, 0xd4909c, 0xe4e0a6,
0x002339, 0xd487d8, 0x184617, 0x5001bb, 0x380a94, 0xd857ef, 0x1c66aa, 0x00e091, 0x503cea, 0xb4f1da, 0x80b03d, 0xe49adc, 0xace4b5, 0xf06d78,
0x58c38b, 0x001ee2, 0x001c43, 0x001d25, 0x3c5a37, 0x549b12, 0x3c8bfe, 0xd0d2b0, 0x448f17, 0x1cddea, 0x78886d, 0x20ee28, 0xb4f61c, 0x08f4ab,
0x00265d, 0xd4e8b2, 0x0808c2, 0xb0c4e7, 0xd890e8, 0x34aa8b, 0x24c696, 0x8c8590, 0x6c96cf, 0x508f4c, 0xd463c6, 0xdc44b6, 0x1007b6, 0x342d0d,
0x181eb0, 0x20d390, 0x343111, 0x34be00, 0x78521a, 0x7825ad, 0xf4d9fb, 0x54bd79, 0x30074d, 0x947be7, 0x5092b9, 0xdc74a8, 0x88d50c, 0xc4ae12,
0x0017c9, 0x00166b, 0x00166c, 0xe47cf9, 0x002454, 0x20d5bf, 0x30cda7, 0x989e63, 0x886b6e, 0xd4dccd, 0x484baa, 0xd4ae05, 0xdca904, 0x6cab31,
0xc87e75, 0x00233a, 0x60a4d0, 0x2c0e3d, 0x7c787e, 0xc0d3c0, 0x440444, 0x4c74bf, 0x5082d5, 0xf0ee10, 0xb81daa, 0x5caf06, 0x64b0a6, 0x7c04d0,
0xc09f05, 0xcc2d83, 0x38295a, 0x4c1a3d, 0xa81b5a, 0xdc6dcd, 0x54fa3e, 0x84fcac, 0xdc0c5c, 0x70700d, 0x186590, 0xf86214, 0x784f43, 0x404d7f,
0x0c8910, 0xfcf136, 0x981dfa, 0x84a466, 0x1867b0, 0xccb11a, 0xb8bbaf, 0x1c48ce, 0x6c5c14, 0xcc61e5, 0x609ac1, 0x20dbab, 0x748d08, 0x9c8ba0,
0x60c5ad, 0x28395e, 0xc4ae12, 0xdc74a8, 0xc087eb, 0x74f61c, 0x986f60, 0xcc088d, 0x38a4ed, 0x0007ab, 0xe8e5d6, 0xc87e75, 0x00265f, 0x00233a,
0x4c189a, 0x3cf591, 0x602101, 0xa89675, 0x608e08, 0x7c2edd, 0x3cf7a4, 0x382dd1, 0x10ddb1, 0x00166c, 0x001599, 0x0012fb, 0xd0667b, 0x1c66aa,
0x342d0d, 0x94d029, 0x308454, 0x087808, 0xd03169, 0xbc5451, 0x641cae, 0x1489fd, 0xbc851f, 0x002491, 0x3c8bfe, 0xd4e8b2, 0xe8039a, 0x30cda7,
0xa4e975, 0xc0a53e, 0x9800c6, 0x787b8a, 0x3866f0, 0x20ee28, 0x08f4ab, 0x2c4401, 0xb8d9ce, 0x002339, 0x5001bb, 0x001247, 0x0015b9, 0xb85e7b,
0x8c8590, 0x68ef43, 0xcc2db7, 0xd4a33d, 0xe4e0a6, 0x70ef00, 0xb0ca68, 0x1c77f6, 0x742344, 0xc8ba94, 0x843838, 0x54880e, 0xf025b7, 0xe492fb,
0x9810e8, 0xb49cdf, 0xdca4ca, 0x8c8fe9, 0x98ca33, 0xfc253f, 0x183451, 0x6cb7f4, 0x181eb0, 0x5c3c27, 0xbc72b1, 0x78f7be, 0x684898, 0x3423ba,
0xc0847a, 0x64200c, 0x74e1b6, 0x0c771a, 0x00f4b9, 0xc8334b, 0xb8f6b1, 0x400e85, 0x88797e, 0xd013fd, 0x888322, 0xe89309, 0x6cd032, 0x3cbdd8,
0xc09f42, 0x189efc, 0x6c3e6d, 0x8c2daa, 0xe4e4ab, 0x58404e, 0xdc0c5c, 0x78c3e9, 0x64899a, 0xf8a9d0, 0xccfa00, 0xa816b2, 0x64bc0c, 0x74a722,
0x2c200b, 0x609ac1, 0xf07960, 0x9c8ba0, 0x28a02b, 0xb44bd2, 0x9c4fda, 0xf01c13, 0x344df7, 0x583f54, 0xc01ada, 0x8c1abf, 0x30cbf8, 0xa0cbfd,
0x1c5cf2, 0x3871de, 0xbc5436, 0x5cf938, 0x4c3275, 0x2cf0a2, 0xecadb8, 0xe45d75, 0x408805, 0x98398e, 0xd0fccc, 0xc09727, 0x84a134, 0x0c5101,
0x9801a7, 0xb48b19, 0xe49a79, 0x406c8f, 0x00c610, 0x70dee2, 0x182032, 0x2cf0a2, 0x68fb7e, 0x6c2483, 0x5cca1a, 0xac0d1b, 0x0034da, 0x202d07,
0x6cc26b, 0x1040f3, 0x001d4f, 0x001e52, 0x001f5b, 0x001ff3, 0x0021e9, 0xb44bd2, 0xdc415f, 0x102ab3, 0xe8b2ac, 0xe49a79, 0xf45c89, 0x20768f,
0x00236c, 0x002500, 0x60fb42, 0xf81edf, 0x90840d, 0xd8a25e, 0xc8bcc8, 0x300d43, 0x607edd, 0x08373d, 0xc488e5, 0x1077b1, 0x64b853, 0x389496,
0x28e7cf, 0xd89e3f, 0x040cce, 0xa4d1d2, 0x7cfadf, 0x101c0c, 0x001124, 0x5056bf, 0x90f1aa, 0x74458a, 0xfcc734, 0x8425db, 0xb0ec71, 0xe458b8,
0x6c709f, 0x0c3e9f, 0x34e2fd, 0x609217, 0x8863df, 0x80e650, 0x006171, 0x088c2c, 0xa49a58, 0x08ee8b, 0x68ed43, 0x70aab2, 0x000f86, 0xf8e079,
0x90fd61, 0x5c97f3, 0x6c4008, 0x24a074, 0xf02475, 0x20a2e4, 0x5cf5da, 0xccc3ea, 0x40786a, 0x38cada, 0x34ab37, 0x18af61, 0x5cf938, 0x002376,
0x649abe, 0x94e96a, 0xac293a, 0x10417f, 0xb844d9, 0xdc2b2a, 0x14205e, 0x38e7d8, 0x188796, 0xb4cef6, 0xcc4463, 0x6c72e7, 0x741bb2, 0xf8a45f,
0x5c1dd9, 0x18f1d8, 0xf86fc1, 0xf099b6, 0x907240, 0x0c4de9, 0xd89695, 0x640980, 0x185936, 0x60fec5, 0xe425e7, 0x7c6d62, 0x40d32d, 0xc42c03,
0x0c3021, 0xf0f61c, 0xb03495, 0x848e0c, 0x949426, 0xe0f5c6, 0x28e14c, 0x9027e4, 0xbc926b, 0x101c0c, 0x080007, 0x0016cb, 0x0017f2, 0x001f5b,
0x54e43a, 0xc8e0eb, 0xa88808, 0x444c0c, 0x84fcfe, 0xe48b7f, 0x5c969d, 0x002436, 0x00254b, 0x6c3e6d, 0xbc6778, 0x20c9d0, 0x68967b, 0x581faa,
0xa8fad8, 0x7014a6, 0xa8667f, 0xd02598, 0xcc29f5, 0xdcd3a2, 0x08c5e1, 0x88c663, 0xa46706, 0x8c5877, 0xa8fad8, 0x008865, 0xbc3baf, 0x3ce072,
0x00bf61, 0xf80cf3, 0x30766f, 0x8c3ae3, 0x78f882, 0xb4f1da, 0x0021fb, 0x84fcfe, 0xe48b7f, 0xd8d1cb, 0xb817c2, 0x7c11be, 0x98d6bb, 0x283737,
0xd013fd, 0xa8b86e, 0xdcbfe9, 0x306a85, 0x4466fc, 0xfca621, 0x0ccb85, 0x50ead6, 0x189efc, 0x804971, 0x7cf05f, 0x109add, 0x848e0c, 0x3c15c2,
0xa4d990, 0xd003df, 0x24fce5, 0xe4b2fb, 0xf83880, 0x241b7a, 0x402619, 0x6c709f, 0x6476ba, 0x34e2fd, 0x04489a, 0x80e650, 0x90fd61, 0x2cf0ee,
0xbcfed9, 0x808223, 0x3830f9, 0x6c006b, 0x38a4ed, 0xb0e235, 0x64cc2e, 0x5c97f3, 0x8c2937, 0xaccf5c, 0x80006e, 0xa4c361, 0xb09fba, 0x0c4de9,
0xd86375, 0x80ad16, 0x2047da, 0x8035c1, 0x9487e0, 0x7c03ab, 0xd4970b, 0xe0f5c6, 0xa0edcd, 0x087045, 0xa88808, 0xc0f2fb, 0x24e314, 0xf0f61c,
0xf48b32, 0x4c49e3, 0x04b167, 0xd8ce3a, 0xb8c74a, 0xfc183c, 0xc0e862, 0x38484c, 0x38c986, 0xd03311, 0xd4f46f, 0x48437c, 0x34a395, 0x787e61,
0xec2ce2, 0x64c753, 0x48fda3, 0x7c035e, 0x70bc10, 0x2c5d34, 0x64c2de, 0x60f81d, 0x5cf5da, 0x18ee69, 0x649abe, 0xac293a, 0x9cfc01, 0x9c35eb,
0x58d9c3, 0x489dd1, 0xd81edd, 0x2034fb, 0xf4afe7, 0x7c9a1d, 0xa483e7, 0xf099bf, 0x94e96a, 0x507a55, 0x5882a8, 0x24da9b, 0xe83a12, 0x80656d,
0xc4e39f, 0x7cd661, 0x70dda8, 0xbc98df, 0x38e7d8, 0xd8b377, 0xb4cef6, 0x38d40b, 0x209bcd, 0xfcf136, 0x18895b, 0x7cf90e, 0x50f0d3, 0x78bdbc,
0xd40b1a, 0x5882a8, 0xb4ae2b, 0x0c413e, 0xd0929e, 0x4480eb, 0xb84fd5, 0x84119e, 0xe4907e, 0x149a10, 0x485073, 0x244b03, 0x74e28c, 0x244b81,
0xec59e7, 0x3059b7, 0x501ac5, 0x1cb094, 0xa0f450, 0x002248, 0xec8892, 0x60af6d, 0xb85a73, 0x981dfa, 0x102f6b, 0xc44202, 0x103047, 0xf884f2,
0xb07994, 0x141aa3, 0xccc3ea, 0x34bb26, 0x40786a, 0xf40b93, 0x68ed43, 0x5c2e59, 0xe0cbee, 0x002248, 0x8031f0, 0xe0d083, 0xd80b9a, 0x5c666c,
0x34bb1f, 0x489d24, 0x000f86, 0xacee9e, 0xc08997, 0x2827bf, 0xf05b7b, 0xc4e1a1, 0x1855e3, 0xe450eb, 0x886440, 0x6070c0, 0xf0c371, 0x987a14,
0x7cf90e, 0xac5a14, 0xb0c559, 0xbcd11f, 0xa0b4a5, 0x80656d, 0x48137e, 0xc83ddc, 0x845733, 0x1819d6, 0xbc98df, 0x70dda8, 0x4c6f9c, 0x4883b4,
0xe83a12, 0x9c0298, 0x6c8336, 0xb8c68e, 0x74458a, 0xa49a58, 0xb4ef39, 0x0017fa, 0x941625, 0x34a8eb, 0xa483e7, 0xf4afe7, 0xac88fd, 0x20326c,
0x14a364, 0x3ca10d, 0x206e9c, 0x183f47, 0x0c715d, 0x0c1420, 0xa80600, 0x6489f1, 0x2034fb, 0xa89ced, 0xe458e7, 0xdc080f, 0xf8e94e, 0xec2ce2,
0x6cf373, 0x78c3e9, 0xc83870, 0x288335, 0x44783e, 0x202d07, 0x98398e, 0x40bc60, 0xe83617, 0x9c648b, 0x344262, 0x14d00d, 0x703a51, 0x04e598,
0x348a7b, 0xbc765e, 0x78009e, 0x68c44d, 0xf8e61a, 0x888322, 0x84b541, 0x6cc7ec, 0xc0bdc8, 0x647bce, 0xa887b3, 0x6c006b, 0x24fce5, 0x9caa1b,
0x0015b9, 0x001df6, 0xece09b, 0x606bbd, 0x0000f0, 0x4844f7, 0x1c5a3e, 0x70fd46, 0x8c83e1, 0x889f6f, 0x482ca0, 0x0ccb85, 0x08d46a, 0x68e7c2,
0xf47b5e, 0x008701, 0xfc4203, 0x1c232c, 0xcc61e5, 0x404e36, 0x9893cc, 0x58b10f, 0x645aed, 0xc0b658, 0x48a91c, 0x50bc96, 0xfc2a9c, 0xa056f3,
0x3ccd93, 0xf06bca, 0x3423ba, 0xd022be, 0xd02544, 0xbc20a4, 0x14f42a, 0x549963, 0x90dd5d, 0xc819f7, 0x3880df, 0x3cdcbc, 0x804e70, 0xd4e6b7,
0xbc851f, 0xb85e7b, 0xc462ea, 0x0023d6, 0x002491, 0x001b98, 0x44f459, 0x04d13a, 0xc48466, 0x347c25, 0xcc2db7, 0x0c9838, 0xd41a3f, 0x5c865c,
0x34c3ac, 0x94d771, 0x4c3c16, 0x9401c2, 0xb43a28, 0xd0c1b1, 0xf008f1, 0x04b167, 0xec8350, 0x2c5491, 0xe42b34, 0x3c2ef9, 0xa04ea7, 0xf0989d,
0x78471d, 0x3816d1, 0xd48890, 0x002566, 0x00265f, 0x5cba37, 0x3096fb, 0xd80831, 0x10f1f2, 0x982d68, 0xb019c6, 0x3866f0, 0x703eac, 0x7c2edd,
0xf0ee10, 0xa43d78, 0xec01ee, 0xb83765, 0xc4576e, 0x90f1aa, 0x78bdbc, 0x3cf7a4, 0x986f60, 0xc49ded, 0x9810e8, 0xc0d012, 0xbca920, 0x48a195,
0xd47ae2, 0x84c0ef, 0x7c1c68, 0xd463c6, 0x7c6456, 0x448f17, 0x04d6aa, 0xf80377, 0x8058f8, 0xdca4ca, 0x8c8fe9, 0x5cba37, 0x2c200b, 0x8866a5,
0x9ce063, 0xf06e0b, 0x5c865c, 0x003de8, 0x08e689, 0x7836cc, 0x08d46a, 0xacc1ee, 0x805a04, 0xbc8385, 0x2c598a, 0xe47dbd, 0x001cb3, 0x24920e,
0x485929, 0x34fcef, 0x002483, 0x001c62, 0x583f54, 0x40b0fa, 0xa8922c, 0xfc4203, 0xa01081, 0xf07960, 0xa0d795, 0xbc4760, 0x04180f, 0x2013e0,
0x98d6f7, 0x505527, 0x0034da, 0xa09169, 0x88365f, 0x9c8c6e, 0xbcffeb, 0x002566, 0xc09f05, 0x606bbd, 0x00214c, 0x0018af, 0x001ee1, 0x00166b,
0x685acf, 0xb4f7a1, 0x785dc8, 0x48c796, 0x804e70, 0x3880df, 0xdc415f, 0x0000f0, 0x8cc8cd, 0xa8f274, 0xd487d8, 0x184617, 0x380a94, 0xd0dfc7,
0x30636b, 0xf45c89, 0x68dbca, 0x044bed, 0x6c8dc1, 0x38cada, 0xa4d18c, 0xd0c1b1, 0x8018a7, 0xf47b5e, 0x70f927, 0xc45006, 0x88329b, 0x1449e0,
0x186590, 0x64b0a6, 0x84fcac, 0x6c19c0, 0x20ab37, 0x203cae, 0x748d08, 0xd02544, 0xbc4486, 0x20d390, 0x9401c2, 0x50fc9f, 0x380b40, 0xb8ff61,
0xa03be3, 0x7c6d62, 0x40d32d, 0xd83062, 0xc42c03, 0x7cc537, 0x70cd60, 0xf0728c, 0x34aa8b, 0x24dbed, 0x68c44d, 0x440444, 0x84b541, 0x006f64,
0xc0d012, 0xd4dccd, 0x484baa, 0xf80377, 0x14bd61, 0xcc25ef, 0xb8782e, 0xdc6672, 0xf8e61a, 0xcc2d8c, 0x98d6f7, 0x700514, 0xe892a4, 0x10683f,
0x000502, 0x0010fa, 0x000393, 0x0016cb, 0x409c28, 0x78886d, 0xa85c2c, 0x40b0fa, 0x0025e5, 0x0021fb, 0x34fcef, 0xbcf5ac, 0x0c4885, 0x0022a9,
0x00db70, 0x0c5101, 0x086d41, 0x04d3cf, 0xbcec5d, 0x80b03d, 0xc83c85, 0x9c2a83, 0xa039f7, 0xb0e235, 0x14c913, 0xe09861, 0xa06090, 0xbc765e,
0xa04ea7, 0x0017f2, 0x001b63, 0x001ec2, 0x002608, 0xa4c361, 0xac7f3e, 0xd85b2a, 0x90c1c6, 0x70a2b3, 0xf40f24, 0x4c57ca, 0xc46ab7, 0x48e9f1,
0x280b5c, 0x90b931, 0x24a2e1, 0x80ea96, 0x600308, 0x04f13e, 0x54724f, 0xc83f26, 0x40163b, 0xc83870, 0x6c8fb5, 0x1c9e46, 0xc0ccf8, 0x9c4fda,
0x48746e, 0xd4f46f, 0x787e61, 0x60f81d, 0x4c7c5f, 0x48e9f1, 0xfce998, 0x8489ad, 0x647791, 0x9ce6e7, 0x9c0298, 0x28987b, 0x54fa3e, 0x0c8910,
0xf099bf, 0x68644b, 0x789f70, 0x24ab81, 0x581faa, 0xa46706, 0x3c0754, 0x78abbb, 0xd8c4e9, 0xbcd11f, 0xf4428f, 0x446d6c, 0x00f46f, 0x0c715d,
0xe4ce8f, 0xe8040b, 0xb8c75d, 0x403cfc, 0x98fe94, 0xd8004d, 0x98b8e3, 0x34bb1f, 0x406f2a, 0x985fd3, 0x60beb5, 0xf8f1b6, 0xf4f1e1, 0x9cd917,
0x80929f, 0x885395, 0x9c04eb, 0xa8968a, 0xdc3714, 0x40331a, 0x94f6a3, 0x9068c3, 0x68dbca, 0x086698, 0xbc5436, 0x044bed, 0x6c8dc1, 0x0cd746,
0xd81d72, 0x70ece4, 0x38c986, 0xfcfc48, 0x4c8d79, 0x207d74, 0xf4f15a, 0x60a37d, 0xd40b1a, 0xfc64ba, 0x9060f1, 0xa4516f, 0x68dfdd, 0x98fae3,
0x042665, 0x2cb43a, 0x689c70, 0x087045, 0x3cab8e, 0x7c6df8, 0x48d705, 0xf0b429, 0xb8782e, 0x000502, 0x000a95, 0x00264a, 0x041e64, 0x001124,
0x78fd94, 0xc88550, 0x286ab8, 0x7cc3a1, 0x3cd0f8, 0x98d6bb, 0x4cb199, 0x002241, 0x7cc537, 0x78ca39, 0x18e7f4, 0x70cd60, 0x8c7b9d, 0xd89e3f,
0x64e682, 0x804971, 0xcc20e8, 0x209bcd, 0xf0b0e7, 0xa056f3, 0x549963, 0xb8c75d, 0x0c74c2, 0x403004, 0x842999, 0x74e2f5, 0xe0c97a, 0x68a86d,
0x28ff3c, 0x1094bb, 0xf01898, 0x48a91c, 0x58b10f, 0x304b07, 0x1496e5, 0x7cc3a1, 0x7073cb, 0x90840d, 0xe80688, 0xec852f, 0x00f4b9, 0x5c95ae,
0x80ceb9, 0xcc2119, 0x0057c1, 0x14c697, 0xfc039f, 0x9c0cdf, 0x007204, 0x9803d8, 0x60c547, 0x685b35, 0x2cb43a, 0x689c70, 0x380f4a, 0x3010e4,
0x90e17b, 0x18810e, 0x608c4a, 0xa4d931, 0x6cc7ec, 0x647bce, 0x584498, 0xa886dd, 0x444c0c, 0xb4f0ab, 0x80929f, 0x9c04eb, 0x5c969d, 0x609217,
0xacc1ee, 0x7802f8, 0x508f4c, 0x04d13a, 0x0cf346, 0x082525, 0xf460e2, 0x84b153, 0xe06678, 0x48d705, 0x041552, 0xcc785f, 0x88cb87, 0xf0c1f1,
0xa45046, 0x009ec8, 0x7c1dd9, 0xa086c6, 0x102ab3, 0xacf7f3, 0x601d91, 0x843835, 0x8c006d, 0xa8968a, 0xf41ba1, 0x60d9c7, 0x3cab8e, 0xf82793,
0x38f9d3, 0x44e66e, 0xe83617, 0x344262, 0xc09ad0, 0xa41232, 0xb8bc5b, 0x907240, 0x908d6c, 0xb8098a, 0x4c7c5f, 0x68644b, 0xc81ee7, 0xa43135,
0xacf6f7, 0x7c38ad, 0x3c20f6, 0x703c69, 0xb8b2f8, 0x005b94, 0x88b291, 0x68d93c, 0x00f76f, 0xc88550, 0x7014a6, 0x985aeb, 0x78d75f, 0xe0b52d,
0x5462e2, 0x4418fd, 0xccd281, 0xd09c7a, 0x4c6f9c, 0x201742, 0x902155, 0x6c94f8, 0xc0cecd, 0x2cae2b, 0xc869cd, 0xa4b805, 0x5cadcf, 0xbc6c21,
0x64a769, 0xbccfcc, 0xa4516f, 0x3c8375, 0x149a10, 0x0ce725, 0xc0335e, 0xf40e22, 0x544e90, 0xc01173, 0xbce63f, 0x7c9122, 0xacbc32, 0x2827bf,
0x20a99b, 0x4c0bbe, 0x7c1e52, 0xdcb4c4, 0x7c6f06, 0x001dd8, 0x0017fa, 0x800184, 0x8cbfa6, 0xc8a823, 0xb0c559, 0xbc1485, 0x9c6c15, 0xc0335e,
0x000a75, 0x0003ff, 0xf8e079, 0x1430c6, 0xe0757d, 0x9cd35b, 0x60af6d, 0xd0929e, 0x083d88, 0x608f5c, 0x109266, 0x281878, 0x847a88, 0xa0f450,
0xb85a73, 0x103047, 0x109266, 0xb047bf, 0x7c0bc6, 0x804e81, 0x244b81, 0xa826d9, 0x00155d, 0x00125a, 0x000d3a, 0x0003ff, 0x18d0c5, 0x1cccd6,
0x50a4c8, 0x8425db, 0xd8c4e9, 0x50c8e5, 0x446d6c, 0x38d40b, 0x647791, 0x582059, 0x74e1b6, 0xf40e01, 0x1495ce, 0x50de06, 0xcc660a, 0xfc1d43,
0x781fdb, 0x08fc88, 0x30c7ae, 0x18227e, 0x00f46f, 0x9ce6e7, 0xe498d1, 0x0024e9, 0xb4c4fc, 0x7cd661, 0x98b8ba, 0x103025, 0xb8b2f8, 0x98460a,
0x5cca1a, 0x70288b, 0x4849c7, 0x205ef7, 0x182666, 0xc06599, 0xcc07ab, 0xb85d0a, 0x7c9a1d, 0x5462e2, 0x149d99, 0x489507, 0xd85575, 0xd411a3,
0xe84e84, 0x50fc9f, 0xe432cb, 0x889b39, 0xbcb1f3, 0x38ece4, 0xccf9e8, 0x04ba8d, 0xe446da, 0x58d9c3, 0x3c8375, 0xb8bc5b, 0xd8ce3a, 0xb8c74a,
0xf0e77e, 0x5ce8eb, 0xb8d9ce, 0x70f927, 0x301966, 0x28bab5, 0x103b59, 0x94f6d6, 0xf82d7c, 0xc09ad0, 0x94b01f, 0x90633b, 0xfcaab6, 0x782327,
0x6cb7f4, 0x001ee1, 0x0018af, 0xbc72b1, 0x78f7be, 0xf49f54, 0x00214c, 0xdcf756, 0x74b587, 0xfcb6d8, 0x18810e, 0x608c4a, 0x241b7a, 0x8cfe57,
0x001632, 0xd0667b, 0x001377, 0x50b7c3, 0x8018a7, 0x444e1a, 0xe8e5d6, 0xc0a600, 0xa823fe, 0x1c427d, 0xcc2119, 0x304b07, 0x000393, 0x702ad5,
0x5492be, 0x101dc0, 0x0021d1, 0xcc2d8c, 0x949aa9, 0x20dbab, 0x5c9960, 0x74eb80, 0x0ce0dc, 0xd868c3, 0xc493d9, 0xa82bb9, 0x1c1ac0, 0x88ae07,
0x88b4a6, 0x2c5491, 0x5c70a3, 0x10f96f, 0xf01c13, 0x00aa70, 0xbcf5ac, 0x40831d, 0xdcd3a2, 0x5c1dd9, 0x68fef7, 0xd07714, 0x587a6a, 0x705aac,
0xccfa00, 0xf8a9d0, 0x805a04, 0x5caf06, 0xb81daa, 0x10f1f2, 0x0025e5, 0x685acf, 0x0ca8a7, 0x2c61f6, 0xd4a33d, 0xf0766f, 0x4098ad, 0x6c4d73,
0x0022a9, 0xc49a02, 0x344df7, 0xd41a3f, 0xcc6ea4, 0xa46cf1, 0x0ca8a7, 0x68ef43, 0xd02b20, 0xd86375, 0x30b4b8, 0x9c8c6e, 0x18f0e4, 0x00bf61,
0x54b802, 0x24181d, 0xf4c248, 0xa8515b, 0xc048e6, 0xd07714, 0x2816a8, 0xd00401, 0xdc5583, 0xb8c111, 0x9ce33f, 0x7867d7, 0x087808, 0x887598,
0x84a134, 0x1c9148, 0xc0ccf8, 0x80ed2c, 0xe8b2ac, 0x8489ad, 0x20768f, 0xc0174d, 0xa407b6, 0x04d6aa, 0x08152f, 0xf4f5db, 0xc0a53e, 0xecd09f,
0x28ed6a, 0x34ab37, 0x60a37d, 0x0056cd, 0xbca920, 0x5082d5, 0x9c84bf, 0xa8be27, 0x94d029, 0x308454, 0x5c5181, 0x608e08, 0x4c189a, 0x58c5cb,
0x00b362, 0xf86214, 0xb0702d, 0xd0c5f3, 0x0023df, 0x0025bc, 0x00264a, 0xb4bff6, 0x74f61c, 0x84c0ef, 0xa8b86e, 0xc40bcb, 0xb83765, 0x14bd61,
0x0026b0, 0x041e64, 0xd49a20, 0x9027e4, 0x60334b, 0x5c5948, 0x60f445, 0xc0d3c0, 0x948bc1, 0x14568e, 0xd4619d, 0x7c5049, 0x64b473, 0x7451ba,
0x5cf7e6, 0xa0d795, 0xcc088d, 0x8c8ef2, 0xf40f24, 0x24f677, 0x7867d7, 0x7802f8, 0xec01ee, 0x682737, 0x58404e, 0xd0c5f3, 0xbc9fef, 0x20ab37,
0x5433cb, 0xd0d2b0, 0xd88f76, 0x3c2ef9, 0x7081eb, 0x086698, 0x9060f1, 0x60f445, 0x2c0e3d, 0x88e87f, 0x9cf48e, 0x5cf7e6, 0xb853ac, 0x203cae,
0x741bb2, 0x28cfe9, 0xe425e7, 0xb019c6, 0x58e28f, 0xac1f74, 0x48bf6b, 0x2cbaba, 0x40d3ae, 0xa03be3, 0x4c3275, 0x3816d1, 0xd0176a, 0xd48890,
0x245ba7, 0xdc56e7, 0x347c25, 0xd4909c, 0x080007, 0x000a95, 0x002241, 0x5492be, 0x949aa9, 0x001c43, 0x002399, 0x0017c9, 0x7cf854, 0xc4731e,
0x18ee69, 0x748114, 0x18f643, 0xd0a637, 0xa01828, 0xd0034b, 0xa43135, 0xec1f72, 0xe4121d, 0xe8508b, 0xf8042e, 0x0023d6, 0x001b98, 0x001a8a,
0x9c35eb, 0x507a55, 0xa0999b, 0x24240e, 0x903c92, 0xa88e24, 0xe8802e, 0x3c5a37, 0xf49f54, 0x34c3ac, 0x44f459, 0x00265d, 0x002567, 0xbcb1f3,
0x68ae20, 0xe0b52d, 0x80be05, 0xd8bb2c, 0xd04f7e, 0x2c1f23, 0x549f13, 0xc462ea, 0x182666, 0x30d6c9, 0xcc07ab, 0x1c62b8, 0xccf9e8, 0xd857ef,
0xb8098a, 0xf0dbe2, 0x8c2937, 0xdc9b9c, 0x98f0ab, 0xf0dbf8, 0xaccf5c, 0x18e2c2, 0x28bab5, 0xe440e2, 0x103b59, 0xd890e8, 0x9852b1, 0x14f42a,
0x3c15c2, 0x04489a, 0xd8cf9c, 0xa886dd, 0x54eaa8, 0xe4c63d, 0x843835, 0x0808c2, 0xccfe3c, 0xb43a28, 0x78a873, 0xd83062, 0xc041f6, 0x001e75,
0xc06394, 0x8c006d, 0xb09fba, 0xdc86d8, 0x78ca39, 0x18e7f4, 0xb8ff61, 0x001c62, 0xf895c7, 0x34145f, 0x0821ef, 0x505527, 0x88c9d0, 0x8c3ae3,
0xdc2b61, 0x1093e9, 0x442a60, 0xe0f847, 0x145a05, 0x28cfda, 0x148fc6, 0x00aa70, 0x60e3ac, 0x4c0bbe, 0xb88d12, 0x845181, 0x348a7b, 0x78009e,
0x283737, 0x045453, 0xf0cba1, 0x30f7c5, 0x008865, 0x40b395, 0x3090ab, 0xacc33a, 0x54f201, 0x70288b, 0x8c8ef2, 0x90b0ed, 0x04d3cf, 0x1c3ade,
0x1ce62b, 0xa0edcd, 0x842999, 0x74e2f5, 0x20c9d0, 0x7073cb, 0x9c207b, 0x509ea7, 0xa88195, 0x88add2, 0xac5f3e, 0xb48b19, 0xbcec5d, 0x28a02b,
0x341298, 0x9c293f, 0x7c0191, 0x70480f, 0xa4b805, 0x587f57, 0x80d605, 0xb8c68e, 0x04fe31, 0x4cbca5, 0xd831cf, 0x188331, 0x9c65b0, 0x8455a5,
0xc869cd, 0xbc6c21, 0x0469f8, 0x749eaf, 0xb841a4, 0xf895ea, 0x50a67f, 0xa87c01, 0x50f520, 0x64b310, 0xa4ebd3, 0xb0d09c, 0x50c8e5, 0x5cf6dc,
0x647033, 0x846878, 0x948bc1, 0x4827ea, 0x388c50, 0xa09347, 0xc8f230, 0x0026ff, 0xa4e4b8, 0xf40b93, 0x1c69a5, 0x94ebcd, 0x141aa3, 0x1430c6,
0x1c77f6, 0xe44790, 0xd4503f, 0x40163b, 0x5c497d, 0xe47dbd, 0x503da1, 0x88074b, 0x3871de, 0x7081eb, 0x78f882, 0x606944, 0x807abf, 0x00092d,
0x508569, 0x1077b1, 0x5cf6dc, 0x380195, 0xbc1485, 0x88d50c, 0x947be7, 0x7c6193, 0x0452f3, 0x90e7c4, 0xa81b5a, 0x2c5bb8, 0xacf7f3, 0xd4970b,
0x54bd79, 0xdc44b6, 0x1007b6, 0xc0174d, 0xa407b6, 0x149f3c, 0xd868c3, 0x8cbebe, 0x14f65a, 0x009ec8, 0x0c1daf, 0x3480b3, 0xf48b32, 0x60fb42,
0xc493d9, 0x00b5d0, 0x8c83e1, 0xfcb6d8, 0x6ce85c, 0x007c2d, 0xf47def, 0x64b9e8, 0xd8a25e, 0x000a27, 0x001d4f, 0x002312, 0x60facd, 0x003ee1,
0x7c8bb5, 0xdcf756, 0x68dfdd, 0x64b473, 0x7451ba, 0x3480b3, 0x2082c0, 0xfc253f, 0x183451, 0x0c771a, 0x286aba, 0x3451c9, 0x406c8f, 0xd023db,
0xfc64ba, 0xc46ab7, 0x00ec0a, 0x38e60a, 0x04e598, 0x2ca9f0, 0x586b14, 0x70dee2, 0x5855ca, 0xdc2b61, 0x40a6d9, 0x4cb199, 0xc09f42, 0x705681,
0x94b01f, 0x94f6d6, 0x40bc60, 0xd85575, 0xd411a3, 0x6489f1, 0x04ba8d, 0x040cce, 0x403cfc, 0x4860bc, 0xf0cba1, 0x182032, 0x34c059, 0xf0d1a9,
0x489507, 0xa89ced, 0xc42ad0, 0xe0897e, 0x103025, 0x8c79f5, 0x90735a, 0x14109f, 0x04f7e4, 0xdc9b9c, 0x54724f, 0x8c7c92, 0xb03495, 0x042665,
}; 0xec3586, 0x54eaa8, 0x28e14c, 0xe4c63d, 0x54e43a, 0x04db56, 0x04e536,
0xe8802e, 0x006171, 0x6c4008, 0x7c6df8, 0x78fd94, 0x2cbe08, 0xac3c0b,
0x701124, 0xf437b7, 0xd8cf9c, 0xa8bbcf, 0xac7f3e, 0x280b5c, 0xacfdec,
0x28f076, 0x20a2e4, 0xbc4cc4, 0xdc3714, 0x40331a, 0xccc760, 0x7c0191,
0x84100d, 0x80d605, 0xdc2b2a, 0x48137e, 0x382de8, 0xc08997, 0x04c23e,
0x380195, 0xb4ae2b, 0x5c5188, 0x0ce725, 0xe0db10, 0x1432d1, 0x1816c9,
0x842e27, 0x0c413e, 0x9000db, 0xb4ef39, 0xc808e9, 0x183a2d, 0x8463d6,
0xb84fd5, 0xc0eefb, 0x206e9c, 0x6c2f2c, 0x18227e, 0x30c7ae, 0x501ac5,
0x6045bd, 0x1cb094, 0xe85b5b, 0x0025ae, 0x001dd8, 0x000a75, 0xa4c939,
0xc0dcda, 0x04b429, 0x48794d,
};

View File

@ -28,6 +28,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 +42,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.7.93
; 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

View File

@ -5,8 +5,9 @@
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
bmeStatus_t bme_status; bmeStatus_t bme_status = {0};
TaskHandle_t BmeTask;
Ticker bmecycler;
#define SEALEVELPRESSURE_HPA (1013.25) #define SEALEVELPRESSURE_HPA (1013.25)
@ -37,6 +38,8 @@ Adafruit_BME280 bme; // I2C
#endif #endif
void bmecycle() { xTaskNotify(irqHandlerTask, BME_IRQ, eSetBits); }
// initialize BME680 sensor // initialize BME680 sensor
int bme_init(void) { int bme_init(void) {
@ -106,6 +109,8 @@ int bme_init(void) {
finish: finish:
I2C_MUTEX_UNLOCK(); // release i2c bus access I2C_MUTEX_UNLOCK(); // release i2c bus access
if (rc)
bmecycler.attach(BMECYCLE, bmecycle);
return rc; return rc;
} // bme_init() } // bme_init()
@ -136,47 +141,37 @@ int checkIaqSensorStatus(void) {
} // checkIaqSensorStatus() } // checkIaqSensorStatus()
#endif #endif
// loop function which reads and processes data based on sensor settings // store current BME sensor data in struct
void bme_loop(void *pvParameters) { void bme_storedata(bmeStatus_t *bme_store) {
if (I2C_MUTEX_LOCK()) { // block i2c bus access
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
#ifdef HAS_BME680 #ifdef HAS_BME680
while (1) { if (iaqSensor.run()) { // if new data is available
// block i2c bus access bme_store->raw_temperature =
if (I2C_MUTEX_LOCK()) { iaqSensor.rawTemperature; // temperature in degree celsius
if (iaqSensor.run()) { // If new data is available bme_store->raw_humidity = iaqSensor.rawHumidity;
bme_status.raw_temperature = bme_store->temperature = iaqSensor.temperature;
iaqSensor.rawTemperature; // Temperature in degree celsius bme_store->humidity =
bme_status.raw_humidity = iaqSensor.rawHumidity; iaqSensor.humidity; // humidity in % relative humidity x1000
bme_status.temperature = iaqSensor.temperature; bme_store->pressure = // pressure in Pascal
bme_status.humidity = (iaqSensor.pressure / 100.0); // conversion Pa -> hPa
iaqSensor.humidity; // Humidity in % relative humidity x1000 bme_store->iaq = iaqSensor.iaqEstimate;
bme_status.pressure = // Pressure in Pascal bme_store->iaq_accuracy = iaqSensor.iaqAccuracy;
(iaqSensor.pressure / 100.0); // conversion Pa -> hPa bme_store->gas = iaqSensor.gasResistance; // gas resistance in ohms
bme_status.iaq = iaqSensor.iaqEstimate; updateState();
bme_status.iaq_accuracy = iaqSensor.iaqAccuracy;
bme_status.gas = iaqSensor.gasResistance; // Gas resistance in Ohms
updateState();
}
I2C_MUTEX_UNLOCK();
} }
}
#elif defined HAS_BME280 #elif defined HAS_BME280
while (1) { bme_store->temperature = bme.readTemperature();
if (I2C_MUTEX_LOCK()) { bme_store->pressure = (bme.readPressure() / 100.0); // conversion Pa -> hPa
bme_status.temperature = bme.readTemperature(); // bme.readAltitude(SEALEVELPRESSURE_HPA);
bme_status.pressure = bme_store->humidity = bme.readHumidity();
(bme.readPressure() / 100.0); // conversion Pa -> hPa bme_store->iaq = 0; // IAQ feature not present with BME280
// bme.readAltitude(SEALEVELPRESSURE_HPA);
bme_status.humidity = bme.readHumidity();
bme_status.iaq = 0; // IAQ feature not present with BME280
I2C_MUTEX_UNLOCK();
}
}
#endif #endif
} // bme_loop() I2C_MUTEX_UNLOCK(); // release i2c bus access
}
} // bme_storedata()
#ifdef HAS_BME680 #ifdef HAS_BME680
void loadState(void) { void loadState(void) {

View File

@ -23,25 +23,23 @@ void doHousekeeping() {
if (cfg.runmode == 1) if (cfg.runmode == 1)
do_reset(); do_reset();
#ifdef HAS_SPI
spi_housekeeping();
#endif
#if (HAS_LORA)
lora_housekeeping();
#endif
// task storage debugging // // task storage debugging //
ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(irqHandlerTask), uxTaskGetStackHighWaterMark(irqHandlerTask),
eTaskGetState(irqHandlerTask)); eTaskGetState(irqHandlerTask));
#if (HAS_LORA)
ESP_LOGD(TAG, "LMiCtask %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(lmicTask), eTaskGetState(lmicTask));
#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",
uxTaskGetStackHighWaterMark(GpsTask), eTaskGetState(GpsTask)); uxTaskGetStackHighWaterMark(GpsTask), eTaskGetState(GpsTask));
#endif #endif
#if (HAS_BME) #ifdef HAS_SPI
ESP_LOGD(TAG, "Bmeloop %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "spiloop %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(BmeTask), eTaskGetState(BmeTask)); uxTaskGetStackHighWaterMark(spiTask), eTaskGetState(spiTask));
#endif #endif
#if (defined HAS_DCF77 || defined HAS_IF482) #if (defined HAS_DCF77 || defined HAS_IF482)
ESP_LOGD(TAG, "Clockloop %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "Clockloop %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(ClockTask), eTaskGetState(ClockTask)); uxTaskGetStackHighWaterMark(ClockTask), eTaskGetState(ClockTask));
@ -59,16 +57,15 @@ void doHousekeeping() {
ESP_LOGI(TAG, "Voltage: %dmV", batt_voltage); ESP_LOGI(TAG, "Voltage: %dmV", batt_voltage);
#endif #endif
// display BME sensor data // display BME680/280 sensor data
#if (HAS_BME)
#ifdef HAS_BME680 #ifdef HAS_BME680
ESP_LOGI(TAG, "BME680 Temp: %.2f°C | IAQ: %.2f | IAQacc: %d", ESP_LOGI(TAG, "BME680 Temp: %.2f°C | IAQ: %.2f | IAQacc: %d",
bme_status.temperature, bme_status.iaq, bme_status.iaq_accuracy); bme_status.temperature, bme_status.iaq, bme_status.iaq_accuracy);
#endif #elif defined HAS_BME280
// display BME280 sensor data
#ifdef HAS_BME280
ESP_LOGI(TAG, "BME280 Temp: %.2f°C | Humidity: %.2f | Pressure: %.0f", ESP_LOGI(TAG, "BME280 Temp: %.2f°C | Humidity: %.2f | Pressure: %.0f",
bme_status.temperature, bme_status.humidity, bme_status.pressure); bme_status.temperature, bme_status.humidity, bme_status.pressure);
#endif
#endif #endif
// check free heap memory // check free heap memory

View File

@ -5,14 +5,19 @@
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
// we use NMEA $GPZDA sentence field 1 for time synchronization
// $GPZDA gives time for preceding pps pulse, but does not has a constant offset
TinyGPSPlus gps; TinyGPSPlus gps;
gpsStatus_t gps_status; TinyGPSCustom gpstime(gps, "GPZDA", 1); // field 1 = UTC time
static const String ZDA_Request = "$EIGPQ,ZDA*39\r\n";
gpsStatus_t gps_status = {0};
TaskHandle_t GpsTask; TaskHandle_t GpsTask;
#ifdef GPS_SERIAL #ifdef GPS_SERIAL
HardwareSerial GPS_Serial(1); // use UART #1 HardwareSerial GPS_Serial(1); // use UART #1
static uint16_t nmea_txDelay_ms = static uint16_t nmea_txDelay_ms =
tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL) / portTICK_PERIOD_MS; (tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL) / portTICK_PERIOD_MS);
#else #else
static uint16_t nmea_txDelay_ms = 0; static uint16_t nmea_txDelay_ms = 0;
#endif #endif
@ -27,7 +32,7 @@ int gps_init(void) {
return 0; return 0;
} }
#if defined GPS_SERIAL #ifdef GPS_SERIAL
GPS_Serial.begin(GPS_SERIAL); GPS_Serial.begin(GPS_SERIAL);
ESP_LOGI(TAG, "Using serial GPS"); ESP_LOGI(TAG, "Using serial GPS");
#elif defined GPS_I2C #elif defined GPS_I2C
@ -55,11 +60,11 @@ int gps_config() {
int rslt = 1; // success int rslt = 1; // success
#if defined GPS_SERIAL #if defined GPS_SERIAL
/* to come */ /* insert user configuration here, if needed */
#elif defined GPS_I2C #elif defined GPS_I2C
/* to come */ /* insert user configuration here, if needed */
#endif #endif
return rslt; return rslt;
@ -68,7 +73,7 @@ int gps_config() {
// store current GPS location data in struct // store current GPS location data in struct
void gps_storelocation(gpsStatus_t *gps_store) { void gps_storelocation(gpsStatus_t *gps_store) {
if (gps.location.isUpdated() && gps.location.isValid() && if (gps.location.isUpdated() && gps.location.isValid() &&
(gps.time.age() < 1500)) { (gps.location.age() < 1500)) {
gps_store->latitude = (int32_t)(gps.location.lat() * 1e6); gps_store->latitude = (int32_t)(gps.location.lat() * 1e6);
gps_store->longitude = (int32_t)(gps.location.lng() * 1e6); gps_store->longitude = (int32_t)(gps.location.lng() * 1e6);
gps_store->satellites = (uint8_t)gps.satellites.value(); gps_store->satellites = (uint8_t)gps.satellites.value();
@ -77,40 +82,55 @@ void gps_storelocation(gpsStatus_t *gps_store) {
} }
} }
// store current GPS timedate in struct // function to fetch current time from struct; note: this is costly
void IRAM_ATTR gps_storetime(gpsStatus_t *gps_store) { time_t fetch_gpsTime(uint16_t *msec) {
if (gps.time.isUpdated() && gps.date.isValid() && (gps.time.age() < 1000)) { time_t time_sec = 0;
// nmea telegram serial delay compensation; not sure if we need this? // poll NMEA $GPZDA sentence
/* #ifdef GPS_SERIAL
if (gps.time.age() > nmea_txDelay_ms) GPS_Serial.print(ZDA_Request);
gps_store->timedate.Second = gps.time.second() + 1; // wait for gps NMEA answer
else vTaskDelay(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL));
gps_store->timedate.Second = gps.time.second(); #elif defined GPS_I2C
*/ Wire.print(ZDA_Request);
#endif
gps_store->timedate.Second = gps.time.second(); // did we get a current time?
gps_store->timedate.Minute = gps.time.minute(); if (gpstime.isUpdated() && gpstime.isValid()) {
gps_store->timedate.Hour = gps.time.hour();
gps_store->timedate.Day = gps.date.day(); tmElements_t tm;
gps_store->timedate.Month = gps.date.month();
gps_store->timedate.Year = String rawtime = gpstime.value();
uint32_t time_bcd = rawtime.toFloat() * 100;
uint32_t delay_ms =
gpstime.age() + nmea_txDelay_ms + NMEA_COMPENSATION_FACTOR;
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
} else ESP_LOGD(TAG, "time [bcd]: %u", time_bcd);
gps_store->timedate = {0};
}
// function to fetch current time from struct; note: this is costly tm.Second = (time_bcd / 100) % 100; // second
time_t fetch_gpsTime(gpsStatus_t value) { tm.Minute = (time_bcd / 10000) % 100; // minute
tm.Hour = time_bcd / 1000000; // hour
tm.Day = gps.date.day(); // day
tm.Month = gps.date.month(); // month
tm.Year = year; // year
time_t t = timeIsValid(makeTime(value.timedate)); // add protocol delay to time with millisecond precision
ESP_LOGD(TAG, "GPS time: %d", t); time_sec = makeTime(tm) + delay_ms / 1000;
return t; *msec = (delay_ms % 1000) ? delay_ms % 1000 : 0;
}
return timeIsValid(time_sec);
} // fetch_gpsTime() } // fetch_gpsTime()
time_t fetch_gpsTime(void) {
uint16_t msec;
return fetch_gpsTime(&msec);
}
// GPS serial feed FreeRTos Task // GPS serial feed FreeRTos Task
void gps_loop(void *pvParameters) { void gps_loop(void *pvParameters) {
@ -119,7 +139,7 @@ void gps_loop(void *pvParameters) {
while (1) { while (1) {
if (cfg.payloadmask && GPS_DATA) { if (cfg.payloadmask && GPS_DATA) {
#if defined 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()) {
gps.encode(GPS_Serial.read()); gps.encode(GPS_Serial.read());
@ -134,7 +154,7 @@ void gps_loop(void *pvParameters) {
} // if } // if
// show NMEA data in verbose mode, useful for debugging GPS // show NMEA data in verbose mode, useful for debugging GPS
ESP_LOGV(TAG, "GPS NMEA data: passed %d / failed: %d / with fix: %d", 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());

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View 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
View 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 64 // Width in pixels (LEDs) of your display
#define LED_MATRIX_HEIGHT 16 // Height in pixels (LEDs ) of your display
// 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

View File

@ -45,10 +45,10 @@ void irqHandler(void *pvParameters) {
refreshTheMatrixDisplay(); refreshTheMatrixDisplay();
#endif #endif
// gps refresh buffer? // BME sensor data to be read?
#if (HAS_GPS) #if (HAS_BME)
if (InterruptStatus & GPS_IRQ) if (InterruptStatus & BME_IRQ)
gps_storelocation(&gps_status); bme_storedata(&bme_status);
#endif #endif
// are cyclic tasks due? // are cyclic tasks due?
@ -59,9 +59,7 @@ void irqHandler(void *pvParameters) {
// is time to be synced? // is time to be synced?
if (InterruptStatus & TIMESYNC_IRQ) { if (InterruptStatus & TIMESYNC_IRQ) {
now(); // ensure sysTime is recent now(); // ensure sysTime is recent
time_t t = timeProvider(); calibrateTime();
if (timeIsValid(t))
setTime(t);
} }
#endif #endif
@ -108,17 +106,6 @@ void IRAM_ATTR ButtonIRQ() {
} }
#endif #endif
#if (HAS_GPS)
void IRAM_ATTR GpsIRQ() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(irqHandlerTask, GPS_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); }

View File

@ -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)

View File

@ -2,13 +2,16 @@
#include "globals.h" #include "globals.h"
#define NUMCHARS 5 #define NUMROWS 16
#define NUMCOLS 64
#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; static const uint32_t DisplaySize = LED_MATRIX_WIDTH * LED_MATRIX_HEIGHT / 8;
uint8_t MatrixDisplayIsOn = 0, displaybuf[DisplaySize] = {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 +19,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 +31,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, NUMROWS - 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,25 +55,58 @@ 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 (ulLastNumMacs != macs.size()) { if (cfg.countermode == 1)
ulLastNumMacs = macs.size();
matrix.clear(); { // cumulative counter mode -> display total number of pax
DrawNumber(String(ulLastNumMacs)); if (ulLastNumMacs != macs.size()) {
ulLastNumMacs = macs.size();
matrix.clear();
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 < NUMCOLS - 1)
col++;
else
ShiftLeft(displaybuf, DisplaySize);
} else
matrix.drawPoint(col, row, 0); // clear current dot
// scale and set new dot
ulLastNumMacs = macs.size();
level = ulLastNumMacs / LINE_DIAGRAM_DIVIDER;
row = level <= NUMROWS ? NUMROWS - 1 - level % NUMROWS : 0;
matrix.drawPoint(col, row, 1);
}
} }
break; break;
@ -164,4 +204,12 @@ uint8_t GetCharWidth(char cChar) {
return CharDescriptor.width; return CharDescriptor.width;
} }
void ShiftLeft(uint8_t *arr, uint32_t len) {
uint32_t i;
for (i = 0; i < len - 1; ++i) {
arr[i] = (arr[i] << 1) | ((arr[i + 1] >> 31) & 1);
}
arr[len - 1] = arr[len - 1] << 1;
}
#endif // HAS_MATRIX_DISPLAY #endif // HAS_MATRIX_DISPLAY

View File

@ -35,7 +35,7 @@
// 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

View File

@ -20,6 +20,7 @@ static const char TAG[] = "lora";
osjob_t sendjob; osjob_t sendjob;
QueueHandle_t LoraSendQueue; QueueHandle_t LoraSendQueue;
TaskHandle_t lmicTask = NULL;
class MyHalConfig_t : public Arduino_LMIC::HalConfiguration_t { class MyHalConfig_t : public Arduino_LMIC::HalConfiguration_t {
@ -423,28 +424,15 @@ 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
ESP_LOGI(TAG, "Starting LMIC..."); ESP_LOGI(TAG, "Starting LMIC...");
xTaskCreatePinnedToCore(lmictask, // task function
os_init(); // initialize lmic run-time environment on core 1 "lmictask", // name of task
LMIC_reset(); // initialize lmic MAC 4096, // stack size of task
LMIC_setLinkCheckMode(0); (void *)1, // parameter of the task
// This tells LMIC to make the receive windows bigger, in case your clock is 2, // priority of the task
// faster or slower. This causes the transceiver to be earlier switched on, &lmicTask, // task handle
// so consuming more power. You may sharpen (reduce) CLOCK_ERROR_PERCENTAGE 1); // CPU core
// 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)
// 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
// from the network. If working with other networks or in other regions,
// this will need to be changed.
LMIC_selectSubBand(1);
#endif
if (!LMIC_startJoining()) { // start joining if (!LMIC_startJoining()) { // start joining
ESP_LOGI(TAG, "Already joined"); ESP_LOGI(TAG, "Already joined");
@ -476,11 +464,6 @@ void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) {
void lora_queuereset(void) { xQueueReset(LoraSendQueue); } void lora_queuereset(void) { xQueueReset(LoraSendQueue); }
void lora_housekeeping(void) {
// ESP_LOGD(TAG, "loraloop %d bytes left",
// uxTaskGetStackHighWaterMark(LoraTask));
}
#if (TIME_SYNC_LORAWAN) #if (TIME_SYNC_LORAWAN)
void IRAM_ATTR user_request_network_time_callback(void *pVoidUserUTCTime, void IRAM_ATTR user_request_network_time_callback(void *pVoidUserUTCTime,
int flagSuccess) { int flagSuccess) {
@ -507,15 +490,8 @@ void IRAM_ATTR user_request_network_time_callback(void *pVoidUserUTCTime,
return; return;
} }
// begin of time critical section // mask application irq to ensure accurate timing
// lock I2C bus and application irq to ensure accurate timing
mask_user_IRQ(); mask_user_IRQ();
if (!I2C_MUTEX_LOCK()) {
ESP_LOGW(TAG, "[%0.3f] Timesync handshake error: i2c bus locking failed",
millis() / 1000.0);
goto finish; // failure
}
// Update userUTCTime, considering the difference between the GPS and UTC // Update userUTCTime, considering the difference between the GPS and UTC
// time, and the leap seconds until year 2019 // time, and the leap seconds until year 2019
@ -529,14 +505,44 @@ void IRAM_ATTR user_request_network_time_callback(void *pVoidUserUTCTime,
time_t requestDelaySec = osticks2ms(ticksNow - ticksRequestSent) / 1000; time_t requestDelaySec = osticks2ms(ticksNow - ticksRequestSent) / 1000;
// Update system time with time read from the network // Update system time with time read from the network
setMyTime(*pUserUTCTime + requestDelaySec, 0); setMyTime(*pUserUTCTime + requestDelaySec, 0, _lora);
finish: finish:
// end of time critical section: release I2C bus and app irq // end of time critical section: release app irq lock
I2C_MUTEX_UNLOCK();
unmask_user_IRQ(); unmask_user_IRQ();
} // user_request_network_time_callback } // user_request_network_time_callback
#endif // TIME_SYNC_LORAWAN #endif // TIME_SYNC_LORAWAN
// LMIC lorawan stack task
void lmictask(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
os_init(); // initialize lmic run-time environment
LMIC_reset(); // initialize lmic MAC
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)
// 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
// from the network. If working with other networks or in other regions,
// this will need to be changed.
LMIC_selectSubBand(1);
#endif
while (1) {
os_runloop_once(); // execute lmic scheduled jobs and events
delay(2); // yield to CPU
}
} // lmictask
#endif // HAS_LORA #endif // HAS_LORA

View File

@ -33,10 +33,10 @@ IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
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
irqhandler 1 2 display, timesync, gps, etc. triggered by timers lmictask 1 2 MCCI LMiC LORAWAN stack
gpsloop 1 2 reads data from GPS via serial or i2c irqhandler 1 1 display, timesync, gps, etc. triggered by timers
bmeloop 1 2 reads data from BME sensor via i2c gpsloop 1 1 reads data from GPS via serial or i2c
looptask 1 1 runs the LMIC LoRa stack (arduino loop) looptask 1 1 arduino loop (unused)
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.
@ -48,7 +48,6 @@ So don't do it if you do not own a digital oscilloscope.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
0 displayIRQ -> display refresh -> 40ms (DISPLAYREFRESH_MS) 0 displayIRQ -> display refresh -> 40ms (DISPLAYREFRESH_MS)
1 ppsIRQ -> pps clock irq -> 1sec 1 ppsIRQ -> pps clock irq -> 1sec
2 gpsIRQ -> gps store data -> 300ms
3 MatrixDisplayIRQ -> matrix mux cycle -> 0,5ms (MATRIX_DISPLAY_SCAN_US) 3 MatrixDisplayIRQ -> matrix mux cycle -> 0,5ms (MATRIX_DISPLAY_SCAN_US)
@ -58,13 +57,13 @@ So don't do it if you do not own a digital oscilloscope.
fired by hardware 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)
GpsIRQ -> esp32 timer 2 -> irqHandlerTask (Core 1)
ButtonIRQ -> external gpio -> irqHandlerTask (Core 1) ButtonIRQ -> external 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)
CYLCIC_IRQ -> housekeeping() -> irqHandlerTask (Core 1) CYLCIC_IRQ -> housekeeping() -> irqHandlerTask (Core 1)
SENDCYCLE_IRQ -> sendcycle() -> irqHandlerTask (Core 1) SENDCYCLE_IRQ -> sendcycle() -> irqHandlerTask (Core 1)
BME_IRQ -> bmecycle() -> irqHandlerTask (Core 1)
// External RTC timer (if present) // External RTC timer (if present)
@ -82,13 +81,11 @@ 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
hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL, *matrixDisplayIRQ = NULL, hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL, *matrixDisplayIRQ = NULL;
*gpsIRQ = NULL;
TaskHandle_t irqHandlerTask = NULL, ClockTask = NULL; TaskHandle_t irqHandlerTask = NULL, ClockTask = NULL;
SemaphoreHandle_t I2Caccess; SemaphoreHandle_t I2Caccess;
bool volatile TimePulseTick = false; bool volatile TimePulseTick = false;
time_t volatile gps_pps_time = 0;
time_t userUTCTime = 0; time_t userUTCTime = 0;
timesource_t timeSource = _unsynced; timesource_t timeSource = _unsynced;
@ -198,17 +195,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
@ -272,7 +277,7 @@ void setup() {
"gpsloop", // name of task "gpsloop", // name of task
2048, // stack size of task 2048, // stack size of task
(void *)1, // parameter of the task (void *)1, // parameter of the task
2, // priority of the task 1, // priority of the task
&GpsTask, // task handle &GpsTask, // task handle
1); // CPU core 1); // CPU core
} }
@ -372,16 +377,8 @@ void setup() {
#elif defined HAS_BME280 #elif defined HAS_BME280
strcat_P(features, " BME280"); strcat_P(features, " BME280");
#endif #endif
if (bme_init()) { if (bme_init())
ESP_LOGI(TAG, "Starting BME sensor..."); ESP_LOGI(TAG, "Starting BME sensor...");
xTaskCreatePinnedToCore(bme_loop, // task function
"bmeloop", // name of task
2048, // stack size of task
(void *)1, // parameter of the task
2, // priority of the task
&BmeTask, // task handle
1); // CPU core
}
#endif #endif
// starting timers and interrupts // starting timers and interrupts
@ -419,14 +416,6 @@ void setup() {
button_init(HAS_BUTTON); button_init(HAS_BUTTON);
#endif // HAS_BUTTON #endif // HAS_BUTTON
// gps buffer read interrupt
#if (HAS_GPS)
gpsIRQ = timerBegin(2, 80, true);
timerAttachInterrupt(gpsIRQ, &GpsIRQ, true);
timerAlarmWrite(gpsIRQ, 300 * 1000, true);
timerAlarmEnable(gpsIRQ);
#endif
// cyclic function interrupts // cyclic function interrupts
sendcycler.attach(SENDCYCLE * 2, sendcycle); sendcycler.attach(SENDCYCLE * 2, sendcycle);
housekeeper.attach(HOMECYCLE, housekeeping); housekeeper.attach(HOMECYCLE, housekeeping);
@ -438,9 +427,9 @@ 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)
gps_storetime(&gps_status); fetch_gpsTime();
#endif #endif
#if (defined HAS_IF482 || defined HAS_DCF77) #if (defined HAS_IF482 || defined HAS_DCF77)
@ -461,15 +450,8 @@ void setup() {
// show compiled features // show compiled features
ESP_LOGI(TAG, "Features:%s", features); ESP_LOGI(TAG, "Features:%s", features);
vTaskDelete(NULL);
} // setup() } // setup()
void loop() { void loop() { vTaskDelete(NULL); }
while (1) {
#if (HAS_LORA)
os_runloop_once(); // execute lmic scheduled jobs and events
#else
delay(2); // yield to CPU
#endif
}
}

View File

@ -48,7 +48,7 @@
#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 LORASFDEFAULT 9 // 7 ... 12 SF, according to LoRaWAN specs
#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]
// Hardware settings // Hardware settings
#define RGBLUMINOSITY 30 // RGB LED luminosity [default = 30%] #define RGBLUMINOSITY 30 // RGB LED luminosity [default = 30%]
@ -58,6 +58,7 @@
// Settings for BME680 environmental sensor // Settings for BME680 environmental sensor
#define BME_TEMP_OFFSET 5.0f // Offset sensor on chip temp <-> ambient temp [default = 5°C] #define BME_TEMP_OFFSET 5.0f // Offset sensor on chip temp <-> ambient temp [default = 5°C]
#define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // update every 360 minutes = 4 times a day #define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // update every 360 minutes = 4 times a day
#define BMECYCLE 1 // bme sensor read cycle in seconds [default = 1 secs]
// OTA settings // OTA settings
#define USE_OTA 1 // set to 0 to disable OTA update #define USE_OTA 1 // set to 0 to disable OTA update

View File

@ -8,7 +8,7 @@ static const char TAG[] = __FILE__;
// helper function // helper function
void do_reset() { void do_reset() {
ESP_LOGI(TAG, "Remote command: restart device"); ESP_LOGI(TAG, "Remote command: restart device");
#if(HAS_LORA) #if (HAS_LORA)
LMIC_shutdown(); LMIC_shutdown();
#endif #endif
delay(3000); delay(3000);
@ -132,7 +132,7 @@ void set_gps(uint8_t val[]) {
} }
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
case 1: case 1:
case 2: case 2:
@ -170,7 +170,7 @@ void set_monitor(uint8_t val[]) {
} }
void set_lorasf(uint8_t val[]) { void set_lorasf(uint8_t val[]) {
#if(HAS_LORA) #if (HAS_LORA)
ESP_LOGI(TAG, "Remote command: set LoRa SF to %d", val[0]); ESP_LOGI(TAG, "Remote command: set LoRa SF to %d", val[0]);
switch_lora(val[0], cfg.txpower); switch_lora(val[0], cfg.txpower);
#else #else
@ -179,7 +179,7 @@ void set_lorasf(uint8_t val[]) {
} }
void set_loraadr(uint8_t val[]) { void set_loraadr(uint8_t val[]) {
#if(HAS_LORA) #if (HAS_LORA)
ESP_LOGI(TAG, "Remote command: set LoRa ADR mode to %s", ESP_LOGI(TAG, "Remote command: set LoRa ADR mode to %s",
val[0] ? "on" : "off"); val[0] ? "on" : "off");
cfg.adrmode = val[0] ? 1 : 0; cfg.adrmode = val[0] ? 1 : 0;
@ -222,7 +222,7 @@ 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]); ESP_LOGI(TAG, "Remote command: set LoRa TXPOWER to %d", val[0]);
switch_lora(cfg.lorasf, val[0]); switch_lora(cfg.lorasf, val[0]);
#else #else
@ -252,7 +252,9 @@ void get_status(uint8_t val[]) {
void get_gps(uint8_t val[]) { void get_gps(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get gps status"); ESP_LOGI(TAG, "Remote command: get gps status");
#if(HAS_GPS) #if (HAS_GPS)
gpsStatus_t gps_status;
gps_storelocation(&gps_status);
payload.reset(); payload.reset();
payload.addGPS(gps_status); payload.addGPS(gps_status);
SendPayload(GPSPORT, prio_high); SendPayload(GPSPORT, prio_high);

View File

@ -46,21 +46,20 @@ uint8_t rtc_init(void) {
} // rtc_init() } // rtc_init()
uint8_t set_rtctime(time_t t, mutexselect_t mutex) { // t is sec epoch time uint8_t set_rtctime(time_t t) { // t is sec epoch time
if (!mutex || I2C_MUTEX_LOCK()) { if (I2C_MUTEX_LOCK()) {
#ifdef RTC_INT // sync rtc 1Hz pulse on top of second #ifdef RTC_INT // sync rtc 1Hz pulse on top of second
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); // off Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); // off
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); // start Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); // start
#endif #endif
Rtc.SetDateTime(RtcDateTime(t - SECS_YR_2000)); // epoch -> sec2000 Rtc.SetDateTime(RtcDateTime(t - SECS_YR_2000)); // epoch -> sec2000
if (mutex) I2C_MUTEX_UNLOCK();
I2C_MUTEX_UNLOCK();
ESP_LOGI(TAG, "RTC time synced"); ESP_LOGI(TAG, "RTC time synced");
return 1; // success return 1; // success
} else { } else {
ESP_LOGE(TAG, "RTC set time failure"); ESP_LOGE(TAG, "RTC set time failure");
return 0; return 0; // failure
} // failure }
} // set_rtctime() } // set_rtctime()
time_t get_rtctime(void) { time_t get_rtctime(void) {
@ -71,8 +70,11 @@ time_t get_rtctime(void) {
t = tt.Epoch32Time(); // sec2000 -> epoch t = tt.Epoch32Time(); // sec2000 -> epoch
} }
I2C_MUTEX_UNLOCK(); I2C_MUTEX_UNLOCK();
return timeIsValid(t);
} else {
ESP_LOGE(TAG, "RTC get time failure");
return 0; // failure
} }
return timeIsValid(t);
} // get_rtctime() } // get_rtctime()
float get_rtctemp(void) { float get_rtctemp(void) {

View File

@ -87,6 +87,8 @@ void sendCounter() {
case GPS_DATA: case GPS_DATA:
// send GPS position only if we have a fix // send GPS position only if we have a fix
if (gps.location.isValid()) { if (gps.location.isValid()) {
gpsStatus_t gps_status;
gps_storelocation(&gps_status);
payload.reset(); payload.reset();
payload.addGPS(gps_status); payload.addGPS(gps_status);
SendPayload(GPSPORT, prio_high); SendPayload(GPSPORT, prio_high);

View File

@ -170,8 +170,4 @@ void spi_enqueuedata(MessageBuffer_t *message, sendprio_t prio) {
void spi_queuereset(void) { xQueueReset(SPISendQueue); } void spi_queuereset(void) { xQueueReset(SPISendQueue); }
void spi_housekeeping(void) {
ESP_LOGD(TAG, "spiloop %d bytes left", uxTaskGetStackHighWaterMark(spiTask));
}
#endif // HAS_SPI #endif // HAS_SPI

View File

@ -1,4 +1,5 @@
#include "timekeeper.h" #include "timekeeper.h"
#include "paxcounter.conf"
#if !(HAS_LORA) #if !(HAS_LORA)
#if (TIME_SYNC_LORASERVER) #if (TIME_SYNC_LORASERVER)
@ -22,31 +23,17 @@ Ticker timesyncer;
void timeSync() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); } void timeSync() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); }
time_t timeProvider(void) { void calibrateTime(void) {
time_t t = 0; time_t t = 0;
uint16_t t_msec = 0;
#if (HAS_GPS) #if (HAS_GPS)
// fetch recent time from last NMEA record // fetch recent time from last NMEA record
t = fetch_gpsTime(gps_status); t = fetch_gpsTime(&t_msec);
if (t) { if (t) {
#ifdef HAS_RTC
set_rtctime(t, do_mutex); // calibrate RTC
#endif
timeSource = _gps; timeSource = _gps;
timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); // regular repeat goto finish;
ESP_LOGD(TAG, "GPS time = %d", t);
return t;
}
#endif
// no time from GPS -> fallback to RTC time while trying lora sync
#ifdef HAS_RTC
t = get_rtctime();
if (t) {
timeSource = _rtc;
timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, timeSync); // short retry
ESP_LOGD(TAG, "RTC time = %d", t);
} }
#endif #endif
@ -58,14 +45,68 @@ time_t timeProvider(void) {
LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime);
#endif #endif
if (!t) { // no time from GPS -> fallback to RTC time while trying lora sync
timeSource = _unsynced; #ifdef HAS_RTC
timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, timeSync); // short retry t = get_rtctime();
if (t) {
timeSource = _rtc;
goto finish;
} }
#endif
return t; goto finish;
} // timeProvider() finish:
setMyTime((uint32_t)t, t_msec, timeSource); // set time
} // 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) {
// 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() {
@ -131,11 +172,6 @@ void IRAM_ATTR CLOCKIRQ(void) {
SyncToPPS(); // advance systime, see microTime.h SyncToPPS(); // advance systime, see microTime.h
// store recent gps time, and try to get gps time if time is not synced
#if (HAS_GPS)
gps_storetime(&gps_status);
#endif
// advance wall clock, if we have // advance wall clock, if we have
#if (defined HAS_IF482 || defined HAS_DCF77) #if (defined HAS_IF482 || defined HAS_DCF77)
xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits, xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits,
@ -209,11 +245,13 @@ void clock_init(void) {
void clock_loop(void *taskparameter) { // ClockTask void clock_loop(void *taskparameter) { // ClockTask
// caveat: don't use now() in this task, it will cause a race condition // caveat: don't use now() in this task, it will cause a race condition
// due to concurrent access to i2c bus for setting rtc! // due to concurrent access to i2c bus when reading/writing from/to rtc chip!
#define nextmin(t) (t + DCF77_FRAME_SIZE + 1) // next minute #define nextmin(t) (t + DCF77_FRAME_SIZE + 1) // next minute
#ifdef HAS_TWO_LED
static bool led1_state = false; static bool led1_state = false;
#endif
uint32_t printtime; uint32_t printtime;
time_t t = *((time_t *)taskparameter), last_printtime = 0; // UTC time seconds time_t t = *((time_t *)taskparameter), last_printtime = 0; // UTC time seconds

View File

@ -128,13 +128,12 @@ void process_timesync_req(void *taskparameter) {
// calculate fraction milliseconds // calculate fraction milliseconds
time_to_set_fraction_msec = (uint16_t)(time_offset_ms.count() % 1000); time_to_set_fraction_msec = (uint16_t)(time_offset_ms.count() % 1000);
setMyTime(time_to_set, time_to_set_fraction_msec); setMyTime(time_to_set, time_to_set_fraction_msec, _lora);
finish: finish:
// end of time critical section: release app irq lock // end of time critical section: release app irq lock
unmask_user_IRQ();
timeSyncPending = false; timeSyncPending = false;
unmask_user_IRQ();
} // infinite while(1) } // infinite while(1)
} }
@ -208,43 +207,8 @@ int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len) {
} }
} }
// adjust system time, calibrate RTC and RTC_INT pps // create task for timeserver handshake processing, called from main.cpp
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec) {
time_t time_to_set = (time_t)(t_sec + 1);
ESP_LOGD(TAG, "[%0.3f] Calculated UTC epoch time: %d.%03d sec",
millis() / 1000.0, time_to_set, t_msec);
if (timeIsValid(time_to_set)) {
// wait until top of second with millisecond precision
vTaskDelay(pdMS_TO_TICKS(1000 - t_msec));
// set RTC time and calibrate RTC_INT pulse on top of second
#ifdef HAS_RTC
set_rtctime(time_to_set, no_mutex);
#endif
// sync pps timer 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 = _lora;
timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); // regular repeat
ESP_LOGI(TAG, "[%0.3f] Timesync finished, time was adjusted",
millis() / 1000.0);
} else
ESP_LOGW(TAG, "[%0.3f] Timesync failed, outdated time calculated",
millis() / 1000.0);
}
void timesync_init() { void timesync_init() {
// create task for timeserver handshake processing, called from main.cpp
xTaskCreatePinnedToCore(process_timesync_req, // task function xTaskCreatePinnedToCore(process_timesync_req, // task function
"timesync_req", // name of task "timesync_req", // name of task
2048, // stack size of task 2048, // stack size of task