├── LICENSE ├── README.md └── luatool ├── init.lua ├── luatool.py ├── main.lua └── telnet_srv.lua /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **luatool** # 2 | 3 | [![Join the chat at https://gitter.im/4refr0nt/luatool](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/4refr0nt/luatool?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | ### Tool for loading Lua-based scripts from file to ESP8266 with nodemcu firmware 6 | 7 | ### Summary 8 | 9 | - Allow easy uploading of any Lua-based script into the ESP8266 flash memory with [NodeMcu firmware](https://github.com/nodemcu/nodemcu-firmware) 10 | 11 | ### Other projects 12 | Another my project for NodeMCU: ESPlorer Integrated Development Environment (IDE) for ESP8266 developers 13 | - [ESPlorer home page and latest binaries](http://esp8266.ru/esplorer/) 14 | - [ESPlorer source code](https://github.com/4refr0nt/ESPlorer) 15 | 16 | ### Requirements 17 | 18 | python 2.7, pyserial (as for esptool) 19 | 20 | ### Discuss 21 | [http://esp8266.ru](http://esp8266.ru/forum/threads/luatool.11/) 22 | 23 | 24 | ### Changelog 25 | v0.6.4 26 | - add TCP as possible transport to connect to the module using the supplied telnet server code 27 | - add --id/-i to query the ID of a module 28 | - add --delete to remove a given file from the module 29 | 30 | v0.6.3 31 | - fix download bug 32 | 33 | v0.6.2 34 | - added support for nested strings 35 | - added check to verify the UART buffer will not overflow. 36 | 37 | v0.6.1 38 | - put short versions of arguments back, see issue #2 39 | - flush serial port, fixes issue #1 40 | 41 | v0.6 42 | - switched to argparse from getopts, renamed some arguments 43 | - removed call-home from main.lua 44 | - added --verbose option to show debugging 45 | - added comments, fixed some grammar, updated README 46 | - chmod 755'd the script so its runnable on POSIX 47 | - checked with nodemcu 0.9.4 20141222 48 | 49 | v0.5 50 | - add new option -r, --restart : auto restart module, send command "node.restart()", after file load 51 | - add new option -d, --dofile : auto run, send command "dofile('file')", after file load 52 | - delete line "lua script loaded by luatool" for correct line number, lines number now equal lines number in original file 53 | - add 0.3 sec delay after write 54 | 55 | 56 | v0.4 57 | - now check proper answer from NodeMCU after send data. 58 | After send string we expect echo line, if not got it, then error message displayed "Error sending data to LuaMCU" 59 | - if lua interpreter error received, then error message displayed "ERROR from LUA interpreter lua:..." 60 | - add heap info and chip id to example file init.lua 61 | - some changes in example file main.lua 62 | 63 | 64 | ### Run 65 | 66 | #### Typical use: 67 | 68 | 69 | Edit file init.lua and set SSID and MasterPassword 70 | Then disconnect any terminal programm, and at command prompt type 71 | 72 | ``` 73 | ./luatool.py --port /dev/ttyUSB0 --src init.lua --dest init.lua --verbose 74 | 75 | Downloader start 76 | Set timeout 3 77 | Set interCharTimeout 3 78 | Stage 1. Deleting old file from flash memory 79 | ->file.remove("init.lua") -> ok 80 | ..................................... 81 | ->file.close() -> ok 82 | --->>> All done <<<--- 83 | 84 | ./luatool.py 85 | 86 | ->file.open("main.lua", "w") -> ok 87 | ->file.close() -> ok 88 | ->file.remove("main.lua") -> ok 89 | ->file.open("main.lua", "w+") -> ok 90 | ->file.writeline([[tmr.alarm(0, 1000, 1, function()]]) -> ok 91 | ->file.writeline([[if wifi.sta.getip() == nil then]]) -> ok 92 | ->file.writeline([[print("Connecting to AP...")]]) -> ok 93 | ->file.writeline([[else]]) -> ok 94 | ->file.writeline([[print('IP: ',wifi.sta.getip())]]) -> ok 95 | ->file.writeline([[tmr.stop(0)]]) -> ok 96 | ->file.writeline([[end]]) -> ok 97 | ->file.writeline([[end)]]) -> ok 98 | ->file.flush() -> ok 99 | ->file.close() -> ok 100 | --->>> All done <<<--- 101 | ``` 102 | Connect you terminal program and send command (or you can use --restart option, when loading file init.lua) 103 | ``` 104 | node.restart() 105 | ``` 106 | after reboot: 107 | ``` 108 | lua script loaded by luatool 0.4 109 | init.lua ver 1.2 110 | set mode=STATION (mode=1) 111 | MAC: 18-FE-34-98-D4-B5 112 | chip: 10015925 113 | heap: 18464 114 | set wifi 115 | NodeMcu 0.9.2 build 20141125 powered by Lua 5.1.4 116 | ``` 117 | 118 | send command (or you can use --dofile option, when loading file main.lua) 119 | ``` 120 | dofile("main.lua") 121 | ``` 122 | connects to your AP and displays MCU's IP address 123 | 124 | ``` 125 | > dofile("main.lua") 126 | > IP: 192.168.1.99 127 | 128 | ``` 129 | 130 | #### Examples: 131 | 132 | ``` 133 | ./luatool.py --port COM4 --src file.lua --dest main.lua --baud 9600 134 | ``` 135 | - --port - COM1-COM128, default /dev/ttyUSB0 136 | - --baud - baud rate, default 9600 137 | - --src - source disk file, default main.lua 138 | - --dest - destination flash file, default main.lua 139 | 140 | If use --dest option with parameter "init.lua" - autostart init.lua after boot. 141 | Be carefully about bugs in lua-script - may cause a boot loop. Use this option after full testing only. 142 | 143 | Running without any parameters: load file "main.lua" via port /dev/ttyUSB0:9600 and place code into "main.lua" file into flash. 144 | 145 | ``` 146 | ./luatool.py 147 | ``` 148 | 149 | after loading file to flash you can connect any terminal programm to ESP8266 and type: 150 | ``` 151 | dofile("main.lua") 152 | ``` 153 | for executing you lua script 154 | 155 | If you want load and autoexecute file main.lua, command dofile("main.lua"), you can use --dofile option 156 | ``` 157 | ./luatool.py --dofile 158 | ``` 159 | 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. 160 | 161 | #### Alternative use: 162 | 163 | This requires a nodemcu based module already configured to meet the following conditions: 164 | 165 | - the module is accessible via TCP/IP 166 | - the telnet server (file: **telnet_srv.lua**) is running 167 | 168 | Now the option **--ip IP[:PORT]** enables you to specify an IP and optionally a port (if changed for the telnet server) 169 | that will be used to communicate with the module via TCP/IP. 170 | 171 | #### Examples: 172 | 173 | ``` 174 | ./luatool.py --ip 192.168.12.34 --src file.lua --dest test.lua --dofile 175 | ``` 176 | 177 | - --ip - the IP to connect to. Note that no Port is given, so 23 will be used (can be changed in **telnet_srv.lua**) 178 | - --src - source disk file, default main.lua 179 | - --dest - destination flash file, default main.lua 180 | - --dofile - run the just uploaded file 181 | 182 | -------------------------------------------------------------------------------- /luatool/init.lua: -------------------------------------------------------------------------------- 1 | print('init.lua ver 1.2') 2 | wifi.setmode(wifi.STATION) 3 | print('set mode=STATION (mode='..wifi.getmode()..')') 4 | print('MAC: ',wifi.sta.getmac()) 5 | print('chip: ',node.chipid()) 6 | print('heap: ',node.heap()) 7 | -- wifi config start 8 | wifi.sta.config("SSID","MasterPassword") 9 | -- wifi config end 10 | -------------------------------------------------------------------------------- /luatool/luatool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # 3 | # ESP8266 luatool 4 | # Author e-mail: 4ref0nt@gmail.com 5 | # Site: http://esp8266.ru 6 | # Contributions from: https://github.com/sej7278 7 | # 8 | # This program is free software; you can redistribute it and/or modify it under 9 | # the terms of the GNU General Public License as published by the Free Software 10 | # Foundation; either version 2 of the License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, but WITHOUT 13 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License along with 17 | # this program; if not, write to the Free Software Foundation, Inc., 51 Franklin 18 | # Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | import sys 21 | import serial 22 | from time import sleep 23 | import socket 24 | import argparse 25 | from os.path import basename 26 | 27 | 28 | tqdm_installed = True 29 | try: 30 | from tqdm import tqdm 31 | except ImportError, e: 32 | if e.message == 'No module named tqdm': 33 | tqdm_installed = False 34 | else: 35 | raise 36 | 37 | version = "0.6.4" 38 | 39 | 40 | class TransportError(Exception): 41 | """Custom exception to represent errors with a transport 42 | """ 43 | def __init__(self, message): 44 | self.message = message 45 | 46 | def __str__(self): 47 | return self.message 48 | 49 | class AbstractTransport: 50 | def __init__(self): 51 | raise NotImplementedError('abstract transports cannot be instantiated.') 52 | 53 | def close(self): 54 | raise NotImplementedError('Function not implemented') 55 | 56 | def read(self, length): 57 | raise NotImplementedError('Function not implemented') 58 | 59 | def writeln(self, data, check=1): 60 | raise NotImplementedError('Function not implemented') 61 | 62 | def writer(self, data): 63 | self.writeln("file.writeline([==[" + data + "]==])\r") 64 | 65 | def performcheck(self, expected): 66 | line = '' 67 | char = '' 68 | i = -1 69 | while char != chr(62): # '>' 70 | char = self.read(1) 71 | if char == '': 72 | raise Exception('No proper answer from MCU') 73 | if char == chr(13) or char == chr(10): # LF or CR 74 | if line != '': 75 | line = line.strip() 76 | if line+'\r' == expected and not args.bar: 77 | sys.stdout.write(" -> ok") 78 | elif line+'\r' != expected: 79 | if line[:4] == "lua:": 80 | sys.stdout.write("\r\n\r\nLua ERROR: %s" % line) 81 | raise Exception('ERROR from Lua interpreter\r\n\r\n') 82 | else: 83 | expected = expected.split("\r")[0] 84 | sys.stdout.write("\r\n\r\nERROR") 85 | sys.stdout.write("\r\n send string : '%s'" % expected) 86 | sys.stdout.write("\r\n expected echo : '%s'" % expected) 87 | sys.stdout.write("\r\n but got answer : '%s'" % line) 88 | sys.stdout.write("\r\n\r\n") 89 | raise Exception('Error sending data to MCU\r\n\r\n') 90 | line = '' 91 | else: 92 | line += char 93 | if char == chr(62) and expected[i] == char: 94 | char = '' 95 | i += 1 96 | 97 | 98 | class SerialTransport(AbstractTransport): 99 | def __init__(self, port, baud, delay): 100 | self.port = port 101 | self.baud = baud 102 | self.serial = None 103 | self.delay = delay 104 | 105 | try: 106 | self.serial = serial.Serial(port, baud) 107 | except serial.SerialException as e: 108 | raise TransportError(e.strerror) 109 | 110 | self.serial.timeout = 3 111 | self.serial.interCharTimeout = 3 112 | 113 | def writeln(self, data, check=1): 114 | if self.serial.inWaiting() > 0: 115 | self.serial.flushInput() 116 | if len(data) > 0 and not args.bar: 117 | sys.stdout.write("\r\n->") 118 | sys.stdout.write(data.split("\r")[0]) 119 | self.serial.write(data) 120 | sleep(self.delay) 121 | if check > 0: 122 | self.performcheck(data) 123 | elif not args.bar: 124 | sys.stdout.write(" -> send without check") 125 | 126 | def read(self, length): 127 | return self.serial.read(length) 128 | 129 | def close(self): 130 | self.serial.flush() 131 | self.serial.close() 132 | 133 | 134 | class TcpSocketTransport(AbstractTransport): 135 | def __init__(self, host, port): 136 | self.host = host 137 | self.port = port 138 | self.socket = None 139 | 140 | try: 141 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 142 | except socket.error as e: 143 | raise TransportError(e.strerror) 144 | 145 | try: 146 | self.socket.connect((host, port)) 147 | except socket.error as e: 148 | raise TransportError(e.strerror) 149 | # read intro from telnet server (see telnet_srv.lua) 150 | self.socket.recv(50) 151 | 152 | def writeln(self, data, check=1): 153 | if len(data) > 0 and not args.bar: 154 | sys.stdout.write("\r\n->") 155 | sys.stdout.write(data.split("\r")[0]) 156 | self.socket.sendall(data) 157 | if check > 0: 158 | self.performcheck(data) 159 | elif not args.bar: 160 | sys.stdout.write(" -> send without check") 161 | 162 | def read(self, length): 163 | return self.socket.recv(length) 164 | 165 | def close(self): 166 | self.socket.close() 167 | 168 | 169 | def decidetransport(cliargs): 170 | if cliargs.ip: 171 | data = cliargs.ip.split(':') 172 | host = data[0] 173 | if len(data) == 2: 174 | port = int(data[1]) 175 | else: 176 | port = 23 177 | return TcpSocketTransport(host, port) 178 | else: 179 | return SerialTransport(cliargs.port, cliargs.baud, cliargs.delay) 180 | 181 | 182 | if __name__ == '__main__': 183 | # parse arguments or use defaults 184 | parser = argparse.ArgumentParser(description='ESP8266 Lua script uploader.') 185 | parser.add_argument('-p', '--port', default='/dev/ttyUSB0', help='Device name, default /dev/ttyUSB0') 186 | parser.add_argument('-b', '--baud', default=9600, help='Baudrate, default 9600') 187 | parser.add_argument('-f', '--src', default='main.lua', help='Source file on computer, default main.lua') 188 | parser.add_argument('-t', '--dest', default=None, help='Destination file on MCU, default to source file name') 189 | parser.add_argument('-c', '--compile', action='store_true', help='Compile lua to lc after upload') 190 | parser.add_argument('-r', '--restart', action='store_true', help='Restart MCU after upload') 191 | parser.add_argument('-d', '--dofile', action='store_true', help='Run the Lua script after upload') 192 | parser.add_argument('-v', '--verbose', action='store_true', help="Show progress messages.") 193 | parser.add_argument('-a', '--append', action='store_true', help='Append source file to destination file.') 194 | parser.add_argument('-l', '--list', action='store_true', help='List files on device') 195 | parser.add_argument('-w', '--wipe', action='store_true', help='Delete all lua/lc files on device.') 196 | parser.add_argument('-i', '--id', action='store_true', help='Query the modules chip id.') 197 | parser.add_argument('-e', '--echo', action='store_true', help='Echo output of MCU until script is terminated.') 198 | parser.add_argument('--bar', action='store_true', help='Show a progress bar for uploads instead of printing each line') 199 | parser.add_argument('--delay', default=0.3, help='Delay in seconds between each write.', type=float) 200 | parser.add_argument('--delete', default=None, help='Delete a lua/lc file from device.') 201 | parser.add_argument('--ip', default=None, help='Connect to a telnet server on the device (--ip IP[:port])') 202 | args = parser.parse_args() 203 | 204 | transport = decidetransport(args) 205 | 206 | if args.bar and not tqdm_installed: 207 | sys.stdout.write("You must install the tqdm library to use the bar feature\n") 208 | sys.stdout.write("To install, at the prompt type: \"pip install tqdm\"\n") 209 | sys.exit(0) 210 | 211 | 212 | if args.list: 213 | transport.writeln("local l = file.list();for k,v in pairs(l) do print('name:'..k..', size:'..v)end\r", 0) 214 | while True: 215 | char = transport.read(1) 216 | if char == '' or char == chr(62): 217 | break 218 | sys.stdout.write(char) 219 | sys.exit(0) 220 | 221 | if args.id: 222 | transport.writeln("=node.chipid()\r", 0) 223 | id="" 224 | while True: 225 | char = transport.read(1) 226 | if char == '' or char == chr(62): 227 | break 228 | if char.isdigit(): 229 | id += char 230 | print("\n"+id) 231 | sys.exit(0) 232 | 233 | if args.wipe: 234 | transport.writeln("local l = file.list();for k,v in pairs(l) do print(k)end\r", 0) 235 | file_list = [] 236 | fn = "" 237 | while True: 238 | char = transport.read(1) 239 | if char == '' or char == chr(62): 240 | break 241 | if char not in ['\r', '\n']: 242 | fn += char 243 | else: 244 | if fn: 245 | file_list.append(fn.strip()) 246 | fn = '' 247 | for fn in file_list[1:]: # first line is the list command sent to device 248 | if args.verbose: 249 | sys.stderr.write("Delete file {} from device.\r\n".format(fn)) 250 | transport.writeln("file.remove(\"" + fn + "\")\r") 251 | sys.exit(0) 252 | 253 | if args.delete: 254 | transport.writeln("file.remove(\"" + args.delete + "\")\r") 255 | sys.exit(0) 256 | 257 | if args.dest is None: 258 | args.dest = basename(args.src) 259 | 260 | # open source file for reading 261 | try: 262 | try: 263 | f = open(args.src, "rt") 264 | except: 265 | import os 266 | base_dir = os.path.dirname(os.path.realpath(__file__)) 267 | f = open(os.path.join(base_dir, args.src), "rt") 268 | os.chdir(base_dir) 269 | except: 270 | sys.stderr.write("Could not open input file \"%s\"\n" % args.src) 271 | sys.exit(1) 272 | 273 | # Verify the selected file will not exceed the size of the serial buffer. 274 | # The size of the buffer is 256. This script does not accept files with 275 | # lines longer than 230 characters to have some room for command overhead. 276 | num_lines = 0 277 | for ln in f: 278 | if len(ln) > 230: 279 | sys.stderr.write("File \"%s\" contains a line with more than 240 " 280 | "characters. This exceeds the size of the serial buffer.\n" 281 | % args.src) 282 | f.close() 283 | sys.exit(1) 284 | num_lines += 1 285 | 286 | # Go back to the beginning of the file after verifying it has the correct 287 | # line length 288 | f.seek(0) 289 | 290 | # set serial timeout 291 | if args.verbose: 292 | sys.stderr.write("Upload starting\r\n") 293 | 294 | # remove existing file on device 295 | if args.append==False: 296 | if args.verbose: 297 | sys.stderr.write("Stage 1. Deleting old file from flash memory") 298 | transport.writeln("file.open(\"" + args.dest + "\", \"w\")\r") 299 | transport.writeln("file.close()\r") 300 | transport.writeln("file.remove(\"" + args.dest + "\")\r") 301 | else: 302 | if args.verbose: 303 | sys.stderr.write("[SKIPPED] Stage 1. Deleting old file from flash memory [SKIPPED]") 304 | 305 | 306 | # read source file line by line and write to device 307 | if args.verbose: 308 | sys.stderr.write("\r\nStage 2. Creating file in flash memory and write first line") 309 | if args.append: 310 | transport.writeln("file.open(\"" + args.dest + "\", \"a+\")\r") 311 | else: 312 | transport.writeln("file.open(\"" + args.dest + "\", \"w+\")\r") 313 | line = f.readline() 314 | if args.verbose: 315 | sys.stderr.write("\r\nStage 3. Start writing data to flash memory...") 316 | if args.bar: 317 | for i in tqdm(range(0, num_lines)): 318 | transport.writer(line.strip()) 319 | line = f.readline() 320 | else: 321 | while line != '': 322 | transport.writer(line.strip()) 323 | line = f.readline() 324 | 325 | # close both files 326 | f.close() 327 | if args.verbose: 328 | sys.stderr.write("\r\nStage 4. Flush data and closing file") 329 | transport.writeln("file.flush()\r") 330 | transport.writeln("file.close()\r") 331 | 332 | # compile? 333 | if args.compile: 334 | if args.verbose: 335 | sys.stderr.write("\r\nStage 5. Compiling") 336 | transport.writeln("node.compile(\"" + args.dest + "\")\r") 337 | transport.writeln("file.remove(\"" + args.dest + "\")\r") 338 | 339 | # restart or dofile 340 | if args.restart: 341 | transport.writeln("node.restart()\r") 342 | if args.dofile: # never exec if restart=1 343 | transport.writeln("dofile(\"" + args.dest + "\")\r", 0) 344 | 345 | if args.echo: 346 | if args.verbose: 347 | sys.stderr.write("\r\nEchoing MCU output, press Ctrl-C to exit") 348 | while True: 349 | sys.stdout.write(transport.read(1)) 350 | 351 | # close serial port 352 | transport.close() 353 | 354 | # flush screen 355 | sys.stdout.flush() 356 | sys.stderr.flush() 357 | if not args.bar: 358 | sys.stderr.write("\r\n--->>> All done <<<---\r\n") 359 | -------------------------------------------------------------------------------- /luatool/main.lua: -------------------------------------------------------------------------------- 1 | tmr.alarm(0, 1000, 1, function() 2 | if wifi.sta.getip() == nil then 3 | print("Connecting to AP...") 4 | else 5 | print('IP: ',wifi.sta.getip()) 6 | tmr.stop(0) 7 | end 8 | end) 9 | -------------------------------------------------------------------------------- /luatool/telnet_srv.lua: -------------------------------------------------------------------------------- 1 | -- a simple telnet server 2 | 3 | -- restart server if needed 4 | if telnet_srv ~= nil then 5 | telnet_srv:close() 6 | end 7 | telnet_srv = net.createServer(net.TCP, 180) 8 | 9 | telnet_srv:listen(23, function(socket) 10 | local fifo = {} 11 | local fifo_drained = true 12 | 13 | local function sender(c) 14 | if #fifo > 0 then 15 | c:send(table.remove(fifo, 1)) 16 | else 17 | fifo_drained = true 18 | end 19 | end 20 | 21 | local function s_output(str) 22 | table.insert(fifo, str) 23 | if socket ~= nil and fifo_drained then 24 | fifo_drained = false 25 | sender(socket) 26 | end 27 | end 28 | 29 | node.output(s_output, 0) -- re-direct output to function s_ouput. 30 | 31 | socket:on("receive", function(c, l) 32 | node.input(l) -- works like pcall(loadstring(l)) but support multiple separate line 33 | end) 34 | socket:on("disconnection", function(c) 35 | node.output(nil) -- un-regist the redirect output function, output goes to serial 36 | end) 37 | socket:on("sent", sender) 38 | 39 | print("Welcome to NodeMCU world.") 40 | end) 41 | 42 | print("Telnet server running...") 43 | --------------------------------------------------------------------------------