diff --git a/MMDVM-Host.cpp b/MMDVM-Host.cpp index 2cf46b85..7a64c34d 100644 --- a/MMDVM-Host.cpp +++ b/MMDVM-Host.cpp @@ -1921,6 +1921,8 @@ bool CMMDVMHost::createModem() return false; } + writeJSONModem(); + return true; } @@ -3522,6 +3524,69 @@ void CMMDVMHost::writeJSONMode(const std::string& mode) WriteJSON("MMDVM", json); } +void CMMDVMHost::buildModemInfo(nlohmann::json& json) +{ + json["timestamp"] = CUtils::createTimestamp(); + + if (m_modem == nullptr) { + json["open"] = false; + return; + } + + json["open"] = true; + json["protocol"] = m_modem->getVersion(); + json["description"] = m_modem->getDescription(); + if (!m_modem->getCPU().empty()) + json["cpu"] = m_modem->getCPU(); + if (!m_modem->getUDID().empty()) + json["udid"] = m_modem->getUDID(); + + nlohmann::json modes = nlohmann::json::array(); + if (m_modem->hasDStar()) + modes.push_back("D-Star"); + if (m_modem->hasDMR()) + modes.push_back("DMR"); + if (m_modem->hasYSF()) + modes.push_back("YSF"); + if (m_modem->hasP25()) + modes.push_back("P25"); + if (m_modem->hasNXDN()) + modes.push_back("NXDN"); + if (m_modem->hasFM()) + modes.push_back("FM"); + if (m_modem->hasPOCSAG()) + modes.push_back("POCSAG"); + json["modes"] = modes; + + std::string protocol = m_conf.getModemProtocol(); + json["connection"] = protocol; + if (protocol == "uart") { + json["port"] = m_conf.getModemUARTPort(); + json["speed"] = m_conf.getModemUARTSpeed(); + } else if (protocol == "i2c") { + json["port"] = m_conf.getModemI2CPort(); + } else if (protocol == "udp") { + json["address"] = m_conf.getModemModemAddress(); + json["port"] = m_conf.getModemModemPort(); + } +} + +void CMMDVMHost::buildModemInfoString(std::string& str) +{ + nlohmann::json json; + buildModemInfo(json); + + str = json.dump(-1, ' ', false, nlohmann::json::error_handler_t::ignore); +} + +void CMMDVMHost::writeJSONModem() +{ + nlohmann::json json; + buildModemInfo(json); + + WriteJSON("Modem", json); +} + void CMMDVMHost::writeJSONMessage(const std::string& message) { nlohmann::json json; diff --git a/MMDVM-Host.h b/MMDVM-Host.h index cf601ec1..47b047a2 100644 --- a/MMDVM-Host.h +++ b/MMDVM-Host.h @@ -44,6 +44,8 @@ #include "Modem.h" #include "Conf.h" +#include + #include class CMMDVMHost @@ -56,6 +58,7 @@ class CMMDVMHost void buildNetworkStatusString(std::string &str); void buildNetworkHostsString(std::string &str); + void buildModemInfoString(std::string &str); private: void setListenOnly(bool on); @@ -229,6 +232,8 @@ class CMMDVMHost void writeJSONMode(const std::string& mode); void writeJSONMessage(const std::string& message); + void buildModemInfo(nlohmann::json& json); + void writeJSONModem(); static void onDisplay(const unsigned char* message, unsigned int length); static void onQSOInfo(const unsigned char* message, unsigned int length); diff --git a/Modem.cpp b/Modem.cpp index 8b62be4d..4c16a5b2 100644 --- a/Modem.cpp +++ b/Modem.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -344,6 +345,9 @@ m_fmExtEnable(false), #endif m_capabilities1(0x00U), m_capabilities2(0x00U), +m_description(), +m_cpu(), +m_udid(), m_serialDataLen(0U) { m_buffer = new unsigned char[BUFFER_LENGTH]; @@ -2307,32 +2311,63 @@ bool CModem::readVersion() if ((resp == RESP_TYPE_MMDVM::OK) && (m_buffer[2U] == MMDVM_GET_VERSION)) { m_protocolVersion = m_buffer[3U]; switch (m_protocolVersion) { - case 1U: + case 1U: { LogInfo("MMDVM protocol version: 1, description: %.*s", m_length - 4U, m_buffer + 4U); + // Firmware built with its chip ID sends it as text after + // a NUL that ends the description. + unsigned int descLen = 0U; + while ((4U + descLen) < m_length && m_buffer[4U + descLen] != 0x00U) + descLen++; + m_description = std::string((char*)(m_buffer + 4U), descLen); + m_cpu.clear(); + m_udid.clear(); + for (unsigned int i = 4U + descLen + 1U; i < m_length && m_buffer[i] != 0x00U; i++) { + if (::isxdigit(m_buffer[i])) + m_udid += char(::toupper(m_buffer[i])); + } + if (!m_udid.empty()) + LogInfo("UDID: %s", m_udid.c_str()); m_capabilities1 = CAP1_DSTAR | CAP1_DMR | CAP1_YSF | CAP1_P25 | CAP1_NXDN; m_capabilities2 = CAP2_POCSAG; if (::strstr((char*)(m_buffer + 4U), "v1.6.") != nullptr) m_capabilities1 |= CAP1_M17; break; + } - case 2U: + case 2U: { LogInfo("MMDVM protocol version: 2, description: %.*s", m_length - 23U, m_buffer + 23U); + m_description = std::string((char*)(m_buffer + 23U), m_length - 23U); + m_cpu.clear(); + m_udid.clear(); + unsigned int udidLen = 0U; switch (m_buffer[6U]) { case 0U: LogInfo("CPU: Atmel ARM, UDID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", m_buffer[7U], m_buffer[8U], m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U], m_buffer[13U], m_buffer[14U], m_buffer[15U], m_buffer[16U], m_buffer[17U], m_buffer[18U], m_buffer[19U], m_buffer[20U], m_buffer[21U], m_buffer[22U]); + m_cpu = "Atmel ARM"; + udidLen = 16U; break; case 1U: LogInfo("CPU: NXP ARM, UDID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", m_buffer[7U], m_buffer[8U], m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U], m_buffer[13U], m_buffer[14U], m_buffer[15U], m_buffer[16U], m_buffer[17U], m_buffer[18U], m_buffer[19U], m_buffer[20U], m_buffer[21U], m_buffer[22U]); + m_cpu = "NXP ARM"; + udidLen = 16U; break; case 2U: LogInfo("CPU: ST-Micro ARM, UDID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", m_buffer[7U], m_buffer[8U], m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U], m_buffer[13U], m_buffer[14U], m_buffer[15U], m_buffer[16U], m_buffer[17U], m_buffer[18U]); + m_cpu = "ST-Micro ARM"; + udidLen = 12U; break; default: LogInfo("CPU: Unknown type: %u", m_buffer[6U]); break; } + for (unsigned int i = 0U; i < udidLen; i++) { + char hex[3U]; + ::snprintf(hex, 3U, "%02X", m_buffer[7U + i]); + m_udid += hex; + } m_capabilities1 = m_buffer[4U]; m_capabilities2 = m_buffer[5U]; + } break; default: @@ -2372,6 +2407,21 @@ bool CModem::readVersion() return false; } +std::string CModem::getDescription() const +{ + return m_description; +} + +std::string CModem::getCPU() const +{ + return m_cpu; +} + +std::string CModem::getUDID() const +{ + return m_udid; +} + bool CModem::readStatus() { assert(m_port != nullptr); diff --git a/Modem.h b/Modem.h index d40b36cd..dff123b1 100644 --- a/Modem.h +++ b/Modem.h @@ -91,6 +91,13 @@ class CModem { unsigned int getVersion() const; + // What the modem said in its version reply: its description, CPU + // (protocol 2 only) and chip ID (protocol 2, or protocol 1 firmware + // built to send one), each empty when it did not say. + std::string getDescription() const; + std::string getCPU() const; + std::string getUDID() const; + #if defined(USE_DSTAR) unsigned int readDStarData(unsigned char* data); #endif @@ -435,6 +442,9 @@ class CModem { #endif unsigned char m_capabilities1; unsigned char m_capabilities2; + std::string m_description; + std::string m_cpu; + std::string m_udid; bool readVersion(); bool readStatus(); diff --git a/RemoteControl.cpp b/RemoteControl.cpp index ba8246db..e14f02ad 100644 --- a/RemoteControl.cpp +++ b/RemoteControl.cpp @@ -212,6 +212,16 @@ REMOTE_COMMAND CRemoteControl::getCommand(const std::string& command) } m_command = REMOTE_COMMAND::CONFIG_HOSTS; + } else if (m_args.at(0U) == "modem") { + // The modem's details as JSON, the same as the "Modem" message + // sent when it was opened. + if (m_host != nullptr) { + m_host->buildModemInfoString(reply); + } else { + reply = "KO"; + } + + m_command = REMOTE_COMMAND::MODEM_INFO; } else { reply = "KO"; } diff --git a/RemoteControl.h b/RemoteControl.h index 76844b35..886e9f27 100644 --- a/RemoteControl.h +++ b/RemoteControl.h @@ -106,7 +106,8 @@ enum class REMOTE_COMMAND { LISTEN_OFF, RELOAD, CONNECTION_STATUS, - CONFIG_HOSTS + CONFIG_HOSTS, + MODEM_INFO }; class CRemoteControl { diff --git a/schema.json b/schema.json index 05625496..9b3f87f6 100644 --- a/schema.json +++ b/schema.json @@ -25,7 +25,9 @@ "loss": {"type": "number", "minimum": 0.0}, "ber": {"type": "number", "minimum": 0.0}, "rssi": {"type": "integer", "minimum": -200, "maximum": -10}, - "timestamp": {"type": "string"} + "timestamp": {"type": "string"}, + "modem_mode": {"type": "string", "enum": ["D-Star", "DMR", "YSF", "P25", "NXDN", "FM", "POCSAG"]}, + "modem_connection": {"type": "string", "enum": ["uart", "udp", "i2c", "null"]} }, "MMDVM": { @@ -182,6 +184,22 @@ "required": ["timestamp", "state"] }, + "Modem": { + "type": "object", + "timestamp": {"$ref": "#/$defs/timestamp"}, + "open": {"type": "boolean"}, + "protocol": {"type": "integer", "enum": [1, 2]}, + "description": {"type": "string"}, + "cpu": {"type": "string"}, + "udid": {"type": "string"}, + "modes": {"type": "array", "items": {"$ref": "#/$defs/modem_mode"}}, + "connection": {"$ref": "#/$defs/modem_connection"}, + "port": {"type": ["string", "integer"]}, + "speed": {"type": "integer"}, + "address": {"type": "string"}, + "required": ["timestamp", "open"] + }, + "M17": { "type": "object", "timestamp": {"$ref": "#/$defs/timestamp"},