2019-03-13 22:08:05 +01:00
|
|
|
#ifdef HAS_DISPLAY
|
2018-07-15 14:28:05 +02:00
|
|
|
|
2019-01-27 21:02:08 +01:00
|
|
|
/*
|
|
|
|
|
|
|
|
Display-Mask (128 x 64 pixel):
|
|
|
|
|
2019-09-27 17:37:16 +02:00
|
|
|
| | |
|
|
|
|
| 11111111112
|
|
|
|
|012345678901234567890 Font
|
|
|
|
----------------------- ---------
|
|
|
|
0|PAX:aabbccdd STRETCHED
|
|
|
|
1|PAX:aabbccdd STRETCHED
|
|
|
|
2|
|
|
|
|
3|B:a.bcV Sats:ab ch:ab SMALL
|
|
|
|
4|WIFI:abcde BLTH:abcde SMALL
|
|
|
|
5|RLIM:abcd Mem:abcdKB SMALL
|
|
|
|
6|27.Feb 2019 20:27:00* SMALL
|
2019-10-12 14:07:55 +02:00
|
|
|
7|yyyyyyyyyyyyy xx SFab SMALL
|
2019-09-27 17:37:16 +02:00
|
|
|
|
|
|
|
* = char {L|G|R|?} indicates time source,
|
|
|
|
inverse = clock controller is active,
|
|
|
|
pulsed = pps input signal is active
|
|
|
|
|
2019-10-12 14:07:55 +02:00
|
|
|
y = LMIC event message
|
|
|
|
xx = payload sendqueue length
|
|
|
|
ab = LMIC spread factor
|
2019-09-27 17:37:16 +02:00
|
|
|
|
2020-03-29 19:41:33 +02:00
|
|
|
MY_FONT_SMALL: 6x8px = 21 chars / line
|
|
|
|
MY_FONT_NORMAL: 8x8px = 16 chars / line
|
|
|
|
MY_FONT_STRETCHED: 16x32px = 8 chars / line
|
2019-01-27 21:02:08 +01:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
2018-07-15 14:28:05 +02:00
|
|
|
// Basic Config
|
|
|
|
#include <esp_spi_flash.h> // needed for reading ESP32 chip attributes
|
2020-03-29 19:41:33 +02:00
|
|
|
#include "globals.h"
|
|
|
|
#include "display.h"
|
2018-07-15 14:28:05 +02:00
|
|
|
|
2019-09-29 16:46:48 +02:00
|
|
|
// local Tag for logging
|
|
|
|
static const char TAG[] = __FILE__;
|
|
|
|
|
2019-10-01 13:02:30 +02:00
|
|
|
// helper array for converting month values to text
|
2019-02-04 20:40:46 +01:00
|
|
|
const char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
2019-04-01 18:01:52 +02:00
|
|
|
uint8_t DisplayIsOn = 0;
|
2020-03-29 12:10:42 +02:00
|
|
|
uint8_t displaybuf[MY_DISPLAY_WIDTH * MY_DISPLAY_HEIGHT / 8] = {0};
|
|
|
|
static uint8_t plotbuf[MY_DISPLAY_WIDTH * MY_DISPLAY_HEIGHT / 8] = {0};
|
2018-07-19 21:53:56 +02:00
|
|
|
|
2019-09-29 16:46:48 +02:00
|
|
|
QRCode qrcode;
|
2020-03-29 12:10:42 +02:00
|
|
|
|
|
|
|
#if (HAS_DISPLAY) == 1
|
2020-03-11 23:47:16 +01:00
|
|
|
SSOLED ssoled;
|
2020-03-29 19:41:33 +02:00
|
|
|
#elif (HAS_DISPLAY) == 2
|
|
|
|
TFT_eSPI tft = TFT_eSPI(MY_DISPLAY_WIDTH, MY_DISPLAY_HEIGHT); // Invoke library
|
2020-03-29 12:10:42 +02:00
|
|
|
#endif
|
2020-03-11 23:47:16 +01:00
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
void dp_setup(int contrast) {
|
2020-03-11 23:47:16 +01:00
|
|
|
|
2020-03-29 19:41:33 +02:00
|
|
|
#if (HAS_DISPLAY) == 1 // I2C OLED
|
|
|
|
int rc = oledInit(&ssoled, OLED_TYPE, OLED_ADDR, MY_DISPLAY_FLIP,
|
|
|
|
MY_DISPLAY_INVERT, USE_HW_I2C, MY_DISPLAY_SDA,
|
|
|
|
MY_DISPLAY_SCL, MY_DISPLAY_RST,
|
|
|
|
OLED_FREQUENCY); // use standard I2C bus at 400Khz
|
2020-03-11 23:47:16 +01:00
|
|
|
assert(rc != OLED_NOT_FOUND);
|
|
|
|
|
|
|
|
// set display buffer
|
|
|
|
oledSetBackBuffer(&ssoled, displaybuf);
|
2020-03-29 12:10:42 +02:00
|
|
|
|
2020-03-29 19:41:33 +02:00
|
|
|
#elif (HAS_DISPLAY) == 2 // SPI TFT
|
2020-03-29 12:10:42 +02:00
|
|
|
|
2020-03-29 19:41:33 +02:00
|
|
|
tft.init();
|
2020-03-29 12:10:42 +02:00
|
|
|
|
2020-03-29 19:41:33 +02:00
|
|
|
if (MY_DISPLAY_FLIP)
|
|
|
|
tft.setRotation(0);
|
|
|
|
else
|
|
|
|
tft.setRotation(2); // portrait
|
2020-03-29 12:10:42 +02:00
|
|
|
|
2020-03-29 19:41:33 +02:00
|
|
|
if (MY_DISPLAY_INVERT)
|
|
|
|
tft.invertDisplay(true); // Tell TFT to invert all displayed colours
|
2020-03-29 12:10:42 +02:00
|
|
|
|
|
|
|
#endif
|
2020-03-11 23:47:16 +01:00
|
|
|
|
|
|
|
// clear display
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_clear();
|
|
|
|
if (contrast)
|
|
|
|
dp_contrast(contrast);
|
2020-03-11 23:47:16 +01:00
|
|
|
}
|
2019-09-29 16:46:48 +02:00
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
void dp_init(bool verbose) {
|
2018-10-03 00:25:05 +02:00
|
|
|
|
2020-03-29 19:41:33 +02:00
|
|
|
#if (HAS_DISPLAY) == 1 // i2c
|
2019-02-02 10:35:20 +01:00
|
|
|
// block i2c bus access
|
2019-07-23 17:53:20 +02:00
|
|
|
if (!I2C_MUTEX_LOCK())
|
2019-07-27 11:59:24 +02:00
|
|
|
ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0);
|
2019-07-23 17:53:20 +02:00
|
|
|
else {
|
2020-03-29 19:41:33 +02:00
|
|
|
#endif
|
2019-09-27 12:47:00 +02:00
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_setup(DISPLAYCONTRAST);
|
2019-09-27 12:47:00 +02:00
|
|
|
|
2019-10-01 13:02:30 +02:00
|
|
|
if (verbose) {
|
|
|
|
|
|
|
|
// show startup screen
|
|
|
|
// to come -> display .bmp file with logo
|
2019-09-27 12:47:00 +02:00
|
|
|
|
|
|
|
// show chip information
|
2019-03-24 01:05:13 +01:00
|
|
|
#if (VERBOSE)
|
2019-10-01 13:02:30 +02:00
|
|
|
esp_chip_info_t chip_info;
|
|
|
|
esp_chip_info(&chip_info);
|
|
|
|
dp_printf(0, 0, 0, 0, "** PAXCOUNTER **");
|
|
|
|
dp_printf(0, 1, 0, 0, "Software v%s", PROGVERSION);
|
|
|
|
dp_printf(0, 3, 0, 0, "ESP32 %d cores", chip_info.cores);
|
|
|
|
dp_printf(0, 4, 0, 0, "Chip Rev.%d", chip_info.revision);
|
|
|
|
dp_printf(0, 5, 0, 0, "WiFi%s%s",
|
|
|
|
(chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
|
|
|
|
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
|
|
|
|
dp_printf(0, 6, 0, 0, "%dMB %s Flash",
|
|
|
|
spi_flash_get_chip_size() / (1024 * 1024),
|
|
|
|
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "int."
|
|
|
|
: "ext.");
|
|
|
|
|
|
|
|
// give user some time to read or take picture
|
2020-03-11 23:47:16 +01:00
|
|
|
dp_dump(displaybuf);
|
2019-10-01 13:02:30 +02:00
|
|
|
delay(2000);
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_clear();
|
2018-07-15 14:28:05 +02:00
|
|
|
#endif // VERBOSE
|
|
|
|
|
2019-03-24 01:05:13 +01:00
|
|
|
#if (HAS_LORA)
|
2019-10-01 13:02:30 +02:00
|
|
|
// generate DEVEUI as QR code and text
|
2020-03-15 13:04:03 +01:00
|
|
|
uint8_t buf[8], *p = buf;
|
2019-10-01 13:02:30 +02:00
|
|
|
char deveui[17];
|
|
|
|
os_getDevEui((u1_t *)buf);
|
2020-03-15 13:04:03 +01:00
|
|
|
snprintf(deveui, 17, "%016llX", (*(uint64_t *)(p)));
|
2019-10-01 13:02:30 +02:00
|
|
|
|
|
|
|
// display DEVEUI as QR code on the left
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_contrast(30);
|
2019-10-01 13:02:30 +02:00
|
|
|
dp_printqr(3, 3, deveui);
|
|
|
|
|
|
|
|
// display DEVEUI as plain text on the right
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(72, 0, MY_FONT_NORMAL, 0, "LORAWAN");
|
|
|
|
dp_printf(72, 1, MY_FONT_NORMAL, 0, "DEVEUI:");
|
2019-10-06 13:14:23 +02:00
|
|
|
for (uint8_t i = 0; i <= 3; i++)
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(80, i + 3, MY_FONT_NORMAL, 0, "%4.4s", deveui + i * 4);
|
2019-10-01 13:02:30 +02:00
|
|
|
|
|
|
|
// give user some time to read or take picture
|
2020-03-11 23:47:16 +01:00
|
|
|
dp_dump(displaybuf);
|
2019-10-01 13:02:30 +02:00
|
|
|
delay(8000);
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_contrast(DISPLAYCONTRAST);
|
|
|
|
dp_clear();
|
2019-09-29 17:38:16 +02:00
|
|
|
#endif // HAS_LORA
|
|
|
|
|
2019-10-01 13:02:30 +02:00
|
|
|
} // verbose
|
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_power(cfg.screenon); // set display off if disabled
|
2019-02-02 10:35:20 +01:00
|
|
|
|
2020-03-29 19:41:33 +02:00
|
|
|
#if (HAS_DISPLAY) == 1 // i2c
|
2019-02-02 10:35:20 +01:00
|
|
|
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
2019-02-22 22:28:35 +01:00
|
|
|
} // mutex
|
2020-03-29 19:41:33 +02:00
|
|
|
#endif
|
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
} // dp_init
|
2018-07-15 14:28:05 +02:00
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
void dp_refresh(bool nextPage) {
|
2018-07-15 14:28:05 +02:00
|
|
|
|
2020-01-20 11:45:11 +01:00
|
|
|
#ifndef HAS_BUTTON
|
2020-01-20 11:41:31 +01:00
|
|
|
static uint32_t framecounter = 0;
|
2020-01-20 11:45:11 +01:00
|
|
|
#endif
|
2019-02-18 21:51:01 +01:00
|
|
|
|
2020-01-19 22:21:45 +01:00
|
|
|
// update histogram
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_plotCurve(macs.size(), false);
|
2019-10-04 18:17:46 +02:00
|
|
|
|
2019-04-01 18:01:52 +02:00
|
|
|
// if display is switched off we don't refresh it to relax cpu
|
|
|
|
if (!DisplayIsOn && (DisplayIsOn == cfg.screenon))
|
|
|
|
return;
|
|
|
|
|
2019-05-31 13:20:11 +02:00
|
|
|
const time_t t =
|
|
|
|
myTZ.toLocal(now()); // note: call now() here *before* locking mutex!
|
|
|
|
|
2018-12-27 17:09:40 +01:00
|
|
|
// block i2c bus access
|
2019-07-23 17:53:20 +02:00
|
|
|
if (!I2C_MUTEX_LOCK())
|
2019-07-27 11:59:24 +02:00
|
|
|
ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0);
|
2019-07-23 17:53:20 +02:00
|
|
|
else {
|
2018-12-27 17:09:40 +01:00
|
|
|
// set display on/off according to current device configuration
|
2019-04-01 18:01:52 +02:00
|
|
|
if (DisplayIsOn != cfg.screenon) {
|
|
|
|
DisplayIsOn = cfg.screenon;
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_power(cfg.screenon);
|
2018-12-27 17:09:40 +01:00
|
|
|
}
|
|
|
|
|
2020-01-20 11:41:31 +01:00
|
|
|
#ifndef HAS_BUTTON
|
|
|
|
// auto flip page if we are in unattended mode
|
|
|
|
if ((++framecounter) > (DISPLAYCYCLE * 1000 / DISPLAYREFRESH_MS)) {
|
|
|
|
framecounter = 0;
|
|
|
|
nextPage = true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_drawPage(t, nextPage);
|
2020-03-11 23:47:16 +01:00
|
|
|
dp_dump(displaybuf);
|
2019-04-01 18:01:52 +02:00
|
|
|
|
|
|
|
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
|
|
|
|
|
|
|
} // mutex
|
|
|
|
} // refreshDisplay()
|
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
void dp_drawPage(time_t t, bool nextpage) {
|
2019-04-01 18:01:52 +02:00
|
|
|
|
2020-01-20 11:12:00 +01:00
|
|
|
// write display content to display buffer
|
|
|
|
// nextpage = true -> flip 1 page
|
|
|
|
|
2020-01-19 22:21:45 +01:00
|
|
|
static uint8_t DisplayPage = 0;
|
2019-09-30 12:36:13 +02:00
|
|
|
char timeState;
|
2019-07-22 22:00:39 +02:00
|
|
|
#if (HAS_GPS)
|
2019-04-01 18:34:57 +02:00
|
|
|
static bool wasnofix = true;
|
2019-07-22 22:00:39 +02:00
|
|
|
#endif
|
2019-04-01 18:01:52 +02:00
|
|
|
|
2020-01-20 11:12:00 +01:00
|
|
|
// line 1/2: pax counter
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 0, MY_FONT_STRETCHED, 0, "PAX:%-4d",
|
2020-01-20 11:12:00 +01:00
|
|
|
macs.size()); // display number of unique macs total Wifi + BLE
|
|
|
|
|
|
|
|
start:
|
|
|
|
|
2020-01-19 22:21:45 +01:00
|
|
|
if (nextpage) {
|
|
|
|
DisplayPage = (DisplayPage >= DISPLAY_PAGES - 1) ? 0 : (DisplayPage + 1);
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_clear();
|
2020-01-19 22:21:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (DisplayPage) {
|
2019-04-01 18:01:52 +02:00
|
|
|
|
|
|
|
// page 0: parameters overview
|
2019-10-01 13:02:30 +02:00
|
|
|
// page 1: pax graph
|
2019-04-01 18:01:52 +02:00
|
|
|
// page 2: GPS
|
|
|
|
// page 3: BME280/680
|
2019-10-01 13:02:30 +02:00
|
|
|
// page 4: time
|
2020-02-26 00:40:09 +01:00
|
|
|
// page 5: lorawan parameters
|
|
|
|
// page 6: blank screen
|
2019-04-01 18:01:52 +02:00
|
|
|
|
2019-10-01 13:02:30 +02:00
|
|
|
// page 0: parameters overview
|
2019-04-01 18:01:52 +02:00
|
|
|
case 0:
|
2018-07-15 14:28:05 +02:00
|
|
|
|
2019-09-27 17:37:16 +02:00
|
|
|
// line 3: wifi + bluetooth counters
|
2019-11-11 15:23:41 +01:00
|
|
|
#if ((WIFICOUNTER) && (BLECOUNTER))
|
|
|
|
if (cfg.wifiscan)
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 3, MY_FONT_SMALL, 0, "WIFI:%-5d", macs_wifi);
|
2019-11-11 15:23:41 +01:00
|
|
|
else
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 3, MY_FONT_SMALL, 0, "%s", "WIFI:off");
|
2019-09-27 17:37:16 +02:00
|
|
|
if (cfg.blescan)
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(66, 3, MY_FONT_SMALL, 0, "BLTH:%-5d", macs_ble);
|
2019-09-27 17:37:16 +02:00
|
|
|
else
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(66, 3, MY_FONT_SMALL, 0, "%s", "BLTH:off");
|
2019-11-11 15:23:41 +01:00
|
|
|
#elif ((WIFICOUNTER) && (!BLECOUNTER))
|
|
|
|
if (cfg.wifiscan)
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 3, MY_FONT_SMALL, 0, "WIFI:%-5d", macs_wifi);
|
2019-11-11 15:23:41 +01:00
|
|
|
else
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 3, MY_FONT_SMALL, 0, "%s", "WIFI:off");
|
2019-11-11 15:23:41 +01:00
|
|
|
#elif ((!WIFICOUNTER) && (BLECOUNTER))
|
|
|
|
if (cfg.blescan)
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 3, MY_FONT_SMALL, 0, "BLTH:%-5d", macs_ble);
|
2019-11-11 15:23:41 +01:00
|
|
|
else
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 3, MY_FONT_SMALL, 0, "%s", "BLTH:off");
|
2019-11-11 15:23:41 +01:00
|
|
|
#else
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 3, MY_FONT_SMALL, 0, "%s", "Sniffer disabled");
|
2019-09-27 17:37:16 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// line 4: Battery + GPS status + Wifi channel
|
2019-09-07 23:10:53 +02:00
|
|
|
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
2019-09-23 15:45:47 +02:00
|
|
|
if (batt_voltage == 0xffff)
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 4, MY_FONT_SMALL, 0, "%s", "USB ");
|
2019-10-01 13:02:30 +02:00
|
|
|
else if (batt_voltage == 0)
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 4, MY_FONT_SMALL, 0, "%s", "No batt");
|
2019-09-23 15:45:47 +02:00
|
|
|
else
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 4, MY_FONT_SMALL, 0, "B:%.2fV", batt_voltage / 1000.0);
|
2018-07-21 21:50:39 +02:00
|
|
|
#endif
|
2019-03-24 01:05:13 +01:00
|
|
|
#if (HAS_GPS)
|
2020-01-03 09:59:10 +01:00
|
|
|
if (gps_hasfix())
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(48, 4, MY_FONT_SMALL, 0, "Sats:%.2d", gps.satellites.value());
|
2020-01-03 09:59:10 +01:00
|
|
|
else // if no fix then display Sats value inverse
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(48, 4, MY_FONT_SMALL, 1, "Sats:%.2d", gps.satellites.value());
|
2018-07-15 14:28:05 +02:00
|
|
|
#endif
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(96, 4, MY_FONT_SMALL, 0, "ch:%02d", channel);
|
2018-07-15 14:28:05 +02:00
|
|
|
|
2019-09-27 17:37:16 +02:00
|
|
|
// line 5: RSSI limiter + free memory
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 5, MY_FONT_SMALL, 0,
|
|
|
|
!cfg.rssilimit ? "RLIM:off " : "RLIM:%-4d", cfg.rssilimit);
|
|
|
|
dp_printf(66, 5, MY_FONT_SMALL, 0, "Mem:%4dKB", getFreeRAM() / 1024);
|
2018-07-15 14:28:05 +02:00
|
|
|
|
2019-09-27 17:37:16 +02:00
|
|
|
// line 6: time + date
|
2019-03-24 19:49:44 +01:00
|
|
|
#if (TIME_SYNC_INTERVAL)
|
2019-02-25 00:26:46 +01:00
|
|
|
timeState = TimePulseTick ? ' ' : timeSetSymbols[timeSource];
|
2019-02-18 21:51:01 +01:00
|
|
|
TimePulseTick = false;
|
2019-09-27 17:37:16 +02:00
|
|
|
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 6, MY_FONT_SMALL, 0, "%02d.%3s %4d", day(t),
|
|
|
|
printmonth[month(t)], year(t));
|
|
|
|
dp_printf(72, 6, MY_FONT_SMALL, 0, "%02d:%02d:%02d", hour(t), minute(t),
|
2019-09-27 17:37:16 +02:00
|
|
|
second(t));
|
|
|
|
|
2019-03-24 19:49:44 +01:00
|
|
|
// display inverse timeState if clock controller is enabled
|
|
|
|
#if (defined HAS_DCF77) || (defined HAS_IF482)
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(120, 6, MY_FONT_SMALL, 1, "%c", timeState);
|
2019-03-24 19:49:44 +01:00
|
|
|
#else
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(120, 6, MY_FONT_SMALL, 0, "%c", timeState);
|
2019-09-27 17:37:16 +02:00
|
|
|
#endif
|
2018-12-27 17:09:40 +01:00
|
|
|
|
2019-03-24 19:49:44 +01:00
|
|
|
#endif // TIME_SYNC_INTERVAL
|
|
|
|
|
2019-09-27 17:37:16 +02:00
|
|
|
// line 7: LORA network status
|
2019-03-24 19:49:44 +01:00
|
|
|
#if (HAS_LORA)
|
2019-10-12 14:07:55 +02:00
|
|
|
// LMiC event display
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 7, MY_FONT_SMALL, 0, "%-16s", lmic_event_msg);
|
2019-09-27 17:37:16 +02:00
|
|
|
// LORA datarate, display inverse if ADR disabled
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(102, 7, MY_FONT_SMALL, !cfg.adrmode, "%-4s",
|
2019-10-12 14:07:55 +02:00
|
|
|
getSfName(updr2rps(LMIC.datarate)));
|
2020-01-20 11:12:00 +01:00
|
|
|
#endif // HAS_LORA
|
2019-04-01 18:01:52 +02:00
|
|
|
break; // page0
|
|
|
|
|
2019-10-01 13:02:30 +02:00
|
|
|
// page 1: pax graph
|
2019-04-01 18:01:52 +02:00
|
|
|
case 1:
|
2020-03-11 23:47:16 +01:00
|
|
|
dp_dump(plotbuf);
|
2019-04-01 18:01:52 +02:00
|
|
|
break; // page1
|
|
|
|
|
2019-10-01 13:02:30 +02:00
|
|
|
// page 2: GPS
|
2019-04-01 18:01:52 +02:00
|
|
|
case 2:
|
|
|
|
#if (HAS_GPS)
|
2020-01-03 09:59:10 +01:00
|
|
|
if (gps_hasfix()) {
|
2019-04-01 18:34:57 +02:00
|
|
|
// line 5: clear "No fix"
|
|
|
|
if (wasnofix) {
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(16, 5, MY_FONT_STRETCHED, 0, " ");
|
2019-04-01 18:34:57 +02:00
|
|
|
wasnofix = false;
|
|
|
|
}
|
2019-04-01 18:01:52 +02:00
|
|
|
// line 3-4: GPS latitude
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 3, MY_FONT_STRETCHED, 0, "%c%07.4f",
|
2019-09-27 12:47:00 +02:00
|
|
|
gps.location.rawLat().negative ? 'S' : 'N', gps.location.lat());
|
2019-04-01 18:01:52 +02:00
|
|
|
|
|
|
|
// line 6-7: GPS longitude
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 6, MY_FONT_STRETCHED, 0, "%c%07.4f",
|
2019-09-27 12:47:00 +02:00
|
|
|
gps.location.rawLat().negative ? 'W' : 'E', gps.location.lng());
|
2019-04-01 18:34:57 +02:00
|
|
|
|
2019-04-01 18:01:52 +02:00
|
|
|
} else {
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(16, 5, MY_FONT_STRETCHED, 1, "No fix");
|
2019-04-01 18:34:57 +02:00
|
|
|
wasnofix = true;
|
2019-04-01 18:01:52 +02:00
|
|
|
}
|
2020-01-19 22:21:45 +01:00
|
|
|
break; // page2
|
2019-04-01 18:01:52 +02:00
|
|
|
#else
|
2020-01-19 22:21:45 +01:00
|
|
|
DisplayPage++; // next page
|
2019-04-01 18:01:52 +02:00
|
|
|
#endif
|
|
|
|
|
2019-10-01 13:02:30 +02:00
|
|
|
// page 3: BME280/680
|
2019-04-02 22:45:16 +02:00
|
|
|
case 3:
|
|
|
|
#if (HAS_BME)
|
|
|
|
// line 2-3: Temp
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 2, MY_FONT_STRETCHED, 0, "TMP:%-2.1f", bme_status.temperature);
|
2019-04-02 22:45:16 +02:00
|
|
|
|
|
|
|
// line 4-5: Hum
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 4, MY_FONT_STRETCHED, 0, "HUM:%-2.1f", bme_status.humidity);
|
2019-04-02 22:45:16 +02:00
|
|
|
|
|
|
|
#ifdef HAS_BME680
|
|
|
|
// line 6-7: IAQ
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 6, MY_FONT_STRETCHED, 0, "IAQ:%-3.0f", bme_status.iaq);
|
2020-01-19 22:21:45 +01:00
|
|
|
#else // is BME280 or BMP180
|
2020-01-02 15:44:09 +01:00
|
|
|
// line 6-7: Pre
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 6, MY_FONT_STRETCHED, 0, "PRE:%-2.1f", bme_status.pressure);
|
2020-01-19 22:21:45 +01:00
|
|
|
#endif // HAS_BME680
|
|
|
|
break; // page 3
|
2019-04-02 22:45:16 +02:00
|
|
|
#else
|
2020-01-19 22:21:45 +01:00
|
|
|
DisplayPage++; // next page
|
|
|
|
#endif // HAS_BME
|
2019-04-02 22:45:16 +02:00
|
|
|
|
2019-10-01 13:02:30 +02:00
|
|
|
// page 4: time
|
|
|
|
case 4:
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 4, MY_FONT_LARGE, 0, "%02d:%02d:%02d", hour(t), minute(t),
|
2019-10-01 13:02:30 +02:00
|
|
|
second(t));
|
|
|
|
break;
|
|
|
|
|
2020-02-26 00:40:09 +01:00
|
|
|
// page 5: lorawan parameters
|
2019-10-06 14:25:29 +02:00
|
|
|
case 5:
|
2020-02-26 00:40:09 +01:00
|
|
|
|
|
|
|
#if (HAS_LORA)
|
|
|
|
// 3|NtwkID:000000 TXpw:aa
|
|
|
|
// 4|DevAdd:00000000 DR:0
|
|
|
|
// 5|CHMsk:0000 Nonce:0000
|
|
|
|
// 6|CUp:000000 CDn:000000
|
|
|
|
// 7|SNR:-0000 RSSI:-0000
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 3, MY_FONT_SMALL, 0, "NetwID:%06X TXpw:%-2d",
|
2020-02-26 00:40:09 +01:00
|
|
|
LMIC.netid & 0x001FFFFF, LMIC.radio_txpow);
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 4, MY_FONT_SMALL, 0, "DevAdd:%08X DR:%1d", LMIC.devaddr,
|
2020-02-26 00:40:09 +01:00
|
|
|
LMIC.datarate);
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 5, MY_FONT_SMALL, 0, "ChMsk:%04X Nonce:%04X", LMIC.channelMap,
|
2020-02-26 00:40:09 +01:00
|
|
|
LMIC.devNonce);
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 6, MY_FONT_SMALL, 0, "fUp:%-6d fDn:%-6d",
|
2020-03-07 12:52:46 +01:00
|
|
|
LMIC.seqnoUp ? LMIC.seqnoUp - 1 : 0,
|
|
|
|
LMIC.seqnoDn ? LMIC.seqnoDn - 1 : 0);
|
2020-03-29 19:41:33 +02:00
|
|
|
dp_printf(0, 7, MY_FONT_SMALL, 0, "SNR:%-5d RSSI:%-5d", (LMIC.snr + 2) / 4,
|
2020-02-26 00:40:09 +01:00
|
|
|
LMIC.rssi);
|
|
|
|
break; // page5
|
|
|
|
#else // don't show blank page if we are unattended
|
|
|
|
DisplayPage++; // next page
|
|
|
|
#endif // HAS_LORA
|
|
|
|
|
|
|
|
// page 6: blank screen
|
|
|
|
case 6:
|
2020-01-20 11:41:31 +01:00
|
|
|
#ifdef HAS_BUTTON
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_clear();
|
2020-01-20 11:41:31 +01:00
|
|
|
break;
|
|
|
|
#else // don't show blank page if we are unattended
|
|
|
|
DisplayPage++; // next page
|
|
|
|
#endif
|
2019-10-06 14:25:29 +02:00
|
|
|
|
2019-04-01 18:01:52 +02:00
|
|
|
default:
|
2020-01-20 11:12:00 +01:00
|
|
|
goto start; // start over
|
2019-04-01 18:01:52 +02:00
|
|
|
|
|
|
|
} // switch
|
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
} // dp_drawPage
|
2018-07-15 14:28:05 +02:00
|
|
|
|
2019-10-01 13:02:30 +02:00
|
|
|
// display helper functions
|
|
|
|
void dp_printf(uint16_t x, uint16_t y, uint8_t font, uint8_t inv,
|
|
|
|
const char *format, ...) {
|
2019-09-27 12:47:00 +02:00
|
|
|
char loc_buf[64];
|
|
|
|
char *temp = loc_buf;
|
|
|
|
va_list arg;
|
|
|
|
va_list copy;
|
|
|
|
va_start(arg, format);
|
|
|
|
va_copy(copy, arg);
|
|
|
|
int len = vsnprintf(temp, sizeof(loc_buf), format, copy);
|
|
|
|
va_end(copy);
|
|
|
|
if (len < 0) {
|
|
|
|
va_end(arg);
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
if (len >= sizeof(loc_buf)) {
|
|
|
|
temp = (char *)malloc(len + 1);
|
|
|
|
if (temp == NULL) {
|
|
|
|
va_end(arg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
len = vsnprintf(temp, len + 1, format, arg);
|
|
|
|
}
|
|
|
|
va_end(arg);
|
2020-03-29 12:10:42 +02:00
|
|
|
#if (HAS_DISPLAY) == 1
|
2020-03-11 23:47:16 +01:00
|
|
|
oledWriteString(&ssoled, 0, x, y, temp, font, inv, false);
|
2020-03-29 12:10:42 +02:00
|
|
|
#elif (HAS_DISPLAY) == 2
|
2020-03-29 19:41:33 +02:00
|
|
|
tft.drawString(temp, x, y, font);
|
2020-03-29 12:10:42 +02:00
|
|
|
#endif
|
2019-09-27 12:47:00 +02:00
|
|
|
if (temp != loc_buf) {
|
|
|
|
free(temp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
void dp_dump(uint8_t *pBuffer) {
|
|
|
|
#if (HAS_DISPLAY) == 1
|
|
|
|
oledDumpBuffer(&ssoled, pBuffer);
|
|
|
|
#elif (HAS_DISPLAY) == 2
|
2020-03-29 19:41:33 +02:00
|
|
|
// no buffered rendering for TFT
|
2020-03-29 12:10:42 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void dp_clear() {
|
|
|
|
#if (HAS_DISPLAY) == 1
|
|
|
|
oledFill(&ssoled, 0, 1);
|
|
|
|
#elif (HAS_DISPLAY) == 2
|
2020-03-29 19:41:33 +02:00
|
|
|
tft.fillScreen(TFT_WHITE);
|
2020-03-29 12:10:42 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void dp_contrast(uint8_t contrast) {
|
|
|
|
#if (HAS_DISPLAY) == 1
|
|
|
|
oledSetContrast(&ssoled, contrast);
|
|
|
|
#elif (HAS_DISPLAY) == 2
|
2020-03-29 19:41:33 +02:00
|
|
|
// no contrast setting for TFT
|
2020-03-29 12:10:42 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void dp_power(uint8_t screenon) {
|
|
|
|
#if (HAS_DISPLAY) == 1
|
|
|
|
oledPower(&ssoled, screenon);
|
|
|
|
#elif (HAS_DISPLAY) == 2
|
|
|
|
// to come
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void dp_shutdown(void) {
|
|
|
|
#if (HAS_DISPLAY) == 1
|
|
|
|
// block i2c bus access
|
|
|
|
if (!I2C_MUTEX_LOCK())
|
|
|
|
ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0);
|
|
|
|
else {
|
|
|
|
cfg.screenon = 0;
|
2020-03-29 19:41:33 +02:00
|
|
|
oledPower(&ssoled, false);
|
2020-03-29 12:10:42 +02:00
|
|
|
delay(DISPLAYREFRESH_MS / 1000 * 1.1);
|
|
|
|
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
|
|
|
}
|
|
|
|
#elif (HAS_DISPLAY) == 2
|
2020-03-29 19:41:33 +02:00
|
|
|
// to come
|
2020-03-29 12:10:42 +02:00
|
|
|
#endif
|
|
|
|
}
|
2020-03-11 23:47:16 +01:00
|
|
|
|
2019-10-01 13:02:30 +02:00
|
|
|
void dp_printqr(uint16_t offset_x, uint16_t offset_y, const char *Message) {
|
2019-09-29 17:38:16 +02:00
|
|
|
uint8_t qrcodeData[qrcode_getBufferSize(QR_VERSION)];
|
2019-09-29 16:46:48 +02:00
|
|
|
qrcode_initText(&qrcode, qrcodeData, QR_VERSION, ECC_HIGH, Message);
|
2019-09-29 23:16:36 +02:00
|
|
|
|
|
|
|
// draw QR code
|
2019-10-01 13:02:30 +02:00
|
|
|
for (uint16_t y = 0; y < qrcode.size; y++)
|
|
|
|
for (uint16_t x = 0; x < qrcode.size; x++)
|
2019-09-29 23:16:36 +02:00
|
|
|
if (!qrcode_getModule(&qrcode, x, y)) // "black"
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_fillRect(x * QR_SCALEFACTOR + offset_x,
|
|
|
|
y * QR_SCALEFACTOR + offset_y, QR_SCALEFACTOR,
|
|
|
|
QR_SCALEFACTOR, false);
|
2019-09-29 23:16:36 +02:00
|
|
|
// draw horizontal frame lines
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_fillRect(0, 0, qrcode.size * QR_SCALEFACTOR + 2 * offset_x, offset_y,
|
|
|
|
false);
|
|
|
|
dp_fillRect(0, qrcode.size * QR_SCALEFACTOR + offset_y,
|
|
|
|
qrcode.size * QR_SCALEFACTOR + 2 * offset_x, offset_y, false);
|
2019-09-29 23:16:36 +02:00
|
|
|
// draw vertical frame lines
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_fillRect(0, 0, offset_x, qrcode.size * QR_SCALEFACTOR + 2 * offset_y,
|
|
|
|
false);
|
|
|
|
dp_fillRect(qrcode.size * QR_SCALEFACTOR + offset_x, 0, offset_x,
|
|
|
|
qrcode.size * QR_SCALEFACTOR + 2 * offset_y, false);
|
2019-09-29 16:46:48 +02:00
|
|
|
}
|
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
void dp_fillRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height,
|
|
|
|
uint8_t bRender) {
|
|
|
|
#if (HAS_DISPLAY) == 1
|
2019-10-01 13:02:30 +02:00
|
|
|
for (uint16_t xi = x; xi < x + width; xi++)
|
2020-03-11 23:47:16 +01:00
|
|
|
oledDrawLine(&ssoled, xi, y, xi, y + height - 1, bRender);
|
2020-03-29 12:10:42 +02:00
|
|
|
#elif (HAS_DISPLAY) == 2
|
2020-03-29 19:41:33 +02:00
|
|
|
tft.drawRect(x, y, width, height, MY_DISPLAY_FGCOLOR);
|
2020-03-29 12:10:42 +02:00
|
|
|
#endif
|
2019-09-29 16:46:48 +02:00
|
|
|
}
|
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
int dp_drawPixel(uint8_t *buf, const uint16_t x, const uint16_t y,
|
|
|
|
const uint8_t dot) {
|
2019-10-01 13:02:30 +02:00
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
if (x > MY_DISPLAY_WIDTH || y > MY_DISPLAY_HEIGHT)
|
2019-10-01 13:02:30 +02:00
|
|
|
return -1;
|
|
|
|
|
2019-10-01 13:35:05 +02:00
|
|
|
uint8_t bit = y & 7;
|
2020-03-29 12:10:42 +02:00
|
|
|
uint16_t idx = y / 8 * MY_DISPLAY_WIDTH + x;
|
2019-10-01 13:02:30 +02:00
|
|
|
|
2019-10-01 13:35:05 +02:00
|
|
|
buf[idx] &= ~(1 << bit); // clear pixel
|
2019-10-01 13:02:30 +02:00
|
|
|
if (dot)
|
2019-10-01 13:35:05 +02:00
|
|
|
buf[idx] |= (1 << bit); // set pixel
|
2019-10-01 13:02:30 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
void dp_scrollHorizontal(uint8_t *buf, const uint16_t width,
|
|
|
|
const uint16_t height, bool left) {
|
2019-10-01 13:02:30 +02:00
|
|
|
|
2019-10-13 17:21:50 +02:00
|
|
|
uint16_t col, page, idx = 0;
|
2019-10-01 13:02:30 +02:00
|
|
|
|
|
|
|
for (page = 0; page < height / 8; page++) {
|
2019-10-05 13:12:58 +02:00
|
|
|
if (left) { // scroll left
|
|
|
|
for (col = 0; col < width - 1; col++) {
|
|
|
|
idx = page * width + col;
|
|
|
|
buf[idx] = buf[idx + 1];
|
|
|
|
}
|
|
|
|
buf[idx + 1] = 0;
|
|
|
|
} else // scroll right
|
|
|
|
{
|
|
|
|
for (col = width - 1; col > 0; col--) {
|
|
|
|
idx = page * width + col;
|
|
|
|
buf[idx] = buf[idx - 1];
|
|
|
|
}
|
|
|
|
buf[idx - 1] = 0;
|
2019-10-01 13:02:30 +02:00
|
|
|
}
|
2019-10-05 13:12:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
void dp_scrollVertical(uint8_t *buf, const uint16_t width,
|
|
|
|
const uint16_t height, int offset) {
|
2019-10-05 13:12:58 +02:00
|
|
|
|
|
|
|
uint64_t buf_col;
|
|
|
|
|
|
|
|
if (!offset)
|
|
|
|
return; // nothing to do
|
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
for (uint16_t col = 0; col < MY_DISPLAY_WIDTH; col++) {
|
2019-10-05 13:12:58 +02:00
|
|
|
// convert column bytes from display buffer to uint64_t
|
2020-03-29 12:10:42 +02:00
|
|
|
buf_col = *(uint64_t *)&buf[col * MY_DISPLAY_HEIGHT / 8];
|
2019-10-05 13:12:58 +02:00
|
|
|
|
2019-10-06 13:14:23 +02:00
|
|
|
if (offset > 0) // scroll down
|
2019-10-13 17:21:50 +02:00
|
|
|
buf_col <<= offset;
|
2019-10-06 13:14:23 +02:00
|
|
|
else // scroll up
|
2019-10-13 17:21:50 +02:00
|
|
|
buf_col >>= abs(offset);
|
2019-10-05 13:12:58 +02:00
|
|
|
|
|
|
|
// write back uint64_t to uint8_t display buffer
|
2020-03-29 12:10:42 +02:00
|
|
|
*(uint64_t *)&buf[col * MY_DISPLAY_HEIGHT / 8] = buf_col;
|
2019-10-01 13:02:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
void dp_plotCurve(uint16_t count, bool reset) {
|
2019-10-01 13:02:30 +02:00
|
|
|
|
2019-10-04 15:47:33 +02:00
|
|
|
static uint16_t last_count = 0, col = 0, row = 0;
|
2019-10-05 13:12:58 +02:00
|
|
|
uint16_t v_scroll = 0;
|
2019-10-01 13:02:30 +02:00
|
|
|
|
2019-10-01 18:06:49 +02:00
|
|
|
if ((last_count == count) && !reset)
|
2019-10-01 13:02:30 +02:00
|
|
|
return;
|
|
|
|
|
2020-03-29 12:10:42 +02:00
|
|
|
if (reset) { // next count cycle?
|
|
|
|
if (col < MY_DISPLAY_WIDTH - 1) // matrix not full -> increment column
|
2019-10-01 13:02:30 +02:00
|
|
|
col++;
|
2019-10-01 18:06:49 +02:00
|
|
|
else // matrix full -> scroll left 1 dot
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_scrollHorizontal(plotbuf, MY_DISPLAY_WIDTH, MY_DISPLAY_HEIGHT, true);
|
2019-10-01 13:02:30 +02:00
|
|
|
|
2019-10-01 18:06:49 +02:00
|
|
|
} else // clear current dot
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_drawPixel(plotbuf, col, row, 0);
|
2019-10-01 18:06:49 +02:00
|
|
|
|
2019-10-06 13:14:23 +02:00
|
|
|
// scroll down, if necessary
|
2020-03-29 12:10:42 +02:00
|
|
|
while ((count - v_scroll) > MY_DISPLAY_HEIGHT - 1)
|
2019-10-05 13:12:58 +02:00
|
|
|
v_scroll++;
|
|
|
|
if (v_scroll)
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_scrollVertical(plotbuf, MY_DISPLAY_WIDTH, MY_DISPLAY_HEIGHT, v_scroll);
|
2019-10-01 13:02:30 +02:00
|
|
|
|
2019-10-01 18:06:49 +02:00
|
|
|
// set new dot
|
2020-03-29 12:10:42 +02:00
|
|
|
// row = MY_DISPLAY_HEIGHT - 1 - (count - v_scroll) % MY_DISPLAY_HEIGHT;
|
|
|
|
row = MY_DISPLAY_HEIGHT - 1 - count - v_scroll;
|
2019-10-01 13:02:30 +02:00
|
|
|
last_count = count;
|
2020-03-29 12:10:42 +02:00
|
|
|
dp_drawPixel(plotbuf, col, row, 1);
|
2019-10-01 13:02:30 +02:00
|
|
|
}
|
|
|
|
|
2019-03-07 15:05:52 +01:00
|
|
|
#endif // HAS_DISPLAY
|