Skip to content

Instantly share code, notes, and snippets.

@alessionossa
Last active February 24, 2024 20:32
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save alessionossa/7277df576a29edda4a29500e4e71bc20 to your computer and use it in GitHub Desktop.
Save alessionossa/7277df576a29edda4a29500e4e71bc20 to your computer and use it in GitHub Desktop.
MicroPython WebREPL relay for remote access

MicroPython WebREPL relay for remote access.

TunnelServer.py

TunnelServer.py hould be executed on a server: it waits on port 12014 for connection from ESP32 and on port 6690 for external connection to forward through TCP tunnel to ESP32.

main.py & boot.py

main.py and boot.py should be loaded on ESP32 board running the latest version of MicroPython.

# boot.py file for ESP32
import webrepl
yourWifiSSID = "my_ssid"
yourWifiPassword = "my_WiFi_password"
def connect():
sta_if = network.WLAN(network.STA_IF)
if not sta_if.isconnected():
print('connecting to network...')
sta_if.active(True)
sta_if.connect(yourWifiSSID, yourWifiPassword)
connect()
webrepl.start()
print("--------------")
print("Ended boot")
print("--------------")
# main.py file for ESP32
import uasyncio
import socket
serverName = 'IP.ADDRESS.OF.SERVER'
serverPort = 12014
localName = 'localhost'
localPort = 8266
async def async_recvfrom(sock, count):
print(f"Entered async_recvfrom(sock, count)")
yield uasyncio.core._io_queue.queue_read(sock)
print(f"async_recvfrom(sock, count) queued read")
return sock.recv(count)
async def _transfer(src, dest, description):
print(f"Started forwarding loop: {description}")
while True:
data = await async_recvfrom(src, 1024)
print(f"Readed {len(data)} bytes from {description}")
if data == b'':
break
dest.write(data)
await uasyncio.sleep_ms(10)
async def tunnelToServer():
tunnelSocket = None
redirectSocket = None
taskRemoteToLocal = None
taskLocalToRemote = None
while True:
try:
needToStartThreads = False
print(f"Checkpoint 1: {needToStartThreads}; {tunnelSocket}; {redirectSocket}")
if tunnelSocket is None:
needToStartThreads = True
tunnelSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tunnelServerTuple = (serverName, serverPort)
print(f"Connecting to {tunnelServerTuple}...")
tunnelSocket.connect(tunnelServerTuple)
print(f"Connected to {tunnelServerTuple}")
print(f"Checkpoint 2: {needToStartThreads}; {tunnelSocket}; {redirectSocket}")
if redirectSocket is None:
needToStartThreads = True
redirectSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
localTuple = ('localhost', 8266)
print(f"Connecting to {localTuple}...")
redirectSocket.connect(localTuple)
print(f"Connected to {localTuple}")
print(f"Checkpoint 3: {needToStartThreads}; {tunnelSocket}; {redirectSocket}")
if needToStartThreads:
print("Starting tasks...")
taskRemoteToLocal = uasyncio.create_task(
_transfer(tunnelSocket, redirectSocket, "remote -> localhost:8266"))
taskLocalToRemote = uasyncio.create_task(
_transfer(redirectSocket, tunnelSocket, "localhost:8266 -> remote"))
print("Tasks started...")
except Exception as exc:
print(f"Error with TCP tunnel: {exc}")
if tunnelSocket is not None:
tunnelSocket.close()
tunnelSocket = None
if redirectSocket is not None:
redirectSocket.close()
redirectSocket = None
if taskRemoteToLocal is not None:
taskRemoteToLocal.cancel()
taskRemoteToLocal = None
if taskLocalToRemote is not None:
taskLocalToRemote.cancel()
taskLocalToRemote = None
print("Tunnel daeomon sleeping")
print(f"Checkpoint 4: {needToStartThreads}; {tunnelSocket}; {redirectSocket}")
await uasyncio.sleep(5)
print(f"Checkpoint 5: {needToStartThreads}; {tunnelSocket}; {redirectSocket}")
# ----------------
# MAIN
task = None
try:
task = uasyncio.create_task(tunnelToServer())
uasyncio.run(tunnelToServer())
except KeyboardInterrupt:
print("Stopped with keyboard")
if task is not None:
print(f"Stopping task { task }")
task.cancel()
import socket
import sys
import _thread
external_host = "0.0.0.0"
external_port = 6690
tunnel_host = "0.0.0.0"
tunnel_port = 12014
def main():
_thread.start_new_thread(server, () )
lock = _thread.allocate_lock()
lock.acquire()
lock.acquire()
def server(*settings):
try:
external_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
external_socket.bind((external_host, external_port)) # listen
external_socket.listen(2)
print(f"*** listening on { external_host }:{ external_port }")
tunnel_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tunnel_socket.bind((tunnel_host, tunnel_port)) # listen
tunnel_socket.listen(2)
print(f"*** listening on { tunnel_host }:{ tunnel_port }")
tunnelclient_socket, esp_address = tunnel_socket.accept()
print(f"*** from { esp_address }:{ tunnel_port } to { external_host }:{ external_port }")
print(f"Waiting for external:6690")
externalclient_socket, client_address = external_socket.accept()
print(f"*** from { client_address }:{ external_port } to { tunnel_host }:{ tunnel_port }")
_thread.start_new_thread(forward, (tunnelclient_socket, externalclient_socket, "tunnel:12014 -> external:6690" ))
_thread.start_new_thread(forward, (externalclient_socket, tunnelclient_socket, "external:6690 -> client:12014" ))
except Exception as error:
print(f"Error { error }")
def forward(source, destination, description):
print(f"Started forwarding loop: { description }")
data = ' '
while data and (source is not None) and (destination is not None):
data = source.recv(1024)
#print(f"*** { description }: { data }")
if data:
destination.sendall(data)
else:
print(f"Closing connection { description }")
source.close()
source = None
destination.close()
destination = None
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment