Skip to content

Commit f601d4d

Browse files
committed
Add driver for 7seg
1 parent 80800ee commit f601d4d

File tree

25 files changed

+289
-68
lines changed

25 files changed

+289
-68
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*!
2+
* @file src/components/display/drivers/dispDrv7Seg.h
3+
*
4+
* Driver for 7-Segment LED Backpack displays.
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_7Seg
16+
#define WS_DISP_DRV_7Seg
17+
18+
#include "dispDrvBase.h"
19+
#include <Adafruit_LEDBackpack.h>
20+
21+
#define LED_BACKPACK_ALIGNMENT_UNSPECIFIED 0 ///< Unspecified alignment
22+
#define LED_BACKPACK_ALIGNMENT_LEFT 1 ///< Left alignment
23+
#define LED_BACKPACK_ALIGNMENT_RIGHT 2 ///< Right alignment
24+
#define LED_BACKPACK_ALIGNMENT_DEFAULT \
25+
LED_BACKPACK_ALIGNMENT_LEFT ///< Default alignment
26+
#define LED_MAX_CHARS \
27+
5 ///< Maximum characters for 7-segment display, including ':'
28+
#define LED_BACKPACK_DEGREE_SYMBOL \
29+
0b01100011 ///< Degree symbol for 7-segment display
30+
31+
/*!
32+
@brief Driver for 7-segment LED backpack displays.
33+
*/
34+
class dispDrv7Seg : public dispDrvBase {
35+
public:
36+
/*!
37+
@brief Constructor for the 7-segment LED matrix.
38+
*/
39+
dispDrv7Seg(TwoWire *i2c, uint16_t sensorAddress)
40+
: dispDrvBase(i2c, sensorAddress), _matrix(nullptr) {
41+
_alignment = LED_BACKPACK_ALIGNMENT_DEFAULT;
42+
}
43+
44+
/*!
45+
@brief Destructor for the 7-segment LED backpack driver.
46+
*/
47+
~dispDrv7Seg() {
48+
if (_matrix) {
49+
delete _matrix;
50+
_matrix = nullptr;
51+
}
52+
}
53+
54+
/*!
55+
@brief Attempts to initialize the 7-segment LED backpack driver.
56+
@return True if the matrix was initialized successfully, false otherwise.
57+
*/
58+
bool begin() override {
59+
_matrix = new Adafruit_7segment();
60+
return _matrix->begin(_sensorAddress, _i2c);
61+
}
62+
63+
/*!
64+
@brief Sets the text alignment for the matrix.
65+
@param alignment
66+
The desired alignment to set (LEFT or RIGHT).
67+
*/
68+
void setAlignment(uint32_t alignment) override {
69+
if (alignment == LED_BACKPACK_ALIGNMENT_RIGHT) {
70+
_alignment = LED_BACKPACK_ALIGNMENT_RIGHT;
71+
} else {
72+
_alignment = LED_BACKPACK_ALIGNMENT_DEFAULT;
73+
}
74+
}
75+
76+
/*!
77+
@brief Writes the first four characters of a message to the Adafruit
78+
7-segment LED matrix.
79+
@param message
80+
The message to be displayed.
81+
*/
82+
void writeMessage(const char *message) override {
83+
if (_matrix == nullptr || message == nullptr) {
84+
return;
85+
}
86+
87+
// Clear before writing
88+
_matrix->clear();
89+
90+
// Calculate the number of characters to display
91+
size_t len_display = min(strlen(message), (size_t)LED_MAX_CHARS);
92+
93+
// Set the starting position based on alignment
94+
int pos_start;
95+
if (_alignment == LED_BACKPACK_ALIGNMENT_LEFT) {
96+
pos_start = 0; // start at the leftmost position of the display
97+
} else {
98+
// Exclude decimal points from the character count because those get
99+
// displayed on a "special" segment of the LED display
100+
int seg_chars = 0;
101+
for (size_t i = 0; i < len_display; i++) {
102+
if (message[i] != '.') {
103+
seg_chars++;
104+
}
105+
}
106+
// start at the rightmost position of the display
107+
switch (seg_chars) {
108+
case 4:
109+
pos_start = 0;
110+
break;
111+
case 3:
112+
pos_start = 1;
113+
break;
114+
case 2:
115+
pos_start = 3; // if 2 characters, start at position 3 is required
116+
// because ':' is position 2 and we need to skip it
117+
break;
118+
case 1:
119+
pos_start = 4;
120+
break;
121+
default:
122+
pos_start = 0; // if no characters or overflow, start at position 0
123+
break;
124+
}
125+
}
126+
127+
// Write to the display's buffer
128+
int cur_idx = pos_start;
129+
for (size_t i = 0; i < len_display; i++) {
130+
131+
// skip position 2
132+
if (cur_idx == 2) {
133+
cur_idx++;
134+
}
135+
// Save the character because if there's a decimal, we need to skip it in
136+
// the buffer
137+
char ch = message[i];
138+
139+
// Look-ahead for a decimal point to attach to the current character
140+
bool display_dot = false;
141+
if (i + 1 < len_display && message[i + 1] == '.') {
142+
display_dot = true;
143+
i++;
144+
len_display++;
145+
} else if (message[i] == 0xC2 && message[i + 1] == 0xB0 &&
146+
i + 1 < strlen(message)) {
147+
// Write degree symbol
148+
_matrix->writeDigitRaw(cur_idx, LED_BACKPACK_DEGREE_SYMBOL);
149+
i++;
150+
cur_idx++;
151+
continue; // skip to next character
152+
}
153+
// Write the character to the display buffer
154+
_matrix->writeDigitAscii(cur_idx, ch, display_dot);
155+
cur_idx++;
156+
}
157+
// Issue the buffered data in RAM to the display
158+
_matrix->writeDisplay();
159+
}
160+
161+
private:
162+
Adafruit_7segment *_matrix;
163+
};
164+
165+
#endif // WS_DISP_DRV_7Seg

src/components/display/drivers/dispDrvBase.h

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ class dispDrvBase {
6363
: _pin_cs(cs), _pin_dc(dc), _pin_mosi(mosi), _pin_sck(sck), _pin_rst(rst),
6464
_pin_miso(miso) {}
6565

66-
6766
/*!
6867
@brief Creates a new I2C output component driver.
6968
@param i2c
@@ -136,21 +135,42 @@ class dispDrvBase {
136135
*/
137136
virtual void setTextSize(uint8_t s) { _text_sz = s; }
138137

138+
/*!
139+
@brief Sets the brightness for the display (if supported).
140+
@param brightness
141+
The brightness level to set.
142+
@note This method can be overridden by derived classes to provide
143+
specific functionality.
144+
*/
145+
virtual void setBrightness(int32_t brightness) { _brightness = brightness; }
146+
147+
/*!
148+
@brief Sets the alignment for the display (if supported).
149+
@param alignment
150+
The alignment to set.
151+
@note This method can be overridden by derived classes to provide
152+
specific functionality.
153+
*/
154+
virtual void setAlignment(uint32_t alignment) { _alignment = alignment; }
155+
139156
protected:
140-
int16_t _pin_dc; ///< Data/Command pin
141-
int16_t _pin_rst; ///< Reset pin
142-
int16_t _pin_cs; ///< Chip Select pin
143-
int16_t _pin_busy; ///< Optional Busy pin
144-
int16_t _pin_sram_cs; ///< Optional EPD SRAM chip select pin
145-
uint16_t _pin_mosi; ///< Optional MOSI pin for SPI TFT displays
146-
uint16_t _pin_miso; ///< Optional MISO pin for SPI TFT displays
147-
uint16_t _pin_sck; ///< Optional SCK pin for SPI TFT displays
157+
int16_t _pin_dc; ///< Data/Command pin
158+
int16_t _pin_rst; ///< Reset pin
159+
int16_t _pin_cs; ///< Chip Select pin
160+
int16_t _pin_busy; ///< Optional Busy pin
161+
int16_t _pin_sram_cs; ///< Optional EPD SRAM chip select pin
162+
uint16_t _pin_mosi; ///< Optional MOSI pin for SPI TFT displays
163+
uint16_t _pin_miso; ///< Optional MISO pin for SPI TFT displays
164+
uint16_t _pin_sck; ///< Optional SCK pin for SPI TFT displays
148165
TwoWire *_i2c; ///< Optional pointer to the I2C driver's Wire object
149166
uint16_t _sensorAddress; ///< Optional I2C sensor address
150-
uint8_t _text_sz = 1; ///< Text size for displaying a message
151-
int16_t _height; ///< Height of the display
152-
int16_t _width; ///< Width of the display
153-
uint8_t _rotation; ///< Rotation of the display
167+
uint8_t _text_sz = 1; ///< Optional Text size for displaying a message
168+
int16_t _height; ///< Optional Height of the display
169+
int16_t _width; ///< Optional Width of the display
170+
uint8_t _rotation; ///< Optional Rotation of the display
171+
int32_t _brightness; ///< Optional Brightness of the display (if supported)
172+
uint32_t _alignment; ///< Optional Alignment of the display (specific to
173+
///< drivers like LED backpacks)
154174
};
155175

156176
#endif // WS_DISP_DRV_BASE_H

src/components/display/drivers/dispDrvSsd1306.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,33 @@
1616
#define WS_DISP_DRV_SSD1306
1717

1818
#include "dispDrvBase.h"
19-
#include <Arduino.h>
2019
#include <Adafruit_SSD1306.h>
20+
#include <Arduino.h>
2121

2222
#define WS_SSD1306_DEFAULT_WIDTH \
2323
128 ///< Default width for a ssd1306 128x64 display
2424
#define WS_SSD1306_DEFAULT_HEIGHT \
2525
64 ///< Default height for a ssd1306 128x64 display
2626

27-
2827
/*!
2928
@brief Driver for SSD1306-based TFT displays.
3029
*/
3130
class dispDrvSsd1306 : public dispDrvBase {
3231
public:
3332
/*!
3433
@brief Constructor for the SSD1306 display driver.
34+
@param i2c
35+
The I2C hardware interface, default is Wire.
36+
@param sensorAddress
37+
The I2C sensor's unique address.
3538
*/
36-
dispDrvSsd1306(TwoWire *i2c, uint16_t sensorAddress) : dispDrvBase(i2c, sensorAddress), _display(nullptr) {
39+
dispDrvSsd1306(TwoWire *i2c, uint16_t sensorAddress)
40+
: dispDrvBase(i2c, sensorAddress), _display(nullptr) {
3741
_i2c = i2c;
3842
_sensorAddress = sensorAddress;
3943
_width = WS_SSD1306_DEFAULT_WIDTH;
4044
_height = WS_SSD1306_DEFAULT_HEIGHT;
41-
}
45+
}
4246

4347
~dispDrvSsd1306() {
4448
if (_display) {
@@ -135,7 +139,7 @@ class dispDrvSsd1306 : public dispDrvBase {
135139
_display->display();
136140
}
137141
}
138-
}
142+
}
139143

140144
private:
141145
Adafruit_SSD1306 *_display;

src/components/display/hardware.cpp

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,10 +343,8 @@ bool DisplayHardware::beginOled(
343343
wippersnapper_display_v1_OledConfig *config,
344344
wippersnapper_display_v1_I2cConfig *i2c_config) {
345345
// Validate pointers
346-
if (config == nullptr || i2c_config == nullptr || !i2c_config->has_i2c) {
347-
WS_DEBUG_PRINTLN("[display] OLED or I2C config is null!");
346+
if (config == nullptr || i2c_config == nullptr || !i2c_config->has_i2c)
348347
return false;
349-
}
350348

351349
// If we already have a display driver assigned to this hardware instance,
352350
// clean it up!
@@ -396,6 +394,60 @@ bool DisplayHardware::beginOled(
396394
return true;
397395
}
398396

397+
/*!
398+
@brief Attempts to configure and initialize an LED Backpack display
399+
@param config
400+
Pointer to the LED Backpack's configuration structure.
401+
@param i2c_config
402+
Pointer to the I2C configuration structure.
403+
@return True if configuration was successful, False otherwise.
404+
*/
405+
bool DisplayHardware::beginLedBackpack(wippersnapper_display_v1_LEDBackpackConfig *config, wippersnapper_display_v1_I2cConfig *i2c_config) {
406+
// Validate pointers
407+
if (config == nullptr || i2c_config == nullptr || !i2c_config->has_i2c)
408+
return false;
409+
410+
// If we already have a display driver assigned to this hardware instance,
411+
// clean it up!
412+
if (_drvDisp) {
413+
delete _drvDisp;
414+
_drvDisp = nullptr;
415+
}
416+
417+
// Initialize OLED display driver based on device name
418+
if (strnlen(i2c_config->i2c.i2c_device_name,
419+
sizeof(i2c_config->i2c.i2c_device_name)) <
420+
sizeof(i2c_config->i2c.i2c_device_name) &&
421+
strcmp(i2c_config->i2c.i2c_device_name, "7seg") == 0) {
422+
_drvDisp = new dispDrv7Seg(WS._i2cPort0->getBus(), i2c_config->i2c.i2c_device_address);
423+
} else {
424+
WS_DEBUG_PRINTLN("[display] Unsupported OLED driver!");
425+
return false;
426+
}
427+
428+
// Validate that the display driver was created successfully
429+
if (!_drvDisp) {
430+
WS_DEBUG_PRINTLN("[display] Failed to create display driver!");
431+
_drvDisp = nullptr;
432+
return false;
433+
}
434+
435+
// Configure display dimensions and text size
436+
_drvDisp->setAlignment(config->alignment);
437+
_drvDisp->setBrightness(config->brightness);
438+
439+
// Initialize the display driver
440+
if (!_drvDisp->begin()) {
441+
WS_DEBUG_PRINTLN("[display] Failed to begin driver!");
442+
delete _drvDisp;
443+
_drvDisp = nullptr;
444+
return false;
445+
}
446+
447+
WS_DEBUG_PRINTLN("[display] LED backpack initialized successfully.");
448+
return true;
449+
}
450+
399451
/*!
400452
@brief Gets the name of the display hardware instance.
401453
@return The name of the display hardware instance.

src/components/display/hardware.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "drivers/dispDrvSt7789.h"
2222
#include "drivers/dispDrvThinkInkGrayscale4Eaamfgn.h"
2323
#include "drivers/dispDrvThinkInkGrayscale4T5.h"
24+
#include "drivers/dispDrv7Seg.h"
2425
#include <functional>
2526
#include <map>
2627

@@ -47,6 +48,7 @@ class DisplayHardware {
4748
wippersnapper_display_v1_SpiConfig *spi_config);
4849
bool beginOled(wippersnapper_display_v1_OledConfig *config,
4950
wippersnapper_display_v1_I2cConfig *i2c_config);
51+
bool beginLedBackpack(wippersnapper_display_v1_LEDBackpackConfig *config, wippersnapper_display_v1_I2cConfig *i2c_config);
5052

5153
//
5254
// API for Adafruit_GFX that abstracts hardware functionality

src/wippersnapper/description/v1/description.pb.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* Automatically generated nanopb constant definitions */
2-
/* Generated by nanopb-0.4.5-dev at Thu Sep 4 19:40:01 2025. */
2+
/* Generated by nanopb-0.4.5-dev at Fri Sep 5 15:04:00 2025. */
33

44
#include "wippersnapper/description/v1/description.pb.h"
55
#if PB_PROTO_HEADER_VERSION != 40

src/wippersnapper/description/v1/description.pb.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* Automatically generated nanopb header */
2-
/* Generated by nanopb-0.4.5-dev at Thu Sep 4 19:40:01 2025. */
2+
/* Generated by nanopb-0.4.5-dev at Fri Sep 5 15:04:00 2025. */
33

44
#ifndef PB_WIPPERSNAPPER_DESCRIPTION_V1_WIPPERSNAPPER_DESCRIPTION_V1_DESCRIPTION_PB_H_INCLUDED
55
#define PB_WIPPERSNAPPER_DESCRIPTION_V1_WIPPERSNAPPER_DESCRIPTION_V1_DESCRIPTION_PB_H_INCLUDED

src/wippersnapper/display/v1/display.pb.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* Automatically generated nanopb constant definitions */
2-
/* Generated by nanopb-0.4.5-dev at Thu Sep 4 19:40:01 2025. */
2+
/* Generated by nanopb-0.4.5-dev at Fri Sep 5 15:04:00 2025. */
33

44
#include "wippersnapper/display/v1/display.pb.h"
55
#if PB_PROTO_HEADER_VERSION != 40
@@ -48,5 +48,3 @@ PB_BIND(wippersnapper_display_v1_DisplayRemoved, wippersnapper_display_v1_Displa
4848

4949

5050

51-
52-

0 commit comments

Comments
 (0)