Firmware for ESP8266 and TMP102/LM75A/AM2320

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
}
3 Likes

Here’s sample webpage output:

MAC: 60019400beb7
Uptime: 80526461
Last Post Time: 80490413
Errors: 
MQTT Post Interval: 1
Sensor Sample Rate: 5 seconds
Sensor Type: LM75A

Current sensor samples (newest on top): 
Temp: 
65.75
65.75
65.75
65.75
65.75
History (newest on top): 
Temp: 
65.70
65.52
65.46
65.30
65.13
64.92
64.75
64.62
64.59
64.61
64.62
64.86
64.73
64.32
64.40
client.connect("ESP8266Client", mqtt_user, mqtt_password);

I’m doing this from memory, so please correct me if I’m wrong, but I think the first parameter is the client id. This has to be unique for each connection (ie. esp8266). The normal way to do this is to use the node id e.g

String(ESP.getChipId())

Also, how do plan to power your esp01? I have a few, but gave up on them because they used too much power for battery operation.

Edit:
Corrected the method name after actually looking it up.

I have 4 ESP modules using ESP8266Client and they all report separate stats to HA.

I believe ESP.getChipId() only returns the last three bytes of the mac address which isn’t enough to guarantee uniqueness across all devices.

The ESP isn’t great for use with batteries. You might be able to sleep/wake the chip to save battery power but even then it’s power hungry. The ESP01 won’t sleep without some soldering.

how do you connect AM2320 to ESP-01?
or you connect ESP module to arduino?

Connect SDA and SCL to GPIO pins 0 and 2
Wire.begin(0,2); //IC2 SDA, SCL pins

is it working only ESP-01 and AM2320 without arduino?

Yes, all you need is the ESP01

it’s working, but the Temperature was 1.94, the Temp was correct after change this line

temp = am2320sensor.getTemperature();// * 1.8 + 32;

i found a little bug in error handling, just remark these lines.

/if(errorsFound != “”) {
errors = errorsFound;
} else {
errors = “”;
}
/

How is reliable lib for am2320 that you use?
I’m using different one and after some time sensor stop responding.

So far it works well. I believe the DHT22 library works with the AM2320 also.