Close

Software stack

A project log for CamperCycle

A rather large project that spans multiple disciplines. Despite of a chronic illness that leaves me fatigued a lot of the time.

timescaleTimescale 03/30/2024 at 08:230 Comments

I have been really fatigued and in pain the last couple of days. On top of that, the weather did not play nice, so I was mostly desk/bed ridden. Still I managed to build some scripts and perhaps it is time to talk about what I use and why.

The front end is a HTML(5)/javascript interface running from the main RPi on a LAMP stack. I have the option to use PHP, but the necessity has not arisen yet. I would PHP in the back-end for talking to databases.

The main backbone of the system is Python. I wrote a socket server that talks to everything be it internal apps, the front-end or the Arduino sensor board. The reason for a socket server is twofold. It offers an entrypoint which is accessible from every level and you are not horsing around with app user sudo lists and permissions and such. Everything is run under the user who started the server.

The code is a bit spaghetti, but for the most part it works. It works around the quirks of socket servers with a hack. Perhaps there are snippets that people find useful :

#!/usr/bin/env python3
import serial
import socket
import sys
import os
import glob, os
import time
from datetime import datetime

import mimetools
from StringIO import StringIO

# Define socket host and port
SERVER_HOST = '192.168.1.41'
SERVER_PORT = 7654

# Create socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
#server_socket.Server(cors_allowed_origins='*')
server_socket.bind((SERVER_HOST, SERVER_PORT))
server_socket.listen(1)
print('Listening on port %s ...' % SERVER_PORT)
# http://192.168.1.41:7654/?key:8e76@r1on&command&leftBlinkOn

ser = serial.Serial('/dev/ttyAMA0', 19200, timeout=1)
ser.reset_input_buffer()

while True:    
    failureMode = False
    # Wait for client connections
    client_connection, client_address = server_socket.accept()
    # Get the client request
    request = str(client_connection.recv(1024).decode())
    #print '----'
    #print request
    reqLength = len(str(request).strip());
    #print reqLength
    #print '----'
    if reqLength > 10:
        request, he = request.split('\r\n', 1)

        # Get the headers
        m = mimetools.Message(StringIO(he))

       # Add request information
        m.dict['method'], m.dict['path'], m.dict['http-version'] = request.split()    
    #    print m['path']
        arguments = m['path'].split('?')
 #   print "args"
 #   print argCount
        argCount = len(arguments)
 #   print "args"
 #   print argCount
        if argCount > 1:
    #        print(arguments[1])
            options = arguments[1].split('&')
            if options[0] != "key:8e76@r1on":
                failureMode = True   
    #    failureMSG = "&Err[3]=Invalid Key Error."
    
            if failureMode == False: 
                key = options[0]
                order = options[1]
                command = options[2]
                if order == "say":
                    #os.system("espeak ") + ('"') + str(command) + ('"')
                    #espeak  -v nl+f5 -s150 "dit is een test" # dutch female
                    status = os.popen('cmus-remote -Q | head -n 1 | awk \'{print $2}\'').read()
                    status = status.replace('\n', ' ').replace('\r', '')[:-1]
                    if (status == "playing"):
                        os.popen('cmus-remote -v 35%')
                    print("'" + status + "'")
                    os.system('espeak -v nl+f5 -s150 ' + ('"') + str(command) + ('"'))            
                    response = 'HTTP/1.0 200 OK'
                    response += "Content-Type: text/html; charset=utf-8\n"
                    response += "Access-Control-Allow-Origin: *\n"
                    response += '\n\n<data><say>\"' + str(command) + '\"</say></data>'
                    if (status == "playing"):
                        os.popen('cmus-remote -v 80%')
                elif order == "audio":
                    response = 'HTTP/1.0 200 OK'
                    response += "Content-Type: text/html; charset=utf-8\n"
                    response += "Access-Control-Allow-Origin: *\n"
                    if command == "play":
                        d = 2
                    elif command == "pause":
                        d = 2
                    elif command == "stop":
                        d = 2
                    elif command == "next":
                        d = 2
                    elif command == "prev":
                        d = 2
                    elif command == "status":
                        status = os.popen('cmus-remote -Q | head -n 1 | awk \'{print $2}\'').read()
                        status = status.replace('\n', ' ').replace('\r', '')[:-1]
                        if status == "":
                            status = "Cmus not running"

                        duration = os.popen('cmus-remote -Q | sed -n 3,3p | awk \'{print $2}\'').read()
                        duration = duration.replace('\n', ' ').replace('\r', '')[:-1]

                        position = os.popen('cmus-remote -Q | sed -n 4,4p | awk \'{print $2}\'').read()
                        position = position.replace('\n', ' ').replace('\r', '')[:-1]

                        mfile = os.popen('cmus-remote -Q | sed -n 2,2p | cut -c 6-').read()
                        mfile = mfile.replace('\n', ' ').replace('\r', '')[:-1]

                        volumeL = os.popen('cmus-remote -Q | sed -n 16,16p | awk \'{print $3}\'').read()
                        volumeL = volumeL.replace('\n', ' ').replace('\r', '')[:-1]

                        volumeR = os.popen('cmus-remote -Q | sed -n 17,17p | awk \'{print $3}\'').read()
                        volumeR = volumeR.replace('\n', ' ').replace('\r', '')[:-1]
 
                

                        response += '\n\n<data>'
                        response += '<status>' + status + '</status>'
                        response += '<file>' + mfile + '</file>'
                        response += '<duration>' + duration + '</duration>'
                        response += '<position>' + position + '</position>'
                        response += '<volumeLeft>' + volumeL + '</volumeLeft>'
                        response += '<volumeRight>' + volumeR + '</volumeRight>'
                        response += '</data>'


                else:
                    strcommand = str("<" + order + "=" + command + ">")
                    print(strcommand)
                    ser.write(strcommand + "\n")
                    response = 'HTTP/1.0 200 OK'
                    response += "Content-Type: text/html; charset=utf-8\n"
                    response += "Access-Control-Allow-Origin: *\n"
                    response += "\n\n<data><module>Device Control...</module>\n\n"
                    response += '<recvData>'
                    response += '<order>' + str(order) + '</order>'
                    response += '<command>' + str(command) + '</command>'
                    response += '</recvData></data>'
            else:
                    response = 'HTTP/1.0 200 OK'
                    response += "Content-Type: text/html; charset=utf-8\n"
                    response += "Access-Control-Allow-Origin: *\n"
                    response = '\n\n<data><module>Device Control...</module>\n\n'
                    response += '<recvData><error>mode1</error></recvData></data>'
       # print chkHost
        else:
            response = 'HTTP/1.0 200 OK'
            response += "Content-Type: text/html; charset=utf-8\n"
            response += "Access-Control-Allow-Origin: *\n"
            response += '\n\n<recvData><error>Packet zero bytes</error></recvData></data>'
    client_connection.sendall(response.encode())
    client_connection.close()
    time.sleep(2)

# Close socket
server_socket.close()

This script always returns and XML file to the caller, regardless of the command was local, for an app or from the serial device.

The serial devise is an Arduino mega. It has its own little console, but mainly gets its commands from the socket server:

#include <dht.h>
#include <LiquidCrystal_I2C.h>
#include  <Wire.h>
#include <secTimer.h>
#include <TinyGPSPlus.h>


dht DHT;

#define DHT11_PIN 6

TinyGPSPlus gps;

LiquidCrystal_I2C lcd(0x27,  16, 2);

secTimer myTimer;


unsigned long seconds=0;

const byte numChars = 32;
char receivedChars[numChars];

int turnValue;
int oldturnValue;

int lightValue=0;
int oldLightValue=0;

int humValue=0;
int oldHumValue=0;
int tempValue=0;
int oldTempValue=0;

int leftTurnStatus = 0;
int rightTurnStatus = 0;

int GPSLon = 0;
int GPSLat = 0;
int GPSsatellites = 0;
int GPSSpeed = 0;
int GPSAltitude = 0;

bool autoIndicator = true;
bool leftIndicator = false;
bool rightIndicator = false;
bool mainLights = false;


boolean newData = false;

int sensorLoop = 0;


const int RELAY_PIN_LEFT = 8;
const int RELAY_PIN_RIGHT = 9;

const int RELAY_PIN_HORN = 10;
const int RELAY_PIN_MAIN = 11;

static const int RXPin = 3, TXPin = 4;
static const uint32_t GPSBaud = 9600;

int buttonState = 0;
int button2State = 0;

String glat = "";

int second1Loop = 0;
int second2Loop = 0;
int second4Loop = 0;
int second10Loop = 0;

String dataBuffer = "";
bool valueChange = false;


bool leftTurnPressed = false;
bool rightTurnPressed = false;
bool leftBlinkOn = false;
bool rightBlinkOn = false;


void setup() {
    delay(200);

    Serial.begin(19200);
    Serial1.begin(9600);
    Serial.println("<init>CCKit is ready></init>\n");

    myTimer.startTimer(); //start the timer
    seconds=myTimer.readTimer();

      pinMode(RELAY_PIN_HORN, OUTPUT);
      pinMode(RELAY_PIN_LEFT, OUTPUT);
      pinMode(RELAY_PIN_RIGHT, OUTPUT);
      pinMode(RELAY_PIN_MAIN, OUTPUT);
      digitalWrite(RELAY_PIN_HORN, HIGH);
       digitalWrite(RELAY_PIN_LEFT, HIGH);
        digitalWrite(RELAY_PIN_RIGHT, HIGH);
        digitalWrite(RELAY_PIN_MAIN, HIGH);

     
     lcd.init();
     lcd.backlight();
     lcd.setCursor(0,0);
     lcd.print("CCkit 1.0 ");
  lcd.setCursor(0,1);
  lcd.print("Dash:ready");
  
  
   int analogValue = analogRead(A0);
   int oldAnalogValue = -1;
   int oldLightValue = -1;


}

void loop() {

    recvWithStartEndMarkers();
    showNewData();

    if (seconds!=myTimer.readTimer()) {
        seconds=myTimer.readTimer();
        second1Loop++;
        second2Loop++;
        second4Loop++;
        second10Loop++;
    }
bool valueChange = false;

      getSensorData();
      getInputData();
  
}

void getInputData(){
 
  if (second1Loop == 1){
      if (leftTurnPressed){
          if (rightBlinkOn){
            rightBlinkOn = false;
                lcd.setCursor(0,0);
                lcd.print("                 ");

          } else {
            leftBlinkOn = true;
          }
      }
      if (rightTurnPressed){
            if (leftBlinkOn){
              leftBlinkOn =false;
               lcd.setCursor(0,0);
               lcd.print("                 ");
            } else {
              rightBlinkOn = true;
            }
      }

      leftTurnPressed = false;
      rightTurnPressed = false;
  }
  
  if (second1Loop == 1){
    
      if (leftBlinkOn){
              digitalWrite(RELAY_PIN_LEFT, LOW);
      }
      if (rightBlinkOn){
              digitalWrite(RELAY_PIN_RIGHT, LOW);
      }
  }
  if (second2Loop ==2){
      if (leftBlinkOn){
            digitalWrite(RELAY_PIN_LEFT, HIGH);
 

      }      
      if (rightBlinkOn){
            digitalWrite(RELAY_PIN_RIGHT, HIGH);
 
      }
  }
      if (!leftBlinkOn){
        digitalWrite(RELAY_PIN_LEFT, HIGH);
      }
      if (!rightBlinkOn){
        digitalWrite(RELAY_PIN_RIGHT, HIGH);
      }
      
// reset secondloops
 if (second1Loop == 1){
  second1Loop = 0;
 }
 if (second2Loop == 2){
  second2Loop = 0;
 }
 if (second4Loop == 4){
  second4Loop = 0;
 }
 if (second10Loop == 10){
  second10Loop = 0;
 }

}


void getSensorData(){

 
 
 if (second1Loop == 1){
  valueChange = false;
 //  ss.begin(GPSBaud);
 }

 if (second2Loop == 2){
  // GPS

      
   smartDelay(500);
   valueChange = true;
   GPSLon = gps.location.lng();
   GPSLat = gps.location.lat();
   GPSSpeed = gps.speed.kmph();
   GPSAltitude = gps.altitude.meters();
   GPSsatellites =  gps.satellites.value();
    Serial.print(GPSLon);
    Serial.print("\n");

 }
 
 if (second4Loop == 4){

  int lightValue = analogRead(A1);
  if (abs(lightValue - oldLightValue) > 10){
      oldLightValue = lightValue;
      valueChange = true;
  }

  int chk = DHT.read11(DHT11_PIN);
  tempValue = DHT.temperature;
   if (tempValue != oldTempValue){
      oldTempValue = tempValue;
      valueChange = true;
   }
  humValue = DHT.humidity;
   if (humValue != oldHumValue){
      oldHumValue = humValue;
      valueChange = true;
   }
 }

  if (second1Loop == 1){
     if (valueChange){
     }
  }

}

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;
 
    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();
        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
            receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

void showNewData() {
    if (newData == true) {
        // process commands
          String order;
          String exec;
          
               lcd.setCursor(0,1);
               lcd.print("                 ");
               lcd.setCursor(0,1);
          lcd.print(receivedChars);

        order = strtok(receivedChars,"=");
        exec = strtok(NULL,"&");

        dataBuffer = "<data>";
        dataBuffer += "  <call>";
        dataBuffer += "   <type>" + order + "</type>";
        dataBuffer += "   <order>" + exec + "</order>";
        dataBuffer += "  </call>";
        

        dataBuffer += "  <results>";

        if (order == "display"){
               lcd.setCursor(0,1);
               lcd.print("                 ");
               lcd.setCursor(0,1);
               lcd.print(exec);


        }
        if (order == "command"){
            if (exec == "clrDisplay"){
               lcd.setCursor(0,1);
               lcd.print("                 ");
            }
            if (exec == "parkOn"){
                      dataBuffer += "   <state name=\"park\">" + exec + "</state>";
            }
            if (exec == "parkOff"){
                      dataBuffer += "   <state name=\"park\">" + exec + "</state>";
            }
            if (exec == "leftBlinkOn"){
                leftBlinkOn = true;
                dataBuffer += "   <state name=\"leftIndicator\">" + String(leftBlinkOn) + "</state>";
            }
            if (exec == "leftBlinkOff"){
              leftBlinkOn = false;
                dataBuffer += "   <state name=\"leftIndicator\">" + String(leftBlinkOn) + "</state>";
            }
            if (exec == "rightBlinkOn"){
              rightBlinkOn = true;
                dataBuffer += "   <state name=\"rightIndicator\">" + String(rightBlinkOn) + "</state>";
            }
            if (exec == "rightBlinkOff"){
              rightBlinkOn = false;
                dataBuffer += "   <state name=\"rightIndicator\">" + String(rightBlinkOn) + "</state>";
            }
            if (exec == "mainLightsOn"){
              digitalWrite(RELAY_PIN_MAIN, LOW);
                dataBuffer += "   <state name=\"mainLights\">" + exec + "</state>";
              
            }
            if (exec == "mainLightsOff"){
              digitalWrite(RELAY_PIN_MAIN, HIGH);
                dataBuffer += "   <state name=\"mainLights\">" + exec + "</state>";
            }
            if (exec == "breakLightsOn"){
                dataBuffer += "   <state name=\"breakLights\">" + exec + "</state>";
            }
            if (exec == "breakLightsOff"){
                dataBuffer += "   <state name=\"breakLights\">" + exec + "</breakLights>";
            }
            if (exec == "setAutoIndicatorOff"){
                dataBuffer += "   <state name=\"autoIndicator\">" + String(autoIndicator) + "</state>";
            }
            if (exec == "setAutoIndicatorOn"){
                dataBuffer += "   <state name=\"autoIndicator\">" + String(autoIndicator) + "</state>";
            }
            if (exec == "reset"){
                dataBuffer += "   <state name=\"reset\">Reset in 2 seconds</state>";
            }
            
        } else if (order == "request"){
            if (exec == "lightStatus" | exec == "allData"){
               dataBuffer += "   <dataSet name=\"lightStatus\">";
               dataBuffer += "      <state name=\"park\">" + exec + "</state>";
               dataBuffer += "      <state name=\"rightIndicator\">" + String(rightBlinkOn) + "</state>";
               dataBuffer += "      <state name=\"mainLights\">" + exec + "</state>";
               dataBuffer += "      <state name=\"breakLights\">" + exec + "</state>";
               dataBuffer += "    </dataSet>";
            } else if (exec == "sensorValues" | exec == "allData"){
              
            } else if (exec == "GPSdata" | exec == "allData"){
               dataBuffer += "   <dataSet name=\"GPSStatus\">";
               dataBuffer += "      <state name=\"GPSLon\">" + String(GPSLon) + "</state>";
               dataBuffer += "      <state name=\"GPSLat\">" + String(GPSLat) + "</state>";
               dataBuffer += "      <state name=\"GPSSpeed\">" + String(GPSSpeed) + "</state>";
               dataBuffer += "      <state name=\"GPSAltitude\">" + String(GPSAltitude) + "</state>";
               dataBuffer += "      <state name=\"GPSsatellites\">" + String(GPSsatellites) + "</state>";
               dataBuffer += "    </dataSet>";
              
            } else if (exec == "sensorData" | exec == "allData"){
               dataBuffer += "   <dataSet name=\"sensorData\">";
               dataBuffer += "      <state name=\"temperature1\">" + String(tempValue) + "</state>";
               dataBuffer += "      <state name=\"humidity1\">" + String(humValue) + "</state>";
               dataBuffer += "    </dataSet>";
 
            } else if (exec == "internalSettings" | exec == "allData"){
               dataBuffer += "   <dataSet name=\"internalSettings\">";
               dataBuffer += "   <state name=\"autoIndicator\">" + String(autoIndicator) + "</state>";
               dataBuffer += "    </dataSet>";
              
            } else {
              //Something went wrong
            }
        }
      Serial.print(dataBuffer);
      dataBuffer = "";
  
        
        newData = false;
    }
}

static void smartDelay(unsigned long ms) {
  unsigned long start = millis();
  do {
    while (Serial1.available()) {
      gps.encode(Serial1.read());
      //Serial.print(Serial1.read());
    }
  } while (millis() - start < ms);
}

static void printFloat(float val, bool valid, int len, int prec) {
  String retVal = "";
  if (!valid) {
    while (len-- > 1) {
//      Serial.print('*');
    }
  //  Serial.print(' ');
  } else {
 //   Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1); // . and -
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i) {
  //    Serial.print(' ');
    }
  }
  smartDelay(0);
  glat = val;
  glat += "!";
  glat += prec;
}

static void printInt(unsigned long val, bool valid, int len) {
  char sz[32] = "*****************";
  if (valid) {
    sprintf(sz, "%ld", val);
  }
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i) {
    sz[i] = ' ';
  }
  if (len > 0) {
    sz[len-1] = ' ';
  }
 // Serial.print(sz);
  smartDelay(0);
}

static void printDateTime(TinyGPSDate &d, TinyGPSTime &t) {
  if (!d.isValid()) {
 //   Serial.print(F("********** "));
  } else {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year());
    Serial.print(sz);
  }

  if (!t.isValid()) {
  //  Serial.print(F("******** "));
  } else {
    char sz[32];
  //  sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second());
    Serial.print(sz);
  }

  printInt(d.age(), d.isValid(), 5);
  smartDelay(0);
}

static void printStr(const char *str, int len) {
  int slen = strlen(str);
  for (int i=0; i<len; ++i) {
 //   Serial.print(i<slen ? str[i] : ' ');
  }
  smartDelay(0);
}

 This script can turn on relays and get GPS and sensor information and pass it to the socket server. Like everything else, it is incomplete, sometimes buggy and rather ugly.

Then there are the odd cases what stuff just needs to be checked. This is done either in Python or in Bash script, because I am a sucker for punishment! This shell script basically monitors the mounted drives and when something changes it does its thing. Its thing being detecting media and GXP files on the device and copying it to the right location or build a playlist and start Cmus. When removed, CMus will be stopped.

All IO with Cmus is done via the socket server, so this script has very little to say about that :

#!/bin/bash

#needs sudo apt-get install screen

echo "CCKit USBMonitor daemon"
echo "_______________________"

active=1
mediaDrive=""
drivelistRaw=$(ls /media/pi/ -h -w 1)
activeDrives=$(ls /media/pi/ -h -w 1 | grep -c '^')

#build compare array
while IFS= read -r line; do
    driveArray+=($line)
done <<< "$drivelistRaw"

#check if old media drive is still mounted
if [ -f /var/www/html/CCKit/tmp/mediaDrive.tmp ] 
then
	oldMediaDrive=$(cat /var/www/html/CCKit/tmp/mediaDrive.tmp)
	mediaPath="/media/pi/${oldMediaDrive}/music/";

  	GPX="/media/pi/${oldMediaDrive}/GPX/"
  	if [ -d $GPX ]
  	then
  		echo "GPX folder found. Copy to main location"
  		cpGPX=$(cp "${GPX}"/* /var/www/html/CCKit/data/routes/gpx/)
  	fi


	if [ -d "$mediaPath" ] 
		then
 			echo "Found Media Drive."
 			echo "Waiting for cmus socket" 
			screen -d -m -S cmus /usr/bin/cmus
    		sleep 1
    		cmus-remote -p
    	else 
    		rm /var/www/html/CCKit/tmp/mediaDrive.tmp
	fi

fi

while [ $active -le 2 ]
do
	#check drives
	drivelistRaw=$(ls /media/pi/ -h -w 1)
	foundDrives=$(ls /media/pi/ -h -w 1 | grep -c '^')
	drivecount=1
	if [ "$foundDrives" != "$activeDrives" ] 
	then
		#driver removed or added
		if [ "$mediaDrive" == "" ]
		then
			echo 'no media drive'
			while IFS= read -r line; do
				if [[ ${driveArray[@]} =~ $line ]]
				then
 					echo "$line" "drive found"
				else
  					# this is the new drive
  					echo "New Drive found :" "$line"
					driveArray+=($line)
					activeDrives=$(ls /media/pi/ -h -w 1 | grep -c '^')
  					# check if drive has a media folder
  					musicf="/media/pi/${line}/music"
  					GPX="/media/pi/${line}/GPX/"
  					if [ -d $GPX ]
  					then
  						echo "GPX folder found. Copy to main location"  		
  						cpGPX=$(cp "${GPX}"/* /var/www/html/CCKit/data/routes/gpx/)
  					fi
  					if [ -d $musicf ]; then
  						echo "Media Directory found."
  						mediaDrive=${musicf}
  						#scan media folder for mp3's
  						MP3listRaw=$(find "${musicf}/" -type f -iname "*.mp3")
  						#echo "" > /var/www/html/CCKit/tmp/tmp.pl
  						while IFS= read -r line; do
    					#echo "file" "$line"
    					echo "$line" >> /var/www/html/CCKit/tmp/tmp.pl
						done <<< "$MP3listRaw"
						sleep 1
						cp /var/www/html/CCKit/tmp/tmp.pl ~/.config/cmus/lib.pl
						cp /var/www/html/CCKit/tmp/tmp.pl ~/.config/cmus/playlist.pl
						echo "$mediaDrive" > /var/www/html/CCKit/tmp/mediaDrive.tmp
						echo "Waiting for cmus socket" 
						#touch /var/www/html/CCKit/tmp/cmus.trigger
						screen -d -m -S cmus /usr/bin/cmus
    					sleep 1
    					cmus-remote -p  					
    				else 
						echo "No mediaFolder Found."
						#pkill cmus				
					fi

				fi			
			done <<< "$drivelistRaw"

		else
			echo 'drive removed'
			#check if it was the media drive
			drivelistRaw=$(ls /media/pi/ -h -w 1)
			activeDrives=$(ls /media/pi/ -h -w 1 | grep -c '^')
			mediaRemoved=false
			while IFS= read -r line; do
				if [[ ${driveArray[@]} == $mediaDrive ]]
				then
					mediaRemoved=true
 					echo "mediaDrive removed"
 				fi	
 			done <<< "$drivelistRaw"

			driveArray=()
			while IFS= read -r line; do
    			driveArray+=($line)
			done <<< "$drivelistRaw"

			#stop player and reset daemon
			if [ $mediaRemoved ]
			then
				rm /var/www/html/CCKit/tmp/mediaDrive.tmp
				mediaDrive=""
				pkill cmus	
			fi
		fi
	fi
	sleep 1
done

So behind the frontend, these 3 types of programmes all tie the stuff together. We have one nice central place where the data can be retrieved from. Now that is what I call a stack.

There are also a couple of packages I use to extract the data or play media, but that is for another time. This is messy enough as it is!

Discussions