• Please visit and share your knowledge at our sister communities:
  • If you have not, please join our official Homebrewing Facebook Group!

    Homebrewing Facebook Group

Monitoring/controlling with Linux on the cheap

Homebrew Talk

Help Support Homebrew Talk:

This site may earn a commission from merchant affiliate links, including eBay, Amazon, and others.
OK, but understand this isn't symlinks. Bash alias handling is a different animal all together. For this alias to work as you desire you're going to need to hack parameter passing to the alias (function, finger, etc).


Yeah it seems simple until I try it! Here is what went wrong:

I always start up my linux with the alias command so that I get the directory listing the way I want:

alias ddir='ls -a -l --color |more'

Works great except that when viewing certain directories from another location, nothing is returned. It makes it seem as though the directory is empty. I worked through the entire command until I hit the |more. Then it screwed up.

So I took that out of the command, deciding that I can simply add |more whenever I really needed it (which honestly is not that often).


Hopefully I will have time to play with this more today. I don't mind Bash scripts. I think that is what I used to use a lot of in college. But PHP would be a more useful language to learn.
 
Hey, what would you recommend as a Graphical display to show the temps and switch state, and for adjusting the settings of the temp controller? I'm looking into using PHP as a scripting language.

EDIT: currently looking at PHP-GTK as a way of incorporating the GTK graphical interface within PHP. Looks like the perfect compliment to PHP scripting. I don't know where I'll find time to actually start programming this stuff, but I think this could be a very useful method of setting up a graphical interface for a control panel.
 
If you're using the Linux machine as a standalone controller, you can develop a simple Python GUI for display and control using Tk or WxPython. If you want to network things, use PHP on the Linux (server side). On the client side, use Ajax and JavaScript to dynamically update a webpage.
 
Most of my background is in the "down on bare metal" stuff, so I don't have a lot of experience with GUI libs and what-not. I'm not sure if I'd use PHP (or any server side scripting) to do much eye-candy on the user side (browser). I'd look for a javascript lib that could be used to display your data using some fancy slider/range/toggle widgets. Get the data from a PHP script via ajax (or create the js on the fly with a PHP script).

I have built a number of web apps using the above model (PHP server side, JS client side, access via ajax). I used YUI for those projects, but YUI is probably better suited to "business" type apps rather than utility types - it doesn't have any fancy gui widgets (that I know of) that could be used to display ranges or states so you'd have to roll-your-own, in which case it'd be easier (and probably wiser) to go find something already done and use that instead.

jQuery is also making a lot of noise out there, but I've never used it.
 
on second thought... Just use whatever PHP GUI lib you're comfortable with. I often get anal about design and scalability, and none of that is important here. Do what feels good. :)
 
If you're using the Linux machine as a standalone controller, you can develop a simple Python GUI for display and control using Tk or WxPython. If you want to network things, use PHP on the Linux (server side). On the client side, use Ajax and JavaScript to dynamically update a webpage.

I peeked briefly at Python, but it seems rather complicated. Do you have any links to some good beginner information?
 
on second thought... Just use whatever PHP GUI lib you're comfortable with. I often get anal about design and scalability, and none of that is important here. Do what feels good. :)

At this point I have no real opinion. I just need something that will display the sensor information and allow me to adjust the setpoints of a script or program to tweak the controller.

It's been many years since I've done ANY programming, and what I did was some Pascal stuff, and a bunch of COBOL/RPG for business reporting mostly. There was a class on Unix that did some cool scripting, mostly in BASH shell.

I'm really just starting out with Linux, scripting, compiling, etc. Maybe I need to try a little bit of everything suggested in order to know what would be easy and work well.

I really just want a simple graphical interface for the user, and the ability to update a webpage on my Slackware server. I assumed that PHP-GTK would provide both. I think PHP has a method of creating a simple web page and updating on a web server?

I'm also thinking that this could work for monitoring a brewing session, not just as a fermentation monitor system. For a brewing controller, I don't see the need to involve a network. I could pick up a small, cheap LCD monitor or old laptop and build it right into a control panel. Or simply attach said laptop to the brew rig when brewday comes around.

Kind of excited to be working in GUI interface. I've never made a "window" program before that I can remember...
 
Designing a GUI is a great way to bog down a simple project. At least for now, skip the GUI, and go with some simple scripts. Bash is easy. PHP works just as well.

Write one script that will echo all sensor data and relay states to stdout (a single instantaneous data dump, not a loop).

Write as many other scripts (or one script that takes command line arguments) as you need for control. Make it so that you can type something like this:
Code:
homer heat on
homer pump on
homer heat off

Use the watch command in one terminal window to continuously run your data dump script. Use another terminal window to call your control script(s) from the command line. You could even schedule your scripts to run at particular times using the "at" command.

This solution is easily networked since you can execute an ssh (or other remote login) shell from almost any computer.

Down the road, these scripts can become the basis for a simple GUI.
 
Designing a GUI is a great way to bog down a simple project. At least for now, skip the GUI, and go with some simple scripts. Bash is easy. PHP works just as well.

I couldn't agree more.... a better use of time would be to go fishing, or drink beer. Or both. :)


Write one script that will echo all sensor data and relay states to stdout (a single instantaneous data dump, not a loop).

Another option is to write that info to the system log file (from bash):
Code:
logger -t fermilab "$fermilabTemp degrees"

The above line will write a entry to the system log file with a tag of "fermilab". If the temp read (stored in the variable $fermilabTemp) was 68.000 degrees, the entry would read "68.000 degrees". Writing to the log file also provides a "history" of your operating temps.

To view the log file entries, in another terminal window use the "tail" command as such:
Code:
[root@x24 ~]# tail -f /var/log/messages |grep fermilab
May 16 13:22:02 x24 fermilab: 66.7625 degrees
May 16 13:23:02 x24 fermilab: 66.7625 degrees
May 16 13:24:03 x24 fermilab: 66.875 degrees
May 16 13:25:02 x24 fermilab: 66.875 degrees
May 16 13:26:02 x24 fermilab: 66.875 degrees
May 16 13:27:03 x24 fermilab: 66.9875 degrees
May 16 13:28:02 x24 fermilab: 66.9875 degrees
May 16 13:29:02 x24 fermilab: 66.9875 degrees
May 16 13:30:03 x24 fermilab: 66.9875 degrees
May 16 13:31:02 x24 fermilab: 67.1 degrees
May 16 13:32:02 x24 fermilab: 67.1 degrees

This runs the "tail" command in "forever" mode, piping it's output into grep, which will filter out only log file records that have the word "fermilab" in it. The advantage to using "tail" is that it doesn't run continuously (low resource use).

You could even schedule your scripts to run at particular times using the "at" command.

Since you're going to want to run the scheduled task more than once, "cron" is a better tool:

http://www.ibm.com/developerworks/linux/library/l-job-scheduling/index.html

I use cron to handle my fermentation fridges. It's a simple script that gets called once every minute. It checks the temps and turns a switch on or off. It writes a few entries to the system log file. That's it. Simple.

I'm also thinking that this could work for monitoring a brewing session,

Certainly. I'm working on a HERMS system that will be driven by this project. Damned day job keeps getting in my way though...

For a brewing controller, I don't see the need to involve a network. I could pick up a small, cheap LCD monitor or old laptop and build it right into a control panel. Or simply attach said laptop to the brew rig when brewday comes around

Just pick up an old laptop - they're cheaper than dirt these days. I bought a 5 lot of IBM x24's last year off ebay for ~$300.
 
I'm learning a lot about this stuff just by the suggestions you've made. For todya I got in some PHP-GTK2 programming and it's not too bad so far. When I get home I may set up for some more php scripting.

I have a few laptops and more than a couple older desktops. Just haven't decided how I want one of them to be set up as the controller. Probably just connect some wires to start, then see if I will want to design the control panel to integrate it, or just have it sitting on the table somewhere.
 
Since you're going to want to run the scheduled task more than once, "cron" is a better tool
Funny, I thought about mentioning cron. Depending on how you set up the rig, it could be very useful (like regularly dumping data to a log file). However, when I mentioned the at utility, I was thinking about tasks like turning valves or heating sparge water at a specific time during the brew day.
 
However, when I mentioned the at utility, I was thinking about tasks like turning valves or heating sparge water at a specific time during the brew day.

ahhh.... ok. Get the water hot before you wake up sort of routine... sure.
 
Actually, timed hop additions are exactly what I'm going to do. :) For some reason, I suck at hopping schedules. My wife has to run the timer or I screw it up fantastically. Since making beer is a procedural sport, the script to run it will be very simple. Do something, make some noise, wait for enter. Rinse, repeat. :)
 
I'm learning a lot about this stuff just by the suggestions you've made. For todya I got in some PHP-GTK2 programming and it's not too bad so far. When I get home I may set up for some more php scripting.

GTK... that makes my head hurt just thinking about it. :)


I have a few laptops and more than a couple older desktops. Just haven't decided how I want one of them to be set up as the controller. Probably just connect some wires to start, then see if I will want to design the control panel to integrate it, or just have it sitting on the table somewhere.

Old desktops will do the trick, but, an old laptop is oh so sweet for something like this. The form factor reasons are obvious, but also consider that depending how good the battery is, you have a built-in UPS with a laptop...
 
So far GTK seems not too bad. I'm basically following along with some tutorials, but it's sinking in. It integrates well with PHP, so I can run the two together easily (or will when I have enough understanding of each to do something useful)

For today, I may look into making a simple timer application in PHP and see how it goes.
 
I just have to try this. My order from Hobby Boards should be here next week. I'm building owfs on a Debian distro as I type this post. If all goes well, my next brew day will be controlled solely by 1-wire devices connected via USB to my NSLU2.
 
I just have to try this. My order from Hobby Boards should be here next week. I'm building owfs on a Debian distro as I type this post. If all goes well, my next brew day will be controlled solely by 1-wire devices connected via USB to my NSLU2.

Sounds great. I have been away from this a bit. Spring has turned on me this year. I haven't even put the laptop in the garage to monitor the lager fermentation that I just started.

I'm going to forget using this as a brewtroller for now. I will want it as a cheap and integrated way to monitor temps and cycle fridge for fermentation though. I may even end up foregoing GTK and just use PHP scripting to fill a web page with information that I can pull up from anywhere.
 
I put together a very simple, cheap enclosure this weekend. Everything is in place and ready for the Hobby Boards parts to arrive. I'll likely add a fan so that the hard drive doesn't overheat. The last picture is washed out - you were supposed to see ten RJ-11 jacks for 1-wire components.

scaledp1030679.jpg


scaledp1030681.jpg


scaledp1030680.jpg
 
Homer, I'll have to post some of the code for the rig's webpage. It will be easy to modify when I get the 1-wire stuff installed. All of the sensor data lives in a really small XML file that gets updated via scripts that used to read microprocessor data from a serial port.

Here's an old screenshot:

brewhutscreenshot.jpg
 
Simply OUTSTANDING!!!

I was about to post some framework code, but lets see where this goes first. :)

Yuri, you look like you're "handy". You should make a telescope and take advantage of those Southwest skies!
 
Aw, don't remind me that I have a mirror blank sitting in my computer room waiting to be figured. That was a bunch of work to do. I really should finish my Ronchi Screen and get it finished. But then I'd have to clean everything out of my side porch and re-clean it so I can polish the mirror blank. It's currently full of scrap wood that I will "probably" never use. But i *could* move all of that to the upstairs of my new garage...

Do you know how much it would cost to have the mirror silvered?
 
Our astro club has our own mirror making/testing facilities: http://telescopelab.com/ so I don't have to locate a special place for my mirror making at home - it stays at the lab until done. :)

Coatings... We do our own, but only for club members. Spectrum (http://www.spectrum-coatings.com/) is who I'd recommend (Bob Royce uses them and I've had a 12.5 redone by them). Last I checked it was about $100 to coat a 10" with the enhanced coatings.

On a side note... Beer and scopes work very well together. In fact, the scope I use for our public sidewalk astronomy events is painted up in a Dale Sr motif. And I have beer related stickers everywhere. Both serve to "break the ice" and helps draw folks "in" who would normally be put-off by a scope. There is nothing like seeing a 10 yr old face light up the first time they see Saturn... I think I enjoy it more than they do. :)
 
Ah, yes, Spectrum was the place we looked at years ago when we started out ATM projects. My friend got ahead of me, and so to avoid contaminating his mirror with my larger grit, I stopped work on mine. We were "supposed" to grind at the same grit level to avoid any problems. As far as I know, he quit working on his during the figuring stage and ended up blowing some windfall cash on a 4.5" Dob mount Meade.

I think his brother has it now. It was neat to see some things, but I never got enough hands-on to really get to know the thing. I borrowed my dad's for a bit once, but they had left it out in the garage with the cap off and I had to clean the mirror and make some major adjustments and I don't think I got it right. Things were still a bit fuzzy.

What I find interesting is that with a lot of patience, and a small bit of science, your average person can build a better scope than they could buy at a store. And for not much money.
 
Here is some code to get you started. Some of it is brutally inelegant...but it works. First up, the html file:

Code:
<!DOCTYPE html>
<html>
  <head>
   <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
   <link href="/images/favicon.ico" rel="icon" type="image/x-icon">
   <link rel=StyleSheet href="css/styles.css" type="text/css">
   <title>K&auml;mpferstrahl Brew Monitor</title>
   <script type="application/javascript" src="./js/gauge.js"></script>
   <script type="application/javascript" src="./js/light.js"></script>
   <script type="application/javascript">

  // global variables to hold data from Ajax requests
  var steamPress = 0;
  var steamTemp = 0;
  var mashTemp = 0;
  var spargeTemp = 0;
  var boilTemp = 0;
  var mashVol = 0;
  var boilVol = 0;
  var lightTest = false;
  var steamElement = 0;

  // Ajax request for xml data generated by Arduino
  function ajaxUpdateValues() {

    var httpRequest;
    var xmlDoc;

    // get the xml document via Ajax/http
    httpRequest = new XMLHttpRequest();
    httpRequest.onreadystatechange=function() {
      if(httpRequest.readyState == 4) {
        steamPress = Number(httpRequest.responseText);
      }
    }
    httpRequest.open("GET","./xml/brewhut.xml",false);
    httpRequest.send(null);
    xmlDoc=httpRequest.responseXML;

    // update the global variables by parsing the xml document
    steamPress=Number(xmlDoc.getElementsByTagName("steamPress")[0].childNodes[0].nodeValue);
    steamTemp=Number(xmlDoc.getElementsByTagName("steamTemp")[0].childNodes[0].nodeValue);
    spargeTemp=Number(xmlDoc.getElementsByTagName("spargeTemp")[0].childNodes[0].nodeValue);
    mashTemp=Number(xmlDoc.getElementsByTagName("mashTemp")[0].childNodes[0].nodeValue);
    boilTemp=Number(xmlDoc.getElementsByTagName("boilTemp")[0].childNodes[0].nodeValue);
    mashVol=Number(xmlDoc.getElementsByTagName("mashVol")[0].childNodes[0].nodeValue);
    boilVol=Number(xmlDoc.getElementsByTagName("boilVol")[0].childNodes[0].nodeValue);
    steamElement=Number(xmlDoc.getElementsByTagName("steamElement")[0].childNodes[0].nodeValue);

  }

  // force the webcam image to reload by appending time data
  function updateImage() {
    var img = document.getElementById("webcam");
    var now = new Date();
    img.src="images/cam.jpg"+ "?" + now.getTime();
  }

  // this function calls the Ajax update and draws the canvas elements
  // it is to be executed repeatedly through the use of the setInterval method
  function update() {

    // update Arduino data
    ajaxUpdateValues();

    // draw gauges
    var canvas = document.getElementById("steamPress");
    var ctx = canvas.getContext("2d");
    drawGauge(ctx, 0, 20, 0.25, 1, "Steam Pressure", "psi", steamPress);

    canvas = document.getElementById("steamTemp");
    ctx = canvas.getContext("2d");
    if (steamTemp < 0) {
      drawGauge(ctx, 60, 260, 5, 10, "Steam Temp", "Error", 0);
    } else {
      drawGauge(ctx, 60, 260, 5, 10, "Steam Temp", "Deg F", steamTemp);
    }

    canvas = document.getElementById("mashTemp");
    ctx = canvas.getContext("2d");
    if (mashTemp < 0) {
      drawGauge(ctx, 20, 220, 5, 10, "Mash Temp", "Error", 0);
    } else {
      drawGauge(ctx, 20, 220, 5, 10, "Mash Temp", "Deg F", mashTemp);
    }

    canvas = document.getElementById("spargeTemp");
    ctx = canvas.getContext("2d");
    if (spargeTemp < 0) {
      drawGauge(ctx, 20, 220, 5, 10, "Sparge Temp", "Error", 0);
    } else {
      drawGauge(ctx, 20, 220, 5, 10, "Sparge Temp", "Deg F", spargeTemp);
    }

    canvas = document.getElementById("boilTemp");
    ctx = canvas.getContext("2d");
    if (boilTemp < 0) {
      drawGauge(ctx, 20, 220, 5, 10, "Boil Temp", "Error", 0);
    } else {
    drawGauge(ctx, 20, 220, 5, 10, "Boil Temp", "Deg F", boilTemp);
    }

    canvas = document.getElementById("mashVol");
    ctx = canvas.getContext("2d");
    if (mashVol < 0) {
      drawGauge(ctx, 0, 24, 0.5, 1, "Mash Volume", "Error", 0);
    } else {
      drawGauge(ctx, 0, 24, 0.5, 1, "Mash Volume", "Gal", mashVol);
    }

    canvas = document.getElementById("boilVol");
    ctx = canvas.getContext("2d");
    if (boilVol < 0) {
      drawGauge(ctx, 0, 24, 0.5, 1, "Boil Volume", "Error", 0);
    } else {
      drawGauge(ctx, 0, 24, 0.5, 1, "Boil Volume", "Gal", boilVol);
    }

    canvas = document.getElementById("steamElement");
    ctx = canvas.getContext("2d");
    drawGauge(ctx, 0, 100, 2, 10, "Heating Element", "%", steamElement);

  }

  // make the script execute at a set interval (in milliseconds)
  function init() {
    setInterval("update();", 500);
    setInterval("updateImage();", 3000);
  }
    </script>
  </head>

  <body onload="init();">

   <div>
    <table style="margin-left:auto;margin-right:auto;">
     <tr>
      <td>
       <table style="margin-left:auto;margin-right:auto;">
        <tr>
         <td>
          <canvas id="steamPress" width="180" height="200">
           This page is incompatible with your browser.
          </canvas>
         </td>
         <td>
          <canvas id="mashVol" width="180" height="200">
           This page is incompatible with your browser.
          </canvas>
         </td>
         <td>
          <canvas id="boilVol" width="180" height="200">
           This page is incompatible with your browser.
          </canvas>
         </td>
        </tr>
        <tr>
         <td>
          <canvas id="steamTemp" width="180" height="200">
           This page is incompatible with your browser.
          </canvas>
         </td>
         <td>
          <canvas id="mashTemp" width="180" height="200">
           This page is incompatible with your browser.
          </canvas>
         </td>
         <td>
          <canvas id="boilTemp" width="180" height="200">
           This page is incompatible with your browser.
          </canvas>
         </td>
        </tr>
        <tr>
         <td></td>
         <td>
          <canvas id="spargeTemp" width="180" height="200">
           This page is incompatible with your browser.
          </canvas>
         </td>
         <td>
          <canvas id="steamElement" width="180" height="200">
           This page is incompatible with your browser.
          </canvas>
         </td>
        </tr>
       </table>
      </td>
      <td style="vertical-align:text-top;">
       <img id="webcam" height="400" src="images/cam.jpg">
      </td>
     </tr>
    </table>
   </div>

   <div style="text-align:center;">
    <span style="font-family: PHANTOM STENCIL; color: #222222; font-size: 69pt;">Kampferstrahl</span>
    <h4>Version 0.2.3, 7 Feb 10 --- <a href="http://twitter.com/kampferstrahl">Twitter</a></h4>
   </div>

   <div>
    <br><br><br><br>
    <p><a href="http://brew-hut">Back to server homepage</a>
    <p style="font-size:125%">
     Arduino's output must be dumped to /var/www/xml/brewhut.xml in real time for this page to be useful.<br>
     A script called brewhut runs `head -n 12 /dev/ttyUSB0 > /var/www/xml/brewhut.xml` about every 250 ms.<br>
     Whenever a sketch is uploaded to Arduino, running `screen /dev/ttyUSB0 115200` will recapture the serial port for terminal use.<br>
   </div>
  </body>
 </html>
 
And here's some javascript for the gauges:

Code:
/*
  Canvas based gauge adapted from the demo at http://ecc.webhop.org:8081/
    (to log in - user: admin    password: bcsdemo)

  Adapted by Yuri, Dec 09
  
*/

// some global style values

var titleFill = "#222222";
var labelFill = "#666666";
var valueFill = "#222222";
var outerBezelDarkFill = "#222222";
var outerBezelLightFill = "#666666";
var innerBezelDarkFill = "#C0C0C0";
var innerBezelLightFill = "#FAFCFF";
var tickStroke = "#222222";
var centerPinStroke = "#222222";
var centerPinFill = "#666666";
var pointerFill = "#C00000";

function drawLabel(ctx, title, label, value) {
  ctx.save(); 
  ctx.font = "bold 1.0em Verdana"; 
  ctx.fillStyle = titleFill; 
  ctx.lineWidth = 1; 
  ctx.translate(90, 2); 
  ctx.textAlign = "center"; 
  ctx.fillText(title, 0, 10); 
  ctx.font = "0.675em Verdana"; 
  ctx.fillStyle = labelFill; 
  ctx.fillText(label, 0, 145); 
  ctx.fillStyle = valueFill; 
  ctx.font = "bold 0.875em Verdana";
  ctx.fillText(value, 0, 130);
  ctx.restore();
}

function drawBezels(ctx) {
  ctx.save();
  var innerStyle = ctx.createRadialGradient(87, 90, 57, 90, 95, 75); 
  innerStyle.addColorStop(0, innerBezelLightFill); 
  innerStyle.addColorStop(0.9, innerBezelDarkFill); 
  ctx.fillStyle = innerStyle; 
  ctx.arc(90, 95, 72, 0, Math.PI * 2, true); 
  ctx.fill(); 
  ctx.beginPath();
  ctx.lineWidth = 9;
  var outerStyle = ctx.createRadialGradient(90, 95, 68, 90, 95, 76);
  outerStyle.addColorStop(0, outerBezelDarkFill);
  outerStyle.addColorStop(0.7, outerBezelLightFill);
  outerStyle.addColorStop(1, outerBezelDarkFill); 
  ctx.strokeStyle = outerStyle;
  ctx.arc(90, 95, 73, 0, Math.PI * 2, true);
  ctx.stroke();
  ctx.restore();
}

function drawTickMarks(ctx, minVal, maxVal, minor, major) {
  ctx.save(); 
  ctx.translate(90, 95); 
  ctx.rotate(Math.PI + Math.PI / 4 - Math.PI / 2); 
  var increment = (2 * Math.PI - Math.PI / 2);
  increment = increment / ((maxVal - minVal) / major);
  ctx.rotate(-increment);
  for (var i = 0; i < (maxVal - minVal) / major + 1; i++) {
    ctx.beginPath(); 
    ctx.rotate(increment); 
    ctx.moveTo(68, 0); 
    ctx.lineTo(60, 0); 
    ctx.stroke();
  }
  ctx.restore();
  ctx.save();
  ctx.translate(90,95);
  ctx.rotate(Math.PI + Math.PI / 4 - Math.PI / 2); 
  increment = (2 * Math.PI - Math.PI / 2);
  increment = increment / ((maxVal - minVal) / minor);
  for (i = 0; i < (maxVal - minVal) / minor; i++) {
    ctx.beginPath(); 
    ctx.rotate(increment); 
    ctx.moveTo(68, 0); 
    ctx.lineTo(64, 0); 
    ctx.stroke();
  }  
  ctx.restore();
  ctx.save();
  ctx.font = "italic .5em Verdana";
  ctx.translate(90, 98); 
  ctx.textAlign = "center";
  increment = (2 * Math.PI - Math.PI / 2);
  increment = 2 * increment / ((maxVal - minVal) / major);   
  for (i = 0; i <= (maxVal - minVal) / major / 2; i++) {
    ctx.beginPath(); 
    ctx.fillText(minVal + i * major * 2,
     52 * Math.cos(Math.PI + increment * i - Math.PI / 4),
     52 * Math.sin(Math.PI + increment * i - Math.PI / 4));
  }
  ctx.restore();
}

function drawCenterPin(ctx) {
  ctx.save(); 
  ctx.beginPath(); 
  ctx.lineWidth = 2; 
  ctx.strokeStyle = centerPinStroke; 
  ctx.fillStyle = centerPinFill; 
  ctx.arc(90, 95, 5, 0, Math.PI * 2, true); 
  ctx.stroke(); 
  ctx.fill(); 
  ctx.restore();
}

function drawPointer(ctx, minVal, maxVal, value) {
  ctx.save(); 
  ctx.translate(90, 95); 
  ctx.rotate(-Math.PI / 2 - Math.PI / 4);
  ctx.rotate((2 * Math.PI - Math.PI / 2) * (value - minVal) / (maxVal - minVal)); 
  ctx.moveTo(-3, 15);
  ctx.beginPath();
  ctx.moveTo(-3, 15);
  ctx.fillStyle = pointerFill;
  ctx.lineTo(0, -60); 
  ctx.lineTo(3, 15);
  ctx.lineTo(-3, 15);
  ctx.fill();
  ctx.restore();
}

function drawGauge(ctx,                   // canvas context
                   minVal, maxVal,        // min and max gauge values
                   minorTick, majorTick,  // tick mark intervals
                   title, label,          // main title and face label
                   value) {               // current value
  ctx.save(); 
  ctx.font = "76% Verdana,Tahoma,Arial,sans-serif"; 
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);   
  drawBezels(ctx);
  drawTickMarks(ctx, minVal, maxVal, minorTick, majorTick); 
  drawLabel(ctx, title, label, value); 
  drawCenterPin(ctx);
  drawPointer(ctx, minVal, maxVal, value);
  ctx.restore();
}
 
Back
Top