Here’s firmware I wrote for an ESP8266, specifically the ESP01. It works with the TMP102 LM75A and AM2320 sensors which you can easily flip between using a #define. It samples the temp/humidity at regular intervals and then averages them every few minutes. The result is posted to HA through MQTT. The MAC address is used in the sensor topic to make the topic name unique. This way I can install the same firmware on all my devices without changing it. It also has a webpage showing the sample history and settings. There are more configuration options including interval times, temp adjustment, etc.
#include <ESP8266WiFi.h>
#include <Wire.h>
#include <PubSubClient.h>
#include <list>
//MQTT settings
#define mqtt_server "192.168.1.1"
#define mqtt_user "USERNAME"
#define mqtt_password "PASSWORD"
#define mqtt_temp_topic "sensor/temp"
#define mqtt_humid_topic "sensor/humid"
#define mqtt_postInterval 2 //Minutes
//WiFi Settings
#define WiFiSSID "SSID"
#define WiFiPSK "PASSWORD"
WiFiClient espClient; //For the MQTT client
PubSubClient client(espClient); //MQTT client
WiFiServer server(80); //Webbrowsers can connect here to get information.
char MAC_char[18]; //MAC address in ascii
String errors(""); //General errors to report to the user.
//Define sensor type and settings.
#define LM75A //TMP102SENSOR, LM75A, AM2320SENSOR
#define TEMP_ADJUSTMENT 0 //This number is added to the sampled temperature if the sensor needs adjusting.
#ifdef LM75A
#include <M2M_LM75A.h>
#define SENSORTYPE "LM75A"
M2M_LM75A lm75a;
#elif defined(AM2320SENSOR)
#include <AM2320.h> //https://github.com/hibikiledo/AM2320/releases
#define SENSORTYPE "AM2320"
AM2320 am2320sensor;
#elif defined(TMP102SENSOR)
#include <SparkFunTMP102.h> //From Spark Fun
#define SENSORTYPE "TMP102"
TMP102 tmpsensor(0x48);
#endif
//Settings for recording samples
#define RESULTS_SAMPLE_RATE 5 //The number of seconds between samples
#define SENSOR_HISTORY_RECORDS 15 //The number of records to keep
std::list<double> tempSamples; //Collected results per interval
std::list<double> humidSamples; //Collected results per interval
std::list<double> tempHistory; //History over time.
std::list<double> humidHistory; //History over time.
void setup() {
uint8_t MAC_array[6];
Serial.begin(9600);
Wire.begin(0,2); //IC2 SDA, SCL pins
connectWiFi();
#ifdef AM2320SENSOR
am2320sensor.begin();
#elif defined(TMP102)
tmpsensor.begin();
#endif
//Get the mac and convert it to a string.
WiFi.macAddress(MAC_array);
for (int i = 0; i < sizeof(MAC_array); ++i) {
sprintf(MAC_char,"%s%02x",MAC_char,MAC_array[i]);
}
}
void loop() {
static int last_result_time = 0; //The last time a sample was taken.
static int lastposttime = 0; //The last time a MQTT topic was posted.
//Reconnect if disconnected.
if(WiFi.status() != WL_CONNECTED) {
connectWiFi();
}
//Check the MQTT connection and process it
while (!client.connected()) {
client.connect("ESP8266Client", mqtt_user, mqtt_password);
delay(250);
}
client.loop();
//Collect the sensor data
if(millis() - last_result_time > RESULTS_SAMPLE_RATE * 1000) {
getSensorData();
last_result_time = millis();
}
//post MQTT data every mqtt_postInterval minutes and on bootup
if(millis() - lastposttime > mqtt_postInterval * 60 * 1000 || lastposttime == 0) {
calcSensorStats();
if(tempHistory.size() > 0) {
client.publish((mqtt_temp_topic+String(MAC_char)).c_str(), String(tempHistory.front()).c_str(), true);
}
if(humidHistory.size() > 0) {
client.publish((mqtt_humid_topic+String(MAC_char)).c_str(), String(humidHistory.front()).c_str(), true);
}
lastposttime = millis();
}
//Some serial debugging info
Serial.println("Temp: " + String(tempHistory.front()));
//If a client connects to the website, print useful info.
WiFiClient client = server.available();
if (client && client.connected()) {
client.println("MAC: " + String(MAC_char));
client.println("Uptime: " + String(millis()));
client.println("Last Post Time: " + String(lastposttime));
client.println("Errors: " + errors);
client.println("MQTT Post Interval: " + String(mqtt_postInterval) + " minutes");
client.println("Sensor Sample Rate: " + String(RESULTS_SAMPLE_RATE) + " seconds");
client.println("Sensor Type: " + String(SENSORTYPE));
client.println("\nCurrent sensor samples (newest on top): ");
client.println("Temp: ");
for(std::list<double>::iterator sensiter = tempSamples.begin(); sensiter != tempSamples.end(); sensiter++) {
client.println(String(*sensiter));
}
if(humidSamples.size() > 0) {
client.println("Humidity: ");
for(std::list<double>::iterator sensiter = humidSamples.begin(); sensiter != humidSamples.end(); sensiter++) {
client.println(String(*sensiter));
}
}
client.println("History (newest on top): ");
client.println("Temp: ");
for(std::list<double>::iterator sensiter = tempHistory.begin(); sensiter != tempHistory.end(); sensiter++) {
client.println(String(*sensiter));
}
if(humidHistory.size() > 0) {
client.println("Humidity: ");
for(std::list<double>::iterator sensiter = humidHistory.begin(); sensiter != humidHistory.end(); sensiter++) {
client.println(String(*sensiter));
}
}
client.stop();
}
//No need to run constantly
delay(1000);
}
void getSensorData() {
double temp = -999;
double humid = -999;
String errorsFound("");
//Collect the temperature from a sensor.
#ifdef AM2320SENSOR
if(am2320sensor.measure()) {
temp = am2320sensor.getTemperature() * 1.8 + 32;
humid = am2320sensor.getHumidity();
errorsFound = "";
} else {
int errorCode = am2320sensor.getErrorCode();
switch (errorCode) {
case 1: errors = "ERR: Sensor is offline"; break;
case 2: errors = "ERR: CRC validation failed."; break;
}
}
#endif
#ifdef TMP102SENSOR
temp = tmpsensor.readTempF();
#endif
#ifdef LM75A
if(lm75a.isConnected()) {
temp = lm75a.getTemperatureInFarenheit();
} else {
temp = -999;
}
#endif
//Adjust the temperature if needed
temp += TEMP_ADJUSTMENT;
//Make sure a valid temperature was sampled
if(temp != -999) {
tempSamples.push_front(temp);
}
if(humid != -999) {
humidSamples.push_front(humid);
}
if(errorsFound != "") {
errors = errorsFound;
} else {
errors = "";
}
}
void calcSensorStats() {
int count = 0;
double tempsum = 0;
double humidsum = 0;
double tempaverage, humidaverage;
if(tempSamples.size() > 0) {
for(std::list<double>::iterator sensiter = tempSamples.begin(); sensiter != tempSamples.end(); sensiter++) {
count++;
tempsum += *sensiter;
}
tempaverage = tempsum / count;
//Add the new data to the history.
tempHistory.push_front(tempaverage);
if(tempHistory.size() > SENSOR_HISTORY_RECORDS) {
tempHistory.pop_back();
}
}
if(humidSamples.size() > 0) {
count = 0;
//Get totals and averages
for(std::list<double>::iterator sensiter = humidSamples.begin(); sensiter != humidSamples.end(); sensiter++) {
count++;
humidsum += *sensiter;
}
humidaverage = humidsum / count;
//Add the new data to the history.
humidHistory.push_front(humidaverage);
if(humidHistory.size() > SENSOR_HISTORY_RECORDS) {
humidHistory.pop_back();
}
}
tempSamples.clear();
humidSamples.clear();
}
void connectWiFi()
{
WiFi.mode(WIFI_STA);
WiFi.begin(WiFiSSID, WiFiPSK);
while (WiFi.status() != WL_CONNECTED)
{
//Delay until connected.
delay(100);
}
//Connect to the mqtt server
client.setServer(mqtt_server, 1883);
//Begin listening to clients connecting to the "webserver".
server.begin(); //Listen for clients connecting to port 80
}