Introduction: Machine ⇒ I2C ⇒ GPRS Cellular Network ⇒ DIY Webpage

Machines have feelings. Sometimes they need to talk. They need to express themselves. Also, we need to make friends with them. Ok, currently they only have the intelligence of a cockroach, but this situation is rapidly improving. Very soon the machines may even become self aware and start crawling around surreptitiously in the back of our cupboards.

In the meantime, we humans can help them with their communication issues and, in my case, they will reward us with bottles of very nice tasting whiskey and hopefully not destroy us all when they eventually take control.

Here I am going to try to explain how we can get a machine that makes whiskey, or any other machine, to let us know that it is doing it's job properly and not getting confused or unhappy. Much of this is going to be done with computer code on what is known as I2C bus and I've written some special code to enable long, complex, numbers to be sent down the bus effectively.

We start off in the machine, with an Arduino Mega taking an array of sensor readings and chopping up the data into smaller chunks and spurting it out every minute or so down the I2C and an Adafruit Fona Feather being triggered at the other end to catch it again (Machine ⇒ Machine). The feather then reassembles the data and transmits it into cyber space via the cellular GPRS network (IoT).

In a small sleepy town called Ipswich, near London, United Kingdom, a computer 'server' picks up the information and processes it via two pieces of computer code called PHP and SQL to dump it in a database. Please note that there are no 'Clouds' ... or any other mysterious weather phenomena involved in this system .... just a big warehouse in Ipswich.

Once safely in the database, we can access the information any time we want and process it via the Interweb using PHP once more and get nice pretty custom made graphs and gauges using HTML and JavaScript on our personal screens. To summarise:

There's lots of places that we can go to on the Interweb that offer to remove the arduous task of creating our own databases (SQL and PHP) and make the whole process easier ..... But I want .... I need .... I must have ..... My own customised DIY website that I can tinker with to eternity. That's just me. I want full control over my precious data, I want to be able to delete little bits of it and generally manipulate it by adding new columns, select particularly juicy bits of data for processing and so on. I guess I'm a bit of a control freak?

You can find the end result (The webpage) here: TEMPERATURES

Step 1: The Database

Databases ARE interesting and ARE fun right? Ok ..... Maybe not to everybody, but they can be very useful.

First of all, this project requires a proper website with something called 'cPanel'. There's plenty of companies who offer cheap or even free websites, but do they offer cPanel and proper databases? Moreover, do they offer a decent customer support service when/if something goes wrong? Trust me, everything will go wrong if it possibly can! For a professional system, we'd also want to be able to make a back up of the database any time it's needed.

The company that I use is called 'United Hosting' and they have an excellent product and superb customer service.

For this project, I'm going to keep things really nice and simple and just monitor two separate temperatures, tempA and tempB.

It's quite easy and intuitive to set up the database in cPanel - just hit the 'new' button in phpMyAdmin and off you go, but be sure to make a note of the database name, the username you are using, the table name and the password for your username. Next, use the 'Structure' tab and add columns for 'Timestamp' and 'ID', then fiddle about with the options until you get the structure looking like the screenshot above. The ID column has to auto increment and the Timestamp must have 'CURRENT_TIMESTAMP' in the appropriate column. Create the appropriate columns for tempA and tempB and you're done.

So why would we ever want or need our own database? The answer is that by using code called SQL and PHP we can 'query' the database and organise the information in all kinds of interesting and exciting ways. We could analyse the last batch of whiskey data and get our website to display the maximum temperature that the boiler reached, which would then tell us something about the possible flavour profile and alcohol content. Basically, a higher boiler temperature indicates more 'heavy' flavour compounds .... and greater hangover potential! By monitoring and manipulating the 'reflux' temperature and boiler temperature data on a graph we can look at the way they interact and get a really good idea of what our whiskey is going to taste like.

Step 2: The Gadgets

To make things really, really simple, I stuck the components on a breadboard but substituted the Mega for a Nano for sheer convenience. The main objective was to get the C++ code working properly and enable data from the Machine (the Nano) to be disassembled, transmitted to the Fona Feather and reassembled for further GPRS transmission. The Feather is an tiny Arduino microprocessor with a cell phone module bolted on to it and can be programmed similar to any other Arduino and communication between the two Arduinos is by way of the I2C bus. To summarise:

  • The Machine (Nano) ⇒ I2C bus ⇒The GPRS transmitter (Feather)

The circuit itself is fairly simple although there's a couple of things to watch out for. Most importantly, the 'chip' version of the DS18B20 is wired differently to the probe with leads attached. The chip has both it's outer legs attached to earth and the probe has the red wire attached to positive. Get this wrong and you will fry the chips.

Getting the Feather to actually work, even with a simple 'Blink' program is a pain in the proverbial and all kinds of special drivers and board profiles need to be uploaded, which is all explained HERE. However, once it's up and running it seems to work well and handle the data ok.

Bill of Materials:

  1. ANT1 Antenna D1 Rectifier Diode package 300 mil [THT]; type Rectifier; part # 1N4001
  2. DS1 DS18B20 1-Wire Temperature Sensor part # DS18B20
  3. DS2 DS18B20 1-Wire Temperature Sensor Probe Cable
  4. LED1 Red (633nm) LED package 5 mm [THT]; color Red (633nm); leg yes
  5. LED2 Blue (470nm) LED package 5 mm [THT]; color Blue (470nm); leg yes
  6. Part1 Arduino Nano (Rev3.0) type Arduino Nano (3.0)
  7. Part3 Adafruit Feather 32u4 FONA variant variant 1; part # Adafruit #3027
  8. Power plug1 Power plug R4 4.7kΩ Resistor package THT; bands 4; resistance 4.7kΩ; tolerance ±5%; pin spacing 400 mil
  9. R5 10kΩ Resistor package THT; bands 4; resistance 10kΩ; tolerance ±5%; pin spacing 400 mil
  10. R6 10kΩ Resistor package THT; bands 4; resistance 10kΩ; tolerance ±5%; pin spacing 400 mil
  11. R7 10kΩ Resistor package THT; bands 4; resistance 10kΩ; tolerance ±5%; pin spacing 400 mil
  12. R8 1kΩ Resistor package THT; bands 4; resistance 1kΩ; tolerance ±5%; pin spacing 400 mil
  13. R9 1kΩ Resistor package THT; bands 4; resistance 1kΩ; tolerance ±5%; pin spacing 400 mil
  14. R10 1kΩ Resistor package THT; bands 4; resistance 1kΩ; tolerance ±5%; pin spacing 400 mil
  15. S1 Taiway 100-SP-X-T1 SPDT Miniature Toggle Switch variant - through hole solder lugs.; package THT; switching circuit SPDT; part # 100-SP-X-T1-0-0-B1-M1
  16. S2 Pushbutton package [THT]
  17. S4 Pushbutton package [THT]
  18. U1 LIPO-1000mAh variant 1000mAh; package lipo-1000

Step 3: Arduino Nano Code - Master

The code is divided into two sets, one for the Nano, which is the 'Master' and one for the Feather, which is the 'Slave'. Essentially, we're breaking down a fairly complex number, namely a 'Float', which could have a value of, for example 98.29. This number is too complex to shove down the I2C bus so it needs to be broken down into pieces with an integer value of anything less than 255 (and no decimal place). For convenience, I broke the float down into individual integer digits, so 98.29 would get transmitted as 9 ..... then 8 .... then 2 ... then 9.

The Nano uses string functions to calculate the length of the float number and remove the decimal place:

testAAAA = nothing + tempZ*100;
stringLengthAA = testAAAA.length();
testAAAA.remove((stringLengthAA-3),3); // Remove decimal place etc.

'nothing' is an 'iniator' to change an integer to a string. It wont work without it!

We process this string into individual characters using the variable digitCharAAAA[] and then spit out individual integers using the variable digitAAAA, which are then of a suitable size for transmission.

n=stringLengthAA-4;<br>      while (n>-1)                                       // Send the data back to front.
      {
      digitCharAAAA[n-1] = testAAAA.charAt(n);
      n--;
      digitAAAA = (digitCharAAAA[n]+nothing).toInt();
      Serial.print("Digit to send:  "); Serial.println(digitAAAA); 
      delay(20); 
      Wire.write(int (digitAAAA));                       // Values must not be greater than 255 (1 byte)?
      }

But ..... We need to first tell the Feather that it needs to take notice of the data appropriately so we have to put a marker, or ID, into the stream of data to enable it to reassemble it AND the data requires a certain amount of 'junk' to be send between the relevant bits. Starting to sound complicated? We also need to tell the Feather how long the data stream is (number of individual integers).

Wire.write(dataPrecursor);                          // Must send this junk data precursor first!<br>Wire.write(numberId);
Wire.write(stringLengthAA-3);                       // Length of the data stream

The ID and junk needs to be longer than one digit but not over 255, so for example the ID could be 101 and the junk 198.

To summarise:

  • Write, or transmit the junk and the ID first, then an integer representing the length of the data stream, then the individual digits of data themselves.

Here's the whole transmission function:

void sendData()<br>{
  //  We're going to turn testAAAA and testBBBB into shorter numbers to transmit:
  //  assume the number is less than 1,000 and we want to preserve the first two decimal places
    testAAAA = nothing + tempZ*100;
    stringLengthAA = testAAAA.length(); 
    testAAAA.remove((stringLengthAA-3),3); // Remove decimal place etc.
    Serial.print("testAAAA: ");Serial.println(testAAAA);
    Wire.write(dataPrecursor);                          // Must send this junk data precursor first!
    Wire.write(numberId);
    Serial.print("numberId is:   ");Serial.println(numberId);  
    Wire.write(stringLengthAA-3);
    n=stringLengthAA-4;
      while (n>-1)                                       // Send the data back to front.
      {
      digitCharAAAA[n-1] = testAAAA.charAt(n);
      n--;
      digitAAAA = (digitCharAAAA[n]+nothing).toInt();
      Serial.print("Digit to send:  "); Serial.println(digitAAAA); 
      delay(20); 
      Wire.write(int (digitAAAA));                       // Values must not be greater than 255 (1 byte)?
      }

Yes it's complicated!

NB. Don't be tempted to simplify the variable names as this causes memory handling errors.

And here's the Master code in it's entirety:

#include 
#include 
#include 
// Data wire is plugged into pin 10 on the Arduino
#define ONE_WIRE_BUS 10
#define TEMPERATURE_PRECISION 12
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress sensor0,sensor1;
int deviceCount =0;
char digitCharAAAA[10];
char digitCharBBBB[10];
float tempA=27.39;
float tempB=29.23;
float tempZ=20.01;
String testAA;
String nothing;            // This is important - the string precursor.
String testAAAA;          // File names need to be of sufficient complexity!
String testBBBB;
int stringLengthAA;
int numberId =101;
int dataPrecursor = 198;  // A randomish number below 254. Be careful it does not conflict with numberIds.
int n = 0;
int digitAAAA;

                    
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() 
{
  pinMode(13,OUTPUT);
  digitalWrite(13, HIGH);   
  delay(1000);                       
  digitalWrite(13, LOW);    
  Wire.begin(); 
  Serial.begin(9600);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  sensors.begin();
  // locate devices on the bus
//  Serial.print("Locating devices...");
//  Serial.print("Found ");
//  deviceCount = sensors.getDeviceCount(), DEC;
//  Serial.print(deviceCount);
//  Serial.println(" devices.");
  
  if (!sensors.getAddress(sensor0, 0)) Serial.println("Unable to find address for Device 0"); 
  if (!sensors.getAddress(sensor1, 1)) Serial.println("Unable to find address for Device 1"); 
  
  sensors.setResolution(sensor0, TEMPERATURE_PRECISION);
  sensors.setResolution(sensor1, TEMPERATURE_PRECISION);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() 
  {
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
   Serial.println("");
   sensors.requestTemperatures();  
   Serial.print("tempA: ");
   tempA =sensors.getTempCByIndex(0); // Change the index value accordingly.
   Serial.println(tempA); 
  
  // sensors.requestTemperatures();  
   Serial.print("tempB: ");
   tempB =sensors.getTempCByIndex(1); // Change the index value accordingly.
   Serial.println(tempB); 
   Serial.println("");  
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////    
   digitalWrite(13, HIGH);
   delay(5000);
   digitalWrite(13, LOW);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   Serial.println("Now we are doing a transmission ....... "); 
   Serial.println(""); 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
   Wire.beginTransmission(9); // transmit to device #9     // Make sure the order is the same in master and slave.
   delay(20);  
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
  // We're going to send two chunks of data, tempA and tempB. Each of them is less than 1,000 and has two digits after the 
  //decimal place.

   tempZ=tempA;
   numberId= 101;
   sendData();
  
   tempZ=tempB;
   numberId= 109;
   sendData();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   Wire.endTransmission();
   Serial.println("");
   Serial.println("Transmission ended ....... ");
   Serial.println("");
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   delay(120000);
}
void sendData()
{
  //  We're going to turn testAAAA and testBBBB into shorter numbers to transmit:
  //  assume the number is less than 1,000 and we want to preserve the first two decimal places
    testAAAA = nothing + tempZ*100;
    stringLengthAA = testAAAA.length(); 
    testAAAA.remove((stringLengthAA-3),3); // Remove decimal place etc.
    Serial.print("testAAAA: ");Serial.println(testAAAA);
    Wire.write(dataPrecursor);                          // Must send this junk data precursor first!
    Wire.write(numberId);
    Serial.print("numberId is:   ");Serial.println(numberId);  
    Wire.write(stringLengthAA-3);
    n=stringLengthAA-4;
      while (n>-1)                                       // Send the data back to front.
      {
      digitCharAAAA[n-1] = testAAAA.charAt(n);
      n--;
      digitAAAA = (digitCharAAAA[n]+nothing).toInt();
      Serial.print("Digit to send:  "); Serial.println(digitAAAA); 
      delay(20); 
      Wire.write(int (digitAAAA));                       // Values must not be greater than 255 (1 byte)?
      }
}      

Step 4: Aduino Feather Code - Slave

A lot of the 'Slave ' code is to do with sending the data to a PHP file called send.php and the crucial part is where a big long string called 'dataString' is created and then changed into a long character array which includes an 'initiator', the web address, the name for each temperature reading eg tempA and the data itself eg result101.

dataString =  initiator + webAddress + "tempA="+result101 + "&tempB="+result109  ;  // To add another temperature reading we would need: +"&tempB="+tempB 
  int n = dataString.length();  Serial.print("Data string to send:     ");Serial.println(dataString);   
  Serial.print("Size of string:  ");Serial.println(n);
  // Builds the url character:
      for (int aa=0;aa<=n;aa++)                                              
      {
          url[aa] = dataString[aa];
      }

This is then sent to the Fona module on the Feather inside the fonaSerial connection using:

if (!fona.HTTP_GET_start(url, &statuscode, (uint16_t *)&length)) {<br>          Serial.println("Failed!");
        }

The data coming into the Feather is read by this bit of code:

void receiveEvent(int bytes)<br>{
  delay(5000); // wait for data to be sent.
  a=10;
  while (a>0) // This looks for the correct numberId to iniate the next if command.
  {
  numberId = Wire.read(); 
  ledFlash();
//  Serial.print("Possible number Id:   ");Serial.println(numberId); 
  
  n=-1;
  compileA = nothing;
  testad = nothing;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
 //   Serial.println(""); 
    if (numberId==101)
    {
    unravelData();
    result101 = (rabbits+nothing).toInt()/100.00;
    }
    else if (numberId==109)
    {
    unravelData();
    result109 = (rabbits+nothing).toInt()/100.00; 
    Serial.print("Result 101:   ");Serial.println(result101,2);     
    Serial.print("Result 109:   ");Serial.println(result109,2); 
    Serial.println("");     
    }

It is then interpreted, or 'unravelled', in this function:

void unravelData()
{
     a=10;
    stringLength = Wire.read(); 
      while (n<stringlength) p="" n++;<=""      ="" string.="" a="" is="" this="" wire.read();="" +="" testad="nothing" compilea;="" compilea="testad" {=""></stringlength)>    //  Serial.print("Compile 109:  ");Serial.println(compileA); 
      }
    
  numberA = compileA;
//  Serial.print("NumberA:  ");Serial.println(numberA); 
//  Serial.print("String length:   ");Serial.println(stringLength); 
  
 //Builds the rabbits character:
      i=stringLength;
      for (int aa=0;aa<=i;aa++)                                              
      {
          rabbits[aa] = compileA[aa];
      }  
 //Now create a string in which the last 3 characters represent the ID. The string can have the last four characters removed before being processed at the next stage.
      resultForNumberId = nothing + rabbits +" & " + numberId;
   
 //   Serial.print("rabbits:  ");Serial.println(rabbits);  
 //   Serial.print("Result for number Id: ");Serial.print(numberId); Serial.print(" is: "); Serial.println(resultForNumberId );
    Serial.println("");  
}

Where 'rabbits' is a character array of the actual data. Eventually, rabbits gets turned into a complex float number using the famous toInt command. Notice the 'nothing' initiator - it wont work without it!

result101 = (rabbits+nothing).toInt()/100.00;

And here's the full Feather Slave code:

#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into pin 10 on the Arduino
#define ONE_WIRE_BUS 10
#define TEMPERATURE_PRECISION 12
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress sensor0,sensor1;
int deviceCount =0;
char digitCharAAAA[10];
char digitCharBBBB[10];
float tempA=27.39;
float tempB=29.23;
float tempZ=20.01;
String testAA;
String nothing;            // This is important - the string precursor.
String testAAAA;          // File names need to be of sufficient complexity!
String testBBBB;
int stringLengthAA;
int numberId =101;
int dataPrecursor = 198;  // A randomish number below 254. Be careful it does not conflict with numberIds.
int n = 0;
int digitAAAA;

                    
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() 
{
  pinMode(13,OUTPUT);
  digitalWrite(13, HIGH);   
  delay(1000);                       
  digitalWrite(13, LOW);    
  Wire.begin(); 
  Serial.begin(9600);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  sensors.begin();
  // locate devices on the bus
//  Serial.print("Locating devices...");
//  Serial.print("Found ");
//  deviceCount = sensors.getDeviceCount(), DEC;
//  Serial.print(deviceCount);
//  Serial.println(" devices.");
  
  if (!sensors.getAddress(sensor0, 0)) Serial.println("Unable to find address for Device 0"); 
  if (!sensors.getAddress(sensor1, 1)) Serial.println("Unable to find address for Device 1"); 
  
  sensors.setResolution(sensor0, TEMPERATURE_PRECISION);
  sensors.setResolution(sensor1, TEMPERATURE_PRECISION);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() 
  {
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
   Serial.println("");
   sensors.requestTemperatures();  
   Serial.print("tempA: ");
   tempA =sensors.getTempCByIndex(0); // Change the index value accordingly.
   Serial.println(tempA); 
  
  // sensors.requestTemperatures();  
   Serial.print("tempB: ");
   tempB =sensors.getTempCByIndex(1); // Change the index value accordingly.
   Serial.println(tempB); 
   Serial.println("");  
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////    
   digitalWrite(13, HIGH);
   delay(5000);
   digitalWrite(13, LOW);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   Serial.println("Now we are doing a transmission ....... "); 
   Serial.println(""); 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
   Wire.beginTransmission(9); // transmit to device #9     // Make sure the order is the same in master and slave.
   delay(20);  
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
  // We're going to send two chunks of data, tempA and tempB. Each of them is less than 1,000 and has two digits after the 
  //decimal place.

   tempZ=tempA;
   numberId= 101;
   sendData();
  
   tempZ=tempB;
   numberId= 109;
   sendData();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   Wire.endTransmission();
   Serial.println("");
   Serial.println("Transmission ended ....... ");
   Serial.println("");
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   delay(120000);
}
void sendData()
{
  //  We're going to turn testAAAA and testBBBB into shorter numbers to transmit:
  //  assume the number is less than 1,000 and we want to preserve the first two decimal places
    testAAAA = nothing + tempZ*100;
    stringLengthAA = testAAAA.length(); 
    testAAAA.remove((stringLengthAA-3),3); // Remove decimal place etc.
    Serial.print("testAAAA: ");Serial.println(testAAAA);
    Wire.write(dataPrecursor);                          // Must send this junk data precursor first!
    Wire.write(numberId);
    Serial.print("numberId is:   ");Serial.println(numberId);  
    Wire.write(stringLengthAA-3);
    n=stringLengthAA-4;
      while (n>-1)                                       // Send the data back to front.
      {
      digitCharAAAA[n-1] = testAAAA.charAt(n);
      n--;
      digitAAAA = (digitCharAAAA[n]+nothing).toInt();
      Serial.print("Digit to send:  "); Serial.println(digitAAAA); 
      delay(20); 
      Wire.write(int (digitAAAA));                       // Values must not be greater than 255 (1 byte)?
      }
}      


Step 5: Database Code

So now that we've got the breadboard layout working, we need to program the server in Ipswich to accept the data into a database and then make it available on the Interweb for everybody to see. Thankfully, this code is a million times more simple than the Arduino Master and Slave code so relax .... The end is in sight!

Create a file called 'send.php' and paste the following code into it:

Can you spot the snippets of SQL code in the below?

<?php
// Create connection
$username = "paddygoa_test";
$password = "testing";
$hostname = "localhost"; 
$databasetable = "testing";

$dbhandle = mysql_connect($hostname, $username, $password) 
or die("Unable to connect to MySQL");

$selected = mysql_select_db("paddygoa_test",$dbhandle) 
or die("Could not select database");

$result = mysql_query("INSERT INTO $databasetable(tempA,tempB) VALUES (
'" . $_GET[tempA] . "',
'" . $_GET[tempB] . "')",
$dbhandle);

/////////////////////////////////////////////////////////////////////////////////

echo "<html>";
echo "<head><Title> ... Success! ... </title>";
echo "</head>";
echo "<body>";
echo "</body>";
echo "</html>";
mysql_close($dbhandle);
?>

Feel free to use my 'test' database. Now the data should get into a database called 'paddygoa_test' and into a table located therein called 'testing'. To create your own database, go back to step 2.

Attachments

Step 6: Webpage Code

Finally, we get to build a webpage., which is a glorious combination of HTML, PHP and JavaScript - our crowning glory! Notice that there are links to some other files including dataTemperature04.php, which creates something called a 'json' output.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>

  <head>
  
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta http-equiv="Cache-control" content="no-cache">
  <meta http-equiv='refresh' content='600';url='temperatures.html'> <!-- Refreshes page every 600 seconds -->
  <META NAME="description" CONTENT="GPRS temperature transmitter">
  <META  HTTP-EQUIV="EXPIRES"  CONTENT="Mon, 31 Dec 2002 23:59:59 GMT">  
  <META NAME="keywords" CONTENT="GPRS temperature transmitter">
  <link REL="SHORTCUT ICON" HREF="goat.ico">  
  </head>
  
    <title>Temperatures</title>
    <script src="http://www.amcharts.com/lib/3/amcharts.js"></script>
    <script src="http://www.amcharts.com/lib/3/serial.js"></script>
    <script src="http://www.amcharts.com/lib/3/plugins/dataloader/dataloader.min.js"></script>

    <script>
    myCallback();    
    var intervalID = window.setInterval(myCallback, 120000);
    function myCallback() {    
    
    
    var chart = AmCharts.makeChart("chartdivPressure", {
     "type": "serial",
      "dataLoader": {
        "url": "dataTemperature04.php",
        "timestamp": true,
   //     "complete": function(chart) { chart.invalidateSize();}
        "format": "json",
        "showErrors": true,
        "noStyles": true,
        "async": true,
        "load": function( options, chart ) {
          // Here the data is already loaded and set to the chart.
          // We can iterate through it and add proper graphs
          for ( var key in chart.dataProvider[ 0 ] ) {
            if ( chart.dataProvider[ 0 ].hasOwnProperty( key ) && key != chart.categoryField ) {
              var graph = new AmCharts.AmGraph();
              graph.valueField = key;
              graph.type = "line";
              graph.title = key,
              graph.lineThickness = 2;
              chart.addGraph( graph );
            }
          }
        }
      },
     "rotate": false,
     "marginTop": 10,
	 
     "categoryField": "category",
     "categoryAxis": {
       "gridAlpha": 0.07,
       "axisColor": "#DADADA",
	   "labelRotation": "90",
       "startOnAxis": false,
       "title": "Date and Time",
     },
	 
     "valueAxes": [{
       "stackable": "false",
       "gridAlpha": 0.07,
       "title": "Temperatures"            
     }],
	 
     "graphs": [],
     "legend": {
       "position": "bottom",
       "valueText": "[[value]]",
       "valueWidth": 100,
       "valueAlign": "left",
       "equalWidths": false,
     },
     "chartCursor": {
       "cursorAlpha": 0
     },
     "chartScrollbar": {
       "color": "FFFFFF"
     }

    });
/////////////////////////////////////////////////////////////////////////////////
 }
/////////////////////////////////////////////////////////////////////////////////      
    </script>
<style type="text/css">
<!--

#MenuLayer {
	position:absolute;
	width:350px;
	height:60px;
	z-index:5;
        left: 400px;
	top: 550px;
}
.style1 {
	font-size: 20px;
	font-weight: bold;
}
</style>    
  </head>
<style type="text/css">
  ul {list-style: none;padding: 0px;margin: 0px;}
  ul li {display: block;position: relative;float: left;border:1px solid #000}
  li ul {display: none;}
  ul li a {display: block;background: #FFA;padding: 5px 10px 5px 10px;text-decoration: none;
           white-space: nowrap;color: #000;}
  ul li a:hover {background: #f0f;}
  li:hover ul {display: block; position: absolute;}
  li:hover li {float: none;}
  li:hover a {background: #47c;}
  li:hover li a:hover {background: #4d2;}
  #drop-nav li ul li {border-top: 0px;}
</style>  

  <body>
<div id="MenuLayer"><ul id="drop-nav">
  <li><a href="http://www.goatindustries.co.uk/weather/">Home</a>
    <ul>
      <li><a href="http://www.goatindustries.co.uk/weather/">Home</a></li>
    </ul>
  </li>
    <li><a href="#">Themes</a>
    <ul>
      <li><a href="http://www.goatindustries.co.uk/weather/chrome.php">Chrome</a></li>
      <li><a href="http://www.goatindustries.co.uk/weather/index.php">Plain</a></li>
      <li><a href="#">Other</a></li>
    </ul>
  </li>
  <li><a href="#">Graphs</a>
    <ul>
      <li><a href="http://www.goatindustries.co.uk/weather/plain.html">Rain and Pressure</a></li>
      <li><a href="http://www.goatindustries.co.uk/weather/temperatures.html">Temperatures</a></li>
      <li><a href="#">Wind Speed</a></li>
      <li><a href="#">Wind Direction</a></li>
      <li><a href="#">Humidity</a></li>
      <li><a href="http://www.goatindustries.co.uk/weather/pressure.html">Pressure</a></li>
      <li><a href="http://www.goatindustries.co.uk/weather/rainfall.html">Rainfall</a></li>
      <li><a href="#">Volts</a></li>	  	  	  
    </ul>
  </li>
  <li><a href="http://www.goatindustries.co.uk/">Contact</a>
    <ul>
      <li><a href="http://www.goatindustries.co.uk/">General Inquiries</a></li>
    </ul>
  </li>
</ul></div>  
     	
     	<div id="chartdivPressure" style="position: absolute; left: 0px; top: 0px; width:1000px; height:590px; z-index: 3"></div>
  </body>

</html>

dataTemperature04.php:

<?php
// we need this so that PHP does not complain about deprectated functions
error_reporting( 0 );

// Connect to MySQL
$link = mysql_connect( 'localhost', 'paddygoa_test', 'testing' );
if ( !$link ) {
  die( 'Could not connect: ' . mysql_error() );
}

// Select the data base
$db = mysql_select_db( 'paddygoa_test', $link );
if ( !$db ) {
  die ( 'Error selecting database \'test\' : ' . mysql_error() );
}
  

// Fetch the data
$query17 = "
  SELECT *
  FROM testing
  ORDER BY id DESC LIMIT 1008";
$result17 = mysql_query( $query17 );

// All good?
if ( !$result17 ) {
  // Nope
  $message  = 'Invalid query: ' . mysql_error() . "\n";
  $message .= 'Whole query: ' . $query17;
  die( $message );
}
// Print out rows

$prefix = '';
echo "[\n";
while ( $row17 = mysql_fetch_assoc( $result17 ) ) {

// Parse timestamp:  
      $timestamp = $row17['time'];
      $epochtime = strtotime($timestamp);   
      $dayhourminutes = date("D H:i", $epochtime);


  echo $prefix . " {\n";
  echo '  "category": "' . $dayhourminutes . '",' . "\n";
  echo '  "TempA": "' . $row17['tempA'] . '",' . "\n";  
  echo '  "TempB": ' . $row17['tempB'] . '' . "\n";
  echo " }";
  $prefix = ",\n";
}
echo "\n]";



// Close the connection
mysql_close( $link );
?>

These files need to go in the correct folders on your website or they wont link up.

Step 7: The Hardware - a Nestbox for the Feather

Now we take the Feather off the breadboard and bolt it into a black box with some connectors, some switches and loads of LEDs to flash on and off as wildly as possible.

Parts:

  1. 8771113 1 M12 Panel mount Female 5W 200 mm, PG 9
  2. 487832 1 Panel mount power socket,2.1mm 5A 12V
  3. 8771141 1 M12 Field Connector male, 5W,PG 7
  4. 9131724 1 switch Push button 12mm blue momentary
  5. 7064060 1 Switch p/button,DPST green,220V,10A
  6. 7064067 1 Switch p/button,DPST red,220V,10A
  7. 7743341 10 Diode Schottky 20V 3A DO201AD
  8. Max001 waterproof box

Step 8: Final

Above is the fully operation automated whiskey still with 4 temperature sensors, one pressure sensor and one alcohol vapour detector.

If you've got 20 minutes to spare, you'll get a free tour of the distillery getting to see inside the control panels and be generally dazzled and bemused by a vast array of flashing lights, noisy pumps, beeping noises and robot voices.

Please in the competitions - top right - Thanks!