diff --git a/IDE_remote/SocketIDE/README.md b/IDE_remote/SocketIDE/README.md new file mode 100644 index 0000000..c1193f5 --- /dev/null +++ b/IDE_remote/SocketIDE/README.md @@ -0,0 +1,10 @@ +# Nouvelle tentative de gestion de NodeMCU à distance + + +## Sources +Cette fois, je teste le pluggin https://atom.io/packages/nodemcu-thingy pour voir si je peux remplacer l'IDE local (connection USB) ESPlorer et travailler en remote via le WIFI. + + +Je suis parti de ce dépôt https://github.com/holtermp/nodemcu-thingy, que j'ai *forké* chez moi sur ce dépôt https://github.com/zuzu59/nodemcu-thingy. + +La source vient de ce dépôt https://github.com/creationix/nodemcu-webide diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/README.md b/IDE_remote/SocketIDE/source/nodemcu-thingy/README.md new file mode 100644 index 0000000..6fd42af --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/README.md @@ -0,0 +1,148 @@ +# nodemcu-thingy + +**THIS PACKAGE IS CURRENTLY UNDER DEVOLOPMENT AND TO BE CONSIDERED ALPHA** + +NodeMCU Thingy is an [atom](https://atom.io/) package for "over the air" development on the NodeMCU platform. +It uses a websocket connection to the esp8266 for communication, so in an ideal world the USB cable should be needed only to upload the basic firmware and the initial websocket server to the NodeMCU. + + +Features include: + +* Upload of files to the NodeMCU +* Download of files from the NodeMCU +* Deletion of files on the NodeMCU +* Interactive Console + +![demo](https://github.com/holtermp/nodemcu-thingy/blob/master/screencasts/console.gif) + +**Setup** + +*Get the firmware* + +Flash your esp8266 with a current NodeMCU firmware. I recommend using a firmware built with Marcel Stoer's excellent online tool at https://nodemcu-build.com/ + +Select branch ```master``` and at least the following 9 modules: + * ```bit``` + * ```crypto``` + * ```file``` + * ```gpio``` + * ```net``` + * ```node``` + * ```tmr``` + * ```uart``` + * ```wifi``` + +Note that ```bit``` and ```crypto``` are not selected by default. + +This creates two versions of the firmware: integer and float. If you do not know which one to use, the integer version is probably ok for you. + +*Flash the firmware* + +Download ```esptool.py``` from https://github.com/espressif/esptool. + +Flash the firmware to the esp8266 with (use the appropriate ```--port``` parameter) +``` +esptool.py --port /dev/ttyUSB0 erase_flash +esptool.py --port /dev/ttyUSB0 write_flash -fm dio 0x00000 nodemcu-master*.bin +``` + +which should produce an output like: + +``` +esptool.py v1.3-dev +Connecting... +Running Cesanta flasher stub... +Erasing flash (this may take a while)... +Erase took 6.6 seconds + +esptool.py v1.3-dev +Connecting... +Auto-detected Flash size: 32m +Running Cesanta flasher stub... +Flash params set to 0x0240 +Writing 401408 @ 0x0... 401408 (100 %) +Wrote 401408 bytes at 0x0 in 34.8 seconds (92.3 kbit/s)... +Leaving... +``` +Give it a few seconds to reboot. + + +*Install websocket server* + +Cd to ```.atom/packages/nodemcu-thingy/mcu/```. +Change ```init.lua``` to match your wireless LAN. + +``` +station_cfg.ssid="your ssid" +station_cfg.pwd="yout wifi password" +wifi.sta.sethostname("your hostname for the esp8266") +``` +Edit ```upload.sh``` and check if the ```--port``` value matches your system and execute ```bash upload.sh```. + +This takes some time and you should see a lot of lines like +``` +->file.writeline([==[end]==]) -> ok +``` +ending in +``` +->file.flush() -> ok +->file.close() -> ok +--->>> All done <<<--- +``` +Restart the esp8266 by pressing the ```RST``` button on the esp8266. + +After a few seconds you should be able to ping the esp8266 using the value from ```wifi.sta.sethostname()``` + +*Configure package nodemcu-thingy* + + +Open Atom's preferences screen (```Edit->Preferences``` or ```Ctrl-,```). Open ```Packages```. Find package ```nodemcu-thingy``` and click ```Settings```. +Enter the ip address or hostname of your esp8266 (value from ```wifi.sta.sethostname()```) into the ```host``` field. + +Activate the package with ```Packages->nodemcu-thingy->Toggle``` + +Click ```Connect```. +If everything worked the red ```Disconnected``` changes to a green ```Connected``` after a few seconds. + +![demo](https://github.com/holtermp/nodemcu-thingy/blob/master/screencasts/connect.gif) + +You are now good to go. +Very brave programmers can now unhook the usb cable from the computer and run +the esp8266 on some other power source (probably a phone charger). + +*Use package nodemcu-thingy* + + +You can upload, download, erase files on the esp8266. + +The installation process has installed 3 files on the esp8266 which are protected from modification by this package: +* ```websocket.lc``` implements the websocket protocol +* ```main.lc``` implements the server side functions for this package +* ```init.lua``` called by the NodeMCU firmware on startup. + * sets up the wifi connection + * starts the websocket server + * calls ```userinit.lua``` if present. + +(The original implementation of these 3 files is borrowed from [nodemcu-webide](https://github.com/creationix/nodemcu-webide)) + +To implement your own functionality upload a custom ```userinit.lua``` to start your own custom code. + +![demo](https://github.com/holtermp/nodemcu-thingy/blob/master/screencasts/upload.gif) + +See https://nodemcu.readthedocs.io/en/master/ for the all the good stuff you can use... + + + + + + + + + + +*Credits* + +The Websocket server on the NodeMCU is a slightly modified version of the one in Creationix's +[nodemcu-webide](https://github.com/creationix/nodemcu-webide) + +The Python luatool comes from 4ref0nt@gmail.com (http://esp8266.ru) diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/keymaps/nodemcu-thingy.json b/IDE_remote/SocketIDE/source/nodemcu-thingy/keymaps/nodemcu-thingy.json new file mode 100644 index 0000000..2182e3b --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/keymaps/nodemcu-thingy.json @@ -0,0 +1,5 @@ +{ + "atom-workspace": { + "ctrl-alt-o": "nodemcu-thingy:toggle" + } +} diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/lib/nodemcu-thingy-view.js b/IDE_remote/SocketIDE/source/nodemcu-thingy/lib/nodemcu-thingy-view.js new file mode 100644 index 0000000..476a4e8 --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/lib/nodemcu-thingy-view.js @@ -0,0 +1,265 @@ +'use babel'; + +var util=require( './util') + + +const ID_CONSOLE = 'ID_CONSOLE' +const ID_BTN_RESTART = 'ID_BTN_RESTART' +const ID_BTN_CONNECT = 'ID_BTN_CONNECT' +const ID_BTN_LIST = 'ID_BTN_LIST' +const ID_CMD_LINE = 'ID_CMD_LINE' +const ID_BADGE_CONNECTION_STATUS = 'ID_BADGE_CONNECTION_STATUS' +const ID_UL_FILE_LIST_ROOT = 'ID_UL_FILE_LIST_ROOT' + +function createButton(label, id) { + var button = document.createElement('button'); + button.classList.add('inline-block-tight'); + button.classList.add('btn'); + var txt= document.createTextNode(label); + button.appendChild(txt) + button.id= id + button.onclick=eventHandler + return button +} + +var handlers=[]; + +function eventHandler(event) { + var id=event.target.id + console.log(id+" pressed...") + func=handlers[id]; + func(event); +} + +export default class NodemcuThingyView { + + + constructor(serializedState) { + // Create root element + this.element = document.createElement('div'); + this.element.classList.add('nodemcu-thingy'); + // Create message element + + const message = document.createElement('div'); + message.classList.add('message'); + message.style.height="100%" + var div = document.createElement('div'); + div.classList.add("block") + + message.appendChild(div); + + div.appendChild(createButton("Connect",ID_BTN_CONNECT)); + div.appendChild(createButton("Restart",ID_BTN_RESTART)); + div.appendChild(createButton("List Files",ID_BTN_LIST)); + div.style.height="100%"; + + var span = document.createElement('span'); + span.classList.add("badge"); + span.classList.add("badge-error"); + var txt= document.createTextNode("Disconnected"); + span.id=ID_BADGE_CONNECTION_STATUS; + span.appendChild(txt) + div.appendChild(span); + + var centerDiv = document.createElement('div'); + centerDiv.classList.add("block") + centerDiv.style.height="80%"; + centerDiv.style.width="100%"; + div.appendChild(centerDiv); + var consoleDiv = document.createElement('div'); + consoleDiv.classList.add('inline-block'); + consoleDiv.style.height="100%"; + consoleDiv.style.width="75%"; + centerDiv.appendChild(consoleDiv); + + + textarea=document.createElement('textarea'); + textarea.classList.add('input-textarea'); + textarea.style.height="100%"; + textarea.style.width="100%"; + textarea.readOnly = true + textarea.id=ID_CONSOLE + //this is needed for cursor movement etc. + textarea.classList.add('native-key-bindings') + consoleDiv.appendChild(textarea); + + var input =document.createElement('input'); + input.classList.add('input-text'); + input.classList.add('native-key-bindings'); + input.type='text' + input.id=ID_CMD_LINE + input.onkeypress=eventHandler + consoleDiv.appendChild(input); + + var fileDiv = document.createElement('div'); + fileDiv.classList.add('inline-block'); + fileDiv.style.height="100%" + centerDiv.appendChild(fileDiv); + + this.createFileListView(fileDiv); + + + + this.element.appendChild(message); + + +/* + // Wonder if we use that one day for automatic upload + const disposable = atom.project.onDidChangeFiles(events => { + for (const event of events) { + if (event.path.indexOf("/.atom")==-1) { + // "created", "modified", "deleted", or "renamed" + console.log(`Event action: ${event.action}`) + // absolute path to the filesystem entry that was touched + console.log(`Event path: ${event.path}`) + + if (event.type === 'renamed') { + console.log(`.. renamed from: ${event.oldPath}`) + } + } + } + }); +*/ + } + + createFileListView(parent) { + var ulRoot = document.createElement('ul'); + ulRoot.classList.add("list-tree") + parent.appendChild(ulRoot); + + var li = document.createElement('li'); + li.classList.add("list-nested-item"); + ulRoot.appendChild(li); + + var liDiv= document.createElement('div'); + liDiv.classList.add("list-item"); + li.appendChild(liDiv); + + var liSpan = document.createElement("span"); + liSpan.classList.add("icon"); + liSpan.classList.add("icon-file-directory") ; + liSpan.innerText="NodeMCU"; + liDiv.appendChild(liSpan); + + var fileListRoot = document.createElement("ul"); + fileListRoot.classList.add("list-tree"); + fileListRoot.id=ID_UL_FILE_LIST_ROOT; + liDiv.appendChild(fileListRoot); +} + registerConnectButtonHandler(func){ + handlers[ID_BTN_CONNECT]=func; + } + + registerRestartButtonHandler(func){ + handlers[ID_BTN_RESTART]=func; + } + + registerListButtonHandler(func){ + handlers[ID_BTN_LIST]=func; + } + + registerCmdLineHandler(func){ + handlers[ID_CMD_LINE]=func; + } + + // Returns an object that can be retrieved when package is activated + serialize() {} + + // Tear down any state and detach + destroy() { + this.element.remove(); + } + + getElement() { + return this.element; + } + + writeToConsole(text){ + textarea=document.getElementById(ID_CONSOLE) + textarea.value+=text; + textarea.scrollTop = textarea.scrollHeight; + } + + getCmdLineValue() { + input=document.getElementById(ID_CMD_LINE) + return input.value + } + + setCmdLineValue(value) { + input=document.getElementById(ID_CMD_LINE) + input.value=value + } + setConnected(connected) { + var span=document.getElementById(ID_BADGE_CONNECTION_STATUS) + if (span!=null){ + var showsConnected=span.classList.contains("badge-success"); + if (connected && !showsConnected){ + span.classList.add("badge-success"); + span.classList.remove("badge-error"); + span.innerHTML='Connected' + } + else if (!connected && showsConnected) { + span.classList.remove("badge-success"); + span.classList.add("badge-error"); + span.innerHTML='Disconnected' + } + } + } + setFileList(list) { + var root=document.getElementById(ID_UL_FILE_LIST_ROOT) + while (root.firstChild) { + root.removeChild(root.firstChild); + } + for (var i = 0; i < list.length; i++) { + var li = document.createElement('li'); + li.classList.add("list-item"); + root.appendChild(li); + + var span = document.createElement('span'); + span.classList.add("icon"); + var iconType="text" + var filename=list[i].substring(0,list[i].indexOf(" (")); + if (util.isWriteprotected(filename)) { + iconType="zip" + span.classList.add('text-subtle'); + } + else{ + if (filename.endsWith(".lua")) { + iconType="code" + } + else + if (filename.endsWith(".lc")) { + iconType="binary" + } + } + span.classList.add("mcu-download"); + span.classList.add("icon-file-"+iconType); + + span.innerText=list[i]; + li.appendChild(span); + } + + } + + + getTitle() { + // Used by Atom for tab text + return 'NodeMCU Thingy'; + } + + getURI() { + // Used by Atom to identify the view when toggling. + return 'atom://nodemcu-thingy'; + } + + getDefaultLocation() { + // This location will be used if the user hasn't overridden it by dragging the item elsewhere. + // Valid values are "left", "right", "bottom", and "center" (the default). + return 'right'; + } + + getAllowedLocations() { + // The locations into which the item can be moved. + return ['left', 'right', 'bottom']; + } +} diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/lib/nodemcu-thingy.js b/IDE_remote/SocketIDE/source/nodemcu-thingy/lib/nodemcu-thingy.js new file mode 100644 index 0000000..ca06690 --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/lib/nodemcu-thingy.js @@ -0,0 +1,342 @@ +'use babel'; + + +const pingInterval=3000 +// how many ms to wait for ping reply +const pongOffset=200 + +var util=require( './util') + +//find the position of the given byte in the byteArray +function findByte(byteArray, byte) { + for(var i=0;i=0) { + if (end==0){ + byteArray=byteArray.slice(1); + } + else{ + var strByte= byteArray.slice(0,end); + //filename + var str=dec.decode(strByte) + byteArray=byteArray.slice(end+1); + end=findByte(byteArray,0); + var strByte= byteArray.slice(0,end); + str = str+" ("+dec.decode(strByte)+")" + byteArray= byteArray.slice(end); + list.push(str); + } + } + return list +} + +function getFileName(event) { + var str=event.target.innerText; + str = str.substring(0,str.indexOf(" ")); + return str +} + + +import NodemcuThingyView from './nodemcu-thingy-view'; +import { CompositeDisposable, Disposable } from 'atom'; +export default { + config:{ + mcu_hostname:{ + title: 'Host', + description: 'Host name or ip of the nodemcu', + type: 'string', + default: '192.168.1.100', + }, + download_dir:{ + title: 'Download folder', + description: 'Defines a folder to receive the file, when you click '+ + '``Download`` in the mcu file view.', + type: 'string', + default: 'mcu', + } + }, + nodemcuThingyView: null, + subscriptions: null, + connection:null, + pongtime:null, + pongReceived:null, + lsRequested:null, + loadRequested:null, + downloadFileName:null, + + activate(state) { + pongReceived=false; + lsRequested=false; + loadRequested=false; + var t=this; + this.startPing(); + + this.nodemcuThingyView = new NodemcuThingyView(state.nodemcuThingyViewState); + this.nodemcuThingyView.registerConnectButtonHandler(() => this.connectMCU()); + this.nodemcuThingyView.registerRestartButtonHandler(() => this.restartMCU()); + this.nodemcuThingyView.registerListButtonHandler(() => this.list()); + this.nodemcuThingyView.registerCmdLineHandler((event) => this.evalCmd(event)); + + this.subscriptions = new CompositeDisposable( + // Add an opener for our view. + atom.workspace.addOpener(uri => { + if (uri === 'atom://nodemcu-thingy') { + return new NodemcuThingyView(); + } + }), + + // Register command that toggles this view + atom.commands.add('atom-workspace', { + 'nodemcu-thingy:toggle': () => this.toggle(), + 'nodemcu-thingy:upload': () => this.upload(), + 'nodemcu-thingy:list': () => this.list(), + 'nodemcu-thingy:sendTest': () => this.sendTest(), + 'nodemcu-thingy:download': (event) => this.download(event), + 'nodemcu-thingy:delete': (event) => this.delete(event), + 'nodemcu-thingy:dofile': (event) => this.dofile(event) + }), + + // Destroy any NodemcuThingyViews when the package is deactivated. + new Disposable(() => { + atom.workspace.getPaneItems().forEach(item => { + if (item instanceof NodemcuThingyView) { + item.destroy(); + } + }); + }) + ); + + }, + + deactivate() { + this.subscriptions.dispose(); + connection.close(); + }, + + serialize() { + return { + nodemcuThingyViewState: this.nodemcuThingyView.serialize() + }; + }, + startPing(){ + var t = this; + const intervalObj = setInterval(() => { + if (typeof connection !== 'undefined' && connection !== null && connection.readyState === WebSocket.OPEN ) { + connection.send("ping"); + } + //expect reply after a certain time + setTimeout(() => { + t.checkPongReceived(); + },pongOffset); + }, pingInterval); + }, + getView(){ + return this.nodemcuThingyView + }, + checkConnectionAndRun(callback) { + if (typeof connection !== 'undefined' && connection !== null && connection.readyState === WebSocket.OPEN ) { + callback(); + } + else { + this.createConnection(callback); + } + } , + createConnection(callback) { + var mcuView=this.nodemcuThingyView; + mcuView.writeToConsole("connecting...\n"); + connection = new WebSocket('ws://'+atom.config.get('nodemcu-thingy.mcu_hostname')); + connection.binaryType = "arraybuffer"; + // Log errors + connection.onerror = function (error) { + console.log('WebSocket Error ' + error); + mcuView.writeToConsole('WebSocket Error ' + error); + }; + + connection.onopen = function (e) { + mcuView.writeToConsole("connected\n"); + callback(); + } + + var fs = require('fs'); + var t=this + // Log messages from the server + connection.onmessage = function (e) { + if (e.data instanceof ArrayBuffer) { + if (e.data.byteLength==4 ){ + var dec= new TextDecoder('utf-8'); + var str=dec.decode(e.data) + if (str == "pong") { + //register reception globally + pongReceived=true; + } + } + if (!pongReceived){ + if(lsRequested) { + lsRequested=false; + var fileList =parseFileList(e.data) + t.getView().setFileList(fileList); + } + if(loadRequested) { + loadRequested=false; + var dec= new TextDecoder('utf-8'); + var str=dec.decode(e.data) + var foldername =atom.config.get('nodemcu-thingy.download_dir') + var wstream = fs.createWriteStream(foldername+'/'+downloadFileName); + wstream.write(str); + t.getView().writeToConsole("Downloaded:\""+foldername+'/'+downloadFileName+"\""); + } + } + } + else { + t.getView().writeToConsole(e.data); + } + } + }, + //registered menu methods + toggle() { + atom.workspace.toggle('atom://nodemcu-thingy'); + }, + sendTest() { + this.checkConnectionAndRun(() => { + enc= new TextEncoder('utf-8'); + var bytes = enc.encode("print(\"testing:\"..node.heap())") + connection.send(bytes); + connection.send('eval:bla'); + }); + }, + download(event) { + var mcuFilename=getFileName(event); + //store filename for websocket response + downloadFileName=mcuFilename; + loadRequested=true; + var foldername =atom.config.get('nodemcu-thingy.download_dir') + var fs = require('fs'); + if (!fs.existsSync(foldername)) { + atom.confirm ({ + message: "Folder \""+foldername+"\" does not exist, create?", + buttons: {"Yes":() => { + fs.mkdirSync(foldername); + connection.send('load:'+mcuFilename); + }, "No":() => {}}}); + } + else{ + connection.send('load:'+mcuFilename); + } + }, + delete(event) { + var mcuFilename=getFileName(event); + if (util.isWriteprotected(mcuFilename)) { + atom.confirm ({ + message: "Cannot delete writeprotected \""+mcuFilename+"\"!", + buttons: { + "OK":() => {}}}); + return + } + var t = this; + atom.confirm ({ + message: "Really delete \""+mcuFilename+"\"?", + buttons: { + "Yes":() => { + this.sendCmd("file.remove(\""+mcuFilename+"\");print(\"Removed "+mcuFilename+" \")"); + //auto refresh file list TODO make eventdriven? + setTimeout(() => { + t.list(); + },2000) + }, + "No":() => {}}}); + }, + dofile(event) { + var mcuFilename=getFileName(event); + var mcuView=this.nodemcuThingyView; + mcuView.writeToConsole("running "+mcuFilename+"...\n"); + this.sendCmd("dofile(\""+mcuFilename+"\")"); + }, + sendCmd(cmd) { + enc= new TextEncoder('utf-8'); + var bytes = enc.encode(cmd) + connection.send(bytes); + connection.send('eval:cmd'); + }, + + evalCmd(event) { + switch(event.key) { + case "ArrowUp": + //FIXME implement + break; + case "ArrowDown": + //FIXME implement + break; + case "Enter": + var t= this; + this.checkConnectionAndRun(() => { + var cmd = t.getView().getCmdLineValue(); + t.sendCmd(cmd); + }); + break; + } + }, + connectMCU(){ + connection=null; + var t = this; + this.checkConnectionAndRun(() => { + t.sendCmd('print("...")'); + }); + }, + restartMCU(){ + var t= this; + this.checkConnectionAndRun(() => { + var cmd = t.getView().getCmdLineValue(); + t.sendCmd('print("Restarting...")'); + t.sendCmd('node.restart()'); + connection.close(); + }); + }, + list(){ + var t= this; + this.checkConnectionAndRun(() => { + lsRequested=true; + connection.send('ls'); + }); + }, + upload() { + this.checkConnectionAndRun(() => { + const editor = atom.workspace.getActiveTextEditor(); + const filePath=editor.getPath(); + if (filePath != null) { + var fs = require('fs'); + var path = require('path'); + if (util.isWriteprotected(path.basename(filePath))) { + atom.confirm ({ + message: "Cannot upload writeprotected \""+path.basename(filePath)+"\"!", + buttons: { + "OK":() => {}}}); + return + } + var contents = fs.readFileSync(filePath, { encoding: 'utf-8'}); + enc= new TextEncoder('utf-8'); + var bytes = enc.encode(contents) + connection.send(bytes); + connection.send('save:'+path.basename(filePath)); + } + }); + }, + checkPongReceived() { + var s="pongReceived:"+pongReceived; + //update status + this.getView().setConnected(pongReceived); + //reset for next ping + pongReceived=false; + } + +}; diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/lib/util.js b/IDE_remote/SocketIDE/source/nodemcu-thingy/lib/util.js new file mode 100644 index 0000000..600e4f4 --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/lib/util.js @@ -0,0 +1,11 @@ +'use babel'; + function isWriteprotected(filename) { + var protectedFiles=["init.lua","websocket.lc", "main.lc"]; + for (var i = 0; i < protectedFiles.length; i++) { + if (protectedFiles[i] === filename) { + return true; + } + } + } + +module.exports.isWriteprotected = isWriteprotected; diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/init.lua b/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/init.lua new file mode 100644 index 0000000..9433115 --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/init.lua @@ -0,0 +1,58 @@ +local bootreasons={ + [0]="power-on", + [1]="hardware watchdog reset", + [2]="exception reset", + [3]="software watchdog reset", + [4]="software restart", + [5]="wake from deep sleep", + [6]="external reset" +} + +local ip = wifi.sta.getip() +if ip then + print("already got:"..ip) +else + print("Connecting...") + wifi.setmode(wifi.STATION) + ip_cfg={} + ip_cfg.ip = "192.168.6.90" + ip_cfg.netmask = "255.255.255.0" + ip_cfg.gateway = "192.168.6.1" + wifi.sta.setip(ip_cfg) + station_cfg={} + station_cfg.ssid="holternet" + station_cfg.pwd="nemosushi_sushinemo" + wifi.sta.sethostname("mybutton1") + wifi.sta.config(station_cfg) + wifi.sta.connect() + wifi.sta.autoconnect(1) + + tmr.alarm(0, 1000, 1, function () + local ip = wifi.sta.getip() + if ip then + tmr.stop(0) + print(ip) + end + end) +end +dofile("websocket.lc") +dofile("main.lc") +if file.exists("userinit.lua") then + --[[ + 0, power-on + 1, hardware watchdog reset +2, exception reset +3, software watchdog reset +4, software restart +5, wake from deep sleep +6, external reset + ]] + _ , reason = node.bootreason() + if (reason<1 or reason > 3) then + dofile("userinit.lua") + else + print ("Bootreason="..reason.." ("..bootreasons[reason].."), skipping userinit.lua") + end +else + print("userinit.lua not found") +end diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/luatool.py b/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/luatool.py new file mode 100755 index 0000000..724b6e9 --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/luatool.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python2 +# +# ESP8266 luatool +# Author e-mail: 4ref0nt@gmail.com +# Site: http://esp8266.ru +# Contributions from: https://github.com/sej7278 +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin +# Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import sys +import serial +from time import sleep +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: + line = '' + char = '' + while char != chr(62): # '>' + char = s.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: + 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] + 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) + sys.stdout.write("\r\n but got answer : '%s'" % line) + sys.stdout.write("\r\n\r\n") + raise Exception('Error sending data to MCU\r\n\r\n') + line = '' + else: + line += char + else: + sys.stdout.write(" -> send without check") + + +def writer(data): + writeln("file.writeline([==[" + data + "]==])\r") + + +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 + + +if __name__ == '__main__': + # parse arguments or use defaults + parser = argparse.ArgumentParser(description='ESP8266 Lua script uploader.') + parser.add_argument('-p', '--port', default='/dev/ttyUSB0', help='Device name, default /dev/ttyUSB0') +# parser.add_argument('-b', '--baud', default=9600, help='Baudrate, default 9600') + parser.add_argument('-b', '--baud', default=115200, help='Baudrate, default 115200') + parser.add_argument('-f', '--src', default='main.lua', help='Source file on computer, default main.lua') + parser.add_argument('-t', '--dest', default=None, help='Destination file on MCU, default to source file name') + parser.add_argument('-c', '--compile', action='store_true', help='Compile lua to lc after upload') + parser.add_argument('-r', '--restart', action='store_true', help='Restart MCU after upload') + parser.add_argument('-d', '--dofile', action='store_true', help='Run the Lua script after upload') + parser.add_argument('-v', '--verbose', action='store_true', help="Show progress messages.") + 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.') + args = parser.parse_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) + while True: + char = s.read(1) + if char == '' or char == chr(62): + break + sys.stdout.write(char) + 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) + file_list = [] + fn = "" + while True: + char = s.read(1) + if char == '' or char == chr(62): + break + if char not in ['\r', '\n']: + fn += char + else: + if fn: + file_list.append(fn.strip()) + fn = '' + 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") + sys.exit(0) + + if args.dest is None: + args.dest = basename(args.src) + + # open source file for reading + try: + f = open(args.src, "rt") + except: + sys.stderr.write("Could not open input file \"%s\"\n" % args.src) + sys.exit(1) + + # Verify the selected file will not exceed the size of the serial buffer. + # The size of the buffer is 256. This script does not accept files with + # lines longer than 230 characters to have some room for command overhead. + for ln in f: + if len(ln) > 230: + sys.stderr.write("File \"%s\" contains a line with more than 240 " + "characters. This exceeds the size of the serial buffer.\n" + % args.src) + f.close() + sys.exit(1) + + # Go back to the beginning of the file after verifying it has the correct + # 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") + + # remove existing file on device + 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") + + # read source file line by line and write to device + if args.verbose: + sys.stderr.write("\r\nStage 2. Creating file in flash memory and write first line") + 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()) + 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") + + # 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") + + # restart or dofile + if args.restart: + writeln("node.restart()\r") + if args.dofile: # never exec if restart=1 + writeln("dofile(\"" + args.dest + "\")\r", 0) + + # close serial port + s.flush() + s.close() + + # flush screen + sys.stdout.flush() + sys.stderr.flush() + sys.stderr.write("\r\n--->>> All done <<<---\r\n") diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/main.lua b/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/main.lua new file mode 100644 index 0000000..6d5d651 --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/main.lua @@ -0,0 +1,63 @@ + +collectgarbage("setmemlimit", 4) +websocket.createServer(80, function (socket) + local data + node.output(function (msg) + return socket.send(msg, 1) + end, 1) + print("New websocket client connected") + + function socket.onmessage(payload, opcode) + if opcode == 1 then + if payload == "ls" then + local list = file.list() + local lines = {} + for k, v in pairs(list) do + lines[#lines + 1] = k .. "\0" .. v + end + socket.send(table.concat(lines, "\0"), 2) + return + -- standard js websockets do not support ping/pong opcodes so we have to + -- fake it + elseif payload == "ping" then + socket.send("pong", 2) + return + end + local command, name = payload:match("^([a-z]+):(.*)$") + if command == "load" then + file.open(name, "r") + socket.send(file.read(), 2) + file.close() + elseif command == "save" then + file.open(name, "w") + file.write(data) + data = nil + file.close() + print("saved:"..name) + elseif command == "compile" then + node.compile(name) + print("compiled:"..name) + elseif command == "run" then + dofile(name) + elseif command == "eval" then + local fn, success, err + fn, err = loadstring(data, name) + if not fn then + fn = loadstring("print(" .. data .. ")", name) + end + data = nil + if fn then + success, err = pcall(fn) + end + if not success then + print(err) + end + else + print("Invalid command: " .. command) + end + elseif opcode == 2 then + data = payload + end + collectgarbage("setmemlimit", 4) + end +end) diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/upload.sh b/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/upload.sh new file mode 100755 index 0000000..2ced598 --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/upload.sh @@ -0,0 +1,5 @@ + #!/bin/sh +chmod +x luatool.py +./luatool.py --port /dev/ttyUSB0 -f websocket.lua -c +./luatool.py --port /dev/ttyUSB0 -f main.lua -c +./luatool.py --port /dev/ttyUSB0 -f init.lua diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/websocket.lua b/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/websocket.lua new file mode 100644 index 0000000..074a7e4 --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/mcu/websocket.lua @@ -0,0 +1,141 @@ +do +local websocket = {} +_G.websocket = websocket +local band = bit.band +local bor = bit.bor +local rshift = bit.rshift +local lshift = bit.lshift +local char = string.char + local byte = string.byte +local sub = string.sub +local applyMask = crypto.mask +local toBase64 = crypto.toBase64 +local hash = crypto.hash + +local function decode(chunk) + if #chunk < 2 then return end + local second = byte(chunk, 2) + local len = band(second, 0x7f) + local offset + if len == 126 then + if #chunk < 4 then return end + len = bor( + lshift(byte(chunk, 3), 8), + byte(chunk, 4)) + offset = 4 + elseif len == 127 then + if #chunk < 10 then return end + len = bor( + -- Ignore lengths longer than 32bit + lshift(byte(chunk, 7), 24), + lshift(byte(chunk, 8), 16), + lshift(byte(chunk, 9), 8), + byte(chunk, 10)) + offset = 10 + else + offset = 2 + end + local mask = band(second, 0x80) > 0 + if mask then + offset = offset + 4 + end + if #chunk < offset + len then return end + local first = byte(chunk, 1) + local payload = sub(chunk, offset + 1, offset + len) + assert(#payload == len, "Length mismatch") + if mask then + payload = applyMask(payload, sub(chunk, offset - 3, offset)) + end + local extra = sub(chunk, offset + len + 1) + local opcode = band(first, 0xf) + return extra, payload, opcode +end + +local function encode(payload, opcode) + opcode = opcode or 2 + assert(type(opcode) == "number", "opcode must be number") + assert(type(payload) == "string", "payload must be string") + local len = #payload + local head = char( + bor(0x80, opcode), + bor(len < 126 and len or len < 0x10000 and 126 or 127) + ) + if len >= 0x10000 then + head = head .. char( + 0,0,0,0, -- 32 bit length is plenty, assume zero for rest + band(rshift(len, 24), 0xff), + band(rshift(len, 16), 0xff), + band(rshift(len, 8), 0xff), + band(len, 0xff) + ) + elseif len >= 126 then + head = head .. char(band(rshift(len, 8), 0xff), band(len, 0xff)) + end + return head .. payload +end + +local guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +local function acceptKey(key) + return toBase64(hash("sha1", key .. guid)) +end + +function websocket.createServer(port, callback) + net.createServer(net.TCP):listen(port, function(conn) + local buffer = false + local socket = {} + local queue = {} + local waiting = false + local function onSend() + if queue[1] then + local data = table.remove(queue, 1) + return conn:send(data, onSend) + end + waiting = false + end + function socket.send(...) + local data = encode(...) + if not waiting then + waiting = true + conn:send(data, onSend) + else + queue[#queue + 1] = data + end + end + + conn:on("receive", function(_, chunk) + if buffer then + buffer = buffer .. chunk + while true do + local extra, payload, opcode = decode(buffer) + if not extra then return end + buffer = extra + socket.onmessage(payload, opcode) + end + end + local _, e, method = string.find(chunk, "([A-Z]+) /[^\r]* HTTP/%d%.%d\r\n") + local key, name, value + while true do + _, e, name, value = string.find(chunk, "([^ ]+): *([^\r]+)\r\n", e + 1) + if not e then break end + if string.lower(name) == "sec-websocket-key" then + key = value + end + end + + if method == "GET" and key then + conn:send( + "HTTP/1.1 101 Switching Protocols\r\n" .. + "Upgrade: websocket\r\nConnection: Upgrade\r\n" .. + "Sec-WebSocket-Accept: " .. acceptKey(key) .. "\r\n\r\n", + function () callback(socket) end) + buffer = "" + else + conn:send( + "HTTP/1.1 404 Not Found\r\nConnection: Close\r\n\r\n", + conn.close) + end + end) + end) +end + +end diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/menus/nodemcu-thingy.json b/IDE_remote/SocketIDE/source/nodemcu-thingy/menus/nodemcu-thingy.json new file mode 100644 index 0000000..47e2024 --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/menus/nodemcu-thingy.json @@ -0,0 +1,44 @@ +{ + "context-menu": { + "atom-text-editor": [ + { + "label": "Upload to NodeMCU", + "command": "nodemcu-thingy:upload" + } + ], + ".mcu-download": [ + { + "label": "Download to Disk", + "command": "nodemcu-thingy:download" + }, + { + "label": "Delete on NodeMCU", + "command": "nodemcu-thingy:delete" + }, + { + "label": "Run on NodeMCU", + "command": "nodemcu-thingy:dofile" + } + ] + }, + "menu": [ + { + "label": "Packages", + "submenu": [ + { + "label": "nodemcu-thingy", + "submenu": [ + { + "label": "Toggle", + "command": "nodemcu-thingy:toggle" + }, + { + "label": "List MCU files", + "command": "nodemcu-thingy:list" + } + ] + } + ] + } + ] +} diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/package.json b/IDE_remote/SocketIDE/source/nodemcu-thingy/package.json new file mode 100644 index 0000000..e5bf4ae --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/package.json @@ -0,0 +1,16 @@ +{ + "name": "nodemcu-thingy", + "main": "./lib/nodemcu-thingy", + "version": "0.0.3", + "description": "Provides \"over the air\" access to your NodeMCU device (work in progress)", + "keywords": [], + "activationCommands": { + "atom-workspace": "nodemcu-thingy:toggle" + }, + "repository": "https://github.com/holtermp/nodemcu-thingy", + "license": "MIT", + "engines": { + "atom": ">=1.0.0 <2.0.0" + }, + "dependencies": {} +} diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/screencasts/connect.gif b/IDE_remote/SocketIDE/source/nodemcu-thingy/screencasts/connect.gif new file mode 100644 index 0000000..962efb2 Binary files /dev/null and b/IDE_remote/SocketIDE/source/nodemcu-thingy/screencasts/connect.gif differ diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/screencasts/console.gif b/IDE_remote/SocketIDE/source/nodemcu-thingy/screencasts/console.gif new file mode 100644 index 0000000..db6c592 Binary files /dev/null and b/IDE_remote/SocketIDE/source/nodemcu-thingy/screencasts/console.gif differ diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/screencasts/delete.gif b/IDE_remote/SocketIDE/source/nodemcu-thingy/screencasts/delete.gif new file mode 100644 index 0000000..899d526 Binary files /dev/null and b/IDE_remote/SocketIDE/source/nodemcu-thingy/screencasts/delete.gif differ diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/screencasts/upload.gif b/IDE_remote/SocketIDE/source/nodemcu-thingy/screencasts/upload.gif new file mode 100644 index 0000000..2b09787 Binary files /dev/null and b/IDE_remote/SocketIDE/source/nodemcu-thingy/screencasts/upload.gif differ diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/spec/nodemcu-thingy-spec.js b/IDE_remote/SocketIDE/source/nodemcu-thingy/spec/nodemcu-thingy-spec.js new file mode 100644 index 0000000..f373e66 --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/spec/nodemcu-thingy-spec.js @@ -0,0 +1,73 @@ +'use babel'; + +import NodemcuThingy from '../lib/nodemcu-thingy'; + +// Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs. +// +// To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit` +// or `fdescribe`). Remove the `f` to unfocus the block. + +describe('NodemcuThingy', () => { + let workspaceElement, activationPromise; + + beforeEach(() => { + workspaceElement = atom.views.getView(atom.workspace); + activationPromise = atom.packages.activatePackage('nodemcu-thingy'); + }); + + describe('when the nodemcu-thingy:toggle event is triggered', () => { + it('hides and shows the modal panel', () => { + // Before the activation event the view is not on the DOM, and no panel + // has been created + expect(workspaceElement.querySelector('.nodemcu-thingy')).not.toExist(); + + // This is an activation event, triggering it will cause the package to be + // activated. + atom.commands.dispatch(workspaceElement, 'nodemcu-thingy:toggle'); + + waitsForPromise(() => { + return activationPromise; + }); + + runs(() => { + expect(workspaceElement.querySelector('.nodemcu-thingy')).toExist(); + + let nodemcuThingyElement = workspaceElement.querySelector('.nodemcu-thingy'); + expect(nodemcuThingyElement).toExist(); + + let nodemcuThingyPanel = atom.workspace.panelForItem(nodemcuThingyElement); + expect(nodemcuThingyPanel.isVisible()).toBe(true); + atom.commands.dispatch(workspaceElement, 'nodemcu-thingy:toggle'); + expect(nodemcuThingyPanel.isVisible()).toBe(false); + }); + }); + + it('hides and shows the view', () => { + // This test shows you an integration test testing at the view level. + + // Attaching the workspaceElement to the DOM is required to allow the + // `toBeVisible()` matchers to work. Anything testing visibility or focus + // requires that the workspaceElement is on the DOM. Tests that attach the + // workspaceElement to the DOM are generally slower than those off DOM. + jasmine.attachToDOM(workspaceElement); + + expect(workspaceElement.querySelector('.nodemcu-thingy')).not.toExist(); + + // This is an activation event, triggering it causes the package to be + // activated. + atom.commands.dispatch(workspaceElement, 'nodemcu-thingy:toggle'); + + waitsForPromise(() => { + return activationPromise; + }); + + runs(() => { + // Now we can test for view visibility + let nodemcuThingyElement = workspaceElement.querySelector('.nodemcu-thingy'); + expect(nodemcuThingyElement).toBeVisible(); + atom.commands.dispatch(workspaceElement, 'nodemcu-thingy:toggle'); + expect(nodemcuThingyElement).not.toBeVisible(); + }); + }); + }); +}); diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/spec/nodemcu-thingy-view-spec.js b/IDE_remote/SocketIDE/source/nodemcu-thingy/spec/nodemcu-thingy-view-spec.js new file mode 100644 index 0000000..cbaf997 --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/spec/nodemcu-thingy-view-spec.js @@ -0,0 +1,9 @@ +'use babel'; + +import NodemcuThingyView from '../lib/nodemcu-thingy-view'; + +describe('NodemcuThingyView', () => { + it('has one valid test', () => { + expect('life').toBe('easy'); + }); +}); diff --git a/IDE_remote/SocketIDE/source/nodemcu-thingy/styles/nodemcu-thingy.less b/IDE_remote/SocketIDE/source/nodemcu-thingy/styles/nodemcu-thingy.less new file mode 100644 index 0000000..110e8b4 --- /dev/null +++ b/IDE_remote/SocketIDE/source/nodemcu-thingy/styles/nodemcu-thingy.less @@ -0,0 +1,8 @@ +// The ui-variables file is provided by base themes provided by Atom. +// +// See https://github.com/atom/atom-dark-ui/blob/master/styles/ui-variables.less +// for a full listing of what's available. +@import "ui-variables"; + +.nodemcu-thingy { +} diff --git a/IDE_remote/WebIDE/Matthieu Borgognon/README.md b/IDE_remote/WebIDE/Matthieu Borgognon/README.md new file mode 100644 index 0000000..4e40dae --- /dev/null +++ b/IDE_remote/WebIDE/Matthieu Borgognon/README.md @@ -0,0 +1,13 @@ +# Le WEB IDE de Matthieu Borgognon + +## Sources +Il a modifié le WEB_IDE de https://github.com/joysfera/nodemcu-web-ide afin de lui ajouter la commande 'rename' + +Son fichier tenu à jour se trouve dans son dépôt ici: + +https://github.com/matbgn/NodeMCU/tree/master/lib/web-ide + +Mais, j'en ai fait une petite copie locale ;-) + + +zf191020.1128 diff --git a/IDE_remote/WebIDE/Matthieu Borgognon/web_ide.lua b/IDE_remote/WebIDE/Matthieu Borgognon/web_ide.lua new file mode 100644 index 0000000..164e3b1 --- /dev/null +++ b/IDE_remote/WebIDE/Matthieu Borgognon/web_ide.lua @@ -0,0 +1,230 @@ +local mPort = 88 + +local function editor(aceEnabled) -- feel free to disable the shiny Ajax.org Cloud Editor + local AceEnabled = aceEnabled == nil and true or aceEnabled + srv = net.createServer(net.TCP) + srv:listen(mPort, function(conn) + + local rnrn = 0 + local Status = 0 + local DataToGet = 0 + local method = "" + local url = "" + local vars = "" + + conn:on("receive", function(sck, payload) + + if Status == 0 then + _, _, method, url, vars = string.find(payload, "([A-Z]+) /([^?]*)%??(.*) HTTP") + end + + if method == "POST" then + + if Status == 0 then + _, _, DataToGet, payload = string.find(payload, "Content%-Length: (%d+)(.+)") + if DataToGet then + DataToGet = tonumber(DataToGet) + rnrn = 1 + Status = 1 + else + print("bad length") + end + end + + if Status == 1 then + local payloadlen = string.len(payload) + local mark = "\r\n\r\n" + local i + for i=1, payloadlen do + if string.byte(mark, rnrn) == string.byte(payload, i) then + rnrn = rnrn + 1 + if rnrn == 5 then + payload = string.sub(payload, i+1, payloadlen) + file.open(url, "w") + file.close() + Status = 2 + break + end + else + rnrn = 1 + end + end + if Status == 1 then + return + end + end + + if Status == 2 then + if payload then + DataToGet = DataToGet - string.len(payload) + file.open(url, "a+") + file.write(payload) + file.close() + else + sck:send("HTTP/1.1 200 OK\r\n\r\nERROR") + Status = 0 + end + + if DataToGet == 0 then + sck:send("HTTP/1.1 200 OK\r\n\r\nOK") + Status = 0 + end + end + + return + end + -- end of POST method handling + + DataToGet = -1 + + if url == "favicon.ico" then + sck:send("HTTP/1.1 404 file not found\r\nServer: NodeMCU IDE\r\nContent-Type: text/html\r\n\r\n404 - File Not FoundYa done goofed.") + return + end + + local sen = "HTTP/1.1 200 OK\r\nServer: NodeMCU IDE\r\nContent-Type: text/html\r\nPragma: no-cache\r\nCache-Control: no-cache\r\n\r\n" + + -- it wants a file in particular + if url ~= "" and vars == "" then + DataToGet = 0 + sck:send(sen) + return + end + + sen = sen .. "NodeMCU IDE" + sen = sen .. "" + sen = sen .. "

NodeMCU IDE

" + + if vars == "edit" then + if AceEnabled then + local mode = 'ace/mode/' + if url:match(".css") then mode = mode .. 'css' + elseif url:match(".html") then mode = mode .. 'html' + elseif url:match(".json") then mode = mode .. 'json' + elseif url:match(".js") then mode = mode .. 'javascript' + else mode = mode .. 'lua' + end + sen = sen .. "
" + .. "" + else + sen = sen .. "
" + .. "" + end + sen = sen .. " [Run File] [Main Page] " + + elseif vars == "run" then + sen = sen .. "Output of the run:
"
+ 
+         function s_output(str) sen = sen .. str end
+         node.output(s_output, 0) -- re-direct output to function s_output.
+ 
+         local st, result = pcall(dofile, url)
+ 
+         -- delay the output capture by 1000 milliseconds to give some time to the user routine in pcall()
+         tmr.alarm(0, 1000, tmr.ALARM_SINGLE, function() 
+             node.output(nil)
+             if result then
+                 local outp = tostring(result):sub(1,1300) -- to fit in one send() packet
+                 result = nil
+                 sen = sen .. outp
+             end
+             sen = sen .. "

[Edit File] [Run Again] [Main Page]" + sck:send(sen) + end) + + return + + elseif vars == "rename" then + file.rename(url:match("(.+)\/"), url:match("\/(.+)")) + url = "" + + elseif vars == "compile" then + collectgarbage() + node.compile(url) + url = "" + + elseif vars == "delete" then + file.remove(url) + url = "" + + elseif vars == "restart" then + node.restart() + return + + end + + local message = {} + message[#message + 1] = sen + sen = nil + if url == "" then + local l = file.list(); + message[#message + 1] = "\n" + for k,v in pairs(l) do + local line = "\n" + end + message[#message + 1] = line + end + remaining, used, total=file.fsinfo() + message[#message + 1] = "
NameSizeEditRenameCompileDeleteRun
" ..k.. "" ..v.. "" + local editable = k:sub(-4, -1) == ".lua" or k:sub(-4, -1) == ".css" or k:sub(-5, -1) == ".html" or k:sub(-5, -1) == ".json" or k:sub(-4, -1) == ".txt" or k:sub(-4, -1) == ".csv" + if editable then + line = line .. "edit" + end + line = line .. "rename" + if k:sub(-4, -1) == ".lua" then + line = line .. "compile" + end + line = line .. "delete" + if ((k:sub(-4, -1) == ".lua") or (k:sub(-3, -1) == ".lc")) then + line = line .. "run

Total: "..total.." Bytes
Used: "..used.." Bytes
Remaining: "..remaining.." Bytes

[New File] " + remaining, used, total=nil + message[#message + 1] = "[Restart]" + end + message[#message + 1] = "" + + local function send_table(sk) + if #message > 0 then + sk:send(table.remove(message, 1)) + else + sk:close() + message = nil + end + end + sck:on("sent", send_table) + send_table(sck) + end) + + conn:on("sent", function(sck) + if DataToGet >= 0 and method == "GET" then + if file.open(url, "r") then + file.seek("set", DataToGet) + local chunkSize = 512 + local line = file.read(chunkSize) + file.close() + if line then + sck:send(line) + DataToGet = DataToGet + chunkSize + if string.len(line) == chunkSize then return end + end + end + end + + sck:close() + sck = nil + end) + end) +end + +local t = tmr.create() +t:alarm(500, tmr.ALARM_AUTO, function() + if (wifi.sta.status() == wifi.STA_GOTIP) then + t:unregister() + t=nil + print("\n--- Web server started ---") + print("NodeMCU Web IDE running at http://"..wifi.sta.getip()..":"..mPort.."/") + editor() + + end +end) \ No newline at end of file diff --git a/WebIDE/test1/nodemcu-web-ide-master/LICENSE b/IDE_remote/WebIDE/web_ide2_zf/source/nodemcu-web-ide-master/LICENSE similarity index 100% rename from WebIDE/test1/nodemcu-web-ide-master/LICENSE rename to IDE_remote/WebIDE/web_ide2_zf/source/nodemcu-web-ide-master/LICENSE diff --git a/WebIDE/test1/nodemcu-web-ide-master/README.md b/IDE_remote/WebIDE/web_ide2_zf/source/nodemcu-web-ide-master/README.md similarity index 100% rename from WebIDE/test1/nodemcu-web-ide-master/README.md rename to IDE_remote/WebIDE/web_ide2_zf/source/nodemcu-web-ide-master/README.md diff --git a/WebIDE/test1/nodemcu-web-ide-master/ide.lua b/IDE_remote/WebIDE/web_ide2_zf/source/nodemcu-web-ide-master/ide.lua similarity index 100% rename from WebIDE/test1/nodemcu-web-ide-master/ide.lua rename to IDE_remote/WebIDE/web_ide2_zf/source/nodemcu-web-ide-master/ide.lua diff --git a/WebIDE/web_ide1.lua b/IDE_remote/WebIDE/web_ide2_zf/web_ide1.lua similarity index 100% rename from WebIDE/web_ide1.lua rename to IDE_remote/WebIDE/web_ide2_zf/web_ide1.lua diff --git a/WebIDE/web_ide2.lua b/IDE_remote/WebIDE/web_ide2_zf/web_ide2.lua similarity index 100% rename from WebIDE/web_ide2.lua rename to IDE_remote/WebIDE/web_ide2_zf/web_ide2.lua diff --git a/WebIDE/web_ide3.html b/IDE_remote/WebIDE/web_ide2_zf/web_ide3_ko.html similarity index 100% rename from WebIDE/web_ide3.html rename to IDE_remote/WebIDE/web_ide2_zf/web_ide3_ko.html