Saturday, July 25, 2015

A real-life NodeJS application

After I resoldered prematurely died amplifier chips and put it all together again I decided to take some easy steps to help my amplifier live longer.
The one of easy ways I see is to unmute it when MPD starts playing and mute when it stops or pauses. That will shut down PWM modulation and basically turn off the power stage so I don't need to manually turn it on or off,

That's seems to be a good job for NodeJS!
Node works just fine on Linux boards like BeagleBone or Raspberry Pi and there are lots of success stories how folks do all kinds of stuff.
No need to invent a wheel and there is a neat Node mpd client ready to use,

I already have some python scripts to control my PWM modulator so the idea is to use Node to invoke them when needed. Sounds easy.

So here my first working Node application. It's not polished but just a simple proof of concept.

var fs = require('fs');
var exec = require('child_process').exec;
var mpdlib = require('mpd'), cmd = mpdlib.cmd;

// hardcoded paths to mute & unmute scripts
var cmd_play = "python /root/cr/unmute.py";
var cmd_stop = "python /root/cr/mute.py";
var errorMonitorInterval = 600000;

var refreshMPD = function() {
   client.sendCommand(cmd("status", []), function(err, msg) {
     if (err) console.log(err);
  if (msg) {
   var data = mpdlib.parseKeyValueMessage(msg);
   console.log(data.state);
   if(data.state == "play"){
    execute(cmd_play, console.log);
   } else if (data.state == "stop" || data.state == "pause") {
    execute(cmd_stop, console.log);
   };
  }
   });
 };

// if something wrong happens I don't want it to flood my log file
var firstErrDate, lastErrMessage = "", errCount = 0;
setInterval(function () { 
 if(errCount>1){
  var date = new Date();
  d = date.toLocaleString();
  console.log("[" + d + "]:\t" + "Last error happend " 
  + errCount + " times in the last " 
  + Math.round((date - firstErrDate) / 60000) + " minutes.");
 }
}, errorMonitorInterval);

var logError = function(err, when){
 var date = new Date();
 d = date.toLocaleString();
 
 var message = err.message || err;
 if(!firstErrDate || lastErrMessage != message){
  if(errCount > 1){
   console.log("[" + d + "]:\t" + message + " happend " + errCount + " times so far."); 
  }
  firstErrDate = date;
  lastErrMessage = message;
  errCount = 1;
  console.log("[" + d + "]:\t" + message + " when " + when);
 } 
 else{  
  errCount++;
 }
}

var execute = function(command, callback){
 try{
     exec(command, function(error, stdout, stderr){ 
      if(error){
       logError(error, "executing \'" + command + "\'");
       callback(error.name); 
      }
      else {
       callback(stdout); 
      }
     });
 }
 catch(err){
  logError(err, "executing \'" + command + "\'");
  callback(err.name);
 }
};

var getDate = function(){
 var d = new Date();
 return d.getMonth() + 1 + "/" + d.getDate() + "/" 
  + d.getFullYear() + " " + d.getHours() + ":" 
  + d.getMinutes() + ":" + d.getSeconds();
};

//===================================
var client = mpdlib.connect({
  port: 6600,
  host: "localhost",
});

client.on("system-player", refreshMPD);
client.on("system", function(name) {
 if(name == "mixer") refreshMPD();
});
client.on("ready", refreshMPD);

The only problem I found with running NodeJS on BBB is that sometimes music has drop-offs. But it is easy to fix as we can lower the priority with nice command. Like:
>nice node amp.js

and music just plays. And amplifier stays ice cold when music is off.
Sweet!

No comments:

Post a Comment