Outline


Introduction

I have a few websites that I update seldomly occasionally.

  • Online Resume
  • This blog site you're currently reading
  • A Digital Ocean VPS is a remote server I use due to My ISP putting a bandwidth cap of monthly 1TB that I have gone over three times in the past two years.

The goal of this project is to have a small box on my desk with three LEDs where each one represent one of the sites that I want monitored, and each light would change color depending on the health of the site (green - ok, red - bad).

Here's a link to the github repo.

Poorly drawn sketch done in 60 seconds

Materials list

All links below are clickable links to Adafruit.com. I'm not affiliated with them nor do I get any form of commision by recommending these items from Adafruit. I just highly recommend their store and tutorials.

Materials

Item price Note
1x Raspberry Pi Zero w/ Headers $14 The cheaper $10 header-less works too, but at the time of writing this one was out of stock.
3x Red and Green Indicator LED 18mm $1.5 each
1x Pack of Resistors $5 will explain which resistance to use later (usually 220 ohms)
1x Female/Male extension jumper wires 20x6" pack $1.95
1x Perma-Proto half-sized Breadboard PCB $4.50
1x Protoboard $5 - $10 optional but useful for testing

Pre-Assembly

Using the jumper cables do the following:

  1. jump any ground pin from the GPIO pins to a common ground on the breadboard.
  2. jump the middle pin of all three LEDs to ground.
  3. jump one BCM pin to one LED+ pin. The BCM pin can be whatever you want, as long as its not a ground or live pin. check out pinout.xyz for a pinout visual.

The 6 BCM pins can be modified in software later so dont worry about which ones to use.


The Code

The only dependency we need is the go-rpio library from stianeikeland
$> go get -u "github.com/stianeikeland/go-rpio"

The flow of the go program goes as follows:

graph TD; A[Map memory and Init the LEDs ] --> B[run go routines] B --> C[Check for response codes] C --> B[loop] B --> D[listen for interrupt] D --> E[terminate goroutines and exit]

Where the go routines are looping every 5 minutes.

You can find the latest version of the code at github.com/alonchis/LED-Simple-Monitoring

Below I'll explain the important parts of the code:

var (
	// BCM PINS on the board.
	pinsIndex = []int{14, 15, 23, 18, 4, 24} //LED1 green, LED1 red, LED2 green, ...

    ...

	//Signal channel to listen for sys interrupt (CTRL+C)
	signalChan = make(chan os.Signal, 1)

	//add websites to monitor here
	sites = [3]string{
		"https://google.com",
		"https://wikipedia.org",
		"https://google.com/notfound",
	}

	period = time.Minute * 5 // changes how often to check status
)
  • pinsIndex are the GPIO pins connected to the anodes of the LEDs.
  • signalChan is used to signal an system interrupt to terminate all goroutines.
  • sites are the websites you wish to monitor.
  • period is the interval you'd like to ping those websites.
func main() {
	// Open and map memory to access gpio, check for errors
	// Unmap gpio memory when done
	//Init all leds
    
    // assuming each LED has two pins for each color
	counter := 0
	for i := 0; i < len(led); i++ {
		led[i].green = rpio.Pin(pinsIndex[counter])
		led[i].red = rpio.Pin(pinsIndex[counter+1])
		counter = counter + 2
	}

	// Run go routines
	for i := 0; i < len(sites); i++ {
		go GetReturnCode(sites[i], led[i])
	}

	//listen for interrupt and teardown (turn off leds)
	cleanupDone := make(chan struct{})
	signal.Notify(signalChan, os.Interrupt)
	go func() {
		<-signalChan
		log.Println("\nReceived an interrupt, stopping services...")
		/** turn off leds on exit */
		for i := 0; i < len(led); i++ {
			led[i].green.Low()
			led[i].red.Low()
		}

		close(cleanupDone)
	}()
	<-cleanupDone
}

This code fires off three go routines. One for each website and LED. Then listens for a system interrupt to exit the program and turn off leds:

// GetReturnCode checks for status code of $sites every $period, exits on os.interrupt
func GetReturnCode(site string, led LED) {
	for {
		select {
		case <-signalChan: // exits on interrupt
			return
		default:
			resp, _ := http.Get(site)
			defer resp.Body.Close()
			if resp.StatusCode >= 400 {
				// handle error, RED on
				led.green.Low()
				led.red.High()
				log.Printf("%s unreachable, error code %v\n", site, resp.StatusCode)
				time.Sleep(period)
				continue
			}

			log.Printf("%s returned %v OK\n", site, resp.StatusCode)
			led.red.Low()
			led.green.High()
			time.Sleep(period)
		}
	}
}

#GetReturnCode is a firing off a non-blocking go channel where it always listens for a signal (In this case, it would be from an interrupt) and exits the function, which in turn also terminated the system thread. otherwise, waits for the time set in period for next check.


Usage

sudo go run led-simple-monitoring.go > /dev/null 2>&1 &

If any of that looks intimidating, here's a breakdown of what happens:

  • sudo go run led-simple-monitoring.go runs the go program.
  • > /dev/null redirects output from the go program to a "black hole".
  • 2>&1 means redirect both stdout (1) and stderr (2) from the go program into wherever it's being redirected (/dev/null).
  • & means run in the background.

If all goes well, to make this more permanent and run even after reboot, build an executable with sudo go build led-simple-monitoring.go

Copy the executable into /usr/bin/
$> sudo cp led-simple-monitoring /usr/bin

Put the text below in /etc/systemd/system/ledmonitoring.service

[Unit]
Description=LED monitoring of website
Requires=local-fs.target
After=network.target
[Service]
ExecStart=/usr/bin/led-simple-monitoring > /var/log/led.log 2>&1 &

[Install]
WantedBy=multi-user.target

and enable the service:

$> sudo systemctl enable ledmonitoring.service
$> sudo systemctl start ledmonitoring.service

and we can monitor the status of the service with
$> sudo systemctl status ledmonitoring.service


Commiting Changes

The Enclosure

This is the part I normally struggle the most. I have done a few projects but coming up with a "finalized" enclosures is where im clueless... and most of my projects just end up in my desk.

I went to my local Home Depot and got a 3 Gang Electrical Box:
test

And a plain wall plate with no holes: blank cover

The advantage is that the total cost is about 10 bucks, and then I would simple cut three holes for the three LEDs and have enough room to add a label of sorts. My only concern was that the box was all exposed metal and there's the potential for short to occur with the raspberry pi or the PCB.

However, I had extra acrylic sheet laying around and so I cut a square big enough to fit inside the metal box and screw it down using the existing holes in the back.

Blank Cover and the LEDs

This is the fun part. The LEDs diameter is about 11/16" or 18mm. I had a 5/8 spade drill bit so I cut the hole and sanded down with a nail file until the LED fit.

sanding down a hole

first hole done

LED installation

Once LED are installed, put some hot glue on the perimeter of the LEDs.

The Adafruit half-sized Breadboard

soldering on the pcb

Next is the pcb. I had some extra headers laying around so I soldered that to the pcb so I can easily bridge the Pi Zero and this PCB.

  1. Solder the resistances in the same way as the Protoboard.
  2. Use One female/male wire for each LED pin (9, 3 ground, 3 for RED LEDs, and 3 for GREEN LEDs) and solder them like we mapped out in the protoboard.
  3. plug the respective wires into each of the LED.
  4. plug the respective wires from the Pi Zero to the PCB.

The End

Testing outside the case

Thats about it! I had a bit of trouble jamming all of this inside the electrical box but with some zip ties I made it work.

This little device will proudly sit on your desk or wherever you want and be able to know if your sites are down for whatever reason.

I did this project as a quick and dirty project that ended up taking way too long, but i've learned a lot LEDs, and golang's goroutines and channels. Hope you learned something fun.

If you have any questions or comments, feel free to reach me at arzarteaga@gmail.com


Handling interrupt signal in golang
Resistor codes
Make an LED blink - rpi
Raspberry PI PWM tutorial
Raspberry pi Pinout
Building a RESTful api in go
Restful api's in go
Mermaid markdown in Ghost