I am trying to build a Node.js App to Monitor some Raspberry Pi's.
Since those Raspberries don’t have a static IP, they send an UDP Broadcast every 5 seconds.
I'm able to catch that Broadcast with Node.js, but I'm failing to trigger a new function to notify the Node.js Clients.
I tried WebSockets, ServerSendEvents and Socket.io.
I'm able to use Example Code and they work just fine.
But I'm not Experienced enough to build a function which will send data to the clients.
Node.js App:
// ==============================================================================================================
// ===== Dependencies ===========================================================================================
// ==============================================================================================================
var dgram = require('dgram');
var http = require('http');
var url = require("url");
var path = require("path");
var fs = require("fs");
// ==============================================================================================================
// ===== HTTP Serv ==============================================================================================
// ==============================================================================================================
var server = http.createServer(function(request, response) {
var uri = url.parse(request.url).pathname, filename = path.join(process.cwd(), uri);
var contentTypesByExtension = {
'.html': "text/html",
'.css': "text/css",
'.js': "text/javascript",
'.svg': "image/svg+xml"
};
fs.exists(filename, function(exists) {
if(!exists) {
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not Found\n");
response.end();
return;
}
if (fs.statSync(filename).isDirectory()) filename += '/index.html';
fs.readFile(filename, "binary", function(err, file) {
if(err) {
response.writeHead(500, {"Content-Type": "text/plain"});
response.write(err + "\n");
response.end();
return;
}
var headers = {};
var contentType = contentTypesByExtension[path.extname(filename)];
if (contentType) headers["Content-Type"] = contentType;
response.writeHead(200, headers);
response.write(file, "binary");
response.end();
});
});
});
// ==============================================================================================================
// ===== HeartBeat Broadcast ====================================================================================
// ==============================================================================================================
var bcast = dgram.createSocket('udp4');
bcast.on('message', function (message) {
console.log("Triggered: UDP Broadcast");
// If UDP Broadcast is received, send message/data to client.
});
bcast.bind(5452, "0.0.0.0");
// ==============================================================================================================
// ===== Start Server ===========================================================================================
// ==============================================================================================================
server.listen(80);
console.log("Static file server running/\nCTRL + C to shutdown");
EDIT:
I think I did not explain myself accurate enough.
I do not want to send a UDP message back.
This UDP Broadcast should fire an (Node.js) event, which should update the html and display the raspberry pi (whom send the UDP Package) as online.
EDIT:
In documentation from official page of nodejs (DOCUMENTATION):
var socket = require('socket.io')(http);
var bcast = dgram.createSocket('udp4');
bcast.bind(5452, "0.0.0.0");
bcast.on('message', function (message, remote) {
////if message is an Object pushed into Buffer////
message = message.toString('utf8');
socket.emit("HTML_Update", message);
//////////////////////////////////Solution for unedited question//////////////////////////
// var msgBuffer = Buffer.from(message.toString(); //creating a buffer //
// bcast.send(msgBuffer, 0, msgBuffer.length, remote.port, remote.address, (err) => { //
// bcast.close(); //
// }); //sending message to remote.address:remote.port (like localhost:23456) //
// //
// **build a function which will send data to the clients** //
//////////////////////////////////Solution for unedited question//////////////////////////
});
"If message is an Object pushed into Buffer" - lets say that one of the RPI turned on and started sending UDP message, what should the message pass to server so server can pass it to display: mac address only because if it sends something You can be sure its on, if it does not send its off simple as that. Also to show that change on client You should initialize TCP sockets on server to pass info to servers web page to update content on html with jquery.
Now here is the HTML java script part (I personally make main.js file and write all java script into it and use import it as src into html). Using jquery in main.js:
$(document).ready(function() {
var time = new Date();
var rpi = {
"list" : ["mac1", "mac2", "mac3"],
"time" : [time.getTime(), time.getTime(), time.getTime()],
"label" : ["label_ID1", "label_ID2", "label_ID3"]};
var socket = io.connect('http://your_server_address:80');
setInterval( function(){
for (var i = 0; i <= 2; i++){
if((rpi.time[i] + 10000) < time.getTime()){
$(rpi.label[i]).text("RPI " + rpi.list[i] + " is DOWN");
}
}
}, 5000);
socket.on("HTML_Update", function(data){
for (var i = 0; i<=2; i++) {
if (data.toString().equals(rpi.list[i])) {
$(rpi.label[i]).text("RPI: "+ rpi.list[i] + " is UP");
rpi.time[i] = time.getTime();
}
}
});
}
If You put text label in html to show if specific rpi is up or down this part of code works in this scheme:
Multiple RPI + Server - RPI sends UDP data with mac to server. Server device is used to receive data and show it on any device as web page and change data if RPI is UP/DOWN.
Related
I believe this is a simple question: I have a websocket server (index.js) that opens a serial port when the browser (index.html) is loaded. I have a scale connected via USB (COM3). From the browser I want to send commands to the scale and receive data back to the browser. My node version is v7.7.4 and npm version is 4.1.2. I have also NPM installed serialport and ws.
index.js
var SerialPort = require('serialport');// include the library
//var SerialPort = serialport.SerialPort; // make a local instance of it
var WebSocketServer = require('ws').Server;
var SERVER_PORT = 8080; // port number for the webSocket server
var wss = new WebSocketServer({port: SERVER_PORT}); // the webSocket server
var connections = new Array; // list of connections to the server
wss.on('connection', handleConnection);
// get port name from the command line:
portName = process.argv[2];
var myPort = new SerialPort(portName, {
baudRate: 9600,
// look for return and newline at the end of each data packet:
parser: SerialPort.parsers.readline("\n")
});
myPort.on('open', showPortOpen);
myPort.on('data', sendSerialData);
myPort.on('close', showPortClose);
myPort.on('error', showError);
// This function broadcasts messages to all webSocket clients
function broadcast(data) {
for (myConnection in connections) { // iterate over the array of connections
connections[myConnection].send(data); // send the data to each connection
}
}
function sendToSerial(data) {
myPort.write(" From: index.js:sendToSerial "+data);
console.log("sendToSerial (index.js): " + data);
// if there are webSocket connections, send the serial data
// to all of them:
if (connections.length > 0) {
broadcast(data);
}}
function handleConnection(client) {
console.log("New Connection"); // you have a new client
connections.push(client); // add this client to the connections array
client.on('message', sendToSerial); // when a client sends a message,
client.on('close', function() { // when a client closes its connection
console.log("connection closed"); // print it out
var position = connections.indexOf(client); // get the client's position in the array
connections.splice(position, 1); // and delete it from the array
});
}
function showPortOpen() {
console.log(portName+' port open. Data rate: ' + myPort.options.baudRate);
}
function sendSerialData(data) {
myPort.write(" From: index.js:sendSerialData "+data);
myPort.write("sendSerialData "+data);
console.log("sendSerialData "+data);
}
function showPortClose() {
console.log('port closed.');
}
function showError(error) {
console.log('Serial port error: ' + error);
}
and index.html
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.8/p5.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.8/addons/p5.dom.js"></script>
<script type="text/javascript">
var text; // variable for the text div you'll create
var socket = new WebSocket("ws://localhost:8080");
function setup() {
// The socket connection needs two event listeners:
socket.onopen = openSocket;
socket.onmessage = showData;
// make a new div and position it at 10, 10:
text = createDiv("xxSensor reading:");
text.position(10,10);
}
function openSocket() {
text.html("Socket open");
socket.send("Hello websocket server - from index.html");
}
function showData(result) {
// when the server returns, show the result in the div:
text.html("Sensor reading: " + result.data);
xPos = int(result.data); // convert result to an integer
text.position(xPos, 10); // position the text
}
I have tried SERVER_PORT = 8081 with the same results.
I am able to see info from index.html in the cmd "node index.js COM3" but the command myPort.write does not get to the index.html browser.
I get:
C:\Users\pmfoo\nodeSerialExample>node index.js COM3
COM3 port open. Data rate: 9600
New Connection
sendToSerial (index.js): Hello websocket server - from index.html
sendSerialData 277.5 g
sendSerialData
where 277.5 g is the output from the scale on COM3.
and in the browser:
Sensor reading: Hello websocket server - from index.html
I followed Tom Igoe's tutorial https://itp.nyu.edu/physcomp/labs/labs-serial-communication/lab-serial-communication-with-node-js/ with only partial results. I cannot write from index.js to index.html nor can I send a command via index.js to the scale. This scale command ("ON" or "OFF" or "PSN") is seen by index.js. Can anyone help me solve these 2 communication problems?
var http = require('http');
var twilio = require('twilio')(ACCOUNT_SID, AUTH_TOKEN);
var qs = require('querystring');
http.createServer(function (req, res) {
var body = '';
req.setEncoding('utf8');
req.on('data', function(data) {
body += data;
});
req.on('end', function() {
var data = qs.parse(body);
var jsonString = JSON.stringify(data);
var jsonDataObject = JSON.parse(jsonString);
// log the received message
console.log(jsonDataObject.Body);
twilio.messages.create({
to:'MY_PHONE_NUMBER',
from:'TWILIO_NUMBER',
body:'Hello World'
}, function(error, message) {
if (error) {
console.log('There was an error.')
console.log(error.message);
}
});
res.writeHead(200, {'Content-Type': 'text/xml'});
res.end();
});
}).listen(1337, '127.0.0.1');
console.log('TwiML servin\' server running at http://127.0.0.1:1337/');
I'm trying to use the Twilio node module to receive a text message and in turn respond to that text message once received. There seems to be no problem receiving the message as I'm able to log the body. But, I get a 401 Authenticate error when I try and respond to that message. I'm using ngrok to expose my localhost so I can hook it into Twilio's API. Please see below:
Where am I going wrong here?
Twilio developer evangelist here.
You actually don't need to use the REST API in order to reply to an incoming message to a Twilio number. You can, in fact, respond to the incoming HTTP request with TwiML that describes the message in response.
To do this, you need to use the <Message> verb. In your application, this would look like:
First, just require the twilio module without the account credentials:
var twilio = require("twilio");
Then, respond to the incoming request with TwiML, like so:
req.on('end', function() {
var data = qs.parse(body);
var jsonString = JSON.stringify(data);
var jsonDataObject = JSON.parse(jsonString);
// log the received message
console.log(jsonDataObject.Body);
var twiml = new twilio.TwimlResponse();
twiml.message("Hello world");
res.writeHead(200, {'Content-Type': 'text/xml'});
res.end(twiml.toString());
});
Let me know if this helps at all.
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
const MessagingResponse = require('twilio').twiml.MessagingResponse;
var server = app.listen(80, function () {
var host = server.address().address
var port = server.address().port
console.log(" web app listening at http://%s:%s", host, port)
})
app.post('/txt', urlencodedParser,(req, res) => {
const twiml = new MessagingResponse();
twiml.message('Finally Twilio works!');
res.status(200);
res.send(twiml.toString());
});
Under your phone number in the console.
You can click webhooks and change it to the http://"putyourserverhere"/txt
This will automatically text back the inbound user.
Enjoy. Make sure you have the newest version of twilio installed.
I'm trying to create a simple Node.js server that receives files from clients via UDP. The problem I'm having is that every time I try to transmit a large file (anything over 100kb), the server doesn't seem to respond. So far I've been successful at transmitting files up to 50kb.
Is there any way to resolve this issue?
Client Code:
var PORT = 33333;
var HOST = 'localhost';
var dgram = require('dgram');
var log = require('sys').log;
var client = dgram.createSocket('udp4');
var fs = require("fs");
fs.readFile('C:\\test.pdf', function (err,data) {
if (err) {
return console.log(err);
}
client.send(data, 0, data.length, PORT, HOST, function(err, bytes) {
if (err)
throw err;
log('UDP file sent to ' + HOST +':'+ PORT);
log('File sise: ' + data.length);
});
});
Server Code:
var PORT = 33333;
var HOST = 'localhost';
var dgram = require('dgram');
var server = dgram.createSocket('udp4');
var fs = require("fs");
var log = require('sys').log;
var wstream = fs.createWriteStream('test.pdf');
wstream.on('finish', function () {
console.log('file has been written');
});
server.on('message', function (message, remote) {
wstream.write(message);
wstream.end();
});
server.bind(PORT, HOST);
From the dgram docs:
The Payload Length field is 16 bits wide, which means that a normal payload cannot be larger than 64K octets including internet header and data (65,507 bytes = 65,535 − 8 bytes UDP header − 20 bytes IP header); this is generally true for loopback interfaces, but such long datagrams are impractical for most hosts and networks.
You can't send datagrams of more than 65,507 bytes. That's a hard limit on UDP. It sounds like you should be using a higher-level protocol for these files.
I have been trying to get my server to work but when I send post data it just keeps loading and no results are given. Here is my noen.js file.
var http = require('http');
var url = require('url');
// Configure our HTTP server to respond with Hello World to all requests.
var server = http.createServer(function (request, response) {
var queryData = url.parse(request.url, true).query;
response.writeHead(200, {"Content-Type": "text/plain"});
if (queryData.name) {
// user told us their name in the GET request, ex: http://host:8000/?name=Tom
var exec = require('child_process').exec;
function puts(error, stdout, stderr) {sys.puts(stdout)}
exec ("casperjs test.js " + queryData.name + '\n');
} else {
response.end("Contact Admin - Not Working\n");
}
});
// Listen on port 8000, IP defaults to 127.0.0.1
server.listen(1213);
Can anyone help me fix this? When I go to
127.0.0.1:8000/?name=tom
I get no response the page just goes into a long loading loop
There is no response.end in case if is true so then response "never" ends.
write at bottom of the if
response.end("something");
And you will get the response;
For get the output of the process to the response:
https://stackoverflow.com/a/3944751/3018595
var http = require('http');
var url = require('url');
// Configure our HTTP server to respond with Hello World to all requests.
var server = http.createServer(function (request, response) {
var queryData = url.parse(request.url, true).query;
response.writeHead(200, {"Content-Type": "text/plain"});
if (queryData.name) {
// user told us their name in the GET request, ex: http://host:8000/?name=Tom
var exec = require('child_process').exec;
exec ("casperjs test.js " + queryData.name + '\n',function(err, stdout, stderr) {
response.end(stdout);
});
} else {
response.end("Contact Admin - Not Working\n");
}
});
// Listen on port 8000, IP defaults to 127.0.0.1
server.listen(1213);
The reason your browser is keep waiting because you are not ending your response. You have to call response.end to let your server complete the response otherwise it will keep thinking that the response is not complete yet. I added a line in your if statement and tested your code and it is working perfectly fine.
added line ** response.end("Request processed successfully...\n");**, assuming that you need to display a different message in case your "else" statement.
I tested url http://:1213/?name=tom
var http = require('http');
var url = require('url');
// Configure our HTTP server to respond with Hello World to all requests.
var server = http.createServer(function (request, response) {
var queryData = url.parse(request.url, true).query;
response.writeHead(200, {"Content-Type": "text/plain"});
if (queryData.name) {
// user told us their name in the GET request, ex: http://host:8000/?name=Tom
var exec = require('child_process').exec;
function puts(error, stdout, stderr) {sys.puts(stdout)}
exec ("casperjs test.js " + queryData.name + '\n');
response.end("Request processed successfully...\n");
} else {
response.end("Contact Admin - Not Working\n");
}
});
// Listen on port 8000, IP defaults to 127.0.0.1
server.listen(1213);
I'm trying to manage a bunch of socket connections. My app is basically an http server that receives posts and passes these along to a socket. When clients open a socket connection, they send a connect message with an id:
{"m":"connect","id":"1"}
The app then saves this id and socket in the id2socket and socket2id maps. On disconnect, the socket/id pair is deleted from the maps.
A post will also contain an id, which indicates the post data should be sent to the socket with that id.
That's great, and this works fine for a single open socket. However, when I have more than one socket open, and then I close a socket, that disconnect wipes everything from the map. I think my understanding of sockets in node is incomplete- is there only a single socket object that is used in the callback? Is there a better way to manage my open socket connections and ids?
start server:
>>node server.js
TCP server listening on 127.0.0.1:5280
HTTP server listening on 127.0.0.1:9002
telnet in:
>>telnet localhost 5280
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
{"m":"connect","id":"123"}
{"m":"connect","id":"123","success":"true"}
server after connection:
>>Connection from 127.0.0.1:57572
received data: {"m":"connect","id":"123"}
id: 1
m: connect
associating uid 1 with socket [object Object]
do a post:
python post.py {"foo":"bar"}
So this works fine for several open sockets (as long as 1 device is id 123, server has this hardwired for now). However, as soon as you close one connection all the socket connections are removed from the map.
Here's my code:
python script to do post:
import sys
import json
import httplib, urllib, urllib2
values = json.loads('{"foo":"bar"}')
headers = {"Content-type": "application/json"}
conn = httplib.HTTPConnection('127.0.0.1', 9002)
headers = {"Content-type": "application/json"}
conn.request("POST", "", json.dumps(values), headers)
response = conn.getresponse()
print "response.status: "+response.status
print "response.reason: "+response.reason
print "response.read: "+response.read()
conn.close()
node server (http and tcp), hardwired to send data to device '123' on post:
var net = require('net'); // tcp-server
var http = require("http"); // http-server
var qs = require('querystring'); // http-post
// Map of sockets to devices
var id2socket = new Object;
var socket2id = new Object;
// Setup a tcp server
var server_plug = net.createServer(function(socket) {
// Event handlers
socket.addListener("connect", function(conn) {
console.log("Connection from " + socket.remoteAddress + ":" + socket.remotePort );
});
socket.addListener("data", function(data) {
console.log("received data: " + data);
try {
request = JSON.parse(data);
response = request;
if(request.m !== undefined && request['id'] !== undefined){ // hack on 'id', id is js obj property
console.log("id: "+request['id']);
console.log("m: "+request.m);
if(request.m == 'connect'){
console.log("associating uid " + request['id'] + " with socket " + socket);
id2socket[request['id']] = socket;
socket2id[socket] = request['id'];
response.success = 'true';
} else {
response.success = 'true';
}
}
socket.write(JSON.stringify(response));
} catch (SyntaxError) {
console.log('Invalid JSON:' + data);
socket.write('{"success":"false","response":"invalid JSON"}');
}
});
socket.on('end', function() {
id = socket2id[socket]
console.log("socket disconnect by id " + id);
// wipe out the stored info
console.log("removing from map socket:"+socket+" id:"+id);
delete id2socket[id];
delete socket2id[socket];
});
socket.on('timeout', function() {
console.log('socket timeout');
});
});
// Setup http server
var server_http = http.createServer(
// Function to handle http:post requests, need two parts to it
// http://jnjnjn.com/113/node-js-for-noobs-grabbing-post-content/
function onRequest(request, response) {
request.setEncoding("utf8");
request.addListener("data", function(chunk) {
request.content += chunk;
});
request.addListener("end", function() {
console.log("post received!");
//console.log("Request received: "+request.content);
if (request.method == 'POST') {
//var json = qs.parse(request.content);
//console.log("Post: "+json);
// HACK TO TEST STUFF:
// send a message to one of the open sockets
try {
var socket = id2socket['123']; //hardwired
socket.write('{"m":"post"}');
} catch (Error) {
console.log("Cannot find socket with id "+'123');
}
}
});
}
);
// Fire up the servers
var HOST = '127.0.0.1';
var PORT = 5280;
var PORT2 = 9002;
server_plug.listen(PORT, HOST);
console.log("TCP server listening on "+HOST+":"+PORT);
server_http.listen(PORT2);
console.log("HTTP server listening on "+HOST+":"+PORT2);
Objects only take strings as keys for their properties. As your log shows, a socket object is converted into the string "[object Object]". As a result, socket #2 overwrites the id from socket #1 in the object, because all sockets are converted into the same string key. So, there is only one property in the object at all times, because all sockets come down to the same key. When you try to remove the id for socket #2, the single property is deleted and the object is empty.
You seem to want a custom property for each separate socket when used as a key. You can use WeakMaps for this. WeakMaps do allow objects as keys (as opposed to string-only keys), but as they're relatively new they may contain bugs at the moment.
(Note that the id2socket map can just be a plain object, because numbers are converted into strings just fine, and each number has its own, distinct string representation*.)
Using WeakMaps is as follows:
var socket2id = new WeakMap; // as if you were doing: var socket2id = {};
socket2id.set(socket, id); // as if you were doing: socket2id[socket] = id;
socket2id.get(socket); // as if you were doing: socket2id[socket];
socket2id.delete(socket); // as if you were doing: delete socket2id[socket];
Make sure to run with node --harmony (>= 0.7) or node --harmony_weakmaps (<= 0.6).
* 0 and -0 are exceptions, but you shouldn't be using -0 anyway because 0 === -0, so it's difficult to differ between them.