From dbb4ce9de7ea3c7fdb46862d333f8641de3a1fb2 Mon Sep 17 00:00:00 2001 From: Patrick Balsiger Date: Fri, 3 Aug 2018 11:01:42 +0000 Subject: [PATCH] Resolve "OTA" --- .gitignore | 3 +- README.md | 6 +- platformio.ini | 23 +- src/MeshNet.cpp | 7 +- src/MeshNet.h | 15 +- src/Network.h | 2 + src/OtaTcpPlugin.cpp | 74 ++++ src/Plugin.h | 15 + src/Sprocket.h | 6 +- src/examples/mesh/config.h | 3 +- src/examples/meshMqttBridge/config.h | 4 +- src/examples/ota/SprocketOTA.cpp | 40 +++ src/examples/ota/config.h | 27 ++ src/examples/ota/main.cpp | 25 ++ tools/ota.js | 116 +++++++ tools/package-lock.json | 488 +++++++++++++++++++++++++++ tools/package.json | 13 + tools/tcp_example.js | 45 +++ 18 files changed, 895 insertions(+), 17 deletions(-) create mode 100644 src/OtaTcpPlugin.cpp create mode 100644 src/Plugin.h create mode 100644 src/examples/ota/SprocketOTA.cpp create mode 100644 src/examples/ota/config.h create mode 100644 src/examples/ota/main.cpp create mode 100644 tools/ota.js create mode 100644 tools/package-lock.json create mode 100644 tools/package.json create mode 100644 tools/tcp_example.js diff --git a/.gitignore b/.gitignore index a368c67..251e8a9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ .piolibdeps .vscode/.browse.c_cpp.db* .vscode/c_cpp_properties.json -.vscode/launch.json \ No newline at end of file +.vscode/launch.json +tools/node_modules \ No newline at end of file diff --git a/README.md b/README.md index f2ca9a0..bd7a948 100644 --- a/README.md +++ b/README.md @@ -7,5 +7,9 @@ A sprocket is a device that has a single purpose, for example a PIR sensor node # Useful commands ```sh # erase flash - esptool --port /dev/ttyUSB0 erase_flash +esptool --port /dev/ttyUSB0 erase_flash + +# OTA +~/.platformio/packages/tool-espotapy/espota.py -i -p 8266 -a -f .pioenvs/ota/firmware.bin + ``` \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 9d3dfcb..96adc9e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,8 +8,8 @@ ; Please visit documentation for the other options and examples ; http://docs.platformio.org/page/projectconf.html -;[platformio] -;env_default = mesh +[platformio] +env_default = mesh [common] framework = arduino @@ -21,19 +21,16 @@ lib_deps = Hash ESPAsyncTCP TaskScheduler + SPIFFS ;[env:build] ;src_filter = +<*> - -;#platform = https://github.com/platformio/platform-espressif8266.git#feature/stage -;#platform = https://github.com/platformio/platform-espressif8266.git -;#platform = espressif8266@~1.6.0 ;platform = ${common.platform} ;board = ${common.board} ;upload_speed = ${common.upload_speed} ;monitor_baud = ${common.monitor_baud} ;framework = ${common.framework} ;lib_deps = ${common.lib_deps} - ;build_flags = -DLED_PIN=2 -g ;upload_port = /dev/ttyUSB0 ;upload_port = 192.168.1.168 @@ -66,4 +63,16 @@ monitor_baud = ${common.monitor_baud} framework = ${common.framework} lib_deps = ${common.lib_deps} painlessMesh - PubSubClient \ No newline at end of file + PubSubClient + +[env:ota] +src_filter = +<*> +<*/plugins/*> - + +platform = espressif8266 +board = esp12e +upload_speed = ${common.upload_speed} +upload_flags = --auth=f4ncy +monitor_baud = ${common.monitor_baud} +framework = ${common.framework} +lib_deps = ${common.lib_deps} + https://gitlab.com/wirelos/painlessMesh.git#feature/ota + ArduinoOTA \ No newline at end of file diff --git a/src/MeshNet.cpp b/src/MeshNet.cpp index b4f4269..5da7068 100644 --- a/src/MeshNet.cpp +++ b/src/MeshNet.cpp @@ -15,15 +15,18 @@ Network* MeshNet::init(){ mesh.onChangedConnections(bind(&MeshNet::changedConnectionCallback, this)); mesh.onNodeTimeAdjusted(bind(&MeshNet::nodeTimeAdjustedCallback, this, _1)); + connectStation(); + + return this; +} +Network* MeshNet::connectStation() { if(config.stationMode){ Serial.println("connect station"); mesh.stationManual(config.stationSSID, config.stationPassword); mesh.setHostname(config.hostname); } - return this; } - void MeshNet::sendTo(uint32_t target, String msg){ mesh.sendSingle(target, msg); } diff --git a/src/MeshNet.h b/src/MeshNet.h index 1c59eb7..0110575 100644 --- a/src/MeshNet.h +++ b/src/MeshNet.h @@ -1,6 +1,12 @@ #ifndef __MESHNET_H__ #define __MESHNET_H__ +#ifdef ESP32 +#include +#elif defined(ESP8266) +#include +#endif // ESP32 + #include #include #include "Network.h" @@ -8,6 +14,7 @@ using namespace std; using namespace std::placeholders; +// FIXME non-mesh config should have it's own struct struct MeshConfig { int stationMode; int channel; @@ -27,14 +34,18 @@ class MeshNet : public Network { MeshNet(MeshConfig cfg); Network* init(); - - void update(); // only needed when no scheduler was passed to mesh.init + Network* connectStation(); + + void update(); void newConnectionCallback(uint32_t nodeId); void changedConnectionCallback(); void nodeTimeAdjustedCallback(int32_t offset); void broadcast(String msg); void sendTo(uint32_t target, String msg); void onReceive(std::function); + int isConnected(){ + return WiFi.status() == WL_CONNECTED; + } }; #endif \ No newline at end of file diff --git a/src/Network.h b/src/Network.h index e9d9632..93a65ea 100644 --- a/src/Network.h +++ b/src/Network.h @@ -14,6 +14,8 @@ class Network { virtual Network* init() { return this; }; virtual Network* init(Scheduler* s) { scheduler = s; return init(); }; virtual Network* connect() { return this; }; + virtual Network* connectStation() { return this; }; + virtual int isConnected(){ return 0; }; virtual void update() {}; virtual void broadcast(String msg){}; virtual void sendTo(uint32_t target, String msg) {}; diff --git a/src/OtaTcpPlugin.cpp b/src/OtaTcpPlugin.cpp new file mode 100644 index 0000000..4e72ae2 --- /dev/null +++ b/src/OtaTcpPlugin.cpp @@ -0,0 +1,74 @@ +#ifndef __OTA_CLASSIC_H__ +#define __OTA_CLASSIC_H__ + +#include "TaskSchedulerDeclarations.h" +#include "ArduinoOTA.h" +#include "MeshNet.h" +#include "Plugin.h" + +using namespace std; +using namespace std::placeholders; + +struct OtaConfig { + int port; + const char* password; +}; + +// TODO rename to OtaTcpPlugin +class OtaTcpPlugin : public Plugin { + private: + OtaConfig config; + Task otaTask; + public: + OtaTcpPlugin(OtaConfig cfg){ + config = cfg; + } + void enable(Scheduler* userScheduler, Network* network){ + scheduler = userScheduler; + + // connect to network + if(!network->isConnected()){ + // TODO when config is refactored, cast is not necessary anymore + static_cast(network)->config.stationMode = 1; + network->connectStation(); + // TODO set service message to mesh to announce station IP + } + // setup task + otaTask.set(TASK_MILLISECOND * 100, TASK_FOREVER, [](){ + ArduinoOTA.handle(); + }); + scheduler->addTask(otaTask); + + // configure OTA + ArduinoOTA.setPort(config.port); + //ArduinoOTA.setHostname(HOSTNAME); + if(strlen(config.password) > 0){ + ArduinoOTA.setPassword(config.password); + } + // setup callbacks + ArduinoOTA.onStart([]() { + Serial.println("OTA: Start"); + }); + ArduinoOTA.onEnd([]() { + Serial.println("OTA: End"); + }); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + Serial.printf("OTA: Progress: %u%%\r", (progress / (total / 100))); + }); + ArduinoOTA.onError([](ota_error_t error) { + Serial.printf("OTA: Error[%u]: ", error); + if (error == OTA_AUTH_ERROR) Serial.println("OTA: Auth Failed"); + else if (error == OTA_BEGIN_ERROR) Serial.println("OTA: Begin Failed"); + else if (error == OTA_CONNECT_ERROR) Serial.println("OTA: Connect Failed"); + else if (error == OTA_RECEIVE_ERROR) Serial.println("OTA: Receive Failed"); + else if (error == OTA_END_ERROR) Serial.println("OTA: End Failed"); + }); + ArduinoOTA.begin(); + otaTask.enable(); + } + void disable(){ + otaTask.disable(); + } +}; + +#endif \ No newline at end of file diff --git a/src/Plugin.h b/src/Plugin.h new file mode 100644 index 0000000..c331e60 --- /dev/null +++ b/src/Plugin.h @@ -0,0 +1,15 @@ +#ifndef __SPROCKET_PLUGIN__ +#define __SPROCKET_PLUGIN__ + +#include +#include + +class Plugin { + protected: + Scheduler* scheduler; + public: + virtual void enable(Scheduler*, Network*); + virtual void disable(); +}; + +#endif \ No newline at end of file diff --git a/src/Sprocket.h b/src/Sprocket.h index aa59182..cf20edd 100644 --- a/src/Sprocket.h +++ b/src/Sprocket.h @@ -3,7 +3,10 @@ #include #include -#include +#include "FS.h" +#ifdef ESP32 +#include "SPIFFS.h" +#endif #include "Network.h" using namespace std; @@ -31,6 +34,7 @@ class Sprocket { virtual Sprocket* activate(); virtual Sprocket* activate(Scheduler*) { return this; } virtual Sprocket* activate(Scheduler*, Network*) { return this; } + virtual Sprocket* enable() { return this; }; // TODO bind onMessage to network->onReceive //virtual void onMessage(uint32_t from, String &msg) {}; }; diff --git a/src/examples/mesh/config.h b/src/examples/mesh/config.h index 391f13b..6b44a23 100644 --- a/src/examples/mesh/config.h +++ b/src/examples/mesh/config.h @@ -18,6 +18,7 @@ #define STATION_SSID "Th1ngs4P" #define STATION_PASSWORD "th3r31sn0sp00n" #define HOSTNAME "mesh-node" -#define MESH_DEBUG_TYPES ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE +#define MESH_DEBUG_TYPES ERROR | CONNECTION | COMMUNICATION +//ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE #endif \ No newline at end of file diff --git a/src/examples/meshMqttBridge/config.h b/src/examples/meshMqttBridge/config.h index f60e49e..5a10475 100644 --- a/src/examples/meshMqttBridge/config.h +++ b/src/examples/meshMqttBridge/config.h @@ -13,7 +13,7 @@ #define STATION_MODE 1 #define WIFI_CHANNEL 11 #define MESH_PORT 5555 -#define MESH_PREFIX "WirelosContraption" +#define MESH_PREFIX "whateverYouLike" #define MESH_PASSWORD "somethingSneaky" #define STATION_SSID "Th1ngs4P" #define STATION_PASSWORD "th3r31sn0sp00n" @@ -22,7 +22,7 @@ // Bridge config #define MQTT_CLIENT_NAME HOSTNAME -#define MQTT_BROKER "iot.eclipse.org" +#define MQTT_BROKER "citadel.lan" #define MQTT_PORT 1883 #define MQTT_TOPIC_ROOT "mesh" diff --git a/src/examples/ota/SprocketOTA.cpp b/src/examples/ota/SprocketOTA.cpp new file mode 100644 index 0000000..ebe4776 --- /dev/null +++ b/src/examples/ota/SprocketOTA.cpp @@ -0,0 +1,40 @@ +#ifndef __MESH_APP__ +#define __MESH_APP__ + +#define DEBUG_ESP_OTA +#include +#include +#include +#include +#include "config.h" + +using namespace std; +using namespace std::placeholders; + +class SprocketOTA : public Sprocket { + public: + MeshNet* net; + OtaTcpPlugin* ota; + + SprocketOTA(SprocketConfig cfg, OtaConfig otaCfg) : Sprocket(cfg) { + ota = new OtaTcpPlugin(otaCfg); + } + + Sprocket* activate(Scheduler* scheduler, Network* network) { + net = static_cast(network); + net->onReceive(bind(&SprocketOTA::dispatch,this, _1, _2)); + ota->enable(scheduler, network); + return this; + } using Sprocket::activate; + + void dispatch( uint32_t from, String &msg ) { + Serial.printf("Sprocket: received from %u msg=%s\n", from, msg.c_str()); + } + + void loop() { + net->update(); + scheduler.execute(); + } +}; + +#endif \ No newline at end of file diff --git a/src/examples/ota/config.h b/src/examples/ota/config.h new file mode 100644 index 0000000..86185cf --- /dev/null +++ b/src/examples/ota/config.h @@ -0,0 +1,27 @@ +#ifndef __OTA_NODE__CONFIG__ +#define __OTA_NODE__CONFIG__ + +// Scheduler config +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_STD_FUNCTION + +// Chip config +#define SERIAL_BAUD_RATE 115200 +#define STARTUP_DELAY 3000 + +// Mesh config +#define STATION_MODE 1 // must be 1 => connected to a normal network to receive update +#define WIFI_CHANNEL 11 +#define MESH_PORT 5555 +#define MESH_PREFIX "whateverYouLike" +#define MESH_PASSWORD "somethingSneaky" +#define STATION_SSID "Th1ngs4P" +#define STATION_PASSWORD "th3r31sn0sp00n" +#define HOSTNAME "ota-node" +#define MESH_DEBUG_TYPES ERROR | STARTUP | CONNECTION + +// OTA config +#define OTA_PORT 8266 +#define OTA_PASSWORD "f4ncy" + +#endif \ No newline at end of file diff --git a/src/examples/ota/main.cpp b/src/examples/ota/main.cpp new file mode 100644 index 0000000..222a804 --- /dev/null +++ b/src/examples/ota/main.cpp @@ -0,0 +1,25 @@ +#include +#include "config.h" +#include "MeshNet.h" +#include "SprocketOTA.cpp" + +MeshNet net({ + STATION_MODE, WIFI_CHANNEL, + MESH_PORT, MESH_PREFIX, MESH_PASSWORD, + STATION_SSID, STATION_PASSWORD, HOSTNAME, + MESH_DEBUG_TYPES +}); + +SprocketOTA sprocket( + { STARTUP_DELAY, SERIAL_BAUD_RATE }, + {OTA_PORT, OTA_PASSWORD} +); + +void setup() { + sprocket.join(net); +} + +void loop() { + sprocket.loop(); + yield(); +} \ No newline at end of file diff --git a/tools/ota.js b/tools/ota.js new file mode 100644 index 0000000..dd20f5f --- /dev/null +++ b/tools/ota.js @@ -0,0 +1,116 @@ +// Mesh msg type = 7 +// OTA paket types: START = 0, DATA = 1, END = 3 +const fs = require('fs'); +const crypto = require('crypto'); +const mqtt = require('mqtt'); + +let filePath = '../.pioenvs/mesh/firmware.bin'; +let broker = 'mqtt://192.168.1.2:1883'; + +const readFile = filePath => new Promise((resolve, reject) => { + fs.readFile(filePath, (err, data) => { + if (err) reject(err); + else resolve(data); + }); +}); + +const checksumFile = (hashName, path) => new Promise((resolve, reject) => { + let hash = crypto.createHash(hashName); + let stream = fs.createReadStream(path); + stream.on('error', err => reject(err)); + stream.on('data', chunk => hash.update(chunk)); + stream.on('end', () => resolve(hash.digest('hex'))); +}); + +const upload = async path => { + let content = await readFile(path); + let md5 = await checksumFile('MD5', path); + let b64Content = content.toString('base64'); + console.log(`MD5 Hash: ${md5}`); + //console.log(b64Content); + //mqttClient(md5, b64Content); +}; + +const initializeUpdateMessage = (fromNode, toNode, md5Hash) => { + return { + dest: toNode, + from: fromNode, + type: 7, + msg: { + type: 0, + md5: md5Hash + } + }; +}; + +const firmwareUpdateMessage = (fromNode, toNode, firmware) => { + return { + dest: toNode, + from: fromNode, + type: 7, + msg: { + type: 1, + data: firmware, + length: firmware.length + } + }; +}; + +const mqttClient = (md5, data) => { + + var client = mqtt.connect(broker); + let target = 3895627464; + // button thingy=> dest: "757307466", + let initMsg = { + dest: target, + from: 1, + type: 7, + msg: { + type: 0, + md5: md5 + } + }; + let dataMsg = { + dest: target, + from: 1, + type: 7, + msg: { + type: 1, + data: data, + length: data.length + } + }; + let sm = { + OTA_INIT: { + FAILED: console.log, + DONE: OTA_DATA.WRITE + }, + OTA_DATA: { + WRITE: console.log, + DONE: OTA_FIN.RESTART, + FAILED: console.log + }, + OTA_FIN: { + RESTART: console.log + } + }; + + client.on('connect', function () { + client.subscribe('/up/wirelos/gateway'); + client.publish('/down/wirelos', JSON.stringify(initMsg)); + }) + + client.on('message', function (topic, message) { + // message is Buffer + console.log(message.toString()); + let obj = JSON.parse(message.toString()); + sm[obj.type][obj.state](obj); + //if(JSON.parse(message.toString()).type == 'OTA_INIT'){ + // client.publish('/down/wirelos', JSON.stringify(dataMsg)); + //} + client.end(); + }) + return client; +}; + +upload(filePath); \ No newline at end of file diff --git a/tools/package-lock.json b/tools/package-lock.json new file mode 100644 index 0000000..0c2ecd0 --- /dev/null +++ b/tools/package-lock.json @@ -0,0 +1,488 @@ +{ + "name": "tools", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "requires": { + "readable-stream": "2.3.6", + "safe-buffer": "5.1.2" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", + "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==" + }, + "callback-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/callback-stream/-/callback-stream-1.1.0.tgz", + "integrity": "sha1-RwGlEmbwbgbqpx/BcjOCLYdfSQg=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.6" + } + }, + "commist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-1.0.0.tgz", + "integrity": "sha1-wMNSUBz29S6RJOPvicmAbiAi6+8=", + "requires": { + "leven": "1.0.2", + "minimist": "1.2.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "1.1.0", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "typedarray": "0.0.6" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "duplexify": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", + "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", + "requires": { + "end-of-stream": "1.4.1", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "stream-shift": "1.0.0" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "1.4.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + } + }, + "glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", + "requires": { + "extend": "3.0.2", + "glob": "7.1.2", + "glob-parent": "3.1.0", + "is-negated-glob": "1.0.0", + "ordered-read-streams": "1.0.1", + "pumpify": "1.5.1", + "readable-stream": "2.3.6", + "remove-trailing-separator": "1.1.0", + "to-absolute-glob": "2.0.2", + "unique-stream": "2.2.1" + } + }, + "help-me": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-1.1.0.tgz", + "integrity": "sha1-jy1QjQYAtKRW2i8IZVbn5cBWo8Y=", + "requires": { + "callback-stream": "1.1.0", + "glob-stream": "6.1.0", + "through2": "2.0.3", + "xtend": "4.0.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "requires": { + "is-relative": "1.0.0", + "is-windows": "1.0.2" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "2.1.1" + } + }, + "is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=" + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "requires": { + "is-unc-path": "1.0.0" + } + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "requires": { + "unc-path-regex": "0.1.2" + } + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "0.0.0" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "leven": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/leven/-/leven-1.0.2.tgz", + "integrity": "sha1-kUS27ryl8dBoAWnxpncNzqYLdcM=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mqtt": { + "version": "2.18.3", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-2.18.3.tgz", + "integrity": "sha512-BXCUugFgA6FOWJGxhvUWtVLOdt6hYTmiMGPksEyKuuF1FQ0ji7UJBJ/0kVRMUtUWCAtPGnt4mZZZgJpzNLcuQg==", + "requires": { + "commist": "1.0.0", + "concat-stream": "1.6.2", + "end-of-stream": "1.4.1", + "help-me": "1.1.0", + "inherits": "2.0.3", + "minimist": "1.2.0", + "mqtt-packet": "5.6.0", + "pump": "3.0.0", + "readable-stream": "2.3.6", + "reinterval": "1.1.0", + "split2": "2.2.0", + "websocket-stream": "5.1.2", + "xtend": "4.0.1" + } + }, + "mqtt-packet": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-5.6.0.tgz", + "integrity": "sha512-QECe2ivqcR1LRsPobRsjenEKAC3i1a5gmm+jNKJLrsiq9PaSQ18LlKFuxvhGxWkvGEPadWv6rKd31O4ICqS1Xw==", + "requires": { + "bl": "1.2.2", + "inherits": "2.0.3", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", + "requires": { + "readable-stream": "2.3.6" + } + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "requires": { + "duplexify": "3.6.0", + "inherits": "2.0.3", + "pump": "2.0.1" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "1.4.1", + "once": "1.4.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc=" + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "split2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", + "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", + "requires": { + "through2": "2.0.3" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "requires": { + "readable-stream": "2.3.6", + "xtend": "4.0.1" + } + }, + "through2-filter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", + "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", + "requires": { + "through2": "2.0.3", + "xtend": "4.0.1" + } + }, + "to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "requires": { + "is-absolute": "1.0.0", + "is-negated-glob": "1.0.0" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" + }, + "unique-stream": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", + "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", + "requires": { + "json-stable-stringify": "1.0.1", + "through2-filter": "2.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "websocket-stream": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.1.2.tgz", + "integrity": "sha512-lchLOk435iDWs0jNuL+hiU14i3ERSrMA0IKSiJh7z6X/i4XNsutBZrtqu2CPOZuA4G/zabiqVAos0vW+S7GEVw==", + "requires": { + "duplexify": "3.6.0", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "safe-buffer": "5.1.2", + "ws": "3.3.3", + "xtend": "4.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "1.0.0", + "safe-buffer": "5.1.2", + "ultron": "1.1.1" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } +} diff --git a/tools/package.json b/tools/package.json new file mode 100644 index 0000000..b0a549e --- /dev/null +++ b/tools/package.json @@ -0,0 +1,13 @@ +{ + "name": "tools", + "version": "1.0.0", + "description": "", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "MIT", + "dependencies": { + "mqtt": "^2.18.3" + } +} diff --git a/tools/tcp_example.js b/tools/tcp_example.js new file mode 100644 index 0000000..18f0ff9 --- /dev/null +++ b/tools/tcp_example.js @@ -0,0 +1,45 @@ +/* +In the node.js intro tutorial (http://nodejs.org/), they show a basic tcp +server, but for some reason omit a client connecting to it. I added an +example at the bottom. +Save the following server in example.js: +*/ + +var net = require('net'); + +var server = net.createServer(function(socket) { + socket.write('Echo server\r\n'); + socket.pipe(socket); +}); + +server.listen(1337, '127.0.0.1'); + +/* +And connect with a tcp client from the command line using netcat, the *nix +utility for reading and writing across tcp/udp network connections. I've only +used it for debugging myself. +$ netcat 127.0.0.1 1337 +You should see: +> Echo server +*/ + +/* Or use this example tcp client written in node.js. (Originated with +example code from +http://www.hacksparrow.com/tcp-socket-programming-in-node-js.html.) */ + +var net = require('net'); + +var client = new net.Socket(); +client.connect(1337, '127.0.0.1', function() { + console.log('Connected'); + client.write('Hello, server! Love, Client.'); +}); + +client.on('data', function(data) { + console.log('Received: ' + data); + client.destroy(); // kill client after server's response +}); + +client.on('close', function() { + console.log('Connection closed'); +});