Raspberry

Pi Powered

CF Server

Status

Monitor

Scott Steinbeck

  • Software Developer
  • 12+ Year of experience 
  • Father
  • Hardware Tinkerer

Hobbies

 
  • Coding on my free time
  • Overcommitting myself
  • Automating everything
  • IOT Development
  • Teaching Coding/Electronics
  • Contributing to open source

Remote control 

lawn mower

Dev

Pi Hole

Pi Dev

Parts

Wiring

Pi Config

Linux Setup

Commandbox install

Jar Loading

Boot Service

Make it work

Make it Pretty

 

What to expect

Parts

  • Raspberry Pi 3+
  • SD Card
  • 2 Jumper wires
  • Micro Usb Cord
  • WS2811 5V DC
    ​​Addressable Round
    LED Pixels
  • 5V - 10 Amp
    DC Power Supply
    with Female 12V
    DC Power Jack

Computer vs microprocessor

Power

Speed

Efficiency

Protocols

Sleep Current

Storage

Complexity

Language

Plug & Play

Wifi/Bluetooth

LTE

Wiring

5V 

Power

Pin 18

(PWM)

GND

PI: Pin 18 -> LEDs: Data/Signal

PI: Ground -> LED:Ground

5V DC: Ground -> LED: Ground

5V DC: 5V+ -> LED: 5V+

PWM

Pulse width modulation

GPIO - General Purpose I/O

Pi Communication Protocols

Configure SD Card

Install Latest Raspbian on SD Card
create empty SSH.TXT
edit config.txt 
#comment out dtparam

#Enable audio (loads snd_bcm2835)
#dtparam=audio=on

After you save those changes

eject the SD Card

Insert in the Pi and plug it in

create wpa_supplicant.conf

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=US

network={
 ssid="<Name of your wireless LAN>"
 psk="<Password for your wireless LAN>"
}

SSH into pi

ssh pi@raspberrypi.local
pi@raspberrypi.local's password: (default is raspberry)

//install wiringpi
sudo apt-get update && sudo apt-get install wiringpi

//disable audio (conflicts with PWM)
nano /etc/modprobe.d/alsa-blacklist.conf

//add line and save
blacklist snd_bcm2835

Install CommanDbox

curl -fsSl https://downloads.ortussolutions.com/debs/gpg | sudo apt-key add -
echo "deb https://downloads.ortussolutions.com/debs/noarch /" | sudo tee -a /etc/apt/sources.list.d/commandbox.list
sudo apt-get update && sudo apt-get install apt-transport-https commandbox

Start writing code

> nano index.cfm

NANO..... REALLY??

WE CAN DO BETTER

VS Code

Remote

Development

Talking to LEDs

<CFLED>

But, when all else fails

look for a JAVA library

Java -> C++

C++ -> Native GPIo

Create Project

 
> cd ~/Desktop/
> mkdir statusLights
> cd statusLights

> box

Find the Library jar







Welcome to CommandBox!
CommandBox> install "jar:https://repo1.maven.org/maven2/com/diozero/diozero-ws281x-java/0.11/diozero-ws281x-java-0.11.jar"

Add Jar Dependency

 

Done.... right?

But how do we use it?

java docs

 

Quick Start 

Task runners

 
component {
  
    function run(){
        classLoad( '/home/pi/Desktop/lightStatus/lib/diozero-ws281x-java-0.11/diozero-ws281x-java-0.11.jar' );

        var freq = 800000; //Default 
        var DMA = 5; //Default
        var pin = 18; // GPIO Pin
        var numOfLights = 50;
        var brightness = 200; //0-255
      	
      	// RGB, GBR, BGR
        var stripType = createObject("java","com.diozero.ws281xj.StripType"); 
        var ledController = createObject("java","com.diozero.ws281xj.rpiws281x.WS281x");
        	
      	ledController.init(freq,DMA,pin,brightness,numofLights,stripType.WS2811_RGB);
              
        for(var ledID=0; ledID < 50; ledID++ ){
            //( ledID * 5) just uses the (ledID x 5) as the color to make a gradient
            ledController.setPixelColourHSL(ledID,(ledID * 5),.5,.5); 
            ledController.render(); //have to render after set
        }
}

lightTest.cfc







> Welcome to CommandBox!
> CommandBox> task run lightTest

Run Task

 

Profit

Quick Start 2 

standard index.cfm

 
<cfscript>
for(var ledID=0; ledID < 50; ledID++ ){
  //( ledID * 5) just uses the (ledID x 5) as the color to make a gradient
  ledController.setPixelColourHSL(ledID,(ledID * 5),.5,.5); 
  ledController.render(); //have to render after set
}
</cfscript>

index.cfm

component{
  
    this.name = "statusLights";

    function onApplicationStart(){

        var freq = 800000; //Default 
        var DMA = 5; //Default
        var pin = 18; // GPIO Pin
        var numOfLights = 50;
        var brightness = 200; //0-255
      	
      	// RGB, GBR, BGR
        var stripType = createObject("java","com.diozero.ws281xj.StripType"); 
        application.ledController = createObject("java","com.diozero.ws281xj.rpiws281x.WS281x");
        application.ledController.init(freq,DMA,pin,brightness,numofLights,stripType.WS2811_RGB);
    }
	
}

Application.cfc

Config settings

 
{
    "dependencies":{
        "diozero-ws281x-java-0.11":"jar:https://repo1.maven.org/maven2/com/diozero/diozero-ws281x-java/0.11/diozero-ws281x-java-0.11.jar"
    },
    "installPaths":{
        "diozero-ws281x-java-0.11":"lib/diozero-ws281x-java-0.11/"
    }
}

box.json

{
    "name": "statuslights",
    "jvm": {
        "heapSize": 1024,
        "minHeapSize": 256
    },
    "app": {
        "libDirs": "lib"
    },
    "web": {
        "host": "raspberrypi.local",
        "http": {
            "port": "80"
        },
        "webroot": "/"
    },
    "force": true
}

Server.json

Animations

 
<cfscript>
  function blink(pixel,hueColor = 0,numberOfBlinks = 5){
  
    for(var i=1; i<numberOfBlinks; i++){
      ledController.setPixelColourHSL(pixel,hueColor,.5,.5); 
      ledController.render(); //have to render after set
      sleep(100);

      ledController.setPixelColourHSL(0,0,0); //OFF
      ledController.render(); //have to render after set
      sleep(100);
    }

  }

  function rainbow(pixel){
    
    for(var i=1; i<360; i++){
      ledController.setPixelColourHSL(pixel,i,.5,.5); 
      ledController.render(); //have to render after set
      sleep(60); //Slow it down so you can see it
    }

  }

</cfscript>

Running Mulitple 

Animations

 
component {
   function run(){
      
      //blink(pixel,hueColor = 0,numberOfBlinks = 5)
      blink(1,0,5);
      blink(2,0,5);
      blink(3,0,5);
      blink(4,0,5);
      blink(5,0,5);
     
      // rainbow(pixel)
      rainbow(6);
      rainbow(7);
      rainbow(8);
      rainbow(9);
      rainbow(10);
      
	}
}

Do we have a problem?

Too

Much

Sleep

Async to the Rescue

 

Parallel Processing

 
<cfscript>
  function rainbow(pixel){
    
    for(var i=1; i<360; i++){
      ledController.setPixelColourHSL(pixel,i,.5,.5); 
      ledController.render(); //have to render after set
      sleep(60); //Slow it down so you can see it
    }

  }

  //Create a thread
  thread { 
    rainbow(1);
  }

  //Use run async
  runAsync(function(){ 
	return rainbow(1);
  });


  //Install Coldbox 6 BE
  //use cbFutures
  async = new coldbox.system.async.AsyncManager();
  async.newFuture( () => rainbow(1) )

</cfscript>

Async

Uptime

 
component{
  function run(){
    fileSystemUtil.createMapping( "/coldbox", getCwd() & "coldbox" );
    //load in diozero java lib
    siteMonitor()
  }

  //Async health check
  function pingServer(id,siteURL){
    cfhttp(method="GET", charset="utf-8", url=siteURL, result="local.result", timeout=10);
    return {
      'id' : id, 
      'healthy': structKeyExists(local.result,'status_code') && local.result.status_code == 200
    };
  }

  //Site Monitor (health check)
  function siteMonitor(){
    async = new coldbox.system.async.AsyncManager();
    var sites =  [
      () => pingServer(4,"https://www.ortussolutions.com"),
      () => pingServer(1,"http://my.agritrackingsystems.com"),
      () => pingServer(2,"http://my.agrimapping.com"),
      () => pingServer(3,"http://app.cropcast.com")
    ]

    async
      .all( sites )
      .get()
      .each( ( healthCheck ) => {
      var ledID = variables.ledIndex['siteMonitor'][healthCheck.id];
      setLEDHue(ledID,60,true);
      setLEDHue(ledID,colorScale((healthCheck.healthy ? 1 : 0 ),'red','blue'),true);
    })
  }

}
Add 'coldbox@be' as a dependency in box.json

Scheduled Tasks

 
component{

    async = new coldbox.system.async.AsyncManager();
    taskManager = async.newScheduledExecutor( name: "myTasks", threads: 20 );

    function run(){
      
        var future = taskManager.newSchedule(updateClock) 
        .every( 60000 ) // Run every minute
        .start();
    }
  
    function updateClock(){

      var dt = now();
    
      //update day of the week (1-7)
      var curr_weekday = datePart('w',dt);
      ...
      //update clock hours (1-24), cut in half for 12hr clock
      var curr_hr = datePart('h',dt);
      ...
      //update clock minutes (0-60) increments of 5
      var curr_min = datePart('n',dt);
      ...
    }

}

Map the LEDs

 

Make a design

 

1

2

3

4

    //LED Lights ID Mapping
    variables.ledIndex = {
        "weekdays": [31, 30, 29, 28, 27, 26, 25],
        "hours": [47, 48, 16, 19, 20, 23, 34, 33, 38, 39, 42, 44],
        "minutes": [46, 49, 17, 18, 21, 22, 35, 36, 37, 40, 41, 45],
        "temp": [15, 14, 13],
        "piStatus": [11, 10, 9, 8],
        "siteMonitor": [7, 6, 5, 4],
        "workMonitor": [3, 2, 1, 0],
        "pomodoro": [24],
        "calStat": [32],
        "genblink": [12],
        "wheatley": [43]
    };

Build Utility Methods

// Turn off LED by ID
function turnOffLED(id,forceUpdate = false){
  variables.ledController.setPixelColourHSL(id,0,0,0);
  if(forceUpdate) updateLights();
}

// Set LED Hue by ID
function setLEDHue(id,hue,forceUpdate = false){
  variables.ledController.setPixelColourHSL(id,hue,1,.5);
  if(forceUpdate) updateLights();
}

// Push light settings to the LEDs
function updateLights(){
  variables.ledController.render();
}

// Blink light
function blink(ledID,hue,delay,blinkNum){
  for(var i=1; i <=blinkNum; i++){
    setLEDHue(ledID,i,true);
    sleep(delay);
    turnOffLED(ledID,true);
    sleep(delay);
  }
}

Build Weekdays & Clock

// Get the corresponding color from a color range based on a percentage
function colorScale(percent, lowColor, highColor){
  if(percent > 1) percent = 1;
  if(!isNumeric(lowColor)) lowColor = variables.colorIndex[lowColor];
  if(!isNumeric(highColor)) highColor = variables.colorIndex[highColor];
  var val = abs(lowColor -  highColor) * percent;
  return (highColor < lowColor) ? lowColor - val : lowColor + val;
}

// update clock (Day of week, hours, minutes, pomodoro timer)
function updateClock(){
        //Needs to be set here, or in the admin to ensure the correct time zone
        SetTimeZone(timezone='America/Los_Angeles'); 
        var currTime = now();

        var dow = datepart('w',currTime); //Day of the week 1-7
        var curr_hour = datepart('h',currTime); //Hour 24hr clock
        var curr_min = datepart('n',currTime); //Minutes

        var hourLEDIdx = variables.ledIndex['hours']; //led Index for hours
        var hourColor  = (curr_hour/24*60)+200; //Hour gradient starting at 200 (lightblue)
        
        var minuteLEDIdx = variables.ledIndex['minutes']; //led Index for minutes
        var minuteColor  = (curr_min/60*60)+30; //Minute gradient starting at 30 (purple)

        // update pomodoro clock (0-25 min.) (25-30 min. Red, Get up and move)
        var halftime = curr_min;
        var pomoLED = variables.ledIndex['pomodoro'][1];
        if(halftime > 30) halftime = halftime - 30;
        if(halftime <= 25){
            setLEDHue(pomoLED,colorScale(halftime/25,'green','purple'));            
        } else {
            blink(pomoLED,0,100,6);
        }
        // Set day of the week ...
        // Set hour of the day  ...
        // Set minute (increments of 5) ...
        updateLights(); //update lights after everything is set
}

Build Status Functions

//System Stats
function SystemStats(){
  var  systemInfo=GetSystemMetrics(); //Get lucee System Metrics
  var heap = getmemoryUsage("heap"); // Java heap usage
  var nonHeap = getmemoryUsage("non_heap"); //Java Non-heap usage
  var leds = variables.ledIndex['piStatus'];

  setLEDHue(leds[1],colorScale(systemInfo.cpuSystem,'green','red'));
  setLEDHue(leds[2],colorScale(heap.used/heap.max, 'green','red'));
  setLEDHue(leds[3],colorScale(systemInfo.queueRequests, 'green','red'));
  setLEDHue(leds[4],colorScale(randRange(0,1), 'green','red'));
  updateLighleds
}


//Site Monitor (health check)
function siteMonitor(){
  pingServer(1,"http://my.agritrackingsystems.com");
  pingServer(2,"http://app.cropcast.com");
  pingServer(3,"https://www.ortussolutions.com");
  pingServer(4,"https://www.google.com/");
}

...

Spend hours poking holes

first prototype

Second prototype

Make it look prettier

 

Finally

Creating a startup service

[Unit]
Description='Status Lights'

[Service]
ExecStart='/usr/local/bin/box' server start name='statuslights' directory='/home/pi/Desktop/lightStatus/' serverConfigFile='/home/pi/Desktop/lightStatus/server.json' --noSaveSettings 

ExecStop='/usr/local/bin/box' server stop name='statuslights' directory='/home/pi/Desktop/lightStatus/' serverConfigFile='/home/pi/Desktop/lightStatus/server.json'

Type=forking
SuccessExitStatus=143
Restart=on-failure
RestartSec=2000ms

[Install]
WantedBy=multi-user.target

Coldbox Site, Content Box (piBox), CFML Site

Creating a startup service

[Unit]
Description='Status Lights'

[Service]
ExecStart='/usr/local/bin/box' task run '/home/pi/Desktop/lightStatus/twinkleTask.cfc'
Type=simple
SuccessExitStatus=143
Restart=on-failure
RestartSec=2000ms

[Install]
WantedBy=multi-user.target

Task "Manager"

Raspberry Pi Powered CF Status Monitor

By uniquetrio2000

Raspberry Pi Powered CF Status Monitor

Never be in the dark when it comes to the status of your builds and servers, with this awesome Raspberry Pi LED Server Status Monitor. Whatever you want to know the status of can be sent directly to an LED Light strip so you know the moment there is a problem or a successful build.

  • 520