155 lines
5.2 KiB
C++
155 lines
5.2 KiB
C++
#include "LCDDisplay.hpp"
|
|
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <log.hpp>
|
|
#include <string>
|
|
|
|
/* lcd size hardcoded to 4x40 idgaf */
|
|
LCDDisplay::LCDDisplay(const std::string& dev, const uint32_t baud) {
|
|
this->dev = dev;
|
|
this->baud = baud;
|
|
}
|
|
|
|
LCDDisplay::~LCDDisplay() {
|
|
this->close();
|
|
}
|
|
|
|
int LCDDisplay::Init() {
|
|
if (const int ret = this->open(); ret < 0) {
|
|
this->close();
|
|
return -1;
|
|
}
|
|
|
|
struct termios term{};
|
|
// Setup the serial port 8 data bits, no parity, 1 stopbit, no flow control
|
|
term.c_cflag = baud | CS8 | CSTOPB | CLOCAL | CREAD;
|
|
term.c_iflag = 0;
|
|
term.c_oflag = 0;
|
|
term.c_lflag = 0;
|
|
tcflush(lcd, TCIFLUSH);
|
|
tcsetattr(lcd, TCSANOW, &term);
|
|
|
|
// init linebuf to spaces
|
|
this->clearBuf();
|
|
|
|
// disable underline and block cursor
|
|
constexpr uint8_t cmdbuf[] = {254, 75, 254, 84};
|
|
::write(lcd, &cmdbuf, sizeof(cmdbuf));
|
|
|
|
// clear display and set cursor home
|
|
this->clear();
|
|
return 0;
|
|
}
|
|
|
|
int LCDDisplay::open(const bool reopen) {
|
|
applog(LOG::INF, "Opening serial port at \"{}\"", dev);
|
|
if (lcd < 0 || reopen)
|
|
lcd = ::open(dev.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); // ::open calls the global open() instead of LCDDisplay::open()
|
|
if (lcd < 0) {
|
|
applog(LOG::ERR, "Error opening serial port \"{}\"", dev); // err
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void LCDDisplay::close() {
|
|
if (lcd >= 0) {
|
|
::close(lcd);
|
|
applog(LOG::INF, "LCD port closed");
|
|
}
|
|
lcd = -1;
|
|
}
|
|
|
|
void LCDDisplay::write(const uint8_t* cmdbuf, const uint8_t len) {
|
|
if (::write(lcd, cmdbuf, len) != len) {
|
|
applog(LOG::ERR, "failed to execute ::write, lost access to port?");
|
|
this->open(true);
|
|
}
|
|
}
|
|
|
|
void LCDDisplay::clear() {
|
|
constexpr uint8_t cmdbuf[] = {254, 88};
|
|
this->write(cmdbuf, sizeof(cmdbuf));
|
|
}
|
|
|
|
void LCDDisplay::setContrast(const uint8_t contrast) {
|
|
applog(LOG::INF, "Set contrast to {}", contrast);
|
|
const uint8_t cmdbuf[] = {254, 80, contrast};
|
|
this->write(cmdbuf, sizeof(cmdbuf));
|
|
}
|
|
|
|
void LCDDisplay::setBacklight(const bool enabled) {
|
|
applog(LOG::INF, "Set backlight to {}", enabled);
|
|
const uint8_t cmdbuf[] = {254, static_cast<uint8_t>(enabled ? 66 : 70), 255};
|
|
this->write(cmdbuf, enabled ? 3 : 2);
|
|
}
|
|
|
|
/* still doesnt work? */
|
|
void LCDDisplay::setBacklight(const uint8_t brightness) {
|
|
applog(LOG::INF, "Set backlight brightness to {}", brightness);
|
|
const uint8_t cmdbuf[] = {254, static_cast<uint8_t>(brightness ? 66 : 70), 255, brightness}; // 3rd char is timer, 4th is brightness
|
|
this->write(cmdbuf, brightness ? 4 : 2);
|
|
}
|
|
|
|
void LCDDisplay::setCursor(const uint8_t row, const uint8_t col) {
|
|
const uint8_t cmdbuf[] = {254, 71, static_cast<uint8_t>(col + 1), static_cast<uint8_t>(row + 1)};
|
|
this->write(cmdbuf, sizeof(cmdbuf));
|
|
}
|
|
|
|
/* reconsider so im not writing every time. prepend custom chars to the write buffer? */
|
|
void LCDDisplay::setCustomChar(const uint8_t index, const std::array<uint8_t, 8> cchar) {
|
|
uint8_t cmdbuf[] = {254, 78, index, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
std::memcpy(&cmdbuf[3], &cchar, sizeof(cchar));
|
|
this->write(cmdbuf, sizeof(cmdbuf));
|
|
}
|
|
|
|
/* immediately writes string to the lcd at current cursor pos */
|
|
void LCDDisplay::writeStr(const std::string& str) {
|
|
this->write(reinterpret_cast<const uint8_t*>(str.c_str()), str.size());
|
|
}
|
|
|
|
/* immediately writes string to the lcd at given cursor pos */
|
|
void LCDDisplay::writeStr(const uint8_t row, const uint8_t col, const std::string& str) {
|
|
this->setCursor(row, col);
|
|
this->write(reinterpret_cast<const uint8_t*>(str.c_str()), str.size());
|
|
}
|
|
|
|
/* writes string to write buffer at calculated pos */
|
|
void LCDDisplay::writeBuf(const uint8_t row, const uint8_t col, const std::string& str) {
|
|
const size_t len = std::min(str.size(), writebuf.size() - col);
|
|
uint8_t pos = 4 + col + (LCD_COLS * (row % 2)); // skip 4 bytes to leave room for set cursor command
|
|
if (row >= 2) pos += 2 * LCD_COLS + 4; // if we're writing to disp2, skip another 4 bytes for set cursor command and 2*40 bytes of disp 1
|
|
std::memcpy(&writebuf[pos], str.c_str(), len);
|
|
}
|
|
|
|
/* initializes the write buffer to spaces */
|
|
void LCDDisplay::clearBuf() {
|
|
std::memset(writebuf.data(), ' ', writebuf.size());
|
|
}
|
|
|
|
/* flushes writebuffer to lcd, hyperoptmized, flushes buffer and commands in a single write operation */
|
|
void LCDDisplay::flushBuf() {
|
|
constexpr uint8_t setDisp[] = {254, 71, 1, 1}; // set cursor to char 1, line 1
|
|
std::memcpy(&writebuf[0], setDisp, 4);
|
|
std::memcpy(&writebuf[2 * LCD_COLS + 4], setDisp, 4);
|
|
writebuf[2 * LCD_COLS + 7] = 3; // overwrite set cursor c1 l1 to c1 l3
|
|
this->write(reinterpret_cast<uint8_t*>(writebuf.data()), writebuf.size());
|
|
}
|
|
|
|
// i have a whole flushbuf func to avoid too many writes, but im writing on every bar, need to rethink this.
|
|
// we could have a separate pre-char buffer and put all the setCustomChar commands to that?
|
|
char LCDDisplay::bar(const uint8_t char_id, const double value) {
|
|
std::array<uint8_t, 8> b{};
|
|
b.fill(0b01110);
|
|
for (uint8_t i = 0; i < (1.0 - value) * 8; i++)
|
|
b[i] = 0;
|
|
this->setCustomChar(char_id, b);
|
|
return cc(char_id);
|
|
}
|