I am trying to host a local server (using Node.js) on a Raspberry Pi. The Pi has an ADC (MCP3008) connected to it, and I have a Python script that continuously samples the ADC and prints the current value. I want to have the Node server run the Python script, and whenever it sees a print statement, to just do a console.log(current value) for the time being. I am new to Node and web development in general, so it may be something simple that I'm missing so that Node will continuously receive data from the Python script. I'm trying to use Socket.io at the moment, as that seems to make sense as the method for Node to see changes from the Python script, but maybe this isn't the best way to do it. The basic webpage is from a tutorial I found (http://www.jaredwolff.com/blog/raspberry-pi-getting-interactive-with-your-server-using-websockets/). The code I am currently using is here:
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, url= require('url')
, fs = require('fs')
, gpio = require('onoff').Gpio
, PythonShell = require('python-shell');
app.listen(5000);
function handler (req, res) {
var path = url.parse(req.url).pathname;
if (path == '/') {
index = fs.readFile(__dirname+'/public/index.html',
function(error,data) {
if (error) {
res.writeHead(500);
return res.end("Error: unable to load index.html");
}
res.writeHead(200,{'Content-Type': 'text/html'});
res.end(data);
});
} else if( /\.(js)$/.test(path) ) {
index = fs.readFile(__dirname+'/public'+path,
function(error,data) {
if (error) {
res.writeHead(500);
return res.end("Error: unable to load " + path);
}
res.writeHead(200,{'Content-Type': 'text/plain'});
res.end(data);
});
} else {
res.writeHead(404);
res.end("Error: 404 - File not found.");
}
}
// Python
var pyshell = new PythonShell('mcp3008.py');
pyshell.run('mcp3008.py', function (err, results) {
if (err) throw err;
console.log('Results: %j', results);
});
io.sockets.on('connection', function (socket) {
pyshell.on('message', function (message) {
console.log(message);
});
});
Thank you for any hints or help that you can provide!
As jfriend00 recommended, I looked into node.js solutions. I had previously tried this, using several mcp3008 packages available on npm, but none of them successfully installed on my Raspberry Pi (model B). However, I ended up rewriting the one located here (https://github.com/fiskeben/mcp3008.js) as a separate .js file, included it with my code (along with some work from the npm spi library), and put it into a loop to read the ADC pin. That's working for now, and should be good enough for my current needs, but it still seems like a more processor-intensive solution than it should be. Thanks for your feedback!
Related
I want Nodejs to run a Python script in a new terminal.
I have a python script named test.py which simply does print('Hello World')
However, when I enter localhost:8080/python_run/ in my browser, I do not know if the script is run.
I can know the output using python.stdout.on('data', (data) => {console.log(`stdout: ${data}`);})
but I want to be able to monitor a large complex python script running which would give many print statements throughout.
I want it to display in a separate terminal instead of seeing only the std.out in the nodejs terminal.
I also wish to only implement these using vanilla nodejs.
The spawn child process of nodejs does not seem to let me run in a separate terminal.
How can I trigger a long complex and probably resource intensive Python script to run with the nodejs server as long as it receives a request (GET request of the url in this case)? If possible I'd also want the Python script to communicate back to the nodejs server once the Python script has finished running, to trigger some change of display of a website.
My current code
const {parse} = require('querystring');
const http = require('http');
const path = require('path');
const fs = require('fs');
var url = require("url");
const {spawn} = require('child_process');
const spawn1 = require('child_process').exec;
const requestHandler = (req, res) => {
fs.readFile("index_noexp.html",(err, data) => {
if(!err) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(data);
}
});
if (/python_run/i.test(req.url)) {
console.log('Now running the dangerous python script');
try {
const { spawn } = require('node:child_process');
var python = spawn('python', ['/Users/damian/nodetest/test.py']);
// var python = spawn1('python', [__dirname + '/test.py']);
python.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
})
} catch (error) {
console.error(error);
} finally {
console.log('Not sure what happened');
}
}
const server = http.createServer(requestHandler);
server.listen(8080);
I have a Django project which is using Angular as frontend. I have a button which on clicking is scanning the tables in the database. I have some print statements views.py file which is printing the scanned results constantly in the IDE console. I want that output in the webpage. I want that live printing of the console output in the frontend. Can any one know how i can achieve this?
You can achieve this by using server sent events. python can push these console logs to the frontend. Not a expert of python so giving a link below to how to send server side events from python to frontend
https://medium.com/code-zen/python-generator-and-html-server-sent-events-3cdf14140e56
In frontend you can listen to url exposed and as soon as server will push any message on this stream frontend can receive it and push it into component's array and can display over ui.
for frontend code, i am giving a minimal example below :-
import { Injectable, NgZone } from "#angular/core";
import { Observable } from "rxjs";
#Injectable({
providedIn: "root"
})
export class SseService {
constructor(private _zone: NgZone) {}
getServerSentEvent(url: string): Observable<any> {
return Observable.create(observer => {
const eventSource = this.getEventSource(url);
eventSource.onmessage = event => {
this._zone.run(() => {
observer.next(event);
});
};
eventSource.onerror = error => {
this._zone.run(() => {
observer.error(error);
});
};
});
}
private getEventSource(url: string): EventSource {
return new EventSource(url);
}
}
you can susbcribe to getServerSentEvent in above method and can continuously receive new messages, which is in your case your console logs.
You can try calling the following function with the information needed to be displayed.
addItem(val:any) {
let node = document.createElement("li")
let textnode = document.createTextNode(val)
node.appendChild(textnode)
document.getElementById("output").appendChild(node)
}
Make sure to have an element with the id="output".
So I have node calling a python scrip but I want to get an object back from python.
I'm currently using Python-shell (https://www.npmjs.com/package/python-shell) but the problem is its listening so I can't actually send the data I get from it
shell.on('message', function(message){
ah = message;
console.log(message);
console.log("#");
});
console.log(ah);
var host = {
"hostName":ah
};
console.log(host);
return response.send(200, host);
the last section of the code will execute well before the python script returns anything via print()
(I also can't put the response.send in the listening function because it may send every time the python script prints)
is there another way of doing this?
If you want to return the results of a script to the client, just wrap this call: PythonShell.run in in a router endpoint:
app.get('/somepath', (req, res)=>{
PythonShell.run('my_script.py', options, function (err, results) {
if (err) throw err;
// results is an array consisting of messages collected during execution
res.send('results: %j', results);
});
});
I just needed a bit of guidance for what I should read more on for the following problem:
I have a few python scripts in a folder on my network drive that I run daily and I want to create a website where I display those scripts in form of a list and when I click on them I want them to be running.
Is there a way to solve this problem with AngularJs ?
Thanks for the help
Okay, So I have a small node server I which I changed a bit for your purpose, I know this isn't an ideal server, but this should do the job. The response on the post request should depend on weather you just want to fire the python script or you want its output too.
From frontend, send a post request with data containing the path to the python script or something else, and change the post case yourself.
Use an angular loop to read through the list, which can be a json served from the static folder in your directory.
This server is not secure by any means and probably can be cracked into in a short while, but for a local scenario it serves the purpose.
Also, if you don't wanna use this, then remember that you can server up the list from an endpoint to the page and populate the HTML using ng-repeat and then send post requests with either the script relative path or the script name and handle paths at the backend, this will be the takeaway. Sorry couldn't post anything for frontend.
var http = require('http');
var fs = require('fs');
var url = require('url');
var colors = require('colors');
var exec = require('child_process').exec;
var staticReg = /^\/static\/\w+\.\w+$/;;
var server = http.createServer(function(request, response){
var url_parts = url.parse(request.url)
if (request.method=="GET"){
process.stdout.write("GET :: ".green + url_parts.path.yellow)
if(staticReg.test(url_parts.path)){
if (fs.existsSync("."+url_parts.path)){
response.writeHead(200)
response.write(fs.readFileSync("."+url_parts.path))
response.end()
process.stdout.write(" :: 200\n".green)
}
else{
response.writeHead(404)
response.end()
process.stdout.write(" :: 404\n".red)
}
return
}
switch (url_parts.path){
case "/":
process.stdout.write(" :: 200\n".green)
var home = fs.readFileSync("./index.html")
response.writeHead(200);
response.write(home);
response.end();
break;
default :
process.stdout.write(" :: 404\n".red)
response.writeHead(404);
response.write("404 not found")
response.end();
break;
}
}
if (request.method=="POST"){
process.stdout.write("POST :: ".green + url_parts.path.yellow)
var body = '';
request.on('data',function(data){
body+=data;
});
request.on('end', function(){
switch(url_parts.path){
case "/fire/":
body=JSON.parse(body)
process.stdout.write(" :: 200\n".green)
command='python '+body["script_path"]
exec(command,function callback(error, stdout, stderr){
// Do error checking here and send response.
})
// Or send response here.
response.writeHead(200);
response.write("Command fired.")
response.end();
break;
}
})
}
})
server.listen(8002);
console.log("Listening to "+"localhost".blue+" on port "+ "8002".green);
server.on('error',function(err){
console.log('Error happened: '+err.toString());
})
Im trying to run the Python server/node.js client HelloWorld example from the ZeroRPC website. All the revelant libraries seemed to have been installed correctly, but when running the example I get the error:
{ name: 'HeartbeatError',
message: 'Lost remote after 10000ms',
traceback: '' }
Has anyone seen this?
I'm using "zerorpc": "^0.9.3"
I come across with the same issue when I was running a time-consuming python code. The way to solve the issue is that you need to modify the library code of zerorpc:
node_modules -> zerorpc -> lib -> channel.js
Change the cooresponding method to
//Runs the heartbeat on this channel
Channel.prototype._runHeartbeat = function() {
var self = this;
return setInterval(function() {
if(util.curTime() > self._heartbeatExpirationTime) {
//If we haven't received a response in 2 * heartbeat rate, send an
//error
// self.emit("heartbeat-error", "Lost remote after " + (HEARTBEAT * 2) + "ms");
// self.close();
}
//Heartbeat on the channel
try {
var event = events.create(self._envelope, self._createHeader(), "_zpc_hb", [0]);
self._socket.send(event);
} catch(e) {
console.error("Error occurred while sending heartbeat:", e);
}
}, HEARTBEAT);
};
In the latest code from github: https://github.com/dotcloud/zerorpc-node
they have solved this issue.
If you can, use gevent.sleep to let zerorpc enough time to process waiting messages, including heartbeat.