diff --git a/src/GlobalVars.h b/src/GlobalVars.h index 45adde97b..ff6fe8748 100644 --- a/src/GlobalVars.h +++ b/src/GlobalVars.h @@ -30,6 +30,7 @@ #include "configuration/Configuration.h" #include "network/connection.h" #include "network/manager.h" +#include "network/remotecmd.h" #include "sensors/SensorManager.h" #include "status/StatusManager.h" @@ -40,5 +41,8 @@ extern SlimeVR::Configuration::Configuration configuration; extern SlimeVR::Sensors::SensorManager sensorManager; extern SlimeVR::Network::Manager networkManager; extern SlimeVR::Network::Connection networkConnection; +#if USE_REMOTE_COMMAND +extern SlimeVR::Network::RemoteCmd networkRemoteCmd; +#endif #endif diff --git a/src/debug.h b/src/debug.h index b2c4968d5..627ae1121 100644 --- a/src/debug.h +++ b/src/debug.h @@ -47,6 +47,8 @@ #define serialDebug false // Set to true to get Serial output for debugging #define serialBaudRate 115200 +#define USE_REMOTE_COMMAND true +#define ALLOW_REMOTE_WIFI_PROV true #define LED_INTERVAL_STANDBY 10000 #define PRINT_STATE_EVERY_MS 60000 diff --git a/src/logging/Logger.cpp b/src/logging/Logger.cpp index de7a5a639..8fc37fecc 100644 --- a/src/logging/Logger.cpp +++ b/src/logging/Logger.cpp @@ -77,6 +77,13 @@ namespace SlimeVR } Serial.printf("[%-5s] [%s] %s\n", levelToString(level), buf, buffer); + + #if USE_REMOTE_COMMAND + if (getRemoteCmdConncted()) + { + getRemoteCmdStream().printf("[%-5s] [%s] %s\n", levelToString(level), buf, buffer); + } + #endif } } } diff --git a/src/logging/Logger.h b/src/logging/Logger.h index 06e46c979..ba51eb204 100644 --- a/src/logging/Logger.h +++ b/src/logging/Logger.h @@ -4,6 +4,7 @@ #include "Level.h" #include "debug.h" #include +#include "RemoteLogHelper.h" namespace SlimeVR { @@ -98,6 +99,21 @@ namespace SlimeVR } Serial.println(); + + #if USE_REMOTE_COMMAND + if (getRemoteCmdConncted()) + { + Stream & networkStream = getRemoteCmdStream(); + networkStream.printf("[%-5s] [%s] %s", levelToString(level), buf, str); + + for (size_t i = 0; i < size; i++) + { + networkStream.print(array[i]); + } + + networkStream.println(); + } + #endif } const char *const m_Prefix; diff --git a/src/logging/RemoteLogHelper.cpp b/src/logging/RemoteLogHelper.cpp new file mode 100644 index 000000000..3ed48ace8 --- /dev/null +++ b/src/logging/RemoteLogHelper.cpp @@ -0,0 +1,11 @@ +#include + +#include "GlobalVars.h" + +namespace SlimeVR { +namespace Logging { +bool getRemoteCmdConncted() { return networkRemoteCmd.isConnected() && networkConnection.isConnected(); } + +Stream& getRemoteCmdStream() { return networkRemoteCmd.getStream(); } +} // namespace Logging +} // namespace SlimeVR diff --git a/src/logging/RemoteLogHelper.h b/src/logging/RemoteLogHelper.h new file mode 100644 index 000000000..0a7fea404 --- /dev/null +++ b/src/logging/RemoteLogHelper.h @@ -0,0 +1,13 @@ +#ifndef LOGGING_RETOMELOGHELPER_H +#define LOGGING_RETOMELOGHELPER_H + +#include + +namespace SlimeVR { +namespace Logging { +bool getRemoteCmdConncted(); +Stream& getRemoteCmdStream(); +} // namespace Logging +} // namespace SlimeVR + +#endif // LOGGING_RETOMELOGHELPER_H diff --git a/src/main.cpp b/src/main.cpp index b4a4dd8ee..917835208 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,6 +39,9 @@ SlimeVR::Status::StatusManager statusManager; SlimeVR::Configuration::Configuration configuration; SlimeVR::Network::Manager networkManager; SlimeVR::Network::Connection networkConnection; +#if USE_REMOTE_COMMAND +SlimeVR::Network::RemoteCmd networkRemoteCmd; +#endif int sensorToCalibrate = -1; bool blinking = false; diff --git a/src/network/connection.cpp b/src/network/connection.cpp index 1f60ad876..3160d8a9c 100644 --- a/src/network/connection.cpp +++ b/src/network/connection.cpp @@ -577,6 +577,9 @@ void Connection::searchForServer() { statusManager.setStatus(SlimeVR::Status::SERVER_CONNECTING, false); ledManager.off(); +#if USE_REMOTE_COMMAND + networkRemoteCmd.reset(); +#endif m_Logger.debug( "Handshake successful, server is %s:%d", diff --git a/src/network/connection.h b/src/network/connection.h index 7b8f7b6a2..8e7cc17f2 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -31,6 +31,7 @@ #include "sensors/sensor.h" #include "wifihandler.h" #include "featureflags.h" +#include "remotecmd.h" namespace SlimeVR { namespace Network { @@ -124,6 +125,8 @@ class Connection { bool beginBundle(); bool endBundle(); + friend RemoteCmd; + private: void updateSensorState(std::vector & sensors); void maybeRequestFeatureFlags(); diff --git a/src/network/featureflags.h b/src/network/featureflags.h index b0820be40..94eb0a131 100644 --- a/src/network/featureflags.h +++ b/src/network/featureflags.h @@ -26,6 +26,7 @@ #include #include +#include "debug.h" /** * Bit packed flags, enum values start with 0 and indicate which bit it is. @@ -73,8 +74,8 @@ class FirmwareFeatures { public: enum EFirmwareFeatureFlags: uint32_t { // EXAMPLE_FEATURE, + REMOTE_COMMAND = 0, B64_WIFI_SCANNING = 1, - // Add new flags here BITS_TOTAL, @@ -83,6 +84,9 @@ class FirmwareFeatures { // Flags to send static constexpr const std::initializer_list flagsEnabled = { // EXAMPLE_FEATURE, +#ifdef USE_REMOTE_COMMAND + REMOTE_COMMAND, +#endif B64_WIFI_SCANNING, // Add enabled flags here diff --git a/src/network/manager.cpp b/src/network/manager.cpp index c27329fd8..213685289 100644 --- a/src/network/manager.cpp +++ b/src/network/manager.cpp @@ -43,9 +43,15 @@ void Manager::update() { if (!wasConnected) { // WiFi was reconnected, rediscover the server and reconnect networkConnection.reset(); + #ifdef USE_REMOTE_COMMAND + networkRemoteCmd.reset(); + #endif } networkConnection.update(); + #ifdef USE_REMOTE_COMMAND + networkRemoteCmd.update(); + #endif } } // namespace Network diff --git a/src/network/remotecmd.cpp b/src/network/remotecmd.cpp new file mode 100644 index 000000000..de8118f5f --- /dev/null +++ b/src/network/remotecmd.cpp @@ -0,0 +1,56 @@ +#include "remotecmd.h" + +#include "GlobalVars.h" + +namespace SlimeVR { +namespace Network { + +void RemoteCmd::reset() { + rcmdClient = WiFiClient(); + rcmdServer.begin(); +} + +void RemoteCmd::update() { + // Check for new connections to remote command + if (rcmdServer.hasClient()) { + if (rcmdClient.connected()) { + // Connection already exists, drop the new one + rcmdServer.accept().stop(); + r_Logger.info("Remote command multi-connection dropped"); + } else { + IPAddress rejectedIP; + rcmdClient = rcmdServer.accept(); + if (networkConnection.isConnected()) { + // Only accept if rcmdClient have the same remote IP as udpmanager + if (rcmdClient.remoteIP() != networkConnection.m_ServerHost) { + rejectedIP = rcmdClient.remoteIP(); + rcmdClient.stop(); + } + } +#if !ALLOW_REMOTE_WIFI_PROV + else { + rejectedIP = rcmdClient.remoteIP(); + rcmdClient.stop(); + } +#endif + if (rcmdClient.connected()) { + r_Logger.info( + "Remote command from %s connected", + rcmdClient.remoteIP().toString().c_str() + ); + } else { + r_Logger.info( + "Remote command from %s dropped", + rejectedIP.toString().c_str() + ); + } + } + } +} + +bool RemoteCmd::isConnected() { return rcmdClient.connected(); } + +Stream& RemoteCmd::getStream() { return rcmdClient; } + +} // namespace Network +} // namespace SlimeVR diff --git a/src/network/remotecmd.h b/src/network/remotecmd.h new file mode 100644 index 000000000..2f39aa62a --- /dev/null +++ b/src/network/remotecmd.h @@ -0,0 +1,53 @@ +/* + SlimeVR Code is placed under the MIT license + Copyright (c) 2023 SlimeVR Contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ +#ifndef SLIMEVR_NETWORK_REMOTECMD_H_ +#define SLIMEVR_NETWORK_REMOTECMD_H_ + +#include +#include + +#include "globals.h" +#include "logging/Logger.h" + +namespace SlimeVR { +namespace Network { + +class RemoteCmd { +public: + void reset(); + void update(); + + bool isConnected(); + Stream& getStream(); + +private: + SlimeVR::Logging::Logger r_Logger = SlimeVR::Logging::Logger("RemoteCmd"); + + WiFiServer rcmdServer = WiFiServer(23); + WiFiClient rcmdClient; +}; + +} // namespace Network +} // namespace SlimeVR + +#endif // SLIMEVR_NETWORK_REMOTECMD_H_ diff --git a/src/serial/serialcommands.cpp b/src/serial/serialcommands.cpp index a85469502..40d94352e 100644 --- a/src/serial/serialcommands.cpp +++ b/src/serial/serialcommands.cpp @@ -39,6 +39,7 @@ namespace SerialCommands { CmdCallback<6> cmdCallbacks; CmdParser cmdParser; CmdBuffer<256> cmdBuffer; + bool cmdFromRemote = false; bool lengthCheck (const char* const text, unsigned int length, const char* const cmd, const char* const name) @@ -147,6 +148,10 @@ namespace SerialCommands { } void cmdGet(CmdParser * parser) { +#if USE_REMOTE_COMMAND && ALLOW_REMOTE_WIFI_PROV + if (cmdFromRemote && !networkConnection.isConnected()) return; +#endif + if (parser->getParamCount() < 2) { return; } @@ -159,43 +164,22 @@ namespace SerialCommands { } if (parser->equalCmdParam(1, "CONFIG")) { - String str = - "BOARD=%d\n" - "IMU=%d\n" - "SECOND_IMU=%d\n" - "IMU_ROTATION=%f\n" - "SECOND_IMU_ROTATION=%f\n" - "BATTERY_MONITOR=%d\n" - "BATTERY_SHIELD_RESISTANCE=%d\n" - "BATTERY_SHIELD_R1=%d\n" - "BATTERY_SHIELD_R2=%d\n" - "PIN_IMU_SDA=%d\n" - "PIN_IMU_SCL=%d\n" - "PIN_IMU_INT=%d\n" - "PIN_IMU_INT_2=%d\n" - "PIN_BATTERY_LEVEL=%d\n" - "LED_PIN=%d\n" - "LED_INVERTED=%d\n"; - - Serial.printf( - str.c_str(), - BOARD, - IMU, - SECOND_IMU, - IMU_ROTATION, - SECOND_IMU_ROTATION, - BATTERY_MONITOR, - BATTERY_SHIELD_RESISTANCE, - BATTERY_SHIELD_R1, - BATTERY_SHIELD_R2, - PIN_IMU_SDA, - PIN_IMU_SCL, - PIN_IMU_INT, - PIN_IMU_INT_2, - PIN_BATTERY_LEVEL, - LED_PIN, - LED_INVERTED - ); + logger.info("BOARD=%d", BOARD); + logger.info("IMU=%d", IMU); + logger.info("SECOND_IMU=%d", SECOND_IMU); + logger.info("IMU_ROTATION=%f", IMU_ROTATION); + logger.info("SECOND_IMU_ROTATION=%f", SECOND_IMU_ROTATION); + logger.info("BATTERY_MONITOR=%d", BATTERY_MONITOR); + logger.info("BATTERY_SHIELD_RESISTANCE=%d", BATTERY_SHIELD_RESISTANCE); + logger.info("BATTERY_SHIELD_R1=%d", BATTERY_SHIELD_R1); + logger.info("BATTERY_SHIELD_R2=%d", BATTERY_SHIELD_R2); + logger.info("PIN_IMU_SDA=%d", PIN_IMU_SDA); + logger.info("PIN_IMU_SCL=%d", PIN_IMU_SCL); + logger.info("PIN_IMU_INT=%d", PIN_IMU_INT); + logger.info("PIN_IMU_INT_2=%d", PIN_IMU_INT_2); + logger.info("PIN_BATTERY_LEVEL=%d", PIN_BATTERY_LEVEL); + logger.info("LED_PIN=%d", LED_PIN); + logger.info("LED_INVERTED=%d", LED_INVERTED); } if (parser->equalCmdParam(1, "TEST")) { @@ -261,11 +245,19 @@ namespace SerialCommands { } void cmdReboot(CmdParser * parser) { +#if USE_REMOTE_COMMAND && ALLOW_REMOTE_WIFI_PROV + if (cmdFromRemote && !networkConnection.isConnected()) return; +#endif + logger.info("REBOOT"); ESP.restart(); } void cmdFactoryReset(CmdParser * parser) { +#if USE_REMOTE_COMMAND && ALLOW_REMOTE_WIFI_PROV + if (cmdFromRemote && !networkConnection.isConnected()) return; +#endif + logger.info("FACTORY RESET"); configuration.reset(); @@ -291,6 +283,10 @@ namespace SerialCommands { } void cmdTemperatureCalibration(CmdParser* parser) { +#if USE_REMOTE_COMMAND && ALLOW_REMOTE_WIFI_PROV + if (cmdFromRemote && !networkConnection.isConnected()) return; +#endif + if (parser->getParamCount() > 1) { if (parser->equalCmdParam(1, "PRINT")) { for (auto sensor : sensorManager.getSensors()) { @@ -333,5 +329,15 @@ namespace SerialCommands { void update() { cmdCallbacks.updateCmdProcessing(&cmdParser, &cmdBuffer, &Serial); + #if USE_REMOTE_COMMAND + if (networkRemoteCmd.isConnected()) { + Stream & networkStream = networkRemoteCmd.getStream(); + cmdFromRemote = true; + while (networkStream.available()) { + cmdCallbacks.updateCmdProcessing(&cmdParser, &cmdBuffer, &networkStream); + } + cmdFromRemote = false; + } + #endif } }