Ultrasonic range finder HCSR04 & network shield ENC28J60 for garbage monitoring

Here is my first "real" Arduino project, hope it can help someone else.


Problem:


We have two garbage vacuum containers (http://www.envacuk.co.uk/products-and-services/our_products/movac-mobile-vacuum-systems) in our basement for our ~80 apartment housing association. See Containers.jpg showing the two containers in our basement.


They are emptied from the street with a vacuum truck which is great, however the containers have a tendency to clog up in the inlet tube due to the garbage bags in the container building up as a pyramid and eventually stopping the entrance. Usually there is still much more volume in the container when this happens, and people just put the garbage on the side resulting in garbage mountain (attracting rats etc). The big problem is that the company emptying the containers does not care about the garbage on the street (of course… their job is only to empty the container in the basement). Sometimes they can not access the outlet due to some car is parked in the wrong place, then however they usually just leave the place without noticing anyone which results in the container clogging up one or two days after.

Now someone has to inform our janitor to urgently come (who normally spend only one day/week at our house). The janitor then has to make sure the containers are first emptied then fill them up with the garbage left on the street which takes allot of time, i.e., money (and risk the creation of a new pyramid).

A better way would be to foresee the blockage and just do something about the pyramid before the inlet is jammed…


Attempted solution:


I have now installed an ultrasonic range finder (HCSR04) to look down the tube and measure the distance down to the pile of garbage. The distance is then uploaded every 15 seccond to Xively via the Ethernet shield (ENC28J60 and yes we happened to have a network in the basement that I could tap into). Will post code as soon as I have had time to tidy it up a bit.

SensorAndCurve.jpg Shows the ultrasonic range finder (HCSR04) mounted with melt-glue looking down into the container inlet tube together with a "typical curve" of distance to the garbage pile.

The system actually shows a weekly trend and I have already been able to notice when the garbage truck hasn't emptied the containers on time.

Now I'm working in a cron-job to send an automated message to the people responsible for emptying if something is looking fishy... (stay tuned for more and code)

The problems solved along the way:


Sopsug.ino

//
// SopsugsMonitor
//
// Author    Micke Malmström
// License    cc
// Inspired by: http://arduino.cc/en/Tutorial/XivelyClient
//              https://forum.pjrc.com/threads/25887-Arduino-code-example-for-ENC28J60-Ethernet-upload-to-Thingspeak


#include <Dhcp.h>
#include <Dns.h>
#include <ethernet_comp.h>
#include <UIPClient.h>
#include <UIPEthernet.h>
#include <UIPServer.h>
#include <UIPUdp.h>

// For ENC28J60 UIPEthernet.h tested with version 1.50
#include "UIPEthernet.h"

//// For Debugging to see if memory is disapearing
//include <MemoryFree.h>



// Each sensor has a name or "Chanel" associated with it like EastContainer. The sensor aims stright down in the container

#define APIKEY         "XXXXXXX"
#define FEEDID         YYYYYYYYY
#define USERAGENT      "ZZZZZZZZ" // user agent is the project name

// define Sensor order and Chanel name
const char* sensorName[] = {"NärmastGården", "NärmastGatan"};//, "EastTube", "WestTube"};
const byte trigPin[] = {5, 7};//, 8, 10}; // define ultrasonic triggerpins
const byte echoPin[] = {6, 8};//, 9, 11}; // define ultrasonic echopins
const byte noSensors = (1);      // Define how many sensors are used
int resetCounter = 0;
unsigned int sensorReading[noSensors];


// Define mac adress of ethernet controler.
// Important! mac has to be unique on the network! i.e., always use a router to make sure no one else on the network is using the same
byte mac[]     = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};

// initialize the library instance:
EthernetClient client;

// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
IPAddress server(216, 52, 233, 122);   // numeric IP for api.xively.com
//char server[] = "api.xively.com";   // name address for xively API

unsigned long lastConnectionTime = 0;          // last time you connected to the server, in milliseconds
boolean lastConnected = false;                 // state of the connection last time through the main loop
const unsigned int postingInterval = 15 * 1000; //delay between updates to Xively.com 10*1000ms=10 sek






// Define the sensor pins, and "start" ethernet shield

void setup() {
  // Specify which ports to use and for what
  for (byte ii = 0; ii < noSensors; ii++) {
    pinMode(echoPin[ii], INPUT);
    pinMode(trigPin[ii], OUTPUT);
    sensorReading[ii] = 1;

    // DEBUGGING:
    // Open serial communications and wait for port to open: Only for debugging to see when device is measureing
//    Serial.begin(9600);
         pinMode(13, OUTPUT); // Only for debugging to see when device is measureing
  }


  // Start ethernet service on the shield
  while (Ethernet.begin(mac) != 1) {
    //    Serial.println("Error getting IP address via DHCP, trying again...");
    delay(1500);
  }
}





//
// Brief	Loop
// Details  Measure distances constantly and upload to Xively server every "postingInterval".
//
void loop() {

  // make measurements and store in sensorReading[ii]
  for (byte ii = 0; ii < noSensors; ii++) {
    // Use floating average by only taking a 40th of the latest valut in to account.
    sensorReading[ii] = (sensorReading[ii] * 39 + readHCSR04(trigPin[ii], echoPin[ii])) / 40;
    
//    Serial.println();//sensorReading[ii]    

// GRAFFIC FEEDBACK FOR ALIGNING THE SENSOR:
//    for (byte jj = 0; jj * 2 < sensorReading[ii]; jj++) {
//      Serial.print(".");
//    }
    
  }



  // DEBUGGING:
  // if there's incoming data from the net connection.
  // send it out the serial port.
  while (client.available()) {
    char c = client.read(); // Do I need to empty the client?
//    Serial.print(c);
  }

  // if there's no net connection, but there was one last time
  // through the loop, then stop the client:
  if (!client.connected() && lastConnected) {

    // DEBUGGING:
//    Serial.println();
//    Serial.println("disconnecting.");



    client.stop();
  }


  // if you're not connected, and "postingInterval" have passed since
  // your last connection, then connect again and send data:
  if (!client.connected() && (millis() - lastConnectionTime > postingInterval)) {

    // the dataString will look like this "sensorName,sensorReading" then new line and repeat for each sensor
    String dataString;

    // store each sensor value in dataString
    for (byte ii = 0; ii < noSensors; ii++) {

      // DEBUGGING:
//      Serial.println(sensorReading[ii]);

      // the first value does not need a new line to start with, all others should have one
      if (ii > 0) {
        dataString += "\n";
      }

      dataString += sensorName[ii];
      dataString += ",";
      dataString += sensorReading[ii];
    }

    // DEBUGGING:
    // does the string look ok?
//    Serial.println(dataString);


    // note the time that the connection was made or attempted:
    lastConnectionTime = sendData(dataString);

  }


  // store the state of the connection for next time through
  // the loop:
  lastConnected = client.connected();

}

LocalLibrary.ino

int readHCSR04(int trigP, int echoP) { //get a distance measurement
// DEBUGGING:
digitalWrite(13, HIGH); // Only for debugging to see when device is measureing

  unsigned int distance = 65535; // Unsigned int range of 0 to 65,535
  
  while (distance > 400) { // if distance is larger than 400 cm try again...
    digitalWrite(trigP, LOW);
    delayMicroseconds(2);
    digitalWrite(trigP, HIGH); // Pulse for 10μs to trigger ultrasonic detection
    delayMicroseconds(10);
    digitalWrite(trigP, LOW);
    distance = pulseIn(echoP, HIGH)  / 29 / 2; // // Read receiver pulse time and Transform pulse time to distance (cm)
  }

// DEBUGGING:
  digitalWrite(13, LOW); // Only for debugging to see when device is measureing

  return distance;   //Ourput distance

}




// this  makes a HTTP connection and send thisData to the server:
unsigned long sendData(String thisData) {

  
  if (client.connect(server, 80)) { // if there's a successful connection upload the data:
// DEBUGGING:
//    Serial.println("connecting...");

    // send the HTTP PUT request:
    client.print("PUT /v2/feeds/");
    client.print(FEEDID);
    client.println(".csv HTTP/1.1");
    client.println("Host: api.xively.com");
    client.print("X-ApiKey: ");
    client.println(APIKEY);
    client.print("User-Agent: ");
    client.println(USERAGENT);
    client.print("Content-Length: ");
    client.println(thisData.length());

    // last pieces of the HTTP PUT request:
    client.println("Content-Type: text/csv");
    client.println("Connection: close");
    client.println();

    client.println(thisData);

  }


  else { // else try resetting ethernetShield
    // DEBUGGING:
        Serial.println("Connection Failed.");   
        Serial.println();
    //    
    resetCounter++;
    
    if (resetCounter >=5 ) {resetEthernetShield();}

    lastConnectionTime = millis(); 
  }

  lastConnectionTime = millis();
  return lastConnectionTime;
}


void resetEthernetShield(){
// DEBUGGING:  
//  Serial.println("Resetting Ethernet Shield.");   
//  Serial.println();
  
  client.stop();
  delay(1000);
  
  while (Ethernet.begin(mac) != 1) {
    //    Serial.println("Error getting IP address via DHCP, trying again...");
    delay(1500);
  }
}

Nice.

So the data now resides on xively and finally I have fixed so that every day a php-script on my website is run (thanks to cron-job.org) that checks the current status of the garbage containers and if it looks fishy it sends an email to our janitor. Will upload it here later...