Skip to content

Commit 645fd4d

Browse files
committed
Add CharLCD
1 parent cc3a3c5 commit 645fd4d

File tree

4 files changed

+211
-5
lines changed

4 files changed

+211
-5
lines changed

src/components/i2c/WipperSnapper_I2C.cpp

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,19 @@ bool WipperSnapper_Component_I2C::initI2CDevice(
852852
}
853853
_drivers_out.push_back(_quadAlphaNum);
854854
WS_DEBUG_PRINTLN("Quad Alphanum. Display Initialized Successfully!");
855+
} else if (strcmp("charlcd", msgDeviceInitReq->i2c_device_name) == 0) {
856+
_charLcd = new WipperSnapper_I2C_Driver_Out_CharLcd(this->_i2c, i2cAddress);
857+
_charLcd->ConfigureCharLcd(
858+
msgDeviceInitReq->i2c_output_add.config.char_lcd_config.rows,
859+
msgDeviceInitReq->i2c_output_add.config.char_lcd_config.columns, true);
860+
if (!_charLcd->begin()) {
861+
WS_DEBUG_PRINTLN("ERROR: Failed to initialize Character LCD!");
862+
_busStatusResponse =
863+
wippersnapper_i2c_v1_BusResponse_BUS_RESPONSE_DEVICE_INIT_FAIL;
864+
return false;
865+
}
866+
_drivers_out.push_back(_charLcd);
867+
WS_DEBUG_PRINTLN("Char LCD Display Initialized Successfully!");
855868
} else {
856869
WS_DEBUG_PRINTLN("ERROR: I2C device type not found!")
857870
_busStatusResponse =
@@ -900,8 +913,9 @@ void WipperSnapper_Component_I2C::updateI2CDeviceProperties(
900913
void WipperSnapper_Component_I2C::deinitI2CDevice(
901914
wippersnapper_i2c_v1_I2CDeviceDeinitRequest *msgDeviceDeinitReq) {
902915
uint16_t deviceAddr = (uint16_t)msgDeviceDeinitReq->i2c_device_address;
903-
std::vector<WipperSnapper_I2C_Driver *>::iterator iter, end;
904916

917+
// Check input (sensor) drivers
918+
std::vector<WipperSnapper_I2C_Driver *>::iterator iter, end;
905919
for (iter = drivers.begin(), end = drivers.end(); iter != end; ++iter) {
906920
if ((*iter)->getI2CAddress() == deviceAddr) {
907921
// Delete the object that iter points to
@@ -920,7 +934,26 @@ void WipperSnapper_Component_I2C::deinitI2CDevice(
920934
}
921935
}
922936

923-
// TODO: Add an implementation for the drivers_out vector!
937+
// Check for output drivers
938+
std::vector<WipperSnapper_I2C_Driver_Out *>::iterator out_iter, out_end;
939+
for (out_iter = _drivers_out.begin(), out_end = _drivers_out.end();
940+
out_iter != out_end; ++out_iter) {
941+
if ((*out_iter)->getI2CAddress() == deviceAddr) {
942+
// Set the driver to nullptr
943+
*out_iter = nullptr;
944+
// ESP-IDF, Erase–remove iter ptr from driver vector
945+
#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266)
946+
*out_iter = nullptr;
947+
_drivers_out.erase(
948+
remove(_drivers_out.begin(), _drivers_out.end(), nullptr),
949+
_drivers_out.end());
950+
#else
951+
// Arduino can not erase-remove, erase only
952+
_drivers_out.erase(out_iter);
953+
#endif
954+
WS_DEBUG_PRINTLN("I2C Device De-initialized!");
955+
}
956+
}
924957

925958
_busStatusResponse = wippersnapper_i2c_v1_BusResponse_BUS_RESPONSE_SUCCESS;
926959
}
@@ -1121,10 +1154,11 @@ bool WipperSnapper_Component_I2C::Handle_I2cDeviceOutputWrite(
11211154
// Call the output_msg
11221155
if (msgDeviceWrite->which_output_msg ==
11231156
wippersnapper_i2c_v1_I2CDeviceOutputWrite_write_led_backpack_tag) {
1124-
driver_out->WriteLedBackpack(&msgDeviceWrite->output_msg.write_led_backpack);
1157+
driver_out->WriteLedBackpack(
1158+
&msgDeviceWrite->output_msg.write_led_backpack);
11251159
} else if (msgDeviceWrite->which_output_msg ==
11261160
wippersnapper_i2c_v1_I2CDeviceOutputWrite_write_char_lcd_tag) {
1127-
// TODO: Write to the char LCD
1161+
driver_out->WriteMessageCharLCD(&msgDeviceWrite->output_msg.write_char_lcd);
11281162
} else {
11291163
WS_DEBUG_PRINTLN("ERROR: Unknown output message type!");
11301164
return false;
@@ -1139,7 +1173,6 @@ bool WipperSnapper_Component_I2C::Handle_I2cDeviceOutputWrite(
11391173
*/
11401174
/*******************************************************************************/
11411175
void WipperSnapper_Component_I2C::update() {
1142-
11431176
// Create response message
11441177
wippersnapper_signal_v1_I2CResponse msgi2cResponse =
11451178
wippersnapper_signal_v1_I2CResponse_init_zero;

src/components/i2c/WipperSnapper_I2C.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include "drivers/WipperSnapper_I2C_Driver_MS8607.h"
5252
#include "drivers/WipperSnapper_I2C_Driver_NAU7802.h"
5353
#include "drivers/WipperSnapper_I2C_Driver_Out.h"
54+
#include "drivers/WipperSnapper_I2C_Driver_Out_CharLcd.h"
5455
#include "drivers/WipperSnapper_I2C_Driver_Out_QuadAlphaNum.h"
5556
#include "drivers/WipperSnapper_I2C_Driver_PCT2075.h"
5657
#include "drivers/WipperSnapper_I2C_Driver_PM25.h"
@@ -199,6 +200,7 @@ class WipperSnapper_Component_I2C {
199200
WipperSnapper_I2C_Driver_MAX17048 *_max17048 = nullptr;
200201
WipperSnapper_I2C_Driver_ADT7410 *_adt7410 = nullptr;
201202
WipperSnapper_I2C_Driver_Out_QuadAlphaNum *_quadAlphaNum = nullptr;
203+
WipperSnapper_I2C_Driver_Out_CharLcd *_charLcd = nullptr;
202204
};
203205
extern Wippersnapper WS;
204206

src/components/i2c/drivers/WipperSnapper_I2C_Driver_Out.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ class WipperSnapper_I2C_Driver_Out : public WipperSnapper_I2C_Driver {
9393
// noop
9494
}
9595

96+
/*!
97+
@brief Writes a message to the LED backpack.
98+
@param msg_write
99+
Pointer to a wippersnapper_i2c_v1_LedBackpackWrite message.
100+
@returns True if the message was written successfully, False otherwise.
101+
*/
96102
bool WriteLedBackpack(wippersnapper_i2c_v1_LedBackpackWrite *msg_write) {
97103
// Check if we should adjust brightness
98104
if (msg_write->adjust_brightness)
@@ -119,6 +125,34 @@ class WipperSnapper_I2C_Driver_Out : public WipperSnapper_I2C_Driver {
119125
}
120126
return true;
121127
}
128+
129+
/*!
130+
@brief Configures a character LCD.
131+
@param rows
132+
The number of rows in the LCD.
133+
@param cols
134+
The number of columns in the LCD.
135+
@param enable_backlight
136+
True if the backlight is enabled, False otherwise.
137+
*/
138+
virtual void ConfigureCharLcd(uint32_t rows, uint32_t cols,
139+
bool enable_backlight) {
140+
// noop
141+
}
142+
143+
/*!
144+
@brief Writes a message to the LCD.
145+
@param write_char_lcd
146+
Points to a CharLCDWrite message.
147+
@returns True if the message was written successfully, False otherwise.
148+
*/
149+
bool WriteMessageCharLCD(wippersnapper_i2c_v1_CharLCDWrite *write_char_lcd) {
150+
WriteMessage(write_char_lcd->message);
151+
// NOTE: While this isn't calling any other funcs in here and ret'ing true,
152+
// I want to keep this function high-level for when we implement backlight
153+
// color and scrolling.
154+
return true;
155+
}
122156
};
123157

124158
#endif // WIPPERSNAPPER_I2C_DRIVER_OUT_H
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*!
2+
* @file WipperSnapper_I2C_Driver_Out_CharLcd.h
3+
*
4+
* Device driver for I2C Character LCDs (HD44780)
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 for Adafruit Industries 2025
11+
*
12+
* MIT license, all text here must be included in any redistribution.
13+
*
14+
*/
15+
16+
#ifndef WIPPERSNAPPER_I2C_DRIVER_OUT_CHARLCD_H
17+
#define WIPPERSNAPPER_I2C_DRIVER_OUT_CHARLCD_H
18+
19+
#include "WipperSnapper_I2C_Driver_Out.h"
20+
#include <Adafruit_LiquidCrystal.h>
21+
22+
/*!
23+
@brief Class that provides a driver interface for a lcd character display.
24+
This class is a wrapper around the Adafruit_LiquidCrystal library.
25+
*/
26+
class WipperSnapper_I2C_Driver_Out_CharLcd
27+
: public WipperSnapper_I2C_Driver_Out {
28+
29+
public:
30+
/*******************************************************************************/
31+
/*!
32+
@brief Constructor for a LCD character display.
33+
@param i2c
34+
The I2C interface.
35+
@param sensorAddress
36+
7-bit device address.
37+
*/
38+
/*******************************************************************************/
39+
WipperSnapper_I2C_Driver_Out_CharLcd(TwoWire *i2c, uint16_t sensorAddress)
40+
: WipperSnapper_I2C_Driver_Out(i2c, sensorAddress) {
41+
_i2c = i2c;
42+
_sensorAddress = sensorAddress;
43+
}
44+
45+
/*!
46+
@brief Destructor for an MS8607 sensor.
47+
*/
48+
~WipperSnapper_I2C_Driver_Out_CharLcd() {
49+
if (_lcd != nullptr) {
50+
delete _lcd;
51+
_lcd = nullptr;
52+
}
53+
}
54+
55+
/*!
56+
@brief Initializes the drvOutQuadAlphaNum component and begins I2C.
57+
@returns True if initialized successfully, False otherwise.
58+
*/
59+
bool begin() {
60+
_lcd = new Adafruit_LiquidCrystal(_sensorAddress, _i2c);
61+
bool did_begin = _lcd->begin(_cols, _rows);
62+
if (did_begin && _enable_backlight) {
63+
_lcd->setBacklight(HIGH);
64+
}
65+
return did_begin;
66+
}
67+
68+
/*!
69+
@brief Writes a message to the LCD.
70+
@note MUST be called prior to begin() to configure the LCD's size
71+
@param rows
72+
The number of rows in the LCD.
73+
@param cols
74+
The number of columns in the LCD.
75+
@param enable_backlight
76+
True if the backlight is enabled, False otherwise.
77+
*/
78+
void ConfigureCharLcd(uint8_t rows, uint8_t cols, bool enable_backlight) {
79+
_rows = rows;
80+
_cols = cols;
81+
_enable_backlight = enable_backlight;
82+
}
83+
84+
/*!
85+
@brief Writes a message to the LCD.
86+
@param message
87+
The message to be displayed.
88+
*/
89+
void WriteMessage(const char *message) {
90+
if (_lcd == nullptr)
91+
return;
92+
93+
// Before writing, let's clear the display
94+
_lcd->clear();
95+
96+
// TODO: Remove all the prints!
97+
// Print the message to the serial
98+
Serial.print("Writing message to Char. LCD: ");
99+
Serial.println(message);
100+
101+
size_t message_length = strlen(message);
102+
size_t cur_idx = 0; // Current index in the message
103+
104+
// Write each row until it hits: \n, or the end of the message, or the last
105+
// column/row position
106+
for (int cur_row = 0; cur_row < _rows && cur_idx < message_length;
107+
cur_row++) {
108+
// Write each row out at the beginning of the row
109+
_lcd->setCursor(0, cur_row);
110+
for (int cur_col = 0; cur_col < _cols && cur_idx < message_length;
111+
cur_col++) {
112+
char c = message[cur_idx];
113+
if (c == '\\' && cur_idx + 1 < message_length &&
114+
message[cur_idx + 1] == 'n') {
115+
cur_idx += 2; // Skip the '\n' character in the buffer
116+
break; // and move to the next row
117+
} else if (c == 194 && cur_idx + 1 < message_length &&
118+
message[cur_idx + 1] == 176) {
119+
cur_idx += 2; // Skip the degree symbol sequence in the buffer
120+
_lcd->write(0xDF); // and write the degree symbol
121+
} else {
122+
_lcd->write(c);
123+
cur_idx++;
124+
}
125+
}
126+
}
127+
}
128+
129+
protected:
130+
Adafruit_LiquidCrystal *_lcd =
131+
nullptr; ///< Pointer to the Adafruit_LiquidCrystal object
132+
uint8_t _rows; ///< Number of rows in the display
133+
uint8_t _cols; ///< Number of columns in the display
134+
bool _enable_backlight; ///< Flag to enable/disable backlight
135+
};
136+
137+
#endif // WIPPERSNAPPER_I2C_DRIVER_OUT_CHARLCD_H

0 commit comments

Comments
 (0)