Sunday, May 12, 2024

"Remote Control Finger" for FlexRadio (or any button)

 

-----

Ever need to have physical access to a button or switch at your house when you are nowhere near your house?  This doesn't happen often with the FlexRadio, but sometimes you really really really need to cycle the power or even more importantly "Press and Hold" the power button for a "WHITE LED REBOOT".  A key feature of the FlexRadio is it's built in remote capability that allows a ham radio operator the easy use the rig from anywhere in the world.  That, of course, is unless if you are on the opposite side of the planet and need access to the rigs front panel power button.

-----

One way to solve this is with a "Remote Control Finger".  We had an Arduino IDE compatible D1 Mini and a hobby servo motor already in the parts box so these acted as the main ingredients for the build.  The "Remote Control Finger" works stand alone when you are on your Local Area Network (LAN) and you just have the rig in the basement, or attic, or antenna shack.  If you are anyway from your LAN it assumes you are running a VPN which if you are a serious FlexRadio remote user you already are doing or should really consider doing.  The D1 Mini boots up as a webserver and presents these options:

-----

Here's a short video of the "Remote Control Finger" in action:

Notice that the "finger" is 3D Printed.  The .STL file is here.   I used a paperclip to hinge the "finger" to the servo. 

-----

My quick, sloppy, but perfectly working IDE code is:// Remote Control Finger
//
// Ardiuno IDE for
// LOLIN(WEMOS) D1 R2 & D1 MINI (should work fine with others)
//
// Controls a low cost hobby servo motor to be moved into two positions
// via a web interface from anywhere in the world.  
//
// This is handy when you need to physically push a button, PTT, or move a switch.
// Inspired by the infrequent need to hold down the FlexRadio power button when
// physical access to the rig is not possible.
//
// Works stand alone when you are on your Local Area Network (LAN).
// Assumes you are running a VPN if not on the LAN, which if you are a serious
// FlexRadio remote user you already are or should really consider doing.
//
// WhiskeyTangoHotel.Com for details
// May 2024

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <Servo.h>

// Adjust the PRESSED and RELEASED servo positions to match your mounting position
const int released_servo_position = 50;  // Value limits are 0 to 180
const int pressed_servo_postion = 65;  // Higher number presses "harder"  Value limits are 0 to 180
const int tap_delay = 750;   // mSeconds to stay in PRESSED position when we just want to tap the power button

const char* ssid = "ur_SSID_name";   // Your SSID
const char* password = "ur_SSID_password";  //Your SSID Password
String FingerStatus =  "STATUS:<br/>Remote Finger is RELEASED and LED OFF";  // This is set as the the 'wake up' state below

ESP8266WebServer server(80);
Servo servo; // Servo object to control the servo motor

void handleRoot() {  // Do this for URL of: http://localIP/  For example http://192.168.1.53/
  String message = "<br/>";
  message = FingerStatus + message;
  message += "<br/>Valid http://" + WiFi.localIP().toString() + " options are: <a href=\"http://" + WiFi.localIP().toString() + "/press\">/press</a> or ";
  message += "<a href=\"http://" + WiFi.localIP().toString() + "/release\">/release</a> or <a href=\"http://" + WiFi.localIP().toString() + "/tap\">/tap</a><br/>";
  for (uint8_t i=0; i<server.args(); i++){
    message += " " + server.argName(i) + ": " + server.arg(i) + "<br/>";
  }
  server.send(404, "text/html", "<b>" + message + "<b/>");
}

void handleNotFound(){  // Do this for URLs that are invalid. For example http://192.168.1.53/junkjunkpage
  String message = "The address " + WiFi.localIP().toString();
  message += server.uri();
  message += " is NOT FOUND!!! ";
  message += "<br/>";
  message = FingerStatus + "<br/><br/>" + message;
  message += "<br/>Valid http://" + WiFi.localIP().toString() + " options are: <a href=\"http://" + WiFi.localIP().toString() + "/press\">/press</a> or ";
  message += "<a href=\"http://" + WiFi.localIP().toString() + "/release\">/release</a> or <a href=\"http://" + WiFi.localIP().toString() + "/tap\">/tap</a><br/>";
  for (uint8_t i=0; i<server.args(); i++){
    message += " " + server.argName(i) + ": " + server.arg(i) + "<br/>";
  }
  server.send(200, "text/html", "<b>" + message + "<b/>");
}

void setup(void){
  // ASAP we want to wake the unit up in RELEASED and not PRESSED condition
  servo.attach(D1); // Attach the servo to pin
  servo.write(released_servo_position); // Move servo to RELEASE position
 
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  Serial.println(" ");

  // Wait for WiFi connection
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print("Trying to connect to ");
    Serial.print(ssid);
    Serial.print(" on ");
    Serial.print(WiFi.localIP());
    Serial.println(".");
    // Blink the LED while trying to connect
    digitalWrite(LED_BUILTIN, LOW);  // turn the On Board LED ON [inverse logic]
    delay(500);    
    digitalWrite(LED_BUILTIN, HIGH);  // turn the On Board LED OFF [inverse logic]
    delay(500);
  }

  // Update the Serial Monitor (for debug)
  Serial.println("");
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.println(WiFi.localIP());
 
  for (int i = 0; i <= 25; i++) {  // Fast flashing of the LED when WiFi connected.
    digitalWrite(LED_BUILTIN, LOW);  // turn the On Board LED ON [inverse logic]
    delay(50);    
    digitalWrite(LED_BUILTIN, HIGH);  // turn the On Board LED OFF [inverse logic]
    delay(50);
  }

  server.on("/", handleRoot);

  server.on("/press", [](){  // Do this for URL of: http://localIP/press  For example http://192.168.1.53/press
    // This is called when http://WiFi.localIP/press is called in a browser
    FingerStatus = "STATUS:<br/>Remote Finger is PRESSED and LED ON";
    FingerStatus += "<br/>";
    FingerStatus += "<br/>Valid http://" + WiFi.localIP().toString() + " options are: <a href=\"http://" + WiFi.localIP().toString() + "/press\">/press</a> or ";
    FingerStatus += "<a href=\"http://" + WiFi.localIP().toString() + "/release\">/release</a> or <a href=\"http://" + WiFi.localIP().toString() + "/tap\">/tap</a><br/>";
    server.send(200, "text/html", "<b>" + FingerStatus + "<b/>");
    FingerStatus = "STATUS:<br/>Remote Finger is PRESSED   and  LED ON";
    digitalWrite(LED_BUILTIN, LOW);  // turn the On Board LED ON [inverse logic]
    // Update the Serial Monitor (for debug)
    Serial.println("STATUS: Remote Finger is PRESSED and LED ON");
    servo.attach(D1); // Attach the servo to pin
    servo.write(pressed_servo_postion); // Move servo full anti clockwise
  });

  server.on("/release", [](){  // Do this for URL of: http://localIP/release  For example http://192.168.1.53/release
    // This is called when http://WiFi.localIP/release is called in a browser
    FingerStatus = "STATUS:<br/>Remote Finger is RELEASED and LED OFF";
    FingerStatus += "<br/>";
    FingerStatus += "<br/>Valid http://" + WiFi.localIP().toString() + " options are: <a href=\"http://" + WiFi.localIP().toString() + "/press\">/press</a> or ";
    FingerStatus += "<a href=\"http://" + WiFi.localIP().toString() + "/release\">/release</a> or <a href=\"http://" + WiFi.localIP().toString() + "/tap\">/tap</a><br/>";
    server.send(200, "text/html", "<b>" + FingerStatus + "<b/>");
    FingerStatus = "STATUS:<br/>Remote Finger is RELEASED   and  LED OFF";
    digitalWrite(LED_BUILTIN, HIGH);  // turn the On Board LED OFF [inverse logic]
    // Update the Serial Monitor (for debug)
    Serial.println("STATUS: Remote Finger is RELEASED and LED OFF");
    servo.attach(D1); // Attach the servo to pin
    servo.write(released_servo_position); // Move servo to released_servo_postion
  });

    server.on("/tap", [](){  // Do this for URL of: http://localIP/press  For example http://192.168.1.53/press
    // This is called when http://WiFi.localIP/press is called in a browser
    FingerStatus = "STATUS:<br/>Remote Finger is PRESSED and LED ON";
    FingerStatus += "<br/>";
    FingerStatus += "<br/>Valid http://" + WiFi.localIP().toString() + " options are: <a href=\"http://" + WiFi.localIP().toString() + "/press\">/press</a> or ";
    FingerStatus += "<a href=\"http://" + WiFi.localIP().toString() + "/release\">/release</a> or <a href=\"http://" + WiFi.localIP().toString() + "/tap\">/tap</a><br/>";
    server.send(200, "text/html", "<b>" + FingerStatus + "<b/>");
    FingerStatus = "STATUS:<br/>Remote Finger is PRESSED   and  LED ON";
    digitalWrite(LED_BUILTIN, LOW);  // turn the On Board LED ON [inverse logic]
    // Update the Serial Monitor (for debug)
    Serial.println("STATUS: Remote Finger is PRESSED and LED ON");
    servo.attach(D1); // Attach the servo to pin
    servo.write(pressed_servo_postion); // Move servo to pressed_servo_postion

    delay(tap_delay);  //  How long is the button pressed

    digitalWrite(LED_BUILTIN, HIGH);  // turn the On Board LED OFF [inverse logic]
    // Update the Serial Monitor (for debug)
    Serial.println("STATUS: Remote Finger is RELEASED and LED OFF");
    servo.attach(D1); // Attach the servo to pin
    servo.write(released_servo_position); // Move servo to released_servo_postion    
  });

  server.onNotFound(handleNotFound);  // Handle an invalid URL and show correct options

  server.begin();   //Wooo Hooo !!!
  // Update the Serial Monitor (for debug)
  Serial.println("Remote Finger HTTP server started at: " + WiFi.localIP().toString());
}

void loop() {   // Do this loop until the Dallas Cowboys win the Super Bowl
  server.handleClient();  // Any URL requests?
  delay(250); // wait some milliseconds, mainly for debounce
}
-----