Skip to content

Commit 49cf9cc

Browse files
committed
Detect SSD1680 automatically
1 parent 05db092 commit 49cf9cc

File tree

4 files changed

+116
-40
lines changed

4 files changed

+116
-40
lines changed

src/Wippersnapper.cpp

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,30 +1684,38 @@ bool cbDecodeDisplayMsg(pb_istream_t *stream, const pb_field_t *field,
16841684
}
16851685

16861686
// Attempt to add or replace a display component
1687-
bool did_add = WS._displayController->Handle_Display_AddOrReplace(&msgAddReq);
1688-
1687+
bool did_add =
1688+
WS._displayController->Handle_Display_AddOrReplace(&msgAddReq);
1689+
16891690
// Create a DisplayResponse message
1690-
wippersnapper_signal_v1_DisplayResponse msgResp = wippersnapper_signal_v1_DisplayResponse_init_zero;
1691-
msgResp.which_payload = wippersnapper_signal_v1_DisplayResponse_display_added_tag;
1691+
wippersnapper_signal_v1_DisplayResponse msgResp =
1692+
wippersnapper_signal_v1_DisplayResponse_init_zero;
1693+
msgResp.which_payload =
1694+
wippersnapper_signal_v1_DisplayResponse_display_added_tag;
16921695
msgResp.payload.display_added.did_add = did_add;
1693-
strncpy(msgResp.payload.display_added.name, msgAddReq.name, sizeof(msgResp.payload.display_added.name));
1696+
strncpy(msgResp.payload.display_added.name, msgAddReq.name,
1697+
sizeof(msgResp.payload.display_added.name));
16941698

1695-
// Encode and publish response back to broker
1696-
memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing));
1697-
pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing));
1698-
if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_DisplayResponse_fields, &msgResp)) {
1699-
WS_DEBUG_PRINTLN("ERROR: Unable to encode display response message!");
1700-
return false;
1701-
}
1699+
// Encode and publish response back to broker
1700+
memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing));
1701+
pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing,
1702+
sizeof(WS._buffer_outgoing));
1703+
if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_DisplayResponse_fields,
1704+
&msgResp)) {
1705+
WS_DEBUG_PRINTLN("ERROR: Unable to encode display response message!");
1706+
return false;
1707+
}
17021708

1703-
size_t msgSz;
1704-
pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_DisplayResponse_fields, &msgResp);
1705-
WS_DEBUG_PRINT("Publishing DisplayResponse Message...");
1706-
if (!WS._mqtt->publish(WS._topic_signal_display_device, WS._buffer_outgoing, msgSz, 1)) {
1707-
WS_DEBUG_PRINTLN("ERROR: Failed to DisplayResponse Response!");
1708-
} else {
1709-
WS_DEBUG_PRINTLN("Published!");
1710-
}
1709+
size_t msgSz;
1710+
pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_DisplayResponse_fields,
1711+
&msgResp);
1712+
WS_DEBUG_PRINT("Publishing DisplayResponse Message...");
1713+
if (!WS._mqtt->publish(WS._topic_signal_display_device, WS._buffer_outgoing,
1714+
msgSz, 1)) {
1715+
WS_DEBUG_PRINTLN("ERROR: Failed to DisplayResponse Response!");
1716+
} else {
1717+
WS_DEBUG_PRINTLN("Published!");
1718+
}
17111719
} else if (field->tag ==
17121720
wippersnapper_signal_v1_DisplayRequest_display_write_tag) {
17131721
// Decode message into a DisplayAddRequest
@@ -1720,12 +1728,12 @@ bool cbDecodeDisplayMsg(pb_istream_t *stream, const pb_field_t *field,
17201728
}
17211729
// Attempt to write to a display
17221730
WS._displayController->Handle_Display_Write(&msgWrite);
1723-
} else if (field->tag == wippersnapper_signal_v1_DisplayRequest_display_remove_tag) {
1731+
} else if (field->tag ==
1732+
wippersnapper_signal_v1_DisplayRequest_display_remove_tag) {
17241733
// Decode message into a DisplayRemoveRequest
17251734
wippersnapper_display_v1_DisplayRemove msgRemove =
17261735
wippersnapper_display_v1_DisplayRemove_init_zero;
1727-
if (!ws_pb_decode(stream,
1728-
wippersnapper_display_v1_DisplayRemove_fields,
1736+
if (!ws_pb_decode(stream, wippersnapper_display_v1_DisplayRemove_fields,
17291737
&msgRemove)) {
17301738
WS_DEBUG_PRINTLN("ERROR: Failure decoding DisplayRemove message!");
17311739
return false;

src/components/display/drivers/dispDrvBase.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
#ifndef WS_DISP_DRV_BASE_H
1616
#define WS_DISP_DRV_BASE_H
1717

18-
#include "Wippersnapper.h"
1918
#include "Adafruit_ThinkInk.h"
19+
#include "Wippersnapper.h"
2020

2121
/*!
2222
@brief Abstract base class for display drivers.

src/components/display/hardware.cpp

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,12 @@ static const std::map<std::string, FnCreateDispDrv> FactoryDrvDisp = {
2828
int16_t busy) -> dispDrvBase * {
2929
return new drvDispThinkInkGrayscale4Eaamfgn(dc, rst, cs, sram_cs, busy);
3030
}},
31-
{"thinkink-magtag-2025",
32-
[](int16_t dc, int16_t rst, int16_t cs, int16_t sram_cs,
33-
int16_t busy) -> dispDrvBase * {
34-
return new drvDispThinkInkGrayscale4Eaamfgn(dc, rst, cs, sram_cs, busy);
35-
}},
3631
{"thinkink-gs4-t5",
3732
[](int16_t dc, int16_t rst, int16_t cs, int16_t sram_cs,
3833
int16_t busy) -> dispDrvBase * {
3934
return new dispDrvThinkInkGrayscale4T5(dc, rst, cs, sram_cs, busy);
40-
}},
41-
{"thinkink-magtag-pre-2025",
42-
[](int16_t dc, int16_t rst, int16_t cs, int16_t sram_cs,
43-
int16_t busy) -> dispDrvBase * {
44-
return new dispDrvThinkInkGrayscale4T5(dc, rst, cs, sram_cs, busy);
45-
}}
46-
};
35+
}}};
4736

48-
49-
5037
/*!
5138
@brief Creates a new display driver instance based on the driver name.
5239
@param driver_name
@@ -169,6 +156,19 @@ bool DisplayHardware::beginEPD(
169156

170157
// TODO: Configure SPI bus selection (UNUSED AS OF RIGHT NOW)
171158

159+
// For "magtag" component name, attempt to autodetect the driver
160+
if (strncmp(_name, "magtag", 6) == 0) {
161+
if (detect_ssd1680(cs, dc, rst)) {
162+
// Detected SSD1680, use EAAMFGN driver
163+
strncpy(_name, "thinkink-gs4-eaamfgn", sizeof(_name) - 1);
164+
_name[sizeof(_name) - 1] = '\0';
165+
} else {
166+
// Did not detect SSD1680, use T5 driver
167+
strncpy(_name, "thinkink-gs4-t5", sizeof(_name) - 1);
168+
_name[sizeof(_name) - 1] = '\0';
169+
}
170+
}
171+
172172
// Create display driver object using the factory function
173173
_drvDisp = CreateDrvDisp(_name, dc, rst, cs, srcs, busy);
174174
if (!_drvDisp) {
@@ -210,4 +210,71 @@ void DisplayHardware::writeMessage(const char *message) {
210210
} else {
211211
WS_DEBUG_PRINTLN("[display] No display driver initialized!");
212212
}
213+
}
214+
215+
/*!
216+
@brief Detects if an SSD1680 EPD is connected using bit-banged SPI.
217+
@param cs
218+
Chip Select pin number.
219+
@param dc
220+
Data/Command pin number.
221+
@param rst
222+
Reset pin number.
223+
@return True if an SSD1680 is detected, False otherwise (IL0373 or different
224+
EPD).
225+
*/
226+
bool DisplayHardware::detect_ssd1680(uint8_t cs, uint8_t dc, uint8_t rst) {
227+
// note: for a complete implementation reference, see
228+
// https://github.com/adafruit/circuitpython/commit/f4316cb2491c815b128acca47f1bb75519fe306e
229+
// Configure SPI pins to bit-bang
230+
pinMode(MOSI, OUTPUT);
231+
pinMode(SCK, OUTPUT);
232+
pinMode(cs, OUTPUT);
233+
pinMode(dc, OUTPUT);
234+
pinMode(rst, OUTPUT);
235+
236+
// Begin transaction by pulling cs and dc LOW
237+
digitalWrite(cs, LOW);
238+
digitalWrite(dc, LOW);
239+
digitalWrite(SCK, LOW);
240+
digitalWrite(rst, HIGH);
241+
242+
// Write to read register 0x71
243+
uint8_t cmd = 0x71;
244+
for (int i = 0; i < 8; i++) {
245+
digitalWrite(MOSI, (cmd & (1 << (7 - i))) != 0);
246+
digitalWrite(SCK, HIGH);
247+
digitalWrite(SCK, LOW);
248+
}
249+
250+
// Set DC high to indicate data and switch MOSI to input with PUR in case
251+
// SSD1680 does not send data back
252+
digitalWrite(dc, HIGH);
253+
delayMicroseconds(1);
254+
pinMode(MOSI, INPUT_PULLUP);
255+
delayMicroseconds(1);
256+
257+
// Read response from register
258+
uint8_t status = 0;
259+
for (int i = 0; i < 8; i++) {
260+
status <<= 1;
261+
if (digitalRead(MOSI)) {
262+
status |= 1;
263+
}
264+
digitalWrite(SCK, HIGH);
265+
delayMicroseconds(1);
266+
digitalWrite(SCK, LOW);
267+
delayMicroseconds(1);
268+
}
269+
270+
// End transaction by pulling CS high
271+
digitalWrite(cs, HIGH);
272+
273+
// Put back MOSI pin as an output
274+
pinMode(MOSI, OUTPUT);
275+
276+
WS_DEBUG_PRINT("[display] Bitbang read 0x71: 0x");
277+
WS_DEBUG_PRINTLN(status, HEX);
278+
279+
return status == 0xFF;
213280
}

src/components/display/hardware.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
#ifndef WS_DISPLAY_HARDWARE_H
1616
#define WS_DISPLAY_HARDWARE_H
1717
#include "Wippersnapper.h"
18-
#include <functional>
19-
#include <map>
2018
#include "drivers/dispDrvBase.h"
2119
#include "drivers/dispDrvThinkInkGrayscale4Eaamfgn.h"
2220
#include "drivers/dispDrvThinkInkGrayscale4T5.h"
21+
#include <functional>
22+
#include <map>
2323

2424
/*!
2525
@brief Interface for interacting with display hardware (TFT, eInk,
@@ -49,6 +49,7 @@ class DisplayHardware {
4949
void writeMessage(const char *message);
5050

5151
private:
52+
bool detect_ssd1680(uint8_t cs, uint8_t dc, uint8_t rst);
5253
char _name[64]; ///< Identifies the hardware instance
5354
wippersnapper_display_v1_DisplayType _type; ///< Display type
5455
dispDrvBase *_drvDisp = nullptr; ///< Base display driver

0 commit comments

Comments
 (0)