From 627e4f1dc33e405260d56ec7f80f1bcbb7fcdfd7 Mon Sep 17 00:00:00 2001 From: Tony Corbett G0WFV Date: Fri, 28 Oct 2016 13:22:20 +0100 Subject: [PATCH 1/7] 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 From 3c614839a240378678f688685e6359ab2f94a722 Mon Sep 17 00:00:00 2001 From: Tony Corbett G0WFV Date: Sat, 29 Oct 2016 11:25:25 +0100 Subject: [PATCH 2/7] Make socket fd writeable in select and select timout now 0 --- LCDproc.cpp | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/LCDproc.cpp b/LCDproc.cpp index eafcf7c..0e41f8b 100644 --- a/LCDproc.cpp +++ b/LCDproc.cpp @@ -37,14 +37,10 @@ #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; +fd_set m_readfds, m_writefds; struct timeval m_timeout; int m_recvsize; unsigned int m_rows(0); @@ -384,29 +380,30 @@ void CLCDproc::clockInt(unsigned int ms) } // We must set all this information on each select we do - FD_ZERO(&m_readmask); // empty readmask + FD_ZERO(&m_readfds); // empty readfds - // 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); + // Then we put all the descriptors we want to wait for in a mask = m_readfds + FD_SET(m_socketfd, &m_readfds); + FD_SET(STDIN_FILENO, &m_readfds); // STDIN_FILENO = 0 (standard input); // Timeout, we will stop waiting for information m_timeout.tv_sec = 0; - m_timeout.tv_usec = SOCKET_TIMEOUT; + m_timeout.tv_usec = 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 + * readfds = &m_readfds * 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) + if (select(m_socketfd + 1, &m_readfds, NULL, NULL, &m_timeout) == -1) LogError("LCDproc, error on select"); // If something was received from the server... - if (FD_ISSET(m_socketfd, &m_readmask)) { + if (FD_ISSET(m_socketfd, &m_readfds)) { m_recvsize = recv(m_socketfd, m_buffer, BUFFER_MAX_LEN, 0); if (m_recvsize == -1) @@ -496,7 +493,7 @@ void CLCDproc::clockInt(unsigned int ms) // only for debugging purposes! /* - if (FD_ISSET(STDIN_FILENO, &m_readmask)) { + if (FD_ISSET(STDIN_FILENO, &m_readfds)) { fgets(m_buffer, BUFFER_MAX_LEN, stdin); if (send(m_socketfd, m_buffer, strlen(m_buffer) + 1, 0) == -1) @@ -527,10 +524,22 @@ int CLCDproc::socketPrintf(int fd, const char *format, ...) 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; + FD_ZERO(&m_writefds); // empty writefds + FD_SET(m_socketfd, &m_writefds); + + m_timeout.tv_sec = 0; + m_timeout.tv_usec = 0; + + if (select(m_socketfd + 1, NULL, &m_writefds, NULL, &m_timeout) == -1) + LogError("LCDproc, error on select"); + + if (FD_ISSET(m_socketfd, &m_writefds)) { + if (send(m_socketfd, buf, strlen(buf) + 1, 0) == -1) { + LogError("LCDproc, cannot send data"); + return -1; + } } + return 1; } From 655de17cdd0076931b2e6fb115f7bd418c4fac1e Mon Sep 17 00:00:00 2001 From: Tony Corbett G0WFV Date: Sat, 29 Oct 2016 11:28:56 +0100 Subject: [PATCH 3/7] Fix typo in MMDVM.ini [LCDproc] section and mend Conf.cpp accordingly --- Conf.cpp | 2 +- MMDVM.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Conf.cpp b/Conf.cpp index 4acc718..ec69a27 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -232,7 +232,7 @@ bool CConf::read() section = SECTION_NEXTION; else if (::strncmp(buffer, "[OLED]", 6U) == 0) section = SECTION_OLED; - else if (::strncmp(buffer, "[LCDproc]", 6U) == 0) + else if (::strncmp(buffer, "[LCDproc]", 9U) == 0) section = SECTION_LCDPROC; else section = SECTION_NONE; diff --git a/MMDVM.ini b/MMDVM.ini index ebef00c..72e9f92 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -166,7 +166,7 @@ Type=3 Brightness=0 Invert=0 -[LCDproc} +[LCDproc] Address=localhost Port=13666 #LocalPort=13667 From 539dfe372ae94bce2fb789964006d6647f12622c Mon Sep 17 00:00:00 2001 From: Tony Corbett G0WFV Date: Sat, 29 Oct 2016 11:41:20 +0100 Subject: [PATCH 4/7] OCD satisfaction aka fix indentation of code! --- LCDproc.cpp | 81 ++++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/LCDproc.cpp b/LCDproc.cpp index 0e41f8b..e288ff3 100644 --- a/LCDproc.cpp +++ b/LCDproc.cpp @@ -106,12 +106,12 @@ bool CLCDproc::open() 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); + 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"); + LogError("LCDproc, cannot connect to server"); return false; } @@ -380,36 +380,35 @@ void CLCDproc::clockInt(unsigned int ms) } // We must set all this information on each select we do - FD_ZERO(&m_readfds); // empty readfds + FD_ZERO(&m_readfds); // empty readfds - // Then we put all the descriptors we want to wait for in a mask = m_readfds - FD_SET(m_socketfd, &m_readfds); - FD_SET(STDIN_FILENO, &m_readfds); // STDIN_FILENO = 0 (standard input); + // Then we put all the descriptors we want to wait for in a mask = m_readfds + FD_SET(m_socketfd, &m_readfds); + FD_SET(STDIN_FILENO, &m_readfds); // STDIN_FILENO = 0 (standard input); - // Timeout, we will stop waiting for information - m_timeout.tv_sec = 0; - m_timeout.tv_usec = 0; - //m_timeout.tv_usec = SOCKET_TIMEOUT; + // Timeout, we will stop waiting for information + m_timeout.tv_sec = 0; + m_timeout.tv_usec = 0; - /* The first parameter is the biggest descriptor + 1. The first one was 0, so - * every other descriptor will be bigger - * - * readfds = &m_readfds - * writefds = we are not waiting for writefds - * exceptfds = we are not waiting for exception fds + /* The first parameter is the biggest descriptor + 1. The first one was 0, so + * every other descriptor will be bigger + * + * readfds = &m_readfds + * writefds = we are not waiting for writefds + * exceptfds = we are not waiting for exception fds */ - if (select(m_socketfd + 1, &m_readfds, NULL, NULL, &m_timeout) == -1) - LogError("LCDproc, error on select"); + if (select(m_socketfd + 1, &m_readfds, NULL, NULL, &m_timeout) == -1) + LogError("LCDproc, error on select"); - // If something was received from the server... - if (FD_ISSET(m_socketfd, &m_readfds)) { - m_recvsize = recv(m_socketfd, m_buffer, BUFFER_MAX_LEN, 0); + // If something was received from the server... + if (FD_ISSET(m_socketfd, &m_readfds)) { + m_recvsize = recv(m_socketfd, m_buffer, BUFFER_MAX_LEN, 0); - if (m_recvsize == -1) - LogError("LCDproc, cannot receive information"); + if (m_recvsize == -1) + LogError("LCDproc, cannot receive information"); - m_buffer[m_recvsize] = '\0'; + m_buffer[m_recvsize] = '\0'; int i = 0; char *argv[256]; @@ -476,7 +475,7 @@ void CLCDproc::clockInt(unsigned int ms) LogDebug("%s", m_buffer1); } } - + /* Restart tokenizing */ argc = 0; newtoken = 1; @@ -513,25 +512,25 @@ int CLCDproc::socketPrintf(int fd, const char *format, ...) unsigned int size = 0; va_start(ap, format); - size = vsnprintf(buf, sizeof(buf), format, ap); - va_end(ap); + size = vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); - if (size < 0) { - LogError("LCDproc, socketPrintf: vsnprintf failed"); - return -1; - } + if (size < 0) { + LogError("LCDproc, socketPrintf: vsnprintf failed"); + return -1; + } - if (size > sizeof(buf)) - LogWarning("LCDproc, socketPrintf: vsnprintf truncated message"); + if (size > sizeof(buf)) + LogWarning("LCDproc, socketPrintf: vsnprintf truncated message"); - FD_ZERO(&m_writefds); // empty writefds - FD_SET(m_socketfd, &m_writefds); + FD_ZERO(&m_writefds); // empty writefds + FD_SET(m_socketfd, &m_writefds); - m_timeout.tv_sec = 0; - m_timeout.tv_usec = 0; + m_timeout.tv_sec = 0; + m_timeout.tv_usec = 0; - if (select(m_socketfd + 1, NULL, &m_writefds, NULL, &m_timeout) == -1) - LogError("LCDproc, error on select"); + if (select(m_socketfd + 1, NULL, &m_writefds, NULL, &m_timeout) == -1) + LogError("LCDproc, error on select"); if (FD_ISSET(m_socketfd, &m_writefds)) { if (send(m_socketfd, buf, strlen(buf) + 1, 0) == -1) { From 3ff711f3ea17a375fccbb4264a98ef35ef3373b7 Mon Sep 17 00:00:00 2001 From: Tony Corbett G0WFV Date: Sat, 29 Oct 2016 11:53:42 +0100 Subject: [PATCH 5/7] One last tweak for consistency! --- MMDVM.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MMDVM.ini b/MMDVM.ini index 72e9f92..de82b79 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -171,4 +171,4 @@ Address=localhost Port=13666 #LocalPort=13667 DisplayClock=1 -UTC=1 +UTC=0 From 44e8f6717a5c590387d03b30904fa89bc6af7f44 Mon Sep 17 00:00:00 2001 From: Tony Corbett G0WFV Date: Sat, 29 Oct 2016 20:00:32 +0100 Subject: [PATCH 6/7] LCDproc: Make dimming the status screen optional If you run another LCDproc client that doesn't dim the display when it displays its info, the display will effectively flash on and off (or dim/bright depending on your LCDd configuration) as it switches screens between MMDVMHost and the other client(s). Making the dimming optional and turning it off in the host stops this "annoyance". --- Conf.cpp | 7 +++++++ Conf.h | 2 ++ LCDproc.cpp | 7 ++++--- LCDproc.h | 3 ++- MMDVM.ini | 1 + MMDVMHost.cpp | 5 ++++- 6 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Conf.cpp b/Conf.cpp index ec69a27..7a78d57 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -592,6 +592,8 @@ bool CConf::read() m_lcdprocDisplayClock = ::atoi(value) == 1; else if (::strcmp(key, "UTC") == 0) m_lcdprocUTC = ::atoi(value) == 1; + else if (::strcmp(key, "DimOnIdle") == 0) + m_lcdprocDimOnIdle = ::atoi(value) == 1; } } @@ -1199,3 +1201,8 @@ bool CConf::getLCDprocUTC() const { return m_lcdprocUTC; } + +bool CConf::getLCDprocDimOnIdle() const +{ + return m_lcdprocDimOnIdle; +} diff --git a/Conf.h b/Conf.h index 22c905a..4bf739d 100644 --- a/Conf.h +++ b/Conf.h @@ -187,6 +187,7 @@ public: unsigned int getLCDprocLocalPort() const; bool getLCDprocDisplayClock() const; bool getLCDprocUTC() const; + bool getLCDprocDimOnIdle() const; private: std::string m_file; @@ -328,6 +329,7 @@ private: unsigned int m_lcdprocLocalPort; bool m_lcdprocDisplayClock; bool m_lcdprocUTC; + bool m_lcdprocDimOnIdle; }; #endif diff --git a/LCDproc.cpp b/LCDproc.cpp index e288ff3..e2165e8 100644 --- a/LCDproc.cpp +++ b/LCDproc.cpp @@ -51,7 +51,7 @@ 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) : +CLCDproc::CLCDproc(std::string address, unsigned int port, unsigned int localPort, const std::string& callsign, unsigned int dmrid, bool displayClock, bool utc, bool duplex, bool dimOnIdle) : CDisplay(), m_address(address), m_port(port), @@ -62,6 +62,7 @@ m_displayClock(displayClock), m_utc(utc), m_duplex(duplex), //m_duplex(true), // uncomment to force duplex display for testing! +m_dimOnIdle(dimOnIdle), m_dmr(false), m_clockDisplayTimer(1000U, 0U, 250U) // Update the clock display every 250ms { @@ -539,7 +540,7 @@ int CLCDproc::socketPrintf(int fd, const char *format, ...) } } - return 1; + return 0; } void CLCDproc::defineScreens() @@ -547,7 +548,7 @@ 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, "screen_set Status -name Status -heartbeat on -priority info -backlight %s", m_dimOnIdle ? "off" : "on"); socketPrintf(m_socketfd, "widget_add Status Callsign string"); socketPrintf(m_socketfd, "widget_add Status DMRNumber string"); diff --git a/LCDproc.h b/LCDproc.h index 2be7515..e2a215b 100644 --- a/LCDproc.h +++ b/LCDproc.h @@ -28,7 +28,7 @@ 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); + CLCDproc(std::string address, unsigned int port, unsigned int localPort, const std::string& callsign, unsigned int dmrid, bool displayClock, bool utc, bool duplex, bool dimOnIdle); virtual ~CLCDproc(); virtual bool open(); @@ -70,6 +70,7 @@ private: bool m_displayClock; bool m_utc; bool m_duplex; + bool m_dimOnIdle; bool m_dmr; CTimer m_clockDisplayTimer; }; diff --git a/MMDVM.ini b/MMDVM.ini index de82b79..7df7948 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -170,5 +170,6 @@ Invert=0 Address=localhost Port=13666 #LocalPort=13667 +DimOnIdle=0 DisplayClock=1 UTC=0 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index b7fbfbc..dcf4c31 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -1037,6 +1037,7 @@ void CMMDVMHost::createDisplay() unsigned int localPort = m_conf.getLCDprocLocalPort(); bool displayClock = m_conf.getLCDprocDisplayClock(); bool utc = m_conf.getLCDprocUTC(); + bool dimOnIdle = m_conf.getLCDprocDimOnIdle(); LogInfo(" Address: %s", address.c_str()); LogInfo(" Port: %u", port); @@ -1046,11 +1047,13 @@ void CMMDVMHost::createDisplay() else LogInfo(" Local Port: %u", localPort); + LogInfo(" Dim Display on Idle: %s", dimOnIdle ? "yes" : "no"); 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); + m_display = new CLCDproc(address.c_str(), port, localPort, m_callsign, dmrid, displayClock, utc, m_duplex, dimOnIdle); #if defined(HD44780) } else if (type == "HD44780") { unsigned int rows = m_conf.getHD44780Rows(); From c2903d9578a07df9e353f3359487097cfec13ca0 Mon Sep 17 00:00:00 2001 From: Tony Corbett G0WFV Date: Sun, 30 Oct 2016 00:25:14 +0300 Subject: [PATCH 7/7] LCDproc: Fix time display error --- LCDproc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LCDproc.cpp b/LCDproc.cpp index e2165e8..e953676 100644 --- a/LCDproc.cpp +++ b/LCDproc.cpp @@ -373,8 +373,8 @@ void CLCDproc::clockInt(unsigned int ms) 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); + 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();