Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions boards/thinknode_m8.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_ThinkNode_M8 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x4405"],
["0x239A", "0x0029"],
["0x239A", "0x002A"]
],
"usb_product": "elecrow_thinknode_m8",
"mcu": "nrf52840",
"variant": "ELECROW-ThinkNode-M8",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"onboard_tools": ["jlink"],
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52840-mdk-rs"
},
"frameworks": ["arduino"],
"name": "elecrow thinknode m8",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "",
"vendor": "ELECROW"
}
48 changes: 44 additions & 4 deletions examples/companion_radio/ui-new/UITask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@
#include <helpers/TxtDataHelpers.h>
#include "../MyMesh.h"
#include "target.h"
#include <time.h>
#ifdef WIFI_SSID
#include <WiFi.h>
#endif

#ifndef UI_TZ_OFFSET
#define UI_TZ_OFFSET 0
#endif

#ifndef AUTO_OFF_MILLIS
#define AUTO_OFF_MILLIS 15000 // 15 seconds
#endif
Expand All @@ -23,7 +28,7 @@
#define UI_RECENT_LIST_SIZE 4
#endif

#if UI_HAS_JOYSTICK
#if UI_HAS_JOYSTICK || UI_HAS_ROTARY_INPUT
#define PRESS_LABEL "press Enter"
#else
#define PRESS_LABEL "long press"
Expand Down Expand Up @@ -224,7 +229,19 @@ class HomeScreen : public UIScreen {
display.setTextSize(2);
sprintf(tmp, "MSG: %d", _task->getMsgCount());
display.drawTextCentered(display.width() / 2, 22, tmp);


#ifdef UI_SHOW_CLOCK
display.setTextSize(3);
uint32_t now = _rtc->getCurrentTime();
int8_t tz = UI_TZ_OFFSET; // for now draw time from Santo Domingo ...
now += (int32_t)tz * 3600;
DateTime dt (now);
sprintf(tmp, "%02d:%02d", dt.hour(), dt.minute());
display.drawTextCentered(display.width() / 2, 60, tmp);
display.setTextSize(1);
sprintf(tmp, "%02d/%02d/%d", dt.day(), dt.month(), dt.year());
display.drawTextCentered(display.width() / 2, 80, tmp);
#endif
#ifdef WIFI_SSID
IPAddress ip = WiFi.localIP();
snprintf(tmp, sizeof(tmp), "IP: %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
Expand All @@ -234,13 +251,21 @@ class HomeScreen : public UIScreen {
if (_task->hasConnection()) {
display.setColor(UIColor::warning_txt);
display.setTextSize(1);
#ifdef UI_SHOW_CLOCK
display.drawTextCentered(display.width() / 2, 110, "< Connected >");
#else
display.drawTextCentered(display.width() / 2, 43, "< Connected >");

#endif
} else if (the_mesh.getBLEPin() != 0) { // BT pin
display.setColor(UIColor::warning_txt);
display.setTextSize(2);
sprintf(tmp, "Pin:%d", the_mesh.getBLEPin());
#ifdef UI_SHOW_CLOCK
display.setTextSize(1);
display.drawTextCentered(display.width() / 2, 110, tmp);
#else
display.setTextSize(2);
display.drawTextCentered(display.width() / 2, 43, tmp);
#endif
}
} else if (_page == HomePage::RECENT) {
the_mesh.getRecentlyHeard(recent, UI_RECENT_LIST_SIZE);
Expand Down Expand Up @@ -720,6 +745,9 @@ void UITask::shutdown(bool restart){
if (restart) {
_board->reboot();
} else {
display.forceFullRefresh();
display.clear();
display.endFrame();
// Power off board including radio, display, GPS and components
_board->powerOff();
}
Expand Down Expand Up @@ -760,6 +788,17 @@ void UITask::loop() {
}
#elif defined(PIN_USER_BTN)
int ev = user_btn.check();
#ifdef UI_HAS_NAV_INPUT
if (ev == BUTTON_EVENT_CLICK) {
c = checkDisplayOn(KEY_ENTER);
} else if (ev == BUTTON_EVENT_LONG_PRESS) {
display.turnOff();
} else if (ev == BUTTON_EVENT_DOUBLE_CLICK) {
c = handleDoubleClick(KEY_SELECT);
} else if (ev == BUTTON_EVENT_TRIPLE_CLICK) {
c = handleTripleClick(KEY_SELECT);
}
#else
if (ev == BUTTON_EVENT_CLICK) {
c = checkDisplayOn(KEY_NEXT);
} else if (ev == BUTTON_EVENT_LONG_PRESS) {
Expand All @@ -769,6 +808,7 @@ void UITask::loop() {
} else if (ev == BUTTON_EVENT_TRIPLE_CLICK) {
c = handleTripleClick(KEY_SELECT);
}
#endif
#endif
#if defined(UI_HAS_ROTARY_INPUT)
RotaryInputEvent rotaryEv = rotary_input.poll();
Expand Down
1 change: 1 addition & 0 deletions src/helpers/ui/DisplayDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class DisplayDriver {

virtual bool isOn() = 0;
virtual bool isEink() { return false; } // default to non-eink, override in eink drivers
virtual void forceFullRefresh() {} // next refresh will be full for eink
virtual void turnOn() = 0;
virtual void turnOff() = 0;
virtual void clear() = 0;
Expand Down
50 changes: 45 additions & 5 deletions src/helpers/ui/GxEPDDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
SPIClass SPI1 = SPIClass(FSPI);
#endif

#ifndef EPD_WASHING_MACHINE_CYCLES
#define EPD_WASHING_MACHINE_CYCLES 0
#endif

// Color scheme
ColorVal UIColor::window_bkg = GxEPD_WHITE;
ColorVal UIColor::title_bkg = GxEPD_WHITE;
Expand All @@ -25,7 +29,6 @@ ColorVal UIColor::popup_bkg = GxEPD_WHITE;
ColorVal UIColor::popup_txt = GxEPD_BLACK;
ColorVal UIColor::corp_blue = GxEPD_BLACK;


bool GxEPDDisplay::begin() {
display.epd2.selectSPI(SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
#ifdef ESP32
Expand All @@ -36,15 +39,27 @@ bool GxEPDDisplay::begin() {
display.init(115200, true, 2, false);
display.setRotation(DISPLAY_ROTATION);
setTextSize(1); // Default to size 1

display.setFullWindow();

for (int i = 0; i < EPD_WASHING_MACHINE_CYCLES; i++) {
display.fillScreen(GxEPD_BLACK);
display.display(false);
delay(2000);
display.fillScreen(GxEPD_WHITE);
display.display(false);
delay(2000);
}

display.setPartialWindow(0, 0, display.width(), display.height());
resetPartialRefreshCounter();

display.fillScreen(GxEPD_WHITE);
display.display(true);
#if DISP_BACKLIGHT
digitalWrite(DISP_BACKLIGHT, LOW);
pinMode(DISP_BACKLIGHT, OUTPUT);
#endif
_init = true;
_isOn = true;
return true;
}

Expand All @@ -55,7 +70,11 @@ void GxEPDDisplay::turnOn() {
#elif defined(EXP_PIN_BACKLIGHT) && !defined(BACKLIGHT_BTN)
expander.digitalWrite(EXP_PIN_BACKLIGHT, HIGH);
#endif
_isOn = true;
if (!_isOn) {
forceFullRefresh();
_isOn = true;
last_display_crc_value=0;
}
}

void GxEPDDisplay::turnOff() {
Expand All @@ -65,6 +84,11 @@ void GxEPDDisplay::turnOff() {
expander.digitalWrite(EXP_PIN_BACKLIGHT, LOW);
#endif
_isOn = false;
display.setFullWindow();
display.fillScreen(GxEPD_WHITE);
display.display(true);
forceFullRefresh();
display.powerOff();
}

void GxEPDDisplay::clear() {
Expand All @@ -77,6 +101,12 @@ void GxEPDDisplay::startFrame(ColorVal bkg) {
display.fillScreen(bkg);
display.setTextColor(_curr_color = UIColor::primary_txt);
display_crc.reset();
if (_cycles_before_full_refresh <= 0) {
display.setPartialWindow(0, 0, display.width(), display.height());
} else {
display.setFullWindow();
display.writeScreenBuffer();
}
}

void GxEPDDisplay::setTextSize(int sz) {
Expand Down Expand Up @@ -178,9 +208,19 @@ uint16_t GxEPDDisplay::getTextWidth(const char* str) {
}

void GxEPDDisplay::endFrame() {
if (_isOn == false) return;
uint32_t crc = display_crc.finalize();
if (crc != last_display_crc_value) {
display.display(true);
if (_cycles_before_full_refresh == 0) {
display.display(false);
display.writeScreenBuffer();
resetPartialRefreshCounter();
} else {
display.display(true);
if (_cycles_before_full_refresh > 0) {
_cycles_before_full_refresh--;
}
}
last_display_crc_value = crc;
}
}
10 changes: 10 additions & 0 deletions src/helpers/ui/GxEPDDisplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@

#include "DisplayDriver.h"

#ifndef EINK_MAX_PARTIAL_REFRESH
// 60 to prevent ghosting when refreshing every minute ...
// set to -1 to disable the counter
#define EINK_MAX_PARTIAL_REFRESH -1
#endif

class GxEPDDisplay : public DisplayDriver {

#if defined(EINK_DISPLAY_MODEL)
Expand All @@ -36,6 +42,8 @@ class GxEPDDisplay : public DisplayDriver {
uint16_t _curr_color;
CRC32 display_crc;
int last_display_crc_value = 0;
int _cycles_before_full_refresh;
int max_partial_refresh = EINK_MAX_PARTIAL_REFRESH; // so this can be changed by an option after

public:
#if defined(EINK_DISPLAY_MODEL)
Expand All @@ -48,6 +56,8 @@ class GxEPDDisplay : public DisplayDriver {

bool isOn() override { return _isOn; }
bool isEink() override { return true; }
void forceFullRefresh() override { _cycles_before_full_refresh = 0; };
void resetPartialRefreshCounter() { _cycles_before_full_refresh = max_partial_refresh; };
void turnOn() override;
void turnOff() override;
void clear() override;
Expand Down
4 changes: 2 additions & 2 deletions src/helpers/ui/MomentaryButton.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ class MomentaryButton {
public:
MomentaryButton(int8_t pin, int long_press_mills=0, bool reverse=false, bool pulldownup=false, bool multiclick=true);
MomentaryButton(int8_t pin, int long_press_mills, int analog_threshold);
void begin();
int check(bool repeat_click=false); // returns one of BUTTON_EVENT_*
virtual void begin();
virtual int check(bool repeat_click=false); // returns one of BUTTON_EVENT_*
void cancelClick(); // suppress next BUTTON_EVENT_CLICK (if already in DOWN state)
uint8_t getPin() { return _pin; }
bool isPressed() const;
Expand Down
34 changes: 34 additions & 0 deletions src/helpers/ui/RotaryInputGPIO.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "RotaryInputGPIO.h"

bool RotaryInputGPIO::begin() {

if (_pin_a >= 0) {
pinMode(_pin_a, _pull_a ? INPUT_PULLUP : INPUT);
}
if (_pin_b >= 0) {
pinMode(_pin_b, _pull_b ? INPUT_PULLUP : INPUT);
}

b_prec = digitalRead(_pin_b);

return true;
}

RotaryInputEvent RotaryInputGPIO::poll() {
RotaryInputEvent ev = RotaryInputEvent::None;
bool a = digitalRead(_pin_a);
bool b = digitalRead(_pin_b);

// this is the simplest scheme and it works well
// for thinknode M8, just read A when B rises ;)
if (!b_prec && b) { // rising edge of A
if (a) {
ev = RotaryInputEvent::Next;
} else {
ev = RotaryInputEvent::Prev;
}
}
b_prec = b;

return ev;
}
21 changes: 21 additions & 0 deletions src/helpers/ui/RotaryInputGPIO.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include <Arduino.h>
#include "RotaryInput.h"

#define BUTTON_EVENT_UP 5
#define BUTTON_EVENT_DOWN 6

class RotaryInputGPIO: public RotaryInput {
int8_t _pin_a;
bool _pull_a;
int8_t _pin_b;
bool _pull_b;
bool b_prec;
public:
RotaryInputGPIO(int8_t pin_a, int8_t pin_b, bool pull_a=false, bool pull_b=false): _pin_a(pin_a), _pin_b(pin_b), _pull_a(pull_a), _pull_b(pull_b) {}

bool begin() override;
RotaryInputEvent poll() override;
bool isReady() const override { return true;}
};
Loading
Loading