From 627e4f1dc33e405260d56ec7f80f1bcbb7fcdfd7 Mon Sep 17 00:00:00 2001 From: Tony Corbett G0WFV Date: Fri, 28 Oct 2016 13:22:20 +0100 Subject: [PATCH] Initial LCDproc support --- Conf.cpp | 50 +++- Conf.h | 13 + LCDproc.cpp | 615 ++++++++++++++++++++++++++++++++++++++++++++ LCDproc.h | 77 ++++++ MMDVM.ini | 7 + MMDVMHost.cpp | 21 ++ Makefile | 2 +- Makefile.Pi | 2 +- Makefile.Pi.HD44780 | 2 +- Makefile.Pi.OLED | 2 +- Makefile.Pi.PCF8574 | 2 +- Makefile.Solaris | 2 +- 12 files changed, 785 insertions(+), 10 deletions(-) create mode 100644 LCDproc.cpp create mode 100644 LCDproc.h diff --git a/Conf.cpp b/Conf.cpp index f79a82b..4acc718 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -47,6 +47,7 @@ enum SECTION { SECTION_HD44780, SECTION_NEXTION, SECTION_OLED, + SECTION_LCDPROC, SECTION_TGREWRITE }; @@ -166,7 +167,12 @@ m_nextionUTC(false), m_nextionIdleBrightness(20U), m_oledType(3), m_oledBrightness(0), -m_oledInvert(0) +m_oledInvert(0), +m_lcdprocAddress(), +m_lcdprocPort(0U), +m_lcdprocLocalPort(0U), +m_lcdprocDisplayClock(false), +m_lcdprocUTC(false) { } @@ -226,7 +232,8 @@ bool CConf::read() section = SECTION_NEXTION; else if (::strncmp(buffer, "[OLED]", 6U) == 0) section = SECTION_OLED; - + else if (::strncmp(buffer, "[LCDproc]", 6U) == 0) + section = SECTION_LCDPROC; else section = SECTION_NONE; @@ -573,9 +580,19 @@ bool CConf::read() m_oledBrightness = (unsigned char)::atoi(value); else if (::strcmp(key, "Brightness") == 0) m_oledInvert = (unsigned char)::atoi(value); - - } + } else if (section == SECTION_LCDPROC) { + if (::strcmp(key, "Address") == 0) + m_lcdprocAddress = value; + else if (::strcmp(key, "Port") == 0) + m_lcdprocPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "LocalPort") == 0) + m_lcdprocLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "DisplayClock") == 0) + m_lcdprocDisplayClock = ::atoi(value) == 1; + else if (::strcmp(key, "UTC") == 0) + m_lcdprocUTC = ::atoi(value) == 1; + } } ::fclose(fp); @@ -1157,3 +1174,28 @@ unsigned char CConf::getOLEDInvert() const { return m_oledInvert; } + +std::string CConf::getLCDprocAddress() const +{ + return m_lcdprocAddress; +} + +unsigned int CConf::getLCDprocPort() const +{ + return m_lcdprocPort; +} + +unsigned int CConf::getLCDprocLocalPort() const +{ + return m_lcdprocLocalPort; +} + +bool CConf::getLCDprocDisplayClock() const +{ + return m_lcdprocDisplayClock; +} + +bool CConf::getLCDprocUTC() const +{ + return m_lcdprocUTC; +} diff --git a/Conf.h b/Conf.h index aad2e5b..22c905a 100644 --- a/Conf.h +++ b/Conf.h @@ -181,6 +181,13 @@ public: unsigned char getOLEDBrightness() const; unsigned char getOLEDInvert() const; + // The LCDproc section + std::string getLCDprocAddress() const; + unsigned int getLCDprocPort() const; + unsigned int getLCDprocLocalPort() const; + bool getLCDprocDisplayClock() const; + bool getLCDprocUTC() const; + private: std::string m_file; std::string m_callsign; @@ -315,6 +322,12 @@ private: unsigned char m_oledType; unsigned char m_oledBrightness; unsigned char m_oledInvert; + + std::string m_lcdprocAddress; + unsigned int m_lcdprocPort; + unsigned int m_lcdprocLocalPort; + bool m_lcdprocDisplayClock; + bool m_lcdprocUTC; }; #endif diff --git a/LCDproc.cpp b/LCDproc.cpp new file mode 100644 index 0000000..eafcf7c --- /dev/null +++ b/LCDproc.cpp @@ -0,0 +1,615 @@ +/* + * Copyright (C) 2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "LCDproc.h" +#include "Log.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_MAX_LEN 128 +#define SOCKET_TIMEOUT 2500 //2500us (2.5ms) works, but 10ms was enough not to disprupt audio processing + +const char* LISTENING = "Listening "; +const char* DEADSPACE = " "; + +int m_socketfd; +char m_buffer[BUFFER_MAX_LEN]; +fd_set m_readmask; +struct timeval m_timeout; +int m_recvsize; +unsigned int m_rows(0); +unsigned int m_cols(0); +bool m_screensDefined(false); +bool m_connected(false); + +char m_buffer1[BUFFER_MAX_LEN]; +char m_buffer2[BUFFER_MAX_LEN]; + +CLCDproc::CLCDproc(std::string address, unsigned int port, unsigned int localPort, const std::string& callsign, unsigned int dmrid, bool displayClock, bool utc, bool duplex) : +CDisplay(), +m_address(address), +m_port(port), +m_localPort(localPort), +m_callsign(callsign), +m_dmrid(dmrid), +m_displayClock(displayClock), +m_utc(utc), +m_duplex(duplex), +//m_duplex(true), // uncomment to force duplex display for testing! +m_dmr(false), +m_clockDisplayTimer(1000U, 0U, 250U) // Update the clock display every 250ms +{ +} + +CLCDproc::~CLCDproc() +{ +} + +bool CLCDproc::open() +{ + const char *server; + unsigned int port, localPort; + struct sockaddr_in serverAddress, clientAddress; + struct hostent *h; + + server = m_address.c_str(); + port = m_port; + localPort = m_localPort; + + + /* Create TCP socket */ + m_socketfd = socket(AF_INET, SOCK_STREAM, 0); + if (m_socketfd == -1) { + LogError("LCDproc, failed to create socket"); + return false; + } + + /* Sets client address (random port - need to specify manual port from ini file?) */ + clientAddress.sin_family = AF_INET; + clientAddress.sin_addr.s_addr = htonl(INADDR_ANY); + //clientAddress.sin_port = htons(0); + clientAddress.sin_port = htons(localPort); + + /* Bind the address to the socket */ + if (bind(m_socketfd, (struct sockaddr *)&clientAddress, sizeof(clientAddress)) == -1) { + LogError("LCDproc, error whilst binding address"); + return false; + } + + /* Lookup the hostname address */ + h = gethostbyname(server); + + /* Sets server address */ + serverAddress.sin_family = h->h_addrtype; + memcpy((char*) &serverAddress.sin_addr.s_addr, h->h_addr_list[0], h->h_length); + serverAddress.sin_port = htons(port); + + if (connect(m_socketfd, (struct sockaddr * )&serverAddress, sizeof(serverAddress))==-1) { + LogError("LCDproc, cannot connect to server"); + return false; + } + + socketPrintf(m_socketfd, "hello"); // Login to the LCD server + return true; +} + +void CLCDproc::setIdleInt() +{ + m_clockDisplayTimer.start(); // Start the clock display in IDLE only + + if (m_screensDefined) { + socketPrintf(m_socketfd, "screen_set DStar -priority hidden"); + socketPrintf(m_socketfd, "screen_set DMR -priority hidden"); + socketPrintf(m_socketfd, "screen_set YSF -priority hidden"); + socketPrintf(m_socketfd, "screen_set P25 -priority hidden"); + socketPrintf(m_socketfd, "widget_set Status Status %u %u Idle", m_cols - 3, m_rows); + } + + m_dmr = false; +} + +void CLCDproc::setErrorInt(const char* text) +{ + assert(text != NULL); + + m_clockDisplayTimer.stop(); // Stop the clock display + + if (m_screensDefined) { + socketPrintf(m_socketfd, "screen_set DStar -priority hidden"); + socketPrintf(m_socketfd, "screen_set DMR -priority hidden"); + socketPrintf(m_socketfd, "screen_set YSF -priority hidden"); + socketPrintf(m_socketfd, "screen_set P25 -priority hidden"); + socketPrintf(m_socketfd, "widget_set Status Status %u %u Error", m_cols - 4, m_rows); + } + + m_dmr = false; +} + +void CLCDproc::setLockoutInt() +{ + m_clockDisplayTimer.stop(); // Stop the clock display + + if (m_screensDefined) { + socketPrintf(m_socketfd, "screen_set DStar -priority hidden"); + socketPrintf(m_socketfd, "screen_set DMR -priority hidden"); + socketPrintf(m_socketfd, "screen_set YSF -priority hidden"); + socketPrintf(m_socketfd, "screen_set P25 -priority hidden"); + socketPrintf(m_socketfd, "widget_set Status Status %u %u Lockout", m_cols - 6, m_rows); + } + + m_dmr = false; +} + +void CLCDproc::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) +{ + assert(my1 != NULL); + assert(my2 != NULL); + assert(your != NULL); + assert(type != NULL); + assert(reflector != NULL); + + m_clockDisplayTimer.stop(); // Stop the clock display + + socketPrintf(m_socketfd, "screen_set DStar -priority foreground"); + socketPrintf(m_socketfd, "widget_set DStar Mode 1 1 \"D-Star\""); + + ::sprintf(m_buffer1, "%.8s", your); + + char *p = m_buffer1; + for (; *p; ++p) { + if (*p == ' ') + *p = '_'; + } + + if (strcmp(reflector, " ") != 0) { + sprintf(m_buffer2, " via %.8s", reflector); + } else { + //bzero(m_buffer2, BUFFER_MAX_LEN); + memset(m_buffer2, 0, BUFFER_MAX_LEN); + } + + if (m_rows == 2) { + socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 %u 2 h 3 \"%.8s/%.4s to %s%s\"", m_cols - 1, my1, my2, m_buffer1, m_buffer2); + } else { + socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 %u 2 h 3 \"%.8s/%.4s\"", m_cols - 1, my1, my2); + socketPrintf(m_socketfd, "widget_set DStar Line3 1 3 %u 3 h 3 \"%s%s\"", m_cols - 1, m_buffer1, m_buffer2); + } + + m_dmr = false; +} + +void CLCDproc::clearDStarInt() +{ + m_clockDisplayTimer.stop(); // Stop the clock display + + socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 15 2 h 3 Listening"); + socketPrintf(m_socketfd, "widget_set DStar Line3 1 3 15 3 h 3 \"\""); + socketPrintf(m_socketfd, "widget_set DStar Line4 1 4 15 4 h 3 \"\""); +} + +void CLCDproc::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) +{ + assert(type != NULL); + + if (!m_dmr) { + m_clockDisplayTimer.stop(); // Stop the clock display + + socketPrintf(m_socketfd, "screen_set DMR -priority foreground"); + + if (m_duplex) { + if (m_rows > 2U) { + socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR"); + } + + if (slotNo == 1U) { + socketPrintf(m_socketfd, "widget_set DMR Slot2 3 %u %u %u h 3 \"Listening\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1); + } else { + socketPrintf(m_socketfd, "widget_set DMR Slot1 3 %u %u %u h 3 \"Listening\"", m_rows / 2, m_cols - 1, m_rows / 2); + } + } else { + socketPrintf(m_socketfd, "widget_set DMR Slot1_ 1 %u \"\"", m_rows / 2); + socketPrintf(m_socketfd, "widget_set DMR Slot2_ 1 %u \"\"", m_rows / 2 + 1); + + socketPrintf(m_socketfd, "widget_set DMR Slot1 1 %u %u %u h 3 \"Listening\"", m_rows / 2, m_cols - 1, m_rows / 2); + socketPrintf(m_socketfd, "widget_set DMR Slot2 1 %u %u %u h 3 \"\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1); + } + } + + if (m_duplex) { + if (m_rows > 2U) { + socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR"); + } + + if (slotNo == 1U) { + socketPrintf(m_socketfd, "widget_set DMR Slot1 3 %u %u %u h 3 \"%s > %s%s\"", m_rows / 2, m_cols - 1, m_rows / 2, src.c_str(), group ? "TG" : "", dst.c_str()); + } else { + socketPrintf(m_socketfd, "widget_set DMR Slot2 3 %u %u %u h 3 \"%s > %s%s\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1, src.c_str(), group ? "TG" : "", dst.c_str()); + } + } else { + socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR"); + + if (m_rows == 2U) { + socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 %u 2 h 3 \"%s > %s%s\"", m_cols - 1, src.c_str(), group ? "TG" : "", dst.c_str()); + } else { + socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 %u 2 h 3 \"%s >\"", m_cols - 1, src.c_str()); + socketPrintf(m_socketfd, "widget_set DMR Slot2 1 3 %u 3 h 3 \"%s%s\"", m_cols - 1, group ? "TG" : "", dst.c_str()); + } + } + + m_dmr = true; +} + +void CLCDproc::clearDMRInt(unsigned int slotNo) +{ + m_clockDisplayTimer.stop(); // Stop the clock display + + if (m_duplex) { + if (slotNo == 1U) { + socketPrintf(m_socketfd, "widget_set DMR Slot1 3 %u %u %u h 3 \"Listening\"", m_rows / 2, m_cols - 1, m_rows / 2); + } else { + socketPrintf(m_socketfd, "widget_set DMR Slot2 3 %u %u %u h 3 \"Listening\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1); + } + } else { + socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 15 2 h 3 Listening"); + socketPrintf(m_socketfd, "widget_set DMR Slot2 1 3 15 3 h 3 \"\""); + } +} + +void CLCDproc::writeFusionInt(const char* source, const char* dest, const char* type, const char* origin) +{ + assert(source != NULL); + assert(dest != NULL); + assert(type != NULL); + assert(origin != NULL); + + m_clockDisplayTimer.stop(); // Stop the clock display + + socketPrintf(m_socketfd, "screen_set YSF -priority foreground"); + socketPrintf(m_socketfd, "widget_set YSF Mode 1 1 \"System Fusion\""); + + if (m_rows == 2U) { + socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 \"%.10s > %s%u\"", source, dest); + } else { + socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 \"%.10s >\"", source); + socketPrintf(m_socketfd, "widget_set YSF Line3 1 3 15 3 h 3 \"%s%u\"", dest); + } + + m_dmr = false; +} + +void CLCDproc::clearFusionInt() +{ + m_clockDisplayTimer.stop(); // Stop the clock display + + socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 Listening"); + socketPrintf(m_socketfd, "widget_set YSF Line3 1 3 15 3 h 3 \"\""); + socketPrintf(m_socketfd, "widget_set YSF Line4 1 4 15 4 h 3 \"\""); +} + +void CLCDproc::writeP25Int(const char* source, bool group, unsigned int dest, const char* type) +{ + assert(source != NULL); + assert(type != NULL); + + m_clockDisplayTimer.stop(); // Stop the clock display + + socketPrintf(m_socketfd, "screen_set P25 -priority foreground"); + socketPrintf(m_socketfd, "widget_set P25 Mode 1 1 P25"); + + if (m_rows == 2U) { + socketPrintf(m_socketfd, "widget_set P25 Line2 1 2 15 2 h 3 \"%.10s > %s%u\"", source, group ? "TG" : "", dest); + } else { + socketPrintf(m_socketfd, "widget_set P25 Line2 1 2 15 2 h 3 \"%.10s >\"", source); + socketPrintf(m_socketfd, "widget_set P25 Line3 1 3 15 3 h 3 \"%s%u\"", group ? "TG" : "", dest); + } + + m_dmr = false; +} + +void CLCDproc::clearP25Int() +{ + m_clockDisplayTimer.stop(); // Stop the clock display + + socketPrintf(m_socketfd, "widget_set P25 Line3 1 2 15 2 h 3 Listening"); + socketPrintf(m_socketfd, "widget_set P25 Line3 1 3 15 3 h 3 \"\""); + socketPrintf(m_socketfd, "widget_set P25 Line4 1 4 15 4 h 3 \"\""); +} + +void CLCDproc::writeCWInt() +{ +} + +void CLCDproc::clearCWInt() +{ +} + +void CLCDproc::clockInt(unsigned int ms) +{ + m_clockDisplayTimer.clock(ms); + + // Idle clock display + if (m_displayClock && m_clockDisplayTimer.isRunning() && m_clockDisplayTimer.hasExpired()) { + time_t currentTime; + struct tm *Time; + time(¤tTime); + + if (m_utc) { + Time = gmtime(¤tTime); + } else { + Time = localtime(¤tTime); + } + + setlocale(LC_TIME,""); + strftime(m_buffer1, 128, "%X", Time); // Time + strftime(m_buffer2, 128, "%x", Time); // Date + + if (m_cols < 26U && m_rows == 2U) { + socketPrintf(m_socketfd, "widget_set Status Time %u 2 \"%s%s\"", m_cols - 9, strlen(m_buffer1) > 8 ? "" : " ", m_buffer1); + } else { + socketPrintf(m_socketfd, "widget_set Status Time %u %u %s", (m_cols - (strlen(m_buffer1) == 8 ? 6 : 8)) / 2, m_rows / 2, m_buffer1); + socketPrintf(m_socketfd, "widget_set Status Date %u %u %s", (m_cols - (strlen(m_buffer1) == 8 ? 6 : 8)) / 2, m_rows / 2 + 1, m_buffer2); + } + + m_clockDisplayTimer.start(); + } + + // We must set all this information on each select we do + FD_ZERO(&m_readmask); // empty readmask + + // Then we put all the descriptors we want to wait for in a mask = m_readmask + FD_SET(m_socketfd, &m_readmask); + FD_SET(STDIN_FILENO, &m_readmask); // STDIN_FILENO = 0 (standard input); + + // Timeout, we will stop waiting for information + m_timeout.tv_sec = 0; + m_timeout.tv_usec = SOCKET_TIMEOUT; + + /* The first parameter is the biggest descriptor + 1. The first one was 0, so + * every other descriptor will be bigger + * + * readfds = &m_readmask + * writefds = we are not waiting for writefds + * exceptfds = we are not waiting for exception fds + */ + + if (select(m_socketfd + 1, &m_readmask, NULL, NULL, &m_timeout) == -1) + LogError("LCDproc, error on select"); + + // If something was received from the server... + if (FD_ISSET(m_socketfd, &m_readmask)) { + m_recvsize = recv(m_socketfd, m_buffer, BUFFER_MAX_LEN, 0); + + if (m_recvsize == -1) + LogError("LCDproc, cannot receive information"); + + m_buffer[m_recvsize] = '\0'; + + int i = 0; + char *argv[256]; + int argc, newtoken; + int len = strlen(m_buffer); + + // Now split the string into tokens... + argc = 0; + newtoken = 1; + + for (i = 0; i < len; i++) { + switch (m_buffer[i]) { + case ' ': + newtoken = 1; + m_buffer[i] = 0; + break; + default: /* regular chars, keep tokenizing */ + if (newtoken) + argv[argc++] = m_buffer + i; + newtoken = 0; + break; + case '\0': + case '\n': + m_buffer[i] = 0; + if (argc > 0) { + if (0 == strcmp(argv[0], "listen")) { + LogDebug("LCDproc, the %s screen is displayed", argv[1]); + } else if (0 == strcmp(argv[0], "ignore")) { + LogDebug("LCDproc, the %s screen is hidden", argv[1]); + } else if (0 == strcmp(argv[0], "key")) { + LogDebug("LCDproc, Key %s", argv[1]); + } else if (0 == strcmp(argv[0], "menu")) { + } else if (0 == strcmp(argv[0], "connect")) { + // connect LCDproc 0.5.7 protocol 0.3 lcd wid 16 hgt 2 cellwid 5 cellhgt 8 + int a; + + for (a = 1; a < argc; a++) { + if (0 == strcmp(argv[a], "wid")) + m_cols = atoi(argv[++a]); + else if (0 == strcmp(argv[a], "hgt")) + m_rows = atoi(argv[++a]); + else if (0 == strcmp(argv[a], "cellwid")) { + //lcd_cellwid = atoi(argv[++a]); + } else if (0 == strcmp(argv[a], "cellhgt")) { + //lcd_cellhgt = atoi(argv[++a]); + } + } + + m_connected = true; + socketPrintf(m_socketfd, "client_set -name MMDVMHost"); + } else if (0 == strcmp(argv[0], "bye")) { + //close the socket- todo + } else if (0 == strcmp(argv[0], "success")) { + //LogDebug("LCDproc, command successful"); + } else if (0 == strcmp(argv[0], "huh?")) { + sprintf(m_buffer1, "LCDproc, command failed:"); + sprintf(m_buffer2, " "); + + int j; + for (j = 1; j < argc; j++) { + strcat(m_buffer1, m_buffer2); + strcat(m_buffer1, argv[j]); + } + LogDebug("%s", m_buffer1); + } + } + + /* Restart tokenizing */ + argc = 0; + newtoken = 1; + break; + } /* switch( m_buffer[i] ) */ + } + } + + if (!m_screensDefined && m_connected) { + defineScreens(); + } + + // Uncomment the next section of code to test server commands from STDIN + // only for debugging purposes! + + /* + if (FD_ISSET(STDIN_FILENO, &m_readmask)) { + fgets(m_buffer, BUFFER_MAX_LEN, stdin); + + if (send(m_socketfd, m_buffer, strlen(m_buffer) + 1, 0) == -1) + LogError("LCDproc, cannot send data"); + } + */ +} + +void CLCDproc::close() +{ +} + +int CLCDproc::socketPrintf(int fd, const char *format, ...) +{ + char buf[BUFFER_MAX_LEN]; + va_list ap; + unsigned int size = 0; + + va_start(ap, format); + size = vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); + + if (size < 0) { + LogError("LCDproc, socketPrintf: vsnprintf failed"); + return -1; + } + + if (size > sizeof(buf)) + LogWarning("LCDproc, socketPrintf: vsnprintf truncated message"); + + if (send(fd, buf, strlen(buf) + 1, 0) == 1) { + LogError("LCDproc, socketSend: cannot send data"); + return -1; + } + return 1; +} + +void CLCDproc::defineScreens() +{ + // The Status Screen + + socketPrintf(m_socketfd, "screen_add Status"); + socketPrintf(m_socketfd, "screen_set Status -name Status -heartbeat on -priority info -backlight off"); + + socketPrintf(m_socketfd, "widget_add Status Callsign string"); + socketPrintf(m_socketfd, "widget_add Status DMRNumber string"); + socketPrintf(m_socketfd, "widget_add Status Title string"); + socketPrintf(m_socketfd, "widget_add Status Status string"); + socketPrintf(m_socketfd, "widget_add Status Time string"); + socketPrintf(m_socketfd, "widget_add Status Date string"); + + socketPrintf(m_socketfd, "widget_set Status Callsign 1 1 %s", m_callsign.c_str()); + socketPrintf(m_socketfd, "widget_set Status DMRNumber %u 1 %u", m_cols - 7, m_dmrid); + socketPrintf(m_socketfd, "widget_set Status Title 1 %u MMDVM", m_rows); + socketPrintf(m_socketfd, "widget_set Status Status %u %u Idle", m_cols - 3, m_rows); + + // The DStar Screen + + socketPrintf(m_socketfd, "screen_add DStar"); + socketPrintf(m_socketfd, "screen_set DStar -name DStar -heartbeat on -priority hidden -backlight on"); + + socketPrintf(m_socketfd, "widget_add DStar Mode string"); + socketPrintf(m_socketfd, "widget_add DStar Line2 scroller"); + socketPrintf(m_socketfd, "widget_add DStar Line3 scroller"); + socketPrintf(m_socketfd, "widget_add DStar Line4 scroller"); + + socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 15 2 h 3 Listening"); + socketPrintf(m_socketfd, "widget_set DStar Line3 1 3 15 3 h 3 \"\""); + socketPrintf(m_socketfd, "widget_set DStar Line4 1 4 15 4 h 3 \"\""); + + // The DMR Screen + + socketPrintf(m_socketfd, "screen_add DMR"); + socketPrintf(m_socketfd, "screen_set DMR -name DMR -heartbeat on -priority hidden -backlight on"); + + socketPrintf(m_socketfd, "widget_add DMR Mode string"); + socketPrintf(m_socketfd, "widget_add DMR Slot1_ string"); + socketPrintf(m_socketfd, "widget_add DMR Slot2_ string"); + socketPrintf(m_socketfd, "widget_add DMR Slot1 scroller"); + socketPrintf(m_socketfd, "widget_add DMR Slot2 scroller"); + + socketPrintf(m_socketfd, "widget_set DMR Slot1_ 1 %u 1", m_rows / 2); + socketPrintf(m_socketfd, "widget_set DMR Slot2_ 1 %u 2", m_rows / 2 + 1); + socketPrintf(m_socketfd, "widget_set DMR Slot1 3 1 15 1 h 3 Listening"); + socketPrintf(m_socketfd, "widget_set DMR Slot2 3 2 15 2 h 3 Listening"); + + // The YSF Screen + + socketPrintf(m_socketfd, "screen_add YSF"); + socketPrintf(m_socketfd, "screen_set YSF -name YSF -heartbeat on -priority hidden -backlight on"); + + socketPrintf(m_socketfd, "widget_add YSF Mode string"); + socketPrintf(m_socketfd, "widget_add YSF Line2 scroller"); + socketPrintf(m_socketfd, "widget_add YSF Line3 scroller"); + socketPrintf(m_socketfd, "widget_add YSF Line4 scroller"); + + socketPrintf(m_socketfd, "widget_set YSF Line2 2 1 15 1 h 3 Listening"); + socketPrintf(m_socketfd, "widget_set YSF Line3 3 1 15 1 h 3 \" \""); + socketPrintf(m_socketfd, "widget_set YSF Line4 4 2 15 2 h 3 \" \""); + + // The P25 Screen + + socketPrintf(m_socketfd, "screen_add P25"); + socketPrintf(m_socketfd, "screen_set P25 -name P25 -heartbeat on -priority hidden -backlight on"); + + socketPrintf(m_socketfd, "widget_add P25 Mode string"); + socketPrintf(m_socketfd, "widget_add P25 Line2 scroller"); + socketPrintf(m_socketfd, "widget_add P25 Line3 scroller"); + socketPrintf(m_socketfd, "widget_add P25 Line4 scroller"); + + socketPrintf(m_socketfd, "widget_set P25 Line3 2 1 15 1 h 3 Listening"); + socketPrintf(m_socketfd, "widget_set P25 Line3 3 1 15 1 h 3 \" \""); + socketPrintf(m_socketfd, "widget_set P25 Line4 4 2 15 2 h 3 \" \""); + + m_screensDefined = true; +} diff --git a/LCDproc.h b/LCDproc.h new file mode 100644 index 0000000..2be7515 --- /dev/null +++ b/LCDproc.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(LCDproc_H) +#define LCDproc_H + +#include "Display.h" +#include "Timer.h" + +#include +#include + +class CLCDproc : public CDisplay +{ +public: + CLCDproc(std::string address, unsigned int port, unsigned int localPort, const std::string& callsign, unsigned int dmrid, bool displayClock, bool utc, bool duplex); + virtual ~CLCDproc(); + + virtual bool open(); + + virtual void close(); + +protected: + virtual void setIdleInt(); + virtual void setErrorInt(const char* text); + virtual void setLockoutInt(); + + virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); + virtual void clearDStarInt(); + + virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); + virtual void clearDMRInt(unsigned int slotNo); + + virtual void writeFusionInt(const char* source, const char* dest, const char* type, const char* origin); + virtual void clearFusionInt(); + + virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type); + virtual void clearP25Int(); + + virtual void writeCWInt(); + virtual void clearCWInt(); + + virtual void clockInt(unsigned int ms); + + virtual int socketPrintf(int fd, const char *format, ...); + + virtual void defineScreens(); + +private: + std::string m_address; + unsigned int m_port; + unsigned int m_localPort; + std::string m_callsign; + unsigned int m_dmrid; + bool m_displayClock; + bool m_utc; + bool m_duplex; + bool m_dmr; + CTimer m_clockDisplayTimer; +}; + +#endif diff --git a/MMDVM.ini b/MMDVM.ini index 0d07a1c..ebef00c 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -165,3 +165,10 @@ IdleBrightness=20 Type=3 Brightness=0 Invert=0 + +[LCDproc} +Address=localhost +Port=13666 +#LocalPort=13667 +DisplayClock=1 +UTC=1 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index 4dd308e..b7fbfbc 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -29,6 +29,7 @@ #include "YSFControl.h" #include "P25Control.h" #include "Nextion.h" +#include "LCDproc.h" #include "Thread.h" #include "Log.h" @@ -1030,6 +1031,26 @@ void CMMDVMHost::createDisplay() serial = new CSerialController(port, SERIAL_9600); m_display = new CNextion(m_callsign, dmrid, serial, brightness, displayClock, utc, idleBrightness); + } else if (type == "LCDproc") { + std::string address = m_conf.getLCDprocAddress(); + unsigned int port = m_conf.getLCDprocPort(); + unsigned int localPort = m_conf.getLCDprocLocalPort(); + bool displayClock = m_conf.getLCDprocDisplayClock(); + bool utc = m_conf.getLCDprocUTC(); + + LogInfo(" Address: %s", address.c_str()); + LogInfo(" Port: %u", port); + + if (localPort == 0 ) + LogInfo(" Local Port: random"); + else + LogInfo(" Local Port: %u", localPort); + + LogInfo(" Clock Display: %s", displayClock ? "yes" : "no"); + if (displayClock) + LogInfo(" Display UTC: %s", utc ? "yes" : "no"); + + m_display = new CLCDproc(address.c_str(), port, localPort, m_callsign, dmrid, displayClock, utc, m_duplex); #if defined(HD44780) } else if (type == "HD44780") { unsigned int rows = m_conf.getHD44780Rows(); diff --git a/Makefile b/Makefile index 51aa676..15eede6 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ LDFLAGS = -g OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \ DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \ - Golay24128.o Hamming.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \ + Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \ P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \ Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o diff --git a/Makefile.Pi b/Makefile.Pi index 0d2ec87..a83845d 100644 --- a/Makefile.Pi +++ b/Makefile.Pi @@ -9,7 +9,7 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \ DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \ - Golay24128.o Hamming.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \ + Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \ P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \ Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o diff --git a/Makefile.Pi.HD44780 b/Makefile.Pi.HD44780 index d3c6f1e..dabc153 100644 --- a/Makefile.Pi.HD44780 +++ b/Makefile.Pi.HD44780 @@ -9,7 +9,7 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \ DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \ - Golay24128.o Hamming.o HD44780.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \ + Golay24128.o Hamming.o HD44780.o LCDProc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \ P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \ Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o diff --git a/Makefile.Pi.OLED b/Makefile.Pi.OLED index 02d1018..f42cdc1 100644 --- a/Makefile.Pi.OLED +++ b/Makefile.Pi.OLED @@ -9,7 +9,7 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \ DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \ - Golay24128.o Hamming.o OLED.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \ + Golay24128.o Hamming.o OLED.o LCDProc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \ P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \ Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o diff --git a/Makefile.Pi.PCF8574 b/Makefile.Pi.PCF8574 index e52d353..2d69894 100644 --- a/Makefile.Pi.PCF8574 +++ b/Makefile.Pi.PCF8574 @@ -9,7 +9,7 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \ DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \ - Golay24128.o Hamming.o HD44780.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \ + Golay24128.o Hamming.o HD44780.o LCDProc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \ P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \ Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o diff --git a/Makefile.Solaris b/Makefile.Solaris index d955b0a..ed073b4 100644 --- a/Makefile.Solaris +++ b/Makefile.Solaris @@ -9,7 +9,7 @@ LDFLAGS = -g OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \ DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \ - Golay24128.o Hamming.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \ + Golay24128.o Hamming.o LCDProc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o \ P25Network.o P25NID.o P25Utils.o QR1676.o RS129.o RS241213.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \ Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o