Skip to content

Commit cf617c5

Browse files
committed
Refactor out hardware file
1 parent ab55789 commit cf617c5

File tree

6 files changed

+183
-71
lines changed

6 files changed

+183
-71
lines changed

src/Wippersnapper.cpp

Lines changed: 59 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1659,7 +1659,8 @@ void cbSignalUARTReq(char *data, uint16_t len) {
16591659
}
16601660

16611661
/*!
1662-
@brief Deserializes a DisplayRequest message and sends it to the display component.
1662+
@brief Deserializes a DisplayRequest message and sends it to the display
1663+
component.
16631664
@param stream
16641665
Incoming data stream from buffer.
16651666
@param field
@@ -1668,11 +1669,13 @@ void cbSignalUARTReq(char *data, uint16_t len) {
16681669
Optional arguments from decoder calling function.
16691670
@returns True if decoded successfully, False otherwise.
16701671
*/
1671-
bool cbDecodeDisplayMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) {
1672+
bool cbDecodeDisplayMsg(pb_istream_t *stream, const pb_field_t *field,
1673+
void **arg) {
16721674
if (field->tag == wippersnapper_signal_v1_DisplayRequest_display_add_tag) {
16731675

16741676
// Decode message into a DisplayAddRequest
1675-
wippersnapper_display_v1_DisplayAddOrReplace msgAddReq = wippersnapper_display_v1_DisplayAddOrReplace_init_zero;
1677+
wippersnapper_display_v1_DisplayAddOrReplace msgAddReq =
1678+
wippersnapper_display_v1_DisplayAddOrReplace_init_zero;
16761679
if (!ws_pb_decode(stream,
16771680
wippersnapper_display_v1_DisplayAddOrReplace_fields,
16781681
&msgAddReq)) {
@@ -1681,8 +1684,10 @@ bool cbDecodeDisplayMsg(pb_istream_t *stream, const pb_field_t *field, void **ar
16811684
}
16821685

16831686
// Attempt to add or replace a display component
1684-
bool did_add = WS._displayController->Handle_Display_AddOrReplace(&msgAddReq);
1685-
// TODO: Add response handling and publishing here, for now it always returns true and doesnt publish back to the broker
1687+
bool did_add =
1688+
WS._displayController->Handle_Display_AddOrReplace(&msgAddReq);
1689+
// TODO: Add response handling and publishing here, for now it always
1690+
// returns true and doesnt publish back to the broker
16861691
}
16871692
return true;
16881693
}
@@ -2406,57 +2411,56 @@ bool Wippersnapper::generateWSTopics() {
24062411
return false;
24072412
}
24082413

2409-
// Create d2b display topic
2414+
// /display topic //
2415+
2416+
// Pre-determine topic size
2417+
topicLen = strlen(WS._config.aio_user) + strlen("/") + strlen(_device_uid) +
2418+
strlen("/wprsnpr/") + strlen(TOPIC_SIGNALS) + strlen("broker") +
2419+
strlen(TOPIC_DISPLAY) + 1;
2420+
2421+
// Pre-allocate memory for topic
24102422
#ifdef USE_PSRAM
2411-
WS._topic_signal_display_brkr = (char *)ps_malloc(
2412-
sizeof(char) * strlen(WS._config.aio_user) + strlen("/") +
2413-
strlen(_device_uid) + strlen("/wprsnpr/") + strlen(TOPIC_SIGNALS) +
2414-
strlen("broker") + strlen(TOPIC_DISPLAY) + 1);
2423+
WS._topic_signal_display_brkr = (char *)ps_malloc(topicLen);
24152424
#else
2416-
WS._topic_signal_display_brkr = (char *)malloc(
2417-
sizeof(char) * strlen(WS._config.aio_user) + strlen("/") +
2418-
strlen(_device_uid) + strlen("/wprsnpr/") + strlen(TOPIC_SIGNALS) +
2419-
strlen("broker") + strlen(TOPIC_DISPLAY) + 1);
2425+
WS._topic_signal_display_brkr = (char *)malloc(topicLen);
24202426
#endif
2427+
2428+
// Generate the topic
24212429
if (WS._topic_signal_display_brkr != NULL) {
2422-
strcpy(WS._topic_signal_display_brkr, WS._config.aio_user);
2423-
strcat(WS._topic_signal_display_brkr, TOPIC_WS);
2424-
strcat(WS._topic_signal_display_brkr, _device_uid);
2425-
strcat(WS._topic_signal_display_brkr, TOPIC_SIGNALS);
2426-
strcat(WS._topic_signal_display_brkr, "broker");
2427-
strcat(WS._topic_signal_display_brkr, TOPIC_DISPLAY);
2428-
} else { // malloc failed
2429-
WS_DEBUG_PRINTLN("ERROR: Failed to add a display topic!");
2430+
snprintf(WS._topic_signal_display_brkr, topicLen, "%s/wprsnpr/%s%sbroker%s",
2431+
WS._config.aio_user, _device_uid, TOPIC_SIGNALS, TOPIC_DISPLAY);
2432+
} else {
2433+
WS_DEBUG_PRINTLN(
2434+
"FATAL ERROR: Failed to allocate memory for DISPLAY topic!");
24302435
return false;
24312436
}
24322437

2433-
// Subscribe to the display sub-topic
2438+
// Subscribe to signal's DISPLAY sub-topic and set callback
24342439
_topic_signal_display_sub =
24352440
new Adafruit_MQTT_Subscribe(WS._mqtt, WS._topic_signal_display_brkr, 1);
24362441
WS._mqtt->subscribe(_topic_signal_display_sub);
24372442
_topic_signal_display_sub->setCallback(cbDisplayMessage);
24382443

2439-
// Create a b2d display topic
2444+
// Calculate length of the topic for device-to-broker DISPLAY topic
2445+
topicLen = strlen(WS._config.aio_user) + strlen("/") + strlen(_device_uid) +
2446+
strlen("/wprsnpr/") + strlen(TOPIC_SIGNALS) + strlen("device") +
2447+
strlen(TOPIC_DISPLAY) + 1;
2448+
2449+
// Allocate memory for dynamic MQTT topic
24402450
#ifdef USE_PSRAM
2441-
WS._topic_signal_display_device = (char *)ps_malloc(
2442-
sizeof(char) * strlen(WS._config.aio_user) + strlen("/") +
2443-
strlen(_device_uid) + strlen("/wprsnpr/") + strlen(TOPIC_SIGNALS) +
2444-
strlen("device") + strlen(TOPIC_DISPLAY) + 1);
2451+
WS._topic_signal_display_device = (char *)ps_malloc(topicLen);
24452452
#else
2446-
WS._topic_signal_display_device = (char *)malloc(
2447-
sizeof(char) * strlen(WS._config.aio_user) + strlen("/") +
2448-
strlen(_device_uid) + strlen("/wprsnpr/") + strlen(TOPIC_SIGNALS) +
2449-
strlen("device") + strlen(TOPIC_DISPLAY) + 1);
2453+
WS._topic_signal_display_device = (char *)malloc(topicLen);
24502454
#endif
2455+
2456+
// Generate the topic if memory was allocated successfully
24512457
if (WS._topic_signal_display_device != NULL) {
2452-
strcpy(WS._topic_signal_display_device, WS._config.aio_user);
2453-
strcat(WS._topic_signal_display_device, TOPIC_WS);
2454-
strcat(WS._topic_signal_display_device, _device_uid);
2455-
strcat(WS._topic_signal_display_device, TOPIC_SIGNALS);
2456-
strcat(WS._topic_signal_display_device, "device");
2457-
strcat(WS._topic_signal_display_device, TOPIC_DISPLAY);
2458-
} else { // malloc failed
2459-
WS_DEBUG_PRINTLN("ERROR: Failed to add a display topic!");
2458+
snprintf(WS._topic_signal_display_device, topicLen,
2459+
"%s/wprsnpr/%s%sdevice%s", WS._config.aio_user, _device_uid,
2460+
TOPIC_SIGNALS, TOPIC_DISPLAY);
2461+
} else {
2462+
WS_DEBUG_PRINTLN(
2463+
"FATAL ERROR: Failed to allocate memory for DISPLAY topic!");
24602464
return false;
24612465
}
24622466

@@ -2953,6 +2957,14 @@ void Wippersnapper::connect() {
29532957
WS._ui_helper->build_scr_monitor();
29542958
#endif
29552959

2960+
WS.pinCfgCompleted = true;
2961+
2962+
// Initialize Digital IO class
2963+
WS._digitalGPIO = new Wippersnapper_DigitalGPIO(20);
2964+
// Initialize Analog IO class
2965+
WS._analogIO = new Wippersnapper_AnalogIO(5, 3.3);
2966+
WS._boardStatus = WS_BOARD_DEF_OK;
2967+
29562968
// Configure hardware
29572969
while (!WS.pinCfgCompleted) {
29582970
WS_DEBUG_PRINTLN(
@@ -2968,6 +2980,13 @@ void Wippersnapper::connect() {
29682980
statusLEDFade(GREEN, 3);
29692981
WS_DEBUG_PRINTLN(
29702982
"Registration and configuration complete!\nRunning application...");
2983+
2984+
// Print out display topics
2985+
WS_DEBUG_PRINTLN("Device-to-Broker DISPLAY topic: ");
2986+
WS_DEBUG_PRINTLN(WS._topic_signal_display_device);
2987+
WS_DEBUG_PRINTLN("Broker-to-Device DISPLAY topic: ");
2988+
WS_DEBUG_PRINTLN(WS._topic_signal_display_brkr);
2989+
delay(500);
29712990
}
29722991

29732992
/**************************************************************************/
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*!
2+
* @file src/components/display/drivers/dispDrvBase.h
3+
*
4+
* Abstract base class for display drivers.
5+
*
6+
* Adafruit invests time and resources providing this open source code,
7+
* please support Adafruit and open-source hardware by purchasing
8+
* products from Adafruit!
9+
*
10+
* Copyright (c) Brent Rubell 2025 for Adafruit Industries.
11+
*
12+
* BSD license, all text here must be included in any redistribution.
13+
*
14+
*/
15+
#ifndef WS_DISP_DRV_BASE_H
16+
#define WS_DISP_DRV_BASE_H
17+
18+
#include "Adafruit_ThinkInk.h"
19+
#include "Wippersnapper.h"
20+
21+
class dispDrvBase {
22+
public:
23+
dispDrvBase(int16_t dc, int16_t rst, int16_t cs, int16_t sram_cs = -1,
24+
int16_t busy = -1)
25+
: _pin_dc(dc), _pin_rst(rst), _pin_cs(cs), _pin_sram_cs(sram_cs),
26+
_pin_busy(busy) {
27+
// Constructor implementation (if we need one)
28+
}
29+
30+
virtual ~dispDrvBase() {
31+
// Destructor implementation (if we need one)
32+
};
33+
34+
// Virtual function to be implemented by derived classes
35+
virtual bool begin(thinkinkmode_t mode, bool reset = true);
36+
37+
protected:
38+
int16_t _pin_dc;
39+
int16_t _pin_rst;
40+
int16_t _pin_cs;
41+
int16_t _pin_busy;
42+
int16_t _pin_sram_cs; // for optional EPD SRAM chip select
43+
};
44+
45+
#endif // WS_DISP_DRV_BASE_H
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*!
2+
* @file src/components/display/drivers/drvDispThinkInkGrayscale4Eaamfgn.h
3+
*
4+
* Driver for ThinkInk 2.9" Grayscale 4-level EAAMFGN display (2025 MagTag).
5+
*
6+
* Adafruit invests time and resources providing this open source code,
7+
* please support Adafruit and open-source hardware by purchasing
8+
* products from Adafruit!
9+
*
10+
* Copyright (c) Brent Rubell 2025 for Adafruit Industries.
11+
*
12+
* BSD license, all text here must be included in any redistribution.
13+
*
14+
*/
15+
#ifndef WS_DRV_THINKINK_GRAYSCALE4_EAAMFGN_H
16+
#define WS_DRV_THINKINK_GRAYSCALE4_EAAMFGN_H
17+
18+
#include "dispDrvBase.h"
19+
20+
class drvDispThinkInkGrayscale4Eaamfgn : public dispDrvBase {
21+
public:
22+
drvDispThinkInkGrayscale4Eaamfgn(int16_t dc, int16_t rst, int16_t cs,
23+
int16_t sram_cs = -1, int16_t busy = -1)
24+
: dispDrvBase(dc, rst, cs, sram_cs, busy), _display(nullptr) {}
25+
26+
~drvDispThinkInkGrayscale4Eaamfgn() {
27+
if (_display) {
28+
delete _display;
29+
_display = nullptr;
30+
}
31+
}
32+
33+
bool begin(thinkinkmode_t mode, bool reset = true) override {
34+
_display = new ThinkInk_290_Grayscale4_EAAMFGN(_pin_dc, _pin_rst, _pin_cs,
35+
_pin_sram_cs, _pin_busy);
36+
if (!_display)
37+
return false; // Allocation failed
38+
39+
// Initialize the display
40+
_display->begin(mode);
41+
// Clear the display buffer
42+
_display->clearBuffer();
43+
_display->display();
44+
return true;
45+
}
46+
47+
private:
48+
ThinkInk_290_Grayscale4_EAAMFGN *_display;
49+
};
50+
51+
#endif // WS_DRV_THINKINK_GRAYSCALE4_EAAMFGN_H

src/components/display/hardware.cpp

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,20 @@ bool DisplayHardware::beginEPD(
5959
wippersnapper_display_v1_EpdSpiConfig *spi_config) {
6060
// Validate pointers
6161
if (config == nullptr || spi_config == nullptr) {
62+
WS_DEBUG_PRINTLN("[display] EPD config or SPI config is null!");
6263
return false;
6364
}
6465

6566
// Validate panel type
6667
if (config->panel ==
6768
wippersnapper_display_v1_EPDThinkInkPanel_EPD_THINK_INK_PANEL_UNSPECIFIED) {
69+
WS_DEBUG_PRINTLN("[display] Unsupported EPD panel type!");
6870
return false; // Unsupported panel type
6971
}
7072

7173
// Validate mode is a correct EPD mode
7274
if (config->mode == wippersnapper_display_v1_EPDMode_EPD_MODE_UNSPECIFIED) {
75+
WS_DEBUG_PRINTLN("[display] Unsupported EPD mode!");
7376
return false; // Unsupported mode
7477
}
7578

@@ -81,24 +84,19 @@ bool DisplayHardware::beginEPD(
8184
_disp_thinkink_grayscale4_eaamfgn = nullptr;
8285
_thinkink_driver =
8386
wippersnapper_display_v1_EPDThinkInkPanel_EPD_THINK_INK_PANEL_UNSPECIFIED;
84-
} else if (
85-
_thinkink_driver ==
86-
wippersnapper_display_v1_EPDThinkInkPanel_EPD_THINK_INK_PANEL_290_GRAYSCALE4_T5) {
87-
delete _disp_thinkink_grayscale4_t5;
88-
_disp_thinkink_grayscale4_t5 = nullptr;
89-
_thinkink_driver =
90-
wippersnapper_display_v1_EPDThinkInkPanel_EPD_THINK_INK_PANEL_UNSPECIFIED;
9187
}
9288

9389
// Parse all SPI bus pins
9490
// Check length
9591
if (strlen(spi_config->pin_dc) < 2 || strlen(spi_config->pin_rst) < 2 ||
9692
strlen(spi_config->pin_cs) < 2) {
93+
WS_DEBUG_PRINTLN("[display] Invalid SPI pin len!");
9794
return false;
9895
}
9996
// SPI pins must start with 'D'
10097
if (spi_config->pin_dc[0] != 'D' || spi_config->pin_rst[0] != 'D' ||
10198
spi_config->pin_cs[0] != 'D') {
99+
WS_DEBUG_PRINTLN("[display] SPI pins must start with 'D'!");
102100
return false;
103101
}
104102

@@ -123,33 +121,35 @@ bool DisplayHardware::beginEPD(
123121
thinkinkmode_t epd_mode;
124122
if (config->mode == wippersnapper_display_v1_EPDMode_EPD_MODE_GRAYSCALE4) {
125123
epd_mode = THINKINK_GRAYSCALE4;
124+
WS_DEBUG_PRINTLN("[display] EPD mode: GRAYSCALE4");
126125
} else if (config->mode == wippersnapper_display_v1_EPDMode_EPD_MODE_MONO) {
127126
epd_mode = THINKINK_MONO;
127+
WS_DEBUG_PRINTLN("[display] EPD mode: MONO");
128128
}
129129

130-
// Assign driver instance based on panel type
130+
// Configure the EPD driver based on panel type
131131
if (config->panel ==
132132
wippersnapper_display_v1_EPDThinkInkPanel_EPD_THINK_INK_PANEL_290_GRAYSCALE4_MFGN) {
133-
_disp_thinkink_grayscale4_eaamfgn =
134-
new ThinkInk_290_Grayscale4_EAAMFGN(dc, rst, cs, srcs, busy);
135-
if (!_disp_thinkink_grayscale4_eaamfgn)
136-
return false; // Allocation failed
137-
// Initialize the display
138-
_disp_thinkink_grayscale4_eaamfgn->begin(epd_mode);
133+
WS_DEBUG_PRINTLN("[display] EPD panel: ThinkInk 290 Grayscale4 MFGN");
139134
_thinkink_driver =
140135
wippersnapper_display_v1_EPDThinkInkPanel_EPD_THINK_INK_PANEL_290_GRAYSCALE4_MFGN;
141-
} else if (
142-
config->panel ==
143-
wippersnapper_display_v1_EPDThinkInkPanel_EPD_THINK_INK_PANEL_290_GRAYSCALE4_T5) {
144-
_disp_thinkink_grayscale4_t5 =
145-
new ThinkInk_290_Grayscale4_T5(dc, rst, cs, srcs, busy);
146-
if (!_disp_thinkink_grayscale4_t5)
136+
_disp_thinkink_grayscale4_eaamfgn =
137+
new drvDispThinkInkGrayscale4Eaamfgn(dc, rst, cs, srcs, busy);
138+
if (!_disp_thinkink_grayscale4_eaamfgn) {
139+
WS_DEBUG_PRINTLN("[display] Failed to allocate ThinkInk driver!");
147140
return false; // Allocation failed
148-
// Initialize the display
149-
_disp_thinkink_grayscale4_t5->begin(epd_mode);
150-
_thinkink_driver =
151-
wippersnapper_display_v1_EPDThinkInkPanel_EPD_THINK_INK_PANEL_290_GRAYSCALE4_T5;
141+
}
142+
if (!_disp_thinkink_grayscale4_eaamfgn->begin(epd_mode)) {
143+
WS_DEBUG_PRINTLN("[display] Failed to initialize ThinkInk driver!");
144+
delete _disp_thinkink_grayscale4_eaamfgn; // Clean up if initialization
145+
// failed
146+
_disp_thinkink_grayscale4_eaamfgn = nullptr;
147+
return false; // Initialization failed
148+
}
149+
WS_DEBUG_PRINTLN("[display] ThinkInk 290 Grayscale4 MFGN driver "
150+
"initialized successfully!");
152151
} else {
152+
WS_DEBUG_PRINTLN("[display] Unsupported EPD panel type!");
153153
return false; // Unsupported panel type
154154
}
155155

src/components/display/hardware.h

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
#ifndef WS_DISPLAY_HARDWARE_H
1616
#define WS_DISPLAY_HARDWARE_H
1717
#include "Wippersnapper.h"
18-
19-
#include "Adafruit_ThinkInk.h"
18+
#include "drivers/dispDrvBase.h"
19+
#include "drivers/dispDrvThinkInkGrayscale4Eaamfgn.h"
2020

2121
/**************************************************************************/
2222
/*!
@@ -45,10 +45,6 @@ class DisplayHardware {
4545
wippersnapper_display_v1_DisplayType _type; ///< Display type
4646
wippersnapper_display_v1_EPDThinkInkPanel
4747
_thinkink_driver; ///< ThinkInk driver type
48-
// TODO: Make these drivers instead?
49-
ThinkInk_290_Grayscale4_EAAMFGN *_disp_thinkink_grayscale4_eaamfgn =
50-
nullptr; //< 2025 MagTag with SSD1680Z chipset
51-
ThinkInk_290_Grayscale4_T5 *_disp_thinkink_grayscale4_t5 =
52-
nullptr; ///< Pre-2025 MagTag with IL0373 chipset
48+
drvDispThinkInkGrayscale4Eaamfgn *_disp_thinkink_grayscale4_eaamfgn = nullptr;
5349
};
5450
#endif // WS_DISPLAY_HARDWARE_H

src/components/register/Wippersnapper_Register.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ bool Wippersnapper::encodePubRegistrationReq() {
7777
/****************************************************************************/
7878
void Wippersnapper::pollRegistrationResp() {
7979
// Blocking loop, WDT reset upon failure.
80+
WS._boardStatus = WS_BOARD_DEF_OK;
8081
while (WS._boardStatus != WS_BOARD_DEF_OK) {
8182
WS_DEBUG_PRINT("Polling for registration message response...");
8283
WS_DEBUG_PRINTLN(WS._boardStatus);

0 commit comments

Comments
 (0)