Author Archives: Eirik

Internet Of Things – Remote Temperature Sensor Project

thingsspeak

Last fall, I ordered the brand new < $5 (now < $3) ESP8266 WiFi microcontroller. Some months later, I finally had the time to play around with this cheap and yet powerful device. It can be used in several ways:

As a standard “Wifi modem”, accepting AT commands over a serial port, typically in conjunction with a regular microcontroller / computer system. Compared with my old Wifi modules, the price and size is much smaller. This usage is pretty standard, and you can upload new firmware versions to improve the features of the device.

With modified firmware, running as an LUA interpreter. This way, one can create LUA programs that will run on the Device, with no need for an additional microcontroller.

As a “regular” microcontroller / development board, where you use an IDE and download code to be run on the ESP8266. There is on Github a great project: Arduino-compatible IDE with ESP8266 support.  I can now use my Arduino IDE and code the ESP8266 pretty much like I would any other Arduino compatible board.  Many libraries will work directly, but of course not those that directly manipulate low level registers etc.

Doing som IOT stuff !

I decided to create a real “Internet of Things” Device, namely a remote temperature sensor. In addition to creating something useful with the EDP8266, I wanted to try out some of the services/IOT plattforms that are available. After some research, I ended up using ThingSpeak. Their free solution accepts unlimited amounts of data, and only limited to an upload frequency of max. 1 new entry every 15 seconds. They have a well documented API, with code examples for several plattforms, as well as a components  for graphing etc. They also have tools for creating automatic actions to received data: For example “Temperature sensor warmer than -18c, send Twitter message”. The great thing with using an IOT Infrastructure like Thingspeak, is that you can make the code on the sensor side simple, and on the “backend” side capitalize on the IOT vendors tools. Then, one can use standard web design mechanisms for end-user “front-end” interface etc.

The Remote temperature sensor

The concept of the Remote Sensor is a wifi-device with one or more temperature sensors.  One should be able to find current temperature and a log of temperature changes, with alerts etc. In this version I have 2 sensors, one inside my project box, and one external sensor on a 1 m. cable. This sensor can then conveniently be placed outdoors through window/ventilation opening, inside the freezed etc.

The system measures each sensor every 15 Seconds, and uploads the value to ThingSpeak if the temperature has changed more than a threshold, for example 0,2c since last upload. If completely stable temperatures, I regardless uploads a value every hour to show that the sensor and connection is alive.

The finished “Product” is quite small, and with an open box looks like the folllowing:

tempssensoropensm

The key is shown to illustrate the small size. Components used:

Component
Approx price
Where to buy
ESP8266 wifi microcontroller$3Multiple on eBay
25W 3-15V to 0.5-30V Auto DC-DC Boost Buck Converter $5,5Multiple on eBay. Note: This Board is a little "overkill" for this Project, a more simple step-Down to 3.3V converter would work fine
DS18B20 sensor (in "transistor" packaging)$0,7Multiple on e-bay etc.
1m Waterproof Digital Thermal Probe Sensor DS18B20$2Sensor in waterproof enclosure, 1 meter cable. Multiple vendors on e-bay.
Mounting Board$1 - $3I like the Adafruit Perma-Proto Quarter-sized Breadboard PCB.
Resistor, 4,7 K Ohm 0,25 W$0,05Any standard resistor will do
Cabinet$1-10
On/Off Switch$0,7
2,1 mm Power Jack$ 0,7

ESP8266 pinout / installation

The ESP8266 must have 3.3 V for both I/O and Power Supply. To program the device, I used the Adafruit FTDI friend, where one can set these by solder jumpers. The ESP8266 is programmed by having the GPIO-0 grounded. For normal use, GPIO-0 has no Connection.

esp8266pinout

Note: There are many versions of boards using the ESP8266. This is a popular, low cost version. There are other versions that exposes more GPIO than this one. For my purposes, one port for the temperature sensors was fine.

Connections:

  • RX goes to TX on the FTDI
  • TX goes to RX on the FTDI
  • GPIO-0 to ground when Programming, no Connection when normal use
  • GND – GND
  • VCC – 3.3V power source (up to 0,3 A !)
  • GPIO-2 is Connected to the sensors, With a 4,7K pull-up resistor connected to VCC.

The temperature sensors I used was the DS18B20 (Maxim / Dallas): Datasheet. It uses a one-wire connection and supports wide range of I/O and VCC. Low cost and widely supported in Libraries.

Connection is VCC –  3.3V, Signal to GPIO-2 and GND-GND.

Code:

/*
 * Read Temperature with Dallas DS18B20, connect to Wifi and send temperature to Thingsspeak infrastrucure
 * An "is alive" temperature reading is sent every hour, else temp changes is sent on every temp change of more than 0,3C
 *
 * This code is in the public domain, without any warranties or guarantees of any sort.
 *
 * The code contains serial port debugging information / status messages - and can be removed for "production" systes. 
 */

#include <ESP8266WiFi.h>
#include <OneWire.h>
#include <DallasTemperature.h>

const char* ssid     = "ssid";            // The  ssid  for your wifi network
const char* password = "wifipassword";    // The  password for your wifi network
String THINGSPEAK_KEY = "ThingSpeakKey";  // The key from ThingSpeak for you channel

#define MINWAIT 15000    // Number of millis minimum before possible updates to ThingSpeak = 15 SEC
#define SHOWLIFE 60      // Nubmer of minutes between sending "proof of life" signal, even with no temp changes   
#define CHANGEDIFF 0.3   // How much temp change should create an update

byte server[]  = { 184, 106, 153, 149 }; // ThingSpeak IP Address: 184.106.153.149

WiFiClient client;

#define ONE_WIRE_BUS 2   // Data wire is plugged into pin 2 on the Arduino

OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance 
DallasTemperature sensors(&amp;oneWire); // Pass our oneWire reference to Dallas Temperature. 

DeviceAddress insideThermometer;
DeviceAddress outsideThermometer;

boolean outsideSensorOK = false;
boolean insideSensorOK = false;

float tempC, LastOutsideTemp, LastInsideTemp = 0;
long lastupdate;

void setup() {

  Serial.begin(115200);

  // Connect to WiFi

   Serial.println();
   Serial.print("Connecting to ");
   Serial.println(ssid);
  
   WiFi.begin(ssid, password);
  
   while (WiFi.status() != WL_CONNECTED) {
       delay(500);
       Serial.print(".");
   }

   Serial.println("WiFi connected");  
   printWifiStatus();
   
   sensors.begin(); 
   
   Serial.print("Found ");
   Serial.print(sensors.getDeviceCount(), DEC);
   Serial.println(" devices.");

   // report parasite power requirements
   Serial.print("Parasite power is: "); 
   if (sensors.isParasitePowerMode()) Serial.println("ON");
      else Serial.println("OFF");
  
   if (!sensors.getAddress(insideThermometer, 0)) {
       Serial.println("Unable to find inside Thermometer"); 
   } else{
       insideSensorOK = true;
       Serial.print("Inside  Thermometer address: ");
       printAddress(insideThermometer);
       Serial.println();
       sensors.setResolution(insideThermometer, 12); 
       LastInsideTemp = sensors.getTempC(insideThermometer);   

   }
   
   if (!sensors.getAddress(outsideThermometer, 1)) {
       Serial.println("Unable to find outside Thermometer"); 
   } else{
       outsideSensorOK = true;
       Serial.print("Outside  Thermometer address: ");
       printAddress(outsideThermometer);
       Serial.println();
       sensors.setResolution(outsideThermometer, 12);    
       LastOutsideTemp = sensors.getTempC(outsideThermometer);   
   }
    
} // setup


void loop() {

   // call sensors.requestTemperatures() to issue a global temperature 
   // request to all devices on the bus

   sensors.requestTemperatures(); // Send the command to get temperatures

   if (outsideSensorOK == true) {
       tempC = sensors.getTempC(outsideThermometer);   
       Serial.print("Outside Temp C: ");
       Serial.print(tempC);
       Serial.print(" Temp F: ");
       Serial.print(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
       Serial.println("");    
       if ( abs(tempC - LastOutsideTemp) > CHANGEDIFF ) {        
                  Serial.print("Outdoor Temp Change larger than threshold - sending to ThingSpeak");
                  lastupdate = millis();
                  LastOutsideTemp = tempC;
                  ToThingsspeak(false, tempC);                  
       } else if (abs(millis()-lastupdate) > (SHOWLIFE * 60 * 1000) ) {
                  Serial.print("Need to send keep alive signal to ThingSpeak");
                  lastupdate = millis();
                  ToThingsspeak(false, tempC);                                    
       } 
   }
   
   delay(MINWAIT);
   sensors.requestTemperatures(); // Send the command to get temperatures

   if (insideSensorOK == true) {
       tempC = sensors.getTempC(insideThermometer);   
       Serial.print(" Inside Temp C: ");
       Serial.print(tempC);
       Serial.print(" Temp F: ");
       Serial.print(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
       Serial.println("");    
       if ( abs(tempC - LastInsideTemp) > CHANGEDIFF ) {        
                  Serial.print("Indoor Temp Change larger than threshold - sending to ThingSpeak");
                  lastupdate = millis();
                  LastInsideTemp = tempC;
                  ToThingsspeak(true, tempC);                  
       } else if (abs(millis()-lastupdate) > (SHOWLIFE * 60 * 1000) ) {
                  Serial.print("Need to send keep alive signal to ThingSpeak");
                  lastupdate = millis();
                  ToThingsspeak(true, tempC);                                    
       } 
   }
       
    delay(MINWAIT);     
   
} // loop

// --------------------------------------------------------------------------------------------------------------

void ToThingsspeak(boolean InsideSensor, float TheTempC)
{

    String addStuff = "field";
    if (InsideSensor == true) addStuff+= "1="; else addStuff += "2="; 
    addStuff+= String(int(TheTempC))+ "." + String(getDecimal(TheTempC)); 
    
    Serial.print("The data into UpdateThingsSpeak: "); 
    Serial.println(addStuff);   
    
    updateThingSpeak(addStuff);  
 }  // ToThingsspeak
 
 
void updateThingSpeak(String tsData) 
{ 
   if (client.connect(server, 80)) 
   {          
      client.print("POST /update HTTP/1.1\n"); 
      client.print("Host: api.thingspeak.com\n"); 
      client.print("Connection: close\n"); 
      client.print("X-THINGSPEAKAPIKEY: "+THINGSPEAK_KEY+"\n"); 
      client.print("Content-Type: application/x-www-form-urlencoded\n"); 
      client.print("Content-Length: "); 
      client.print(tsData.length()); 
      client.print("\n\n"); 
      client.print(tsData); 
         
      if (!client.connected()) 
      Serial.println("Connection to ThingSpeak failed after POST");    
      
    } else 
    {      
      Serial.println("Connection to ThingSpeak Failed before POST");    
    } 
    
} //  updateThingSpeak
      
long getDecimal(float val)
{
 int intPart = int(val);
 long decPart = 100*(val-intPart); //I am multiplying by 100 assuming that the foat values will have a maximum of 2 decimal places
 if(decPart>0)return(decPart);           //return the decimal part of float number if it is available 
 else if(decPart<0)return((-1)*decPart); //if negative, multiply by -1
 else if(decPart=0)return(00);           //return 0 if decimal part of float number is not available
}

// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}

void printWifiStatus() {
   // print the SSID of the network you're attached to:
   Serial.print("SSID: ");
   Serial.println(WiFi.SSID());

   // print your WiFi shield's IP address:
   IPAddress ip = WiFi.localIP();
   Serial.print("IP Address: ");
   Serial.println(ip);

   // print the received signal strength:
   long rssi = WiFi.RSSI();
   Serial.print("signal strength (RSSI):");
   Serial.print(rssi);
   Serial.println(" dBm");
}


Note that a lot of the above code can be deleted for “production” purposes.  All the logging to Serial ports is to show some of the features of the libraries.

Seeing it in action. This is the ecternal sensor, published as an Public Channel on ThingSpeak.

So, this is how warm/cold it is in Oslo right now !

I have ordered some relays and more sensors: I will explore more in this area in the future, With more posts on this blog as I progress..

 

 

 

 

 

 

 

 

IR beam barrier receiver: Code with interrupt handler for Arduino

This post contains code and information on basic circuitry used to detect a break in an IT barrier solution using Ardiuno and Interrupts.

Parts:

The resistor and capacitor is according to the Datasheet optional to improve on electrical overstress. The connection is described in the datasheet.

The TSOP4038 can handle an continuous 38 KHz IR signal, while other low cost IR receivers seem to get “overloaded” with too long IR signals received. Also, I like the packaging: compact and “mounting friendly”, with only a small pinhole needed for the IR receiver “eye”. The TSOP4038 can be gotten in Norway from the large distributors in small quantities for less than USD 0,7.

The TSOP4038 signal is connected to Arduino Pin 2. The reason for the usage of this pin, is that it is one of two ports on the regular Arduino that can support interrupt triggering. The code could of course be made without the use of interrupts, for example by reading the pin that the IR reiceiver is connected to on a regular basis. This is probably OK for applications such as alarms and surveillance, where delays of some tenths of seconds to not matter. In my race timing application, I must have high precision on when exactly the beam was broken. To achieve this I must use Interrupts.

In my current sample code I trigger an LED to turn off whenever the IR beam has been broken for more the minimum time. In all IR beam barrier solutions, you want the duty cycle of the IR senders to be less than 50% in order to be able to boost up the IR signals. See my other post on creating the IR sender part. My code must therefore be able to handle several “down” IR packets before it can confirm that the beam was actually broken when the first packet disappeared. So, how precise is the break detection? Assuming a 25% duty cycle, where there will be IR in one out of 4 packets, the maximum time before a break is detected is 0,0001056 seconds. Since this is a tenth of one thousand of a second, and we in Alpine racing only are concerned with a hundred of a second precision, we are in good shape since we are about 100x the needed precision.

#define PIN_DETECT 2 // This is where the IR detector "out" signal is connected to
#define PIN_STATUS 13 // Used to indicate a break in the barrier, could be a buzzer or something else

volatile boolean irbeamup = false; // This value is changed by the Interrupt code 
                                   // and is used both inside and outside the Interrupt code = volatile 
long lastuptime = 0;               // Indicates how many milliseconds since we last had a beam detected

void irchanged()
// Interrupt Service Routine for detecting change in the IR reading
// This is connected to Pin 2 on the Arduino
// If there is IR, the value is low, if there is no IR, the value is high
// We want to execute and exit the code as quick as possible, so we only read the value to set the
// boolean irbeamup flag

{
   if (digitalRead(PIN_DETECT) == LOW) irbeamup = true;
   else irbeamup = false;
}

void setup()
{
    pinMode(PIN_DETECT, INPUT);             // This is where we read the IR detector
    pinMode(PIN_STATUS, OUTPUT);            // To show some status
    attachInterrupt(0, irchanged, CHANGE);  // Here the magic is happening: On a CHANGE on Port 0 (Pin 2), 
                                            // the irchanged function will interrupt whatever is going on
}

void loop() {
   if (irbeamup == true) {                     // We have a beam 
        lastuptime = millis();                 // So set the current time to lastuptime
        digitalWrite(PIN_STATUS, HIGH);        // Light the LED, we have a beam
   }
   else {                                      // A break in the beam was detected
                                               // However, if less than 100 mS since last IR was detected,
                                               // I do not yet indicate a break
        if (millis() - lastuptime > 100) {
                  digitalWrite(PIN_STATUS, LOW);
                  // Other actions for the break. We know we last had a beam at lastuptime.
        }
   }
}

First Post

This is my first post in my new personal blog Stafferier.com.  I am using WordPress under Linux,  hosted by Webhostinghub. I will refine the site with better design and graphics later. I have just installed some basic plugins + multilingual support.