Introduction: Range and Temperature Safety Sensor (EF 230 Project)

Here is a simple guide on how to create your own mini "safety system" using an Sparkfun ESP8266, that is designed to monitor safety distance from oncoming obstacles and temperature for a train's safety. This is just a project and cannot monitor a real train! This design uses an ESP8266, a HC-SRO4 Ultrasonic Sensor, a MPL3115A2 altitude sensor, and some LEDs.

Step 1: Parts and Materials

Materials required are:

(1x) Sparkfun ESP8266 Thing Dev

(1x) Breadboard

(1x) HC-SRO4 Ultrasonic Sensor

(1x) MPL3115A2 Altitude Sensor

(1x) Green LED

(1x) Yellow LED

(1x) Red LED

(3x) 330 ohm Resistors

(15x) Jumper wires

(15x) Female-Female Cables

Step 2: Setup

The circuit should be setup as follows:

(Every connection to the breadboard will use a Female-Female cable > Jumper Cable > Breadboard type connection.)

-Connect 5V Pin from ESP8266 to positive channel on breadboard.

-Connect GND Pin from ESP8266 to negative channel on breadboard.

HC-SR04:

-Connect VCC to positive channel on breadboard using only jumper cable.

-Connect GND to negative channel on breadboard using only jumper cable.

-Connect Trig Pin to Pin 5 on ESP8266.

-Connect Echo Pin to Pin 12 on ESP8266.

MPL3115A2:

-Connect VCC to 3.3V on ESP8266.

-Connect GND to GND on ESP8266.

-Connect SDA to Pin 2 on ESP8266.

-Connect SCL to Pin 14 on ESP8266.

LEDs: Connect each LED to the breadboard.

-Green LED long leg from breadboard to Pin 13 on ESP8266.

-Yellow LED long leg from breadboard to Pin 0 on ESP8266.

-Red LED long leg from breadboard to Pin 4 on ESP8266.

-All of the LEDs short legs should be connected to breadboard negative channel with 330 ohm resistor

Step 3: Assembly - Breadboard

Link the breadboard to the ESP8266 using jumper cables. Link the 5V pin from the ESP8266 to the positive channel of the breadboard and the GND pin to the negative channel. Use picture above for reference.

Step 4: Assembly - Ultrasonic Sensor

When connecting the HC-SRO4 ultrasonic sensor, a good place too put the ultrasonic sensor as far down breadboard as possible and make sure that it is facing out. Next, you should connect the GND pin on the ultrasonic sensor to the negative channel on the breadboard. Next connect the Trig pin on the sensor to pin 5 on the ESP8266 and connect the Echo pin on the sensor to pin 12 on the ESP8266. Lastly, connect the VCC pin on the ultrasonic sensor to the positive channel on the breadboard. Refer to picture above.

Step 5: Assembly - Altitude/Temperature Sensor

When connecting this sensor the most ideal place is on the opposite side from the Range Finder. First, connect VCC pin to 3.3V pin on the ESP8266 and GND pins on both should be linked together. Finally, connect SDA pin to Pin 2 on ESP8266 and the SCL pin to Pin 14 on. Again, use picture for help!

Step 6: Assembly - LEDs

Each LED should be connected to the breadboard, preferably on the closest side to the positive and negative channels you connected the ESP8266. Connect long legs to respective pins on ESP8266, in this case we used pins 13, 0 , and 4 for LEDs green, yellow, and red respectively. Lastly connect short legs to negative channel on breadboard using 330 ohm resistor. Use picture above as guide. Sorry for the bad cable management.

Step 7: Code

For this code you will need to have the Arduino software and MATLAB installed along with a Thingspeak.com channel setup for the data to be uploaded to. You will need to create a new channel on Thingspeak and then create three fields for Temperature, Altitude, and Range. This code will upload that data to Thingspeak to which the MATLAB code will download that data and display warning messages if thresholds are met. The Arduino code is as follows:

//MP3115 declarations
#include // for IIC communication

#define STATUS 0x00 #define OUT_P_MSB 0x01 #define OUT_P_CSB 0x02 #define OUT_P_LSB 0x03 #define OUT_T_MSB 0x04 #define OUT_T_LSB 0x05 #define DR_STATUS 0x06 #define OUT_P_DELTA_MSB 0x07 #define OUT_P_DELTA_CSB 0x08 #define OUT_P_DELTA_LSB 0x09 #define OUT_T_DELTA_MSB 0x0A #define OUT_T_DELTA_LSB 0x0B #define WHO_AM_I 0x0C #define F_STATUS 0x0D #define F_DATA 0x0E #define F_SETUP 0x0F #define TIME_DLY 0x10 #define SYSMOD 0x11 #define INT_SOURCE 0x12 #define PT_DATA_CFG 0x13 #define BAR_IN_MSB 0x14 #define BAR_IN_LSB 0x15 #define P_TGT_MSB 0x16 #define P_TGT_LSB 0x17 #define T_TGT 0x18 #define P_WND_MSB 0x19 #define P_WND_LSB 0x1A #define T_WND 0x1B #define P_MIN_MSB 0x1C #define P_MIN_CSB 0x1D #define P_MIN_LSB 0x1E #define T_MIN_MSB 0x1F #define T_MIN_LSB 0x20 #define P_MAX_MSB 0x21 #define P_MAX_CSB 0x22 #define P_MAX_LSB 0x23 #define T_MAX_MSB 0x24 #define T_MAX_LSB 0x25 #define CTRL_REG1 0x26 #define CTRL_REG2 0x27 #define CTRL_REG3 0x28 #define CTRL_REG4 0x29 #define CTRL_REG5 0x2A #define OFF_P 0x2B #define OFF_T 0x2C #define OFF_H 0x2D

#define MPL3115A2_ADDRESS 0x60 // 7-bit I2C address

long startTime; float temperature=15; float pressure=300; float altitude=300; //end of MP3115

// defines pins numbers Range Finder const int trigPin = 5; const int echoPin = 12; const int ledPinY = 0; const int ledPinR = 4; const int ledPinG =13; // defines variables Range Finder long duration; int distance; int safetyDistance;

#include

const char* ssid = "Network ssid"; const char* password = "Network password";

const char* host = "api.thingspeak.com"; const char* streamId = "Write API Code";//put your public key in the quotation marks const char* privateKey = "P4pBao280eiYndvgezY0";//put your private key in the quotation marks //below is an example feed //http://data.sparkfun.com/input/DJqz9MZQrjijQXKpDoj2?private_key=P4pBao280eiYndvgezY0&pot1=19.12

void setup() { pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output pinMode(echoPin, INPUT); // Sets the echoPin as an Input pinMode(ledPinY, OUTPUT); pinMode(ledPinG, OUTPUT); pinMode(ledPinR, OUTPUT);

Wire.begin(); // join i2c bus //Serial.begin(57600); // start serial for output

if (IIC_Read(WHO_AM_I) == 196) Serial.println("MPL3115A2 online!"); else Serial.println("No response - check connections");

// Configure the sensor setModeAltimeter(); // Measure altitude above sea level in meters //setModeBarometer(); // Measure pressure in Pascals from 20 to 110 kPa

setOversampleRate(7); // Set Oversample to the recommended 128 enableEventFlags(); // Enable all three pressure and temp event flags

Serial.begin(9600); delay(10);

// We start by connecting to a WiFi network

Serial.println(); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) { delay(100); Serial.print("."); }

Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); }

int value = 51;

void loop() {

// Clears the trigPin digitalWrite(trigPin, LOW); delayMicroseconds(2);

// Sets the trigPin on HIGH state for 10 micro seconds digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW);

// Reads the echoPin, returns the sound wave travel time in microseconds duration = pulseIn(echoPin, HIGH);

// Calculating the distance distance= duration*0.034/2;

safetyDistance = distance; if (safetyDistance <40){ digitalWrite(ledPinG, HIGH); } else{digitalWrite(ledPinG,LOW); } if (safetyDistance<25){ digitalWrite(ledPinY, HIGH); } else { digitalWrite(ledPinY, LOW); } if (safetyDistance<10){ digitalWrite(ledPinR, HIGH);} else {digitalWrite(ledPinR, LOW); } if (safetyDistance>40 || safetyDistance<=0){ Serial.println("Out Of Range");} else { Serial.print("Distance(cm): "); Serial.println(distance); } //++value;

Serial.print("connecting to "); Serial.println(host);

// Use WiFiClient class to create TCP connections WiFiClient client; const int httpPort = 80; if (!client.connect(host, httpPort)) { Serial.println("connection failed"); return; } https: //api.thingspeak.com/update?api_key=BIEQS4AI9H77ZWEK&field1=900&field2=1

// We now create a URI for the request String url = "/update?api_key="; url += streamId; url += "&field1="; url += temperature; url += "&field2="; url += altitude; url += "&field3="; url+= safetyDistance; //sample channel= http://data.sparkfun.com/input/DJqz9MZQrjijQXKpDoj2?private_key=P4pBao280eiYndvgezY0&pot1=19.12 //thingspeak= https://api.thingspeak.com/update?api_key=BIEQS4AI9H77ZWEK&field1=900&field2=1 Serial.print("Requesting URL: "); Serial.println(url);

// This will send the request to the server client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"); int timeout = millis() + 5000; while (client.available() == 0) { if (timeout - millis() < 0) { Serial.println(">>> Client Timeout !"); client.stop(); return; } }

// Read all the lines of the reply from server and print them to Serial while (client.available()) { String line = client.readStringUntil('\r'); Serial.print(line); } Serial.print("posted temp value= "); Serial.print(temperature); Serial.print("posted altitude value= "); Serial.print(altitude); Serial.println(); Serial.println("closing connection"); delay(10); //MP3115 startTime = millis();

altitude = readAltitude(); Serial.print("Altitude(m):"); Serial.print(altitude, 2);

altitude = readAltitudeFt(); Serial.print(" Altitude(ft):"); Serial.print(altitude, 2);

pressure = readPressure(); Serial.print(" Pressure(Pa):"); Serial.println(pressure, 2); /* float temperature = readTemp(); Serial.print(" Temp(c):"); Serial.print(temperature, 2); */ temperature = readTempF(); Serial.print(" Temp(f):"); Serial.print(temperature, 2);

Serial.print(" time diff:"); Serial.print(millis() - startTime);

Serial.println();

//delay(1); }

//MP3115 Functions

//Returns the number of meters above sea level float readAltitude() { toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading

//Wait for PDR bit, indicates we have new pressure data int counter = 0; while ( (IIC_Read(STATUS) & (1 << 1)) == 0) { if (++counter > 100) return (-999); //Error out delay(1); }

// Read pressure registers Wire.beginTransmission(MPL3115A2_ADDRESS); Wire.write(OUT_P_MSB); // Address of data to get Wire.endTransmission(false); // Send data to I2C dev with option for a repeated start. THIS IS NECESSARY and not supported before Arduino V1.0.1! Wire.requestFrom(MPL3115A2_ADDRESS, 3); // Request three bytes

//Wait for data to become available counter = 0; while (Wire.available() < 3) { if (counter++ > 100) return (-999); //Error out delay(1); }

byte msb, csb, lsb; msb = Wire.read(); csb = Wire.read(); lsb = Wire.read();

toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading

// The least significant bytes l_altitude and l_temp are 4-bit, // fractional values, so you must cast the calulation in (float), // shift the value over 4 spots to the right and divide by 16 (since // there are 16 values in 4-bits). float tempcsb = (lsb >> 4) / 16.0;

float altitude = (float)( (msb << 8) | csb) + tempcsb;

return (altitude); }

//Returns the number of feet above sea level float readAltitudeFt() { return (readAltitude() * 3.28084); }

//Reads the current pressure in Pa //Unit must be set in barometric pressure mode float readPressure() { toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading

//Wait for PDR bit, indicates we have new pressure data int counter = 0; while ( (IIC_Read(STATUS) & (1 << 2)) == 0) { if (++counter > 100) return (-999); //Error out delay(1); }

// Read pressure registers Wire.beginTransmission(MPL3115A2_ADDRESS); Wire.write(OUT_P_MSB); // Address of data to get Wire.endTransmission(false); // Send data to I2C dev with option for a repeated start. THIS IS NECESSARY and not supported before Arduino V1.0.1! Wire.requestFrom(MPL3115A2_ADDRESS, 3); // Request three bytes

//Wait for data to become available counter = 0; while (Wire.available() < 3) { if (counter++ > 100) return (-999); //Error out delay(1); }

byte msb, csb, lsb; msb = Wire.read(); csb = Wire.read(); lsb = Wire.read();

toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading

// Pressure comes back as a left shifted 20 bit number long pressure_whole = (long)msb << 16 | (long)csb << 8 | (long)lsb; pressure_whole >>= 6; //Pressure is an 18 bit number with 2 bits of decimal. Get rid of decimal portion.

lsb &= 0b00110000; //Bits 5/4 represent the fractional component lsb >>= 4; //Get it right aligned float pressure_decimal = (float)lsb / 4.0; //Turn it into fraction

float pressure = (float)pressure_whole + pressure_decimal;

return (pressure); }

float readTemp() { toggleOneShot(); //Toggle the OST bit causing the sensor to immediately take another reading

//Wait for TDR bit, indicates we have new temp data int counter = 0; while ( (IIC_Read(STATUS) & (1 << 1)) == 0) { if (++counter > 100) return (-999); //Error out delay(1); }

// Read temperature registers Wire.beginTransmission(MPL3115A2_ADDRESS); Wire.write(OUT_T_MSB); // Address of data to get Wire.endTransmission(false); // Send data to I2C dev with option for a repeated start. THIS IS NECESSARY and not supported before Arduino V1.0.1! Wire.requestFrom(MPL3115A2_ADDRESS, 2); // Request two bytes

//Wait for data to become available counter = 0; while (Wire.available() < 2) { if (++counter > 100) return (-999); //Error out delay(1); }

byte msb, lsb; msb = Wire.read(); lsb = Wire.read();

// The least significant bytes l_altitude and l_temp are 4-bit, // fractional values, so you must cast the calulation in (float), // shift the value over 4 spots to the right and divide by 16 (since // there are 16 values in 4-bits). float templsb = (lsb >> 4) / 16.0; //temp, fraction of a degree

float temperature = (float)(msb + templsb);

return (temperature); Serial.print("Temperature"); Serial.println(temperature); }

//Give me temperature in fahrenheit! float readTempF() { return ((readTemp() * 9.0) / 5.0 + 32.0); // Convert celsius to fahrenheit }

//Sets the mode to Barometer //CTRL_REG1, ALT bit void setModeBarometer() { byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings tempSetting &= ~(1 << 7); //Clear ALT bit IIC_Write(CTRL_REG1, tempSetting); }

//Sets the mode to Altimeter //CTRL_REG1, ALT bit void setModeAltimeter() { byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings tempSetting |= (1 << 7); //Set ALT bit IIC_Write(CTRL_REG1, tempSetting); }

//Puts the sensor in standby mode //This is needed so that we can modify the major control registers void setModeStandby() { byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings tempSetting &= ~(1 << 0); //Clear SBYB bit for Standby mode IIC_Write(CTRL_REG1, tempSetting); }

//Puts the sensor in active mode //This is needed so that we can modify the major control registers void setModeActive() { byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings tempSetting |= (1 << 0); //Set SBYB bit for Active mode IIC_Write(CTRL_REG1, tempSetting); }

//Setup FIFO mode to one of three modes. See page 26, table 31 //From user jr4284 void setFIFOMode(byte f_Mode) { if (f_Mode > 3) f_Mode = 3; // FIFO value cannot exceed 3. f_Mode <<= 6; // Shift FIFO byte left 6 to put it in bits 6, 7.

byte tempSetting = IIC_Read(F_SETUP); //Read current settings tempSetting &= ~(3 << 6); // clear bits 6, 7 tempSetting |= f_Mode; //Mask in new FIFO bits IIC_Write(F_SETUP, tempSetting); }

//Call with a rate from 0 to 7. See page 33 for table of ratios. //Sets the over sample rate. Datasheet calls for 128 but you can set it //from 1 to 128 samples. The higher the oversample rate the greater //the time between data samples. void setOversampleRate(byte sampleRate) { if (sampleRate > 7) sampleRate = 7; //OS cannot be larger than 0b.0111 sampleRate <<= 3; //Align it for the CTRL_REG1 register

byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings tempSetting &= 0b11000111; //Clear out old OS bits tempSetting |= sampleRate; //Mask in new OS bits IIC_Write(CTRL_REG1, tempSetting); }

//Clears then sets the OST bit which causes the sensor to immediately take another reading //Needed to sample faster than 1Hz void toggleOneShot(void) { byte tempSetting = IIC_Read(CTRL_REG1); //Read current settings tempSetting &= ~(1 << 1); //Clear OST bit IIC_Write(CTRL_REG1, tempSetting);

tempSetting = IIC_Read(CTRL_REG1); //Read current settings to be safe tempSetting |= (1 << 1); //Set OST bit IIC_Write(CTRL_REG1, tempSetting); }

//Enables the pressure and temp measurement event flags so that we can //test against them. This is recommended in datasheet during setup. void enableEventFlags() { IIC_Write(PT_DATA_CFG, 0x07); // Enable all three pressure and temp event flags }

// These are the two I2C functions in this sketch. byte IIC_Read(byte regAddr) { // This function reads one byte over IIC Wire.beginTransmission(MPL3115A2_ADDRESS); Wire.write(regAddr); // Address of CTRL_REG1 Wire.endTransmission(false); // Send data to I2C dev with option for a repeated start. THIS IS NECESSARY and not supported before Arduino V1.0.1! Wire.requestFrom(MPL3115A2_ADDRESS, 1); // Request the data... return Wire.read(); }

void IIC_Write(byte regAddr, byte value) { // This function writes one byto over IIC Wire.beginTransmission(MPL3115A2_ADDRESS); Wire.write(regAddr); Wire.write(value); Wire.endTransmission(true); }

The following code will take the data on Thingspeak and and put it into a MATLAB while loop which will then display warning messages when the "train" reaches unsafe conditions.

while true %Will take data from thingspeak.com that is being transmitted from the arduino board.
%Will display a warning box if the temp or range goes above the values. Temp = thingSpeakRead(426974,'Fields',[1]); if Temp>=75%degrees farenheight ; h=warndlg('TEMP WARNING') waitfor(h); end range=thingSpeakRead(426974,'Fields',[3]); if range<15 %Centimeters g=warndlg('WARNING TERRAIN') waitfor(g) end end