Skip to content

Commit

Permalink
Merge pull request #35 from shing19m/ip_based
Browse files Browse the repository at this point in the history
Add support to connect to a module via TCP & telnet server
  • Loading branch information
4refr0nt committed Nov 5, 2015
2 parents d10fb7e + fd4ec34 commit 1efbb0c
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 61 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,25 @@ If you want load and autoexecute file main.lua, command dofile("main.lua"), you
./luatool.py --dofile
```
Typically, place wifi.setmode, wifi.sta.config commands to init.lua file for connecting to you AP with low risk of boot loop, and other code place to main.lua for manually start and debug.

####Alternative use:

This requires a nodemcu based module already configured to meet the following conditions:

- the module is accessible via TCP/IP
- the telnet server (file: **telnet_srv.lua**) is running

Now the option **--ip IP[:PORT]** enables you to specify an IP and optionally a port (if changed for the telnet server)
that will be used to communicate with the module via TCP/IP.

####Examples:

```
./luatool.py --ip 192.168.12.34 --src file.lua --dest test.lua --dofile
```

- --ip - the IP to connect to. Note that no Port is given, so 23 will be used (can be changed in **telnet_srv.lua**)
- --src - source disk file, default main.lua
- --dest - destination flash file, default main.lua
- --dofile - run the just uploaded file

194 changes: 133 additions & 61 deletions luatool/luatool.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,39 +20,55 @@
import sys
import serial
from time import sleep
import socket
import argparse
from os.path import basename


version = "0.6.3"


def writeln(data, check=1):
if s.inWaiting() > 0:
s.flushInput()
if len(data) > 0:
sys.stdout.write("\r\n->")
sys.stdout.write(data.split("\r")[0])
s.write(data)
sleep(0.3)
if check > 0:
class TransportError(Exception):
"""Custom exception to represent errors with a transport
"""
def __init__(self, message):
self.message = message


class AbstractTransport:
def __init__(self):
raise NotImplementedError('abstract transports cannot be instantiated.')

def close(self):
raise NotImplementedError('Function not implemented')

def read(self, length):
raise NotImplementedError('Function not implemented')

def writeln(self, data, check=1):
raise NotImplementedError('Function not implemented')

def writer(self, data):
self.writeln("file.writeline([==[" + data + "]==])\r")

def performcheck(self, expected):
line = ''
char = ''
while char != chr(62): # '>'
char = s.read(1)
char = self.read(1)
if char == '':
raise Exception('No proper answer from MCU')
if char == chr(13) or char == chr(10): # LF or CR
if line != '':
line = line.strip()
if line+'\r' == data:
if line+'\r' == expected:
sys.stdout.write(" -> ok")
else:
if line[:4] == "lua:":
sys.stdout.write("\r\n\r\nLua ERROR: %s" % line)
raise Exception('ERROR from Lua interpreter\r\n\r\n')
else:
data = data.split("\r")[0]
expected = expected.split("\r")[0]
sys.stdout.write("\r\n\r\nERROR")
sys.stdout.write("\r\n send string : '%s'" % data)
sys.stdout.write("\r\n expected echo : '%s'" % data)
Expand All @@ -62,28 +78,89 @@ def writeln(data, check=1):
line = ''
else:
line += char
else:
sys.stdout.write(" -> send without check")


def writer(data):
writeln("file.writeline([==[" + data + "]==])\r")
class SerialTransport(AbstractTransport):
def __init__(self, port, baud):
self.port = port
self.baud = baud
self.serial = None

try:
self.serial = serial.Serial(port, baud)
except serial.SerialException as e:
raise TransportError(e.strerror)

def openserial(args):
# Open the selected serial port
try:
s = serial.Serial(args.port, args.baud)
except:
sys.stderr.write("Could not open port %s\n" % (args.port))
sys.exit(1)
if args.verbose:
sys.stderr.write("Set timeout %s\r\n" % s.timeout)
s.timeout = 3
if args.verbose:
sys.stderr.write("Set interCharTimeout %s\r\n" % s.interCharTimeout)
s.interCharTimeout = 3
return s
self.serial.timeout = 3
self.serial.interCharTimeout = 3

def writeln(self, data, check=1):
if self.serial.inWaiting() > 0:
self.serial.flushInput()
if len(data) > 0:
sys.stdout.write("\r\n->")
sys.stdout.write(data.split("\r")[0])
self.serial.write(data)
sleep(0.3)
if check > 0:
self.performcheck(data)
else:
sys.stdout.write(" -> send without check")

def read(self, length):
return self.serial.read(length)

def close(self):
self.serial.flush()
self.serial.close()


class TcpSocketTransport(AbstractTransport):
def __init__(self, host, port):
self.host = host
self.port = port
self.socket = None

try:
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error as e:
raise TransportError(e.strerror)

try:
self.socket.connect((host, port))
except socket.error as e:
raise TransportError(e.strerror)
# read intro from telnet server (see telnet_srv.lua)
self.socket.recv(50)

def writeln(self, data, check=1):
if len(data) > 0:
sys.stdout.write("\r\n->")
sys.stdout.write(data.split("\r")[0])
self.socket.sendall(data)
if check > 0:
self.performcheck(data)
else:
sys.stdout.write(" -> send without check")

def read(self, length):
return self.socket.recv(length)

def close(self):
self.socket.close()


def decidetransport(cliargs):
if cliargs.ip:
data = cliargs.ip.split(':')
host = data[0]
if len(data) == 2:
port = int(data[1])
else:
port = 23
return TcpSocketTransport(host, port)
else:
return SerialTransport(cliargs.port, cliargs.baud)


if __name__ == '__main__':
Expand All @@ -100,26 +177,27 @@ def openserial(args):
parser.add_argument('-a', '--append', action='store_true', help='Append source file to destination file.')
parser.add_argument('-l', '--list', action='store_true', help='List files on device')
parser.add_argument('-w', '--wipe', action='store_true', help='Delete all lua/lc files on device.')
parser.add_argument('-i', '--id', action='store_true', help='Query the modules chip id.')
parser.add_argument('--delete', default=None, help='Delete a lua/lc file from device.')
parser.add_argument('-i', '--id', action='store_true', help='Query the modules chip id.')
parser.add_argument('--delete', default=None, help='Delete a lua/lc file from device.')
parser.add_argument('--ip', default=None, help='Connect to a telnet server on the device (--ip IP[:port])')
args = parser.parse_args()

transport = decidetransport(args)

if args.list:
s = openserial(args)
writeln("local l = file.list();for k,v in pairs(l) do print('name:'..k..', size:'..v)end\r", 0)
transport.writeln("local l = file.list();for k,v in pairs(l) do print('name:'..k..', size:'..v)end\r", 0)
while True:
char = s.read(1)
char = transport.read(1)
if char == '' or char == chr(62):
break
sys.stdout.write(char)
sys.exit(0)

if args.id:
s = openserial(args)
writeln("=node.chipid()\r", 0)
transport.writeln("=node.chipid()\r", 0)
id=""
while True:
char = s.read(1)
char = transport.read(1)
if char == '' or char == chr(62):
break
if char.isdigit():
Expand All @@ -128,12 +206,11 @@ def openserial(args):
sys.exit(0)

if args.wipe:
s = openserial(args)
writeln("local l = file.list();for k,v in pairs(l) do print(k)end\r", 0)
transport.writeln("local l = file.list();for k,v in pairs(l) do print(k)end\r", 0)
file_list = []
fn = ""
while True:
char = s.read(1)
char = transport.read(1)
if char == '' or char == chr(62):
break
if char not in ['\r', '\n']:
Expand All @@ -145,12 +222,11 @@ def openserial(args):
for fn in file_list[1:]: # first line is the list command sent to device
if args.verbose:
sys.stderr.write("Delete file {} from device.\r\n".format(fn))
writeln("file.remove(\"" + fn + "\")\r")
transport.writeln("file.remove(\"" + fn + "\")\r")
sys.exit(0)

if args.delete:
s = openserial(args)
writeln("file.remove(\"" + args.delete + "\")\r")
transport.writeln("file.remove(\"" + args.delete + "\")\r")
sys.exit(0)

if args.dest is None:
Expand Down Expand Up @@ -178,9 +254,6 @@ def openserial(args):
# line length
f.seek(0)

# Open the selected serial port
s = openserial(args)

# set serial timeout
if args.verbose:
sys.stderr.write("Upload starting\r\n")
Expand All @@ -189,9 +262,9 @@ def openserial(args):
if args.append==False:
if args.verbose:
sys.stderr.write("Stage 1. Deleting old file from flash memory")
writeln("file.open(\"" + args.dest + "\", \"w\")\r")
writeln("file.close()\r")
writeln("file.remove(\"" + args.dest + "\")\r")
transport.writeln("file.open(\"" + args.dest + "\", \"w\")\r")
transport.writeln("file.close()\r")
transport.writeln("file.remove(\"" + args.dest + "\")\r")
else:
if args.verbose:
sys.stderr.write("[SKIPPED] Stage 1. Deleting old file from flash memory [SKIPPED]")
Expand All @@ -201,39 +274,38 @@ def openserial(args):
if args.verbose:
sys.stderr.write("\r\nStage 2. Creating file in flash memory and write first line")
if args.append:
writeln("file.open(\"" + args.dest + "\", \"a+\")\r")
transport.writeln("file.open(\"" + args.dest + "\", \"a+\")\r")
else:
writeln("file.open(\"" + args.dest + "\", \"w+\")\r")
transport.writeln("file.open(\"" + args.dest + "\", \"w+\")\r")
line = f.readline()
if args.verbose:
sys.stderr.write("\r\nStage 3. Start writing data to flash memory...")
while line != '':
writer(line.strip())
transport.writer(line.strip())
line = f.readline()

# close both files
f.close()
if args.verbose:
sys.stderr.write("\r\nStage 4. Flush data and closing file")
writeln("file.flush()\r")
writeln("file.close()\r")
transport.writeln("file.flush()\r")
transport.writeln("file.close()\r")

# compile?
if args.compile:
if args.verbose:
sys.stderr.write("\r\nStage 5. Compiling")
writeln("node.compile(\"" + args.dest + "\")\r")
writeln("file.remove(\"" + args.dest + "\")\r")
transport.writeln("node.compile(\"" + args.dest + "\")\r")
transport.writeln("file.remove(\"" + args.dest + "\")\r")

# restart or dofile
if args.restart:
writeln("node.restart()\r")
transport.writeln("node.restart()\r")
if args.dofile: # never exec if restart=1
writeln("dofile(\"" + args.dest + "\")\r", 0)
transport.writeln("dofile(\"" + args.dest + "\")\r", 0)

# close serial port
s.flush()
s.close()
transport.close()

# flush screen
sys.stdout.flush()
Expand Down
36 changes: 36 additions & 0 deletions luatool/telnet_srv.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--
-- setup a telnet server that hooks the sockets input
--
function setupTelnetServer()
inUse = false
function listenFun(sock)
if inUse then
sock:send("Already in use.\n")
sock:close()
return
end
inUse = true

function s_output(str)
if(sock ~=nil) then
sock:send(str)
end
end

node.output(s_output, 0)

sock:on("receive",function(sock, input)
node.input(input)
end)

sock:on("disconnection",function(sock)
node.output(nil)
inUse = false
end)

sock:send("Welcome to NodeMCU world.\n> ")
end

telnetServer = net.createServer(net.TCP, 180)
telnetServer:listen(23, listenFun)
end

0 comments on commit 1efbb0c

Please sign in to comment.