├── USBSwitchController ├── .gitignore ├── images │ ├── BoardBottomClean.jpg │ ├── BoardTopAttached.jpg │ ├── BoardAttachmentPoints.jpg │ └── WiresAttachedNodeMcu.jpg ├── ConfigStatic.h.example └── USBSwitchController.ino ├── .github └── FUNDING.yml └── README.md /USBSwitchController/.gitignore: -------------------------------------------------------------------------------- 1 | ConfigStatic.h 2 | ConfigStatic.h-* 3 | images/BoardAttachmentPoints.psd -------------------------------------------------------------------------------- /USBSwitchController/images/BoardBottomClean.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SciLor/usbswitchcontroller/master/USBSwitchController/images/BoardBottomClean.jpg -------------------------------------------------------------------------------- /USBSwitchController/images/BoardTopAttached.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SciLor/usbswitchcontroller/master/USBSwitchController/images/BoardTopAttached.jpg -------------------------------------------------------------------------------- /USBSwitchController/images/BoardAttachmentPoints.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SciLor/usbswitchcontroller/master/USBSwitchController/images/BoardAttachmentPoints.jpg -------------------------------------------------------------------------------- /USBSwitchController/images/WiresAttachedNodeMcu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SciLor/usbswitchcontroller/master/USBSwitchController/images/WiresAttachedNodeMcu.jpg -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: SciLor # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##USB Switch Controller with mqtt/wifi for ESP8266 / ESP32 2 | 3 | A basic implementation for the software side to make an usb switch automatically detect a computer and switch to it if its the only one. 4 | [ROLINE USB 3.0 Switch 14.01.2312](https://www.amazon.de/ROLINE-Switch-inkl-Kabel-Manuell/dp/B01N7FSTX3) 5 | 6 | ##Wiring 7 | Please don't forget a small resistor (>1000ohms should work) between the input lines D1, D2, D7, to prevent to much leakage as the ESP is 3.3V and the input lines are 5.0V. 8 | 9 | ![Attachment Points Hub](https://github.com/SciLor/usbswitchcontroller/blob/master/USBSwitchController/images/BoardAttachmentPoints.jpg?raw=true) 10 | ![Attachment Points Node](https://github.com/SciLor/usbswitchcontroller/blob/master/USBSwitchController/images/WiresAttachedNodeMcu.jpg?raw=true) 11 | 12 | There may have been an easier solution as there seems to be a type of UART. But this way it is made as an IOT-device with mqtt 13 | 14 | [Like my work? Spread the word and donate!](http://www.scilor.com/donate.html) 15 | -------------------------------------------------------------------------------- /USBSwitchController/ConfigStatic.h.example: -------------------------------------------------------------------------------- 1 | //Switch when already one USB-Input is active and the second one comes online 2 | //#define CFG_SWITCH_ON_SECOND_DEVICE 3 | 4 | //PINs 5 | #define CFG_PIN_SWITCH D0 6 | #define CFG_PIN_USB1 D1 7 | #define CFG_PIN_USB2 D2 8 | #define CFG_PIN_USBP D7 9 | #define CFG_PIN_STATE1 D5 10 | #define CFG_PIN_STATE2 D6 11 | 12 | //MQTT Control 13 | #define CFG_MQTT_ACTIVE 14 | #define CFG_HOSTNAME "USBSwitchController" 15 | #define CFG_WIFI_NAME "wifiname" 16 | #define CFG_WIFI_PASS "wifipass" 17 | #define CFG_MQTT_SERVER "192.168.0.222" 18 | #define CFG_MQTT_PORT 1883 19 | #define CFG_MQTT_NAME "mqttuser" 20 | #define CFG_MQTT_PASS "mqttpass" 21 | 22 | #define CFG_MQTT_TOPIC "usbswitch" 23 | #define CFG_MQTT_TOPIC_STATE_USB1 "usbswitch/state/usb1" 24 | #define CFG_MQTT_TOPIC_STATE_USB2 "usbswitch/state/usb2" 25 | #define CFG_MQTT_TOPIC_STATE_USBP "usbswitch/state/usbp" 26 | #define CFG_MQTT_TOPIC_STATE_SWITCH "usbswitch/state/switch" 27 | #define CFG_MQTT_TOPIC_COMMAND "usbswitch/cmd" 28 | 29 | //MISC 30 | #define CFG_IDLE_AFTER_SWITCH_MS 1000 -------------------------------------------------------------------------------- /USBSwitchController/USBSwitchController.ino: -------------------------------------------------------------------------------- 1 | #include "ConfigStatic.h" 2 | 3 | #if defined(ESP8266) 4 | #include 5 | #include 6 | #elif defined(ESP32) 7 | #include 8 | #include 9 | #endif 10 | #include 11 | 12 | #define STATE_UNKNOWN 0 13 | #define STATE_USB1 1 14 | #define STATE_USB2 2 15 | 16 | const char* mqttBroker = CFG_MQTT_SERVER; 17 | const char* mqttUser = CFG_MQTT_NAME; 18 | const char* mqttPassword = CFG_MQTT_PASS; 19 | 20 | int powerUSB1 = -1; 21 | int powerUSB2 = -1; 22 | int powerUSBP = -1; 23 | int lastState = -1; 24 | unsigned long lastSwitch; 25 | WiFiClient wifi; 26 | PubSubClient mqtt(wifi); 27 | 28 | void mqttCallback(char* topic, byte* payload, unsigned int length); 29 | void connectIfNeeded(); 30 | void startupSwitch(); 31 | 32 | void setup() { 33 | Serial.begin(9600); 34 | Serial.println("setup"); 35 | 36 | pinMode(CFG_PIN_SWITCH, OUTPUT); 37 | 38 | pinMode(CFG_PIN_USB1, INPUT); 39 | pinMode(CFG_PIN_USB2, INPUT); 40 | pinMode(CFG_PIN_USBP, INPUT_PULLUP); 41 | 42 | pinMode(CFG_PIN_STATE1, INPUT); 43 | pinMode(CFG_PIN_STATE2, INPUT); 44 | digitalWrite(CFG_PIN_SWITCH, HIGH); 45 | delay(500); 46 | 47 | WiFi.mode(WIFI_STA); 48 | WiFi.hostname(CFG_HOSTNAME); 49 | mqtt.setServer(mqttBroker, 1883); 50 | mqtt.setCallback(mqttCallback); 51 | } 52 | 53 | void connectIfNeeded() { 54 | #if defined(CFG_MQTT_ACTIVE) 55 | if (WiFi.status() != WL_CONNECTED) { 56 | Serial.println("connect wifi"); 57 | WiFi.begin(CFG_WIFI_NAME, CFG_WIFI_PASS); 58 | if (WiFi.waitForConnectResult() == WL_CONNECTED) { 59 | Serial.println(F("WiFi connected")); 60 | Serial.print(F("IP address: ")); 61 | Serial.println(WiFi.localIP()); 62 | } else { 63 | Serial.println(F("failed")); 64 | delay(1000); 65 | } 66 | } 67 | if (WiFi.status() == WL_CONNECTED) { 68 | if (!mqtt.connected()) { 69 | Serial.println("connect mqtt"); 70 | 71 | if (mqtt.connect(CFG_MQTT_TOPIC, mqttUser, mqttPassword, CFG_MQTT_TOPIC, 0, true, "offline")) { 72 | mqtt.publish(CFG_MQTT_TOPIC, "online", true); 73 | Serial.println("subscribe mqtt"); 74 | mqtt.subscribe(CFG_MQTT_TOPIC_COMMAND); 75 | lastState = -1; 76 | powerUSB1 = -1; 77 | powerUSB2 = -1; 78 | powerUSBP = -1; 79 | } else { 80 | Serial.print(F("failed, rc=")); 81 | Serial.println(mqtt.state()); 82 | } 83 | } 84 | } 85 | #endif 86 | } 87 | 88 | void loop() { 89 | int usb1 = digitalRead(CFG_PIN_USB1); 90 | int usb2 = digitalRead(CFG_PIN_USB2); 91 | int usbp = digitalRead(CFG_PIN_USBP); 92 | 93 | char buffer[2]; 94 | if (powerUSB1 != usb1) { 95 | Serial.print("usb1: "); 96 | Serial.println(usb1); 97 | itoa(usb1, buffer, 2); 98 | mqtt.publish(CFG_MQTT_TOPIC_STATE_USB1, buffer); 99 | } 100 | if (powerUSB2 != usb2) { 101 | Serial.print("usb2: "); 102 | Serial.println(usb2); 103 | itoa(usb2, buffer, 2); 104 | mqtt.publish(CFG_MQTT_TOPIC_STATE_USB2, buffer); 105 | } 106 | if (powerUSBP != usbp) { 107 | Serial.print("usbp: "); 108 | Serial.println(usbp); 109 | itoa(usbp, buffer, 2); 110 | mqtt.publish(CFG_MQTT_TOPIC_STATE_USBP, buffer); 111 | } 112 | 113 | if (usb1 == HIGH && usb2 == LOW) { 114 | powerUSB1 = usb1; 115 | powerUSB2 = usb2; 116 | doSwitch(STATE_USB1); 117 | } else if (usb1 == LOW && usb2 == HIGH) { 118 | powerUSB1 = usb1; 119 | powerUSB2 = usb2; 120 | doSwitch(STATE_USB2); 121 | } else if (usb1 == HIGH && usb2 == HIGH) { 122 | #if defined(CFG_SWITCH_ON_SECOND_DEVICE) 123 | if (powerUSB1 == LOW) { 124 | doSwitch(STATE_USB1); 125 | } else if (powerUSB2 == LOW) { 126 | doSwitch(STATE_USB2); 127 | } 128 | #endif 129 | powerUSB1 = usb1; 130 | powerUSB2 = usb2; 131 | } else if (usb1 == LOW && usb2 == LOW) { 132 | powerUSB1 = usb1; 133 | powerUSB2 = usb2; 134 | } 135 | if (usbp != powerUSBP) { 136 | powerUSBP = usbp; 137 | } 138 | getState(); 139 | mqtt.loop(); 140 | connectIfNeeded(); 141 | delay(100); 142 | } 143 | 144 | void publishState(int state) { 145 | if (lastState != state) { 146 | if (state == STATE_USB1) { 147 | Serial.println("publish STATE_USB1"); 148 | mqtt.publish(CFG_MQTT_TOPIC_STATE_SWITCH, "usb1"); 149 | } else if (state == STATE_USB2) { 150 | Serial.println("publish STATE_USB2"); 151 | mqtt.publish(CFG_MQTT_TOPIC_STATE_SWITCH, "usb2"); 152 | } else { 153 | Serial.println("publish STATE_UNKNOWN"); 154 | mqtt.publish(CFG_MQTT_TOPIC_STATE_SWITCH, "unknown"); 155 | } 156 | lastState = state; 157 | } 158 | } 159 | 160 | void doSwitch(int targetState) { 161 | int currentState = getState(); 162 | while (currentState == STATE_UNKNOWN) { 163 | Serial.print("waiting STATE_UNKNOWN: "); 164 | delay(100); 165 | currentState = getState(); 166 | } 167 | if (currentState != targetState) { 168 | if (!isRecentlySwitched()) { 169 | Serial.print("currentState: "); 170 | Serial.println(currentState); 171 | Serial.print("targetState: "); 172 | Serial.println(targetState); 173 | switchState(); 174 | } else { 175 | Serial.println("State not switched, just switched..."); 176 | delay(100); 177 | } 178 | } 179 | } 180 | void switchState() { 181 | digitalWrite(CFG_PIN_SWITCH, LOW); 182 | delay(50); 183 | digitalWrite(CFG_PIN_SWITCH, HIGH); 184 | delay(50); 185 | lastSwitch = millis(); 186 | } 187 | boolean isRecentlySwitched() { 188 | unsigned long timeDiff = millis() - lastSwitch; 189 | if (timeDiff > CFG_IDLE_AFTER_SWITCH_MS) { 190 | return false; 191 | } 192 | return true; 193 | } 194 | 195 | int getState() { 196 | int state1 = digitalRead(CFG_PIN_STATE1); 197 | int state2 = digitalRead(CFG_PIN_STATE2); 198 | int state = STATE_UNKNOWN; 199 | 200 | if (state1 == HIGH && state2 == LOW) { 201 | state = STATE_USB1; 202 | } else if (state2 == HIGH && state1 == LOW) { 203 | state = STATE_USB2; 204 | } 205 | publishState(state); 206 | } 207 | 208 | void mqttCallback(char* topic, byte* payload, unsigned int length) { 209 | Serial.print("Message arrived ["); 210 | Serial.print(topic); 211 | Serial.print("] "); 212 | for (int i = 0; i < length; i++) { 213 | Serial.print((char)payload[i]); 214 | } 215 | Serial.println(); 216 | 217 | if (length == 1) { 218 | if ((char)payload[0] == '1') { 219 | doSwitch(STATE_USB1); 220 | } else if ((char)payload[0] == '2') { 221 | doSwitch(STATE_USB2); 222 | } 223 | } else if (length == 4) { 224 | if ((char)payload[0] == 'u' && (char)payload[1] == 's' && (char)payload[2] == 'b') { 225 | if ((char)payload[3] == '1') { 226 | doSwitch(STATE_USB1); 227 | } else if ((char)payload[3] == '2') { 228 | doSwitch(STATE_USB2); 229 | } 230 | } 231 | } 232 | } 233 | 234 | --------------------------------------------------------------------------------