Temperature monitoring script with email alerts

We have quite a bit of expensive equipment in our server room and we’ve had the A/C fail a couple times. As a result, I’ve installed a raspberry pi zero with a DS18B20 temperature sensor connected to it to monitor the temperature of the room.  If it goes above a set threshold, it will send an email to the engineers so we can log in and shut stuff down until the problem is fixed.

 

This project branches off from the one I did earlier on monitoring temperature with a raspberry pi and MRTG.  This too uses MRTG but I won’t get into the details of that- you can see how I set that up here.

 

The big piece here is the alerting logic.  You’d be surprised how fast the temp can go up in a small room with lots of gear putting out a lot of heat.  For that reason, I monitor the temperature every minute.  If the current temperature exceeds the threshold I set in the script, it fires an email and sets an alert flag to true.  The reason I did this is so we don’t get an email every minute while the temperature is above threshold.  How irritating would that be?  So another piece of logic in the script checks to see if the alert flag has been tripped.  If it has, no email is sent until the temperature comes down below the threshold.  Then an all clear email is sent and the cycle repeats itself.

 

I used the instructions here to set up ssmtp on the pi.  In my case, I used our comcast email relay since we have comcast so the instructions for that are a little different.  You can also use your company’s own mail relay if you have one internally that can be used to send email to external addresses.  As has been my practice lately, I’ve uploaded the code to GitHub here for you to do with as you please.

 

As always, if you have any constructive criticism or comments, feel free to leave them below and I’ll get back to you ASAP.

Internet Ping Meter (part 2 of 2)

Onto the fun stuff!  Below is the python script that does most of the heavy lifting.  Remember with Python, indentation is critical.  It’s actually used to delimit things like functions rather than more traditional delimiters like {}.  Best practice is to use spaces, not tabs for indentation because they can be inconsistent and cause problems.  To avoid this, I like to use an IDE such as notepad++ or the Arduino IDE. It does a great job of taking care of the spacing and indentation. It will even go through the entire script and fix any indentation errors you have automatically. Highly recommended. FYI- You’ll also need to install the PySerial module for this to work:

#!/usr/bin/python

##
## Internet Ping Meter v1.0 - Eric Steed
##
## 01/03/17 - first version - EPS
##
import serial
import sys
import subprocess
import time
latency = 0
ping_targets="8.8.8.8 4.2.2.2 208.67.220.220"
retVal = 0
failLevel = 0
lastLEDStatus = ""

##
## Define array variable alertLevel[] and assign color codes to be sent to the NeoPixel.
## Based on the number of total ping failures, iterate the failLevel by one and
## send the appropriate color code.
##
clearLED = "ic"
alertLevel = []
alertLevel = ["h","g","f","e","d"]

##
## Open the serial port to talk to the NeoPixel. Have to wait for it to initialize
## before we start sending signals
##
port = serial.Serial("/dev/ttyACM0", baudrate=9600, timeout=1)
time.sleep(3)

##
## Green = h
## Greenish Yellow = g
## Yellow = f
## Orange = e
## Red = d
## Black = i
##
## LED #'s
##
## 1-9 = 1-9
## 10 = a
## 11 = b
## 12 = c
##
##
## I'm using a NeoPixel ring with 12 LED Segments to indicate the average latency of
## multiple established servers on the internet.  This way I can tell visually if
## my internet connection is slow, or even down.
##
## To control the NeoPixel, I've assigned specific characters to indicate how many
## LED's to illuminate and what color.  When we tell the NeoPixel to illuminate a
## given number of LED's, we have to account for the fact that the last command
## string that was sent is persistent in that the LED stays lit even when the next
## command string comes in.  For example, if reading 1 determines that 4 LED's
## should be lit, then reading 2 calls for 3 LED's, you wouldn't be able to see that
## because all 4 LED's were still illuminated from the previous cycle.
##
## To account for this, we send an instruction to "illuminate" all 12 LED's with
## the color Black before sending the actual value desired.  This is done by
## assigning a value of 'ic' to the variable clearLED.  I've also added some logic
## at the end of the infinite while loop that says don't send any instructions
## unless there's been a change since the last one.  This gets rid of the blinking
## effect that I was seeing on every update- rather annoying!
##

##
## I'm using the subprocess library for now unless I can get the native Python ping library
## to do it for me.  If stdout is null for a given target, return 0.
##
def doPing(host):
    import os,platform
    pingOutput = subprocess.Popen(["ping -c 1 -w 1 " + host + " | grep rtt | awk -F/ '{print $5}' | awk -F. '{print $1}'"], stdout=subprocess.PIPE, shell=True)
    (out, err) = pingOutput.communicate()
    if (out.rstrip('\n') == ''):
        return 0
    else:
        return out.rstrip('\n')

##
## Get average latency from all of the ping targets. Had to cast the output of
## doPing() into an integer to be able to do math against it
##
while True:
    count=0
    for x in ping_targets.split():
        retVal = int(doPing(x))
        #print "latency = [{0}]".format(retVal)
        # print "type = [{0}]".format(type(retVal))
        if (retVal > 0):
            latency += retVal
            count+=1

    ##
    ## If count is zero, that means we were not able to successfully ping
    ## any of the targets and we should start incrementing the failure count.
    ## Furthermore, if we have been incrementing failLevel and we are now
    ## able to ping, reset the failLevel back to 0 at that time.
    ##
    if (count == 0):
        # Increase failure level
        #print "Failed to ping any host"
        failLevel += 1
        if (failLevel > 4):
            failLevel = 4

    else:
        latency=(latency/count)
        failLevel = 0

    ##
    ## Set LEDStatus to the appropriate value based on latency and failure count
    ##

    #print "Average Latency = [{0}]".format(latency)

            if (latency > 1) and (latency <= 10):                 #print "1-10"                 LEDStatus = clearLED + alertLevel[failLevel] + "1"         elif (latency >= 11) and (latency <= 20):                 #print "11-20"                 LEDStatus = clearLED + alertLevel[failLevel] + "2"         elif (latency >= 21) and (latency <= 30):                 #print "21-30"                 LEDStatus = clearLED + alertLevel[failLevel] + "3"         elif (latency >= 31) and (latency <= 40):                 #print "31-40"                 LEDStatus = clearLED + alertLevel[failLevel] + "4"         elif (latency >= 41) and (latency <= 50):                 #print "41-50"                 LEDStatus = clearLED + alertLevel[failLevel] + "5"         elif (latency >= 51) and (latency <= 60):                 #print "51-60"                 LEDStatus = clearLED + alertLevel[failLevel] + "6"         elif (latency >= 61) and (latency <= 70):                 #print "61-70"                 LEDStatus = clearLED + alertLevel[failLevel] + "7"         elif (latency >= 71) and (latency <= 80):                 #print "71-80"                 LEDStatus = clearLED + alertLevel[failLevel] + "8"         elif (latency >= 81) and (latency <= 90):                 #print "81-90"                 LEDStatus = clearLED + alertLevel[failLevel] + "9"         elif (latency >= 91) and (latency <= 100):
                #print "91-100"
                LEDStatus = clearLED + alertLevel[failLevel] + "a"

        else:
                #print "latency greater than 101"
                LEDStatus = clearLED + alertLevel[failLevel] + "c"

    ##
    ## If the latency is within a different range than the last iteration, send
    ## the command to update the LED count on the NeoPixel.  Otherwise you get
    ## a rather annoying blinking effect as the LED's are updated even if it's the
    ## same measurement as the last time.
    ##
    if (LEDStatus != lastLEDStatus):
        port.write(LEDStatus)
        lastLEDStatus = LEDStatus

    #time.sleep(5)
    #print LEDStatus
    latency = 0

I left the debugging code in the script if you want to uncomment them and watch the terminal as the script runs to see what’s going on. Most of the script is fairly straightforward so I won’t dwell too much on explaining it step by step.

Now, onto the Arduino code. I’m using the Arduino basically as a driver for the NeoPixel. Again- I could have probably just used the Pi by itself, but what fun would that be?

#include <Adafruit_NeoPixel.h>

//
// Internet Ping Meter v1.0 - Eric Steed
//
// 01/03/17 - first version - EPS
//
// Set up variables
byte leds = 0;
uint8_t delayVal = 30;

// Set the PIN number that the NeoPixel is connected to
#define PIN   7

// How bright are the LED's (0-255)
#define INTENSITY 60

// Set color to Green to start
uint8_t  r = 0;
uint8_t  g = INTENSITY;
uint8_t  b = 0;

// Set the number of pixels on the NeoPixel
#define NUMPIXELS   12

// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

// Initialize everything and prepare to start
void setup()
{
  uint8_t i;

  // Set up the serial port for communication
  Serial.begin(9600);
  Serial.println("Started Serial Monitor");

  // This initializes the NeoPixel library.
  pixels.begin();

  // This sets all the pixels to "off"
  for (i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(0, 0, 0));
    pixels.show();
  }

  // Cycle each pixel through the primary colors to make sure they work, then turn them all off
  // Red
  for (i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(INTENSITY, 0, 0));
    pixels.show();
    delay(delayVal);
  }

  // Green
  for (i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(0, INTENSITY, 0));
    pixels.show();
    delay(delayVal);
  }

  // Blue
  for (i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(0, 0, INTENSITY));
    pixels.show();
    delay(delayVal);
  }

  // White
  for (i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(INTENSITY, INTENSITY, INTENSITY));
    pixels.show();
    delay(delayVal);
  }

  // Turn off all LED's
  for (i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(0, 0, 0));
    pixels.show();
  }
}

// Main loop
//
// When sending LED signals, send the color code first, then the number of LED's to
// turn on.  For example 6 Green LED's would be h6, 11 Red LED's would be db, all
// 12 LED's to Black would be ic
void loop()
{
  uint8_t i;
  if (Serial.available())
  {
    char ch = Serial.read();
    // Serial.print("ch = ");
    // Serial.println(ch);
    int led = ch - '0';

    // Serial.print("led = ");
    // Serial.println(led);

    // Set Color of LED based on how many fails in a row
    //RED = 52(d)
    //ORANGE = 53(e)
    //YELLOW = 54(f)
    //YELLOW-GREEN = 55(g)
    //GREEN = 56(h)
    //BLACK = 57(i)

    switch (led) {
      // Set color to RED
      case 52: {
          r = INTENSITY;
          g = 0;
          b = 0;
        }
        break;

      // Set color to ORANGE
      case 53: {
          r = INTENSITY;
          g = (INTENSITY / 2);
          b = 0;
        }
        break;

      // Set color to YELLOW
      case 54: {
          r = INTENSITY;
          g = INTENSITY;
          b = 0;
        }
        break;

      // Set color to YELLOW-GREEN
      case 55: {
          r = (INTENSITY / 2);
          g = INTENSITY;
          b = 0;
        }
        break;

      // Set color to GREEN
      case 56: {
          r = 0;
          g = INTENSITY;
          b = 0;
        }
        break;

      // Set color to BLACK
      case 57: {
          r = 0;
          g = 0;
          b = 0;
        }
        break;

      // To save on code, if we receive a 0 through a 9, turn on that
      // number of LED's
      case 0 ... 9:
        for (i = 0; i < led; i++) {
          pixels.setPixelColor(i, pixels.Color(r, g, b));
          pixels.show();
        }
        break;

      // If we receive an "a", turn on 10 LED's
      case 49:
        for (i = 0; i < 10; i++) {
          pixels.setPixelColor(i, pixels.Color(r, g, b));
          pixels.show();
        }
        break;

      // If we receive a "b", turn on 11 LED's
      case 50:

        for (i = 0; i < 11; i++) {
          pixels.setPixelColor(i, pixels.Color(r, g, b));
          pixels.show();
        }
        break;

      // If we receive a "c", turn on 12 LED's
      case 51:

        for (i = 0; i < 12; i++) {
          pixels.setPixelColor(i, pixels.Color(r, g, b));
          pixels.show();
        }
        break;

      // For testing, insert a delay if we see a ,
      case -4:
        delay(delayVal * 10);
        break;

      default:
        // if nothing else matches, do the default
        // default is optional
        break;
    }
    // I had to add this bit of code to fix a problem where the Arduino buffer
    // apparently filled up after a very short time.  It would set the LED's on
    // but then pause for 2-3 seconds before it would receive the next command.
    // This tells the Arduino to flush out the buffer immediately.
    Serial.flush();
  }
}

If it’s not already evident, I’m not very adept at either Python or Arduino coding. I’m just starting out. The most frustrating thing for me is stumbling across syntax issues with code. 9 times out of 10, I know it’s possible to do something but I just can’t get the syntax right or use the correct modules. All this comes with time so maybe in a year, this code would be half or 1/3 the size it is right now.

Once you have everything installed and tested (you can turn on and off the LED’s), you have to connect the Pi to your network. I would consider this device to be a single purpose device and not put anything else on it that could interfere with the script and timing. They’re cheap enough that you should be able to justify this.

 

You can find this code on Github at https://github.com/esteed/Internet-Ping-Meter.  Please feel free to make modifications and generate a pull request- I’m always looking for a better mousetrap!

 

I hope this has helped you even a little bit. I had a great time setting it up and I look forward to making enhancements. The first one will be to indicate current upstream and downstream throughput using white and blue LED’s basically overlaid on the top of the latency indicators. Wish me luck!!

Internet Ping Meter (part 1 of 2)

THE INTERNET IS DOWN!!

How many of you “home IT support technicians” have heard this before?  I hear it a lot, so I decided to create a device that would notify me visually when problems occur.  Sometimes it winds up being a flaky wifi router that either reboots or just needs to take a breath.  Other times, it’s our Comcast connection in which case I can’t do anything other than call and file an outage.  The kids seem to have a hard time with understanding that even though I’ve explained it to them a hundred times.

A little background on the reason for this project.  At the company I work for, we employ a WAN load balancer which uses a series of pings to major internet presences such as google, AT&T or OpenDNS servers.  Basically the device pings each of those addresses once per second and based on specific criteria, can determine if one of the two internet connections is down and can take appropriate action.

This is what made me decide to develop my version of the ping meter.  There are a number of projects like this for the raspberry pi that involve some sort of visual representation.  I wanted to put together a project that incorporated both the raspberry pi, an arduino board and the NeoPixel ring.  This was mainly a project for me to learn how to integrate multiple devices.  Honestly I could probably have done this without the Arduino but I wanted to challenge myself a little.

At this point, I have the device working the way I want.  My next challenge is to package the device into something more aesthetically pleasing.  WAF (Wife Acceptance Factor) is an important aspect to any geek project like this if it’s gonna be displayed somewhere that’s visible.  I’m thinking maybe a small picture frame or maybe some sort of glass object that looks nice.

Here is a list of the parts you’ll need:

  • Raspberry Pi (any model should work)
  • Arduino board (I used an UNO but even that is overkill)
  • NeoPixel LED ring (12 LED segments)
  • Micro SD card (at least 4gb)
  • USB Type A to USB type B (printer/scanner cable)
  • 5V Micro USB power source (iPad charging brick is perfect)

I haven’t tested using a Pi Zero yet but I don’t see why it wouldn’t work.  I also have an Arduino Trinket (5v version) that I’m trying to use for this however out of the box it doesn’t support serial communication.  For size reasons, this combination would be perfect for just about any implementation where room is an issue.  You could just as easily use a larger NeoPixel ring or even a strip with some very minor code modifications.

There are two programs that are used to make this system work.  One is the “firmware” that you load onto the Arduino board itself.  The other is the python script that runs on the Pi.  Basically I use the Pi to ping 3 different IP addresses, and use the NeoPixel ring to display the average ping latency in LED segments.  If I can’t ping all three then I start to progressively change the color of the LED’s from green to red.  Throughout this project I learned a lot about programming in python, Arduino and interacting with external physical devices.  I first started by just getting the LED’s to turn on and off.  I borrowed a lot of code from examples and implemented the same routines to get the NeoPixel to do what I wanted to.

I tried to sprinkle comments throughout the code to explain what I’m doing and why.  Most of these were added after I made a breakthrough in something that was kicking my ass for awhile so I would know how to fix the problem the next time around.  I won’t focus a lot on how to install the OS on your Pi or how to download code to the Arduino- there are a LOT of helpful resources on the internet that can walk you through it.  Also, in the spirit of this being a learning exercise for me- I think it’s valuable for someone starting out fresh to do the research and have a basic understanding of what’s going on rather than just copying and pasting code.  If you’re trying to put this together and run into problems, feel free to comment on the article and I’ll do my best to answer questions.

In the next article, I’ll show you the code and how it all works.  Stay tuned!

What do a Subway sandwich and a computer have in common?

original raspberry-pi-zero-_3510862b

 

That’s right, you can get either one of them for $5!  Introducing the newest member to the Raspberry Pi family, the Pi Zero.  As you can see by the picture above next to the deck of cards, it’s quite a bit smaller than a foot long sandwich, but that doesn’t stop it from packing quite a punch!  The tiny new SOC (System On a Chip) computer is just that- a full fledged computer capable of running Linux with a desktop environment.  Granted, it’s not the snappiest performer in that capacity but still super cool that it can pull it off!

 

Here are the specs (gratuitously lifted from raspberry pi’s website):

  • A Broadcom BCM2835 application processor
    • 1GHz ARM11 core (40% faster than Raspberry Pi 1)
  • 512MB of LPDDR2 SDRAM
  • A micro-SD card slot
  • A mini-HDMI socket for 1080p60 video output
  • Micro-USB sockets for data and power
  • An unpopulated 40-pin GPIO header
    • Identical pinout to Model A+/B+/2B
  • An unpopulated composite video header

 

So- on to the gotcha’s.  With a $5 computer, yo’re gonna to have to make some minor investments in additional hardware to make it functional.  Here’s the list of absolute must have’s to even get up and going:

  • MicroSD Card (preferably 8gb or bigger and class 10 or faster)
  • Micro USB power source capable of providing at least 1A at 5v
  • Micro USB to USB Type A Female converter
  • USB Wi-Fi or ethernet adapter (make sure it’s supported first)

You can technically get up and running with this much hardware, however you have no video out and would have to rely on pre-configuring the OS to somehow get on the network and allow SSH to get in.  Not very functional but a working minimal config once you have it set up the way you want.  Raspberry Pi has taken the Spirit Airlines approach to the Zero.  They give you only what you need to work (the Bare Fare), allowing you to decide what extras you want to pay for and which ones you skip.  In order to configure your Zero initially, you’ll need a couple more things:

  • Mini HDMI to HDMI cable (or Mini HDMI to HDMI converter with an HDMI cable coming out of it)
  • USB Hub for mouse, keyboard and wired or wireless network connectivity
  • Keyboard and Mouse

This will get you connected to a Monitor/TV that has HDMI inputs so you can see what you’re doing.  It also provides for an input method via the keyboard and mouse.  At the end of this article, I’ll post a list of some of the essential hardware, how much I paid for it and where I got it.

 

t5WCokG

One of the reasons I bought one of these is it’s ability to serve as a very capable media center device.  In one of my earlier posts, I talked about something called OpenELEC.  It’s a Linux distribution that includes Kodi which is an open source home theater software package also based on Linux.  The OpenELEC package combines the Linux OS and Kodi into an interface that’s very well suited to a TV and remote control.  Best of all, it runs on the entire line of Raspbery Pi’s!  I’ll be posting soon about one of the other alternatives to OpenELEC called OSMC.  The concept is the same, however OSMC includes a full raspbian Linux OS that isn’t as hands off as OpenELEC is.  As a result, it’s much more easily configured and customized without having to learn all the in’s and out’s of the underlying OpenELEC OS components.

 

Architecture-and-Source

The reason that the Zero can pull this off is mainly due to it’s built in hardware video decoder.  The GPU (Graphics Processing Unit) has discreet hardware functions dedicated for video encoding and decoding (recording and playback).  This means that video playback such as 1080p at 60fps doesn’t rely on the processor to decode and display the video stream, slowing other operations down.  It’s all done in hardware- very similar to the Playstation 4, XBox ONE, or any other gaming platform that has dedicated graphics hardware.  All that’s left for the diminutive Zero to do is render the on-screen menus, take care of assorted housekeeping and perform other OS related stuff.

 

Pibow_Zero_1_of_3_1024x1024There are a number of “cases” for the Zero out on the market now.  I use the term case rather loosely because as you can see to the left, it’s mainly two layers of plastic sandwiching the Pi Zero between it.  There are also quite a few 3d designs that “makers” can download and print on their 3d printer. Others can be bought in brick and mortar stores like MicroCenter or ordered online from websites like Adafruit, Raspberry Pi’s swag store, or Pimoroni (a popular “maker” website based in the UK) to name just a few.  You don’t technically need a case, but it’s a good idea to keep shorts, static discharge or any other molestation from occurring to your sweet innocent little computer.  With the tiny form factor this device affords, you can easily slap a case on it, connect it to your living room TV and attach it to the back of your set via 2 way adhesive tape- nobody would even know it’s there!

 

As of this writing, I’m not aware of any MicroSD cards that are bigger than 512gb.  Granted that’s a LOT of storage but that comes at a fairly steep price- about $400 on amazon.com.  I’m sure as higher density chips come out that price will fall, but the better bet would be to cobble together some 4TB hard drives in a desktop computer and use it for network storage of your multimedia files.  This is what I’m doing and it works perfectly!  I have multiple Raspberry Pi’s throughout the house on each TV that can play back my entire collection of movies, pictures, music and any other multimedia I choose to host on my media server.

 

raspberry-pi-2-pinoutmaxresdefault

There are also a number of other things that any of the Raspberry Pi family is capable of doing, including interacting with the physical world via it’s GPIO pins.  The Zero doesn’t come out of the box with the 40pin header required to use the GPIO, however it’s easily soldered onto the board.  I have a couple Pi 2’s that have temperature sensors hooked up to them and I track the temperature via MRTG graphs.  I also hope to set up an animated Christmas light display using a SainSmart 16 Channel relay board that is controlled by the Pi turning on and off each individual circuit.  It could also be used for home automation in that regard.

 

I could go on and on about all the things that these little buggers can do, but this article is focused on the Zero.  Below I’ll list some of the hardware (with prices and source) that you’ll need in order to put the Zero into service.  Add it all up and you’ll have to purchase at least another $16+ worth of hardware to really get some use out of it.  Granted I went as cheap as I could find online and I didn’t factor in any shipping or tax so your total could very well be north of $20.  For that, you can almost get a Raspberry Pi B+ that has full size HDMI, built in Ethernet and 4 full size Type A USB ports as well.  But c’mon- look how small this thing is- you can hide it in a can of Altoids and have room to spare for cripes sakes!

 

4105_large

61xpLcWy1cL._SL1500_

31+9SnMkOAL

61IVHo1bOBL._SL1001_

61+xxjf-4BL._SL1400_

 

There are a number of remote control apps that allow your phone/tablet to serve as the remote control for Kodi.  What’s really cool is that Kodi also supports the CEC (Consumer Electronics Control) standard which allows you to control some devices via the HDMI protocol.  This means that in a lot of cases, you can simply use the remote that came with your TV to navigate through Kodi without any additional hardware needed!