In this guide, we will be using the ESP8266 and BerryIMU to record temperature and pressure and display the output onto a web page.
Google charts is used to display the data in an easy to read format.

Components used are;
- ESP8266 (Adafruit Feather Huzzah)
- BerryIMU
Summary
- NTP is used to get the correct time of day.
- The free available RAM is calculated to work out how much data we can store,
- Google charts is used to display chart data.
- 4 web pages are created:
"/" - home page which is used to display the gauges.
"/chart.html" - Is used to display chart.
"/table.html" - Is used to display the data in a table.
"/data.json"- Is used by the home page to update the the gauge values.
Hook Up
The below diagram shows how to connect a BerryIMU to an ESP8266 microcontroller, in this case it is the Adafruit Feather Huzzah .

Prepare Arduino IDE
The Arduino IDE needs to be updated with the board packages for the ESP8266. This is very easy to do and both Sparkfun & Adafruit have detailed guides on how to do this;
Sparkfun Arduino IDE and ESP8266
Adafruit Arduino IDE and ESP8266
The Code
The code can be found here. Download the entire Git repo. The code for this guide can be found under the directory;
ESP8266-BerryIMU/BerryIMU_ESP8266_Graph_Temperature/
Wireless SSID
The first thing to do is update the code with your wireless network settings.
const char* ssid = "******"; const char* password = "***************";
Setup()
Here we will only cover the important items within setup().
I2C is initialised.
Wire.begin(4,5);
The first value is the SDA pin and the second specifies the SCL pin. On the Adafruit Feather Huzzah we use pins 4 and 5.
Any pin on the ESP8266 can be used for I2C.
Wireless is then enabled and then we try and connect to the wireless network. The IP address is then printed to the serial console.
WiFi.begin(ssid, password); // Wait for connection while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); //Print IP to console Serial.print("IP address: "); Serial.println(WiFi.localIP()); delay(3);
Then the web services is started.
server.begin();
Calibration data from the BMP180 on BerryIMU is then read.
Each BMP180 is different, at time of manufacture, calibration data is written to EEPROM on the BMP180.
readCalBMP180();
Time
Setup time using NTP, the correct time is needed for logging.. We will not be covering NTP in this guide
//Setup time using NTP Serial.print("Resolving NTP Server IP "); WiFi.hostByName(timerServerDNSName, timeServer); Serial.println(timeServer.toString()); Serial.print("Starting UDP... "); Udp.begin(localPort); Serial.print("local port: "); Serial.println(Udp.localPort()); Serial.println("Waiting for NTP sync"); setSyncProvider(getNtpTime);
NTP time is returned as UTC. To set it to the correct timezone, modify the below line with timezone offset.
const int timeZone = 11; ; // Sydney, Australia
RAM setup
During setup we need to calculate how much RAM can be used to store data. This is done in allocateRam().
Once the ESP8266 is up an running, the remaining RAM is divided up into;
- RAM for TCP/IP Stack. If using the refresh function on the main page which has gauges, then there needs to be enough RAM left to accommodate the TCP sessions for the refresh.
- RAM for the array which stores temperature and pressure data.
- RAM for the ESP8266 to run.
We will allocate as much RAM as we can to track historical data. This means, we need to work out how much RAM we will need for the TCP/IP stack and we also need enough for the ESP8266 to run.
Once we get this value, we can allocate the rest of the RAM to the array used to store the data.
Working out RAM for TCP/IP Stack
Each TCP session expires every 2mins. (Default MSL is 2mins for Windows https:technet.microsoft.com/en-us/library/cc938217.aspx)
196KB RAM per refresh.
To calculate how much RAM is needed for the TCP/IP stack;
number-of-refreshes-in-2-minutes x RAM per REFRESH = RAM required for TCP/IP stack.
We then need to reserve some spare RAM for the ESP8266 to run. 10,000 KB should be enough.
Final formula:
(number-of-refreshes-in-2-minutes x RAM per REFRESH) + SPARE RAM = ALLOCATED_RAM
E.g.
For a refresh of every 1 second
(120x196KB) + 10,000 = 33,520
For a refresh of every 3 seconds
(40x196KB) + 10,000 = 17,840
The less frequent the refresh will result in a smaller value needed to be reserved. And this also means more RAM can be used to store historical data for a longer period.
If refresh isn't used, then leave ALLOCATED_RAM to 10,000
Array to Store data
Once we know how much RAM to allocated for TCP/IP and ESP8266 to run, we can use the rest of the RAM to store the data.
//Get freem RAM in bytes uint32_t free=system_get_free_heap_size() - ALLOCATED_RAM;
Divide the free RAM by the size of the variables used to store the data. This will allow us to work out the maximum number of records we can store.
numberOfRows = free / (sizeof(float)*2+ sizeof(unsigned long)); // 2 x float for temp and pressure. one x long for time.
Now re-declare the arrays with the number of rows.
tempCdata = new float [numberOfRows]; pressData = new float [numberOfRows]; timeStamp = new unsigned long [numberOfRows];
Reading Temperature
The temperature and pressure is read within the main loop.
First, we need to let the BMP180 know that we want to read the temperature.
status = startTemperature();
The function startTemperature() informs the BMP180 that we want to read the temperature by sending BMP180_COMMAND_TEMPERATURE (0x2E) to the register BMP180_REG_CONTROL (0xF4). It returns the time to wait before a value can be read.
The below code reads the temperature value once the delay value in 'status' has expired. The temperature returned is in Celsius, this is also converted to Fahrenheit and stored in 'F'.
if (status != 0){ delay(status); getTemperature(T); Serial.print("temperature: "); Serial.print(T,2); Serial.print(" deg C, "); F = (9.0/5.0)*T+32.0; Serial.print(F,2); Serial.print(" deg F "); }
Reading Pressure
We now need to inform the BMP180 that we want to read the pressure. When information the BMP180 that we want to read pressure, we also need to specify the oversampling to use. Below, we specify 3.
status = startPressure(3);
The startPressure() function sends BMP180_COMMAND_PRESSURE3 (0xF4) to the register BMP180_REG_CONTROL (0xF4).It returns the time to wait before a value can be read.
The below code reads the pressure value once the delay value in 'status' has expired.
//Read pressure status = startPressure(3); if (status != 0){ // Wait for the measurement to complete: delay(status); status = getPressure(P,T); if (status != 0){ // Print out the measurement: Serial.print("absolute pressure: "); Serial.print(P,2); Serial.print(" mb, "); Serial.print(P*0.0295333727,2); Serial.print(" inHg "); } }
Recording Data
The portion of code below adds the current recorded temperature and pressure to the array.
It first checks to see if the poll period is expired.
It then checks to make sure each value is a number.
The variable count is used to work out if the array is full or not. When the ESP8266 is first started, the array is empty and we just keep adding values to the next available row. Once the array is full, we cycle the data and then add the latest values to the end.
The current time is also captured.
// Check if poll period has expired if (millis()>=timeKeeper){ timeKeeper = millis() + (POLLPERIOD * 1000); // Update timeKeeper with the latest time + poll period (multiply by 1,000 as millis() is milliseconds) unsigned long currentTime = now(); // Update each row in the array until it is full if(!(isnan(currentTime)) || !(isnan(T))|| !(isnan(P))){ // Make sure that all values are a number before updating if (count < numberOfRows){ tempCdata[count] = T; // Temperature pressData[count] = P; // Pressure timeStamp[count] = currentTime; //Current time count++; } else{ for (int i = 0; i<(count) ; i++){ // Cycle the array. Move everything forward by one and add the new values to the end tempCdata[ i] = tempCdata[ i+1]; pressData[ i] = pressData[ i+1]; timeStamp[ i] = timeStamp[ i+1]; } tempCdata[numberOfRows] = T; pressData[numberOfRows] = P; timeStamp[numberOfRows] = currentTime; } } }
Web Server and Web Pages
The web server functionally we using is very basic and has no error checking. In saying that, it is all we need and it performs the job well.
Our ESP8266 web server will have four pages;
- "/" Root or the home page. This is where the gauges are displayed.
- "/data.json" is only used to provide data for the gauge refresh.
- "/chart.html" shows the historical data within a chart.
- "/table.html" is where all the data is shown in a table
The code below will check to see if a client is connected, it then reads the requested web page path and stores it in sPath.
WiFiClient client = server.available(); ///////////////////////////////////// // Read the first line of the request ///////////////////////////////////// String sRequest = client.readStringUntil('\r'); client.flush(); // get path; end of path is either space or ? // Syntax is e.g. GET /?show=1234 HTTP/1.1 String sPath="",sParam="", sCmd=""; String sGetstart="GET "; int iStart,iEndSpace,iEndQuest; iStart = sRequest.indexOf(sGetstart); if (iStart >= 0) { iStart+=+sGetstart.length(); iEndSpace = sRequest.indexOf(" ",iStart); iEndQuest = sRequest.indexOf("?",iStart); // are there parameters? if(iEndSpace > 0) { if(iEndQuest > 0) { // there are parameters sPath = sRequest.substring(iStart,iEndQuest); sParam = sRequest.substring(iEndQuest,iEndSpace); } else { // NO parameters sPath = sRequest.substring(iStart,iEndSpace); } } }
The gauges, chart and table use Google Charts to display the data. This is a great option as it takes the load away from the ESP8266.
When a web browser requests a page from the ESP8266, the ESP8266 will respond with a HTML page which will have the temperature and pressure data, as well as code which will force the browser to use google charts to display this data.
Creating HTML pages with the ESP8266
Creating HTML pages for the ESP8266 web server is easy, however the pages have to be built from scratch. This means you have to include html headers and GET response codes.
The code snippet below is the code which creates the page for the chart. Here is an example of what the final page looks like.
The first thing we do is declare a string(htmlContent) which is used to store the html content.
And then we just keep adding html code to the string and send it to the web browser with client.print(htmlContent);
It is important that you don't make the string to large before sending to the web browser, otherwise the ESP8266 will crash. You can see that in the code snippet below that halfway through the html page, I send the string and then starting building the string again.
else if (sPath=="/chart.html"){ //Standard html header stuff here. htmlContent = ("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n"); htmlContent += ("<html><head><title>chart</title>\n"); //load google charts and create a button htmlContent += ("<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20type%3D%5C%22text%2Fjavascript%5C%22%20src%3D%5C%22https%3A%2F%2Fwww.google.com%2Fjsapi%3Fautoload%3D%7B'modules'%3A%5B%7B'name'%3A'visualization'%2C'version'%3A'1'%2C'packages'%3A%5B'corechart'%5D%7D%5D%7D%5C%22%3E%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<script>" title="<script>" />\n"); htmlContent += ("<button id=\"change-chart\"></button>"); htmlContent += ("<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20type%3D%5C%22text%2Fjavascript%5C%22%3E%20google.setOnLoadCallback(drawChart)%3B%5Cn%22)%3B%0A%20%20%20%20htmlContent%20%2B%3D%20(%22var%20button%20%3D%20document.getElementById('change-chart')%3B%22)%3B%0A%20%20%0A%20%20%20%20%2F%2FCreate%20a%20function%20to%20draw%20the%20chart%20and%20then%20add%20the%20data%20into%20a%20table%0A%20%20%20%20htmlContent%20%2B%3D%20(%22function%20drawChart()%20%7Bvar%20data%20%3D%20google.visualization.arrayToDataTable(%5B%5Cn%22)%3B%0A%20%20%20%20htmlContent%20%2B%3D%20(%22%5B'Local%20Time'%2C%20'Temperature%20C'%2C%20'Temperature%20F'%2C%20'Pressure'%5D%2C%5Cn%22)%3B%0A%20%20%20%20%2F%2FSend%20what%20we%20have%20to%20the%20client%20(web%20browser)%0A%20%20%20%20client.print(htmlContent)%3B%0A%20%20%0A%20%20%20%20%2F%2FHere%20we%20loop%20through%20the%20temp%20and%20pressure%20data%20to%20place%20it%20into%20the%20html%20source%20code%0A%20%20%20%20for%20(int%20i%20%3D%200%3B%20i%3C%20count%20%3B%20i%2B%2B)%7B%0A%20%20%20%20%20%20htmlContent%20%3D%20(%22%5Bnew%20Date(%22%20%2B%20String(timeStamp%5Bi%5D)%20%2B%20%20%22000)%2C%22%20%2B%20String(tempCdata%5Bi%5D)%20%2B%20%20%22%2C%22%20%2B%20String((9.0%2F5.0)*tempCdata%5Bi%5D%2B32.0)%20%2B%20%22%2C%22%20%2B%20String(pressData%5Bi%5D)%20%2B%20%22%5D%2C%5Cn%22)%3B%0A%20%20%20%20%20%20client.print(htmlContent)%3B%0A%20%20%20%20%7D%0A%20%20%20%20htmlContent%20%3D%20(%22%5D)%3B%5Cn%22)%3B%0A%0A%20%20%20%20%2F%2FContinue%20to%20build%20the%20rest%20of%20the%20web%20page.%20%20Here%20we%20create%20three%20function%20that%20the%20buttons%20uses%20to%20dsiplay%20the%20chart%20data.%0A%20%20%20%20htmlContent%20%2B%3D%20(%22function%20drawChartCelsius()%20%7Bvar%20tempCview%20%3D%20new%20google.visualization.DataView(data)%3B%5Cn%20%20%20%20tempCview.setColumns(%5B0%2C1%5D)%3B%5Cn%20%20%20%20chart.draw(tempCview%2C%20optionsCelsius)%3B%5Cn%20%20%20%20button.innerText%20%3D%20'Change%20to%20Fahrenheit'%3B%5Cn%20%20%20%20button.onclick%20%3D%20drawChartFahrenheit%3B%7D%5Cn%22)%3B%0A%20%20%20%20htmlContent%20%2B%3D%20(%22function%20drawChartFahrenheit()%20%7Bvar%20tempFview%20%3D%20new%20google.visualization.DataView(data)%3B%5Cn%20%20%20%20tempFview.setColumns(%5B0%2C2%5D)%3B%5Cn%20%20%20%20chart.draw(tempFview%2C%20optionsFahrenheit)%3B%5Cn%20%20%20%20button.innerText%20%3D%20'Change%20to%20Pressure'%3B%5Cn%20%20%20%20button.onclick%20%3D%20drawChartPressure%3B%7D%5Cn%22)%3B%0A%20%20%20%20htmlContent%20%2B%3D%20(%22function%20drawChartPressure()%20%7Bvar%20tempPressureView%20%3D%20new%20google.visualization.DataView(data)%3B%5Cn%20%20%20%20tempPressureView.setColumns(%5B0%2C3%5D)%3B%5Cn%20%20%20%20chart.draw(tempPressureView%2C%20optionsPressure)%3B%5Cn%20%20%20%20button.innerText%20%3D%20'Change%20to%20Celsius'%3B%5Cn%20%20%20%20button.onclick%20%3D%20drawChartCelsius%3B%7D%5Cn%22)%3B%0A%0A%20%20%20%20%2F%2Fspecify%20date%20format%20and%20then%20update%20x%20labels%20with%20this%20time%20format%0A%20%20%20%20htmlContent%20%2B%3D%20(%22var%20formatter%20%3D%20new%20google.visualization.DateFormat(%7B%20formatType%3A%20'short'%2CtimeZone%3A%200%7D)%3B%5Cn%20%20formatter.format(data%2C%200)%3B%5Cn%22)%3B%0A%20%20%20%20htmlContent%20%2B%3D%20(%22%2F%2F%20Set%20X-Axis%20Labels%5Cnvar%20xTicks%20%3D%20%5B%5D%3B%5Cn%22)%3B%0A%20%20%20%20htmlContent%20%2B%3D%20(%22for%20(var%20i%20%3D%200%3B%20i%20%3C%20data.getNumberOfRows()%3B%20i%2B%2B)%20%7B%5Cn%22)%3B%0A%20%20%20%20htmlContent%20%2B%3D%20(%22%20%20%20xTicks.push(%7B%5Cn%20%20%20%20v%3A%20data.getValue(i%2C%200)%2C%5Cn%20%20%20%20f%3A%20data.getFormattedValue(i%2C%200)%20%7D)%3B%5Cn%7D%5Cn%22)%3B%0A%0A%20%20%20%20%2F%2FHere%20are%20three%20chart%20options%20used%20for%20each%20chart.%20%20E.g.%20colour%2C%20chart%20title%2C%20etc..%0A%20%20%20%20htmlContent%20%2B%3D%20(%22var%20optionsPressure%20%3D%20%7B'height'%3A%20320%2CchartArea%3A%7Btop%3A20%2C%20height%3A%5C%2260%25%5C%22%7D%2ChAxis%3A%7Bgridlines%3A%7Bcolor%3A'transparent'%7D%2Cticks%3AxTicks%2CslantedText%3A%20true%2CslantedTextAngle%20%3A70%2CtextStyle%3A%7BfontSize%3A%2011%7D%20%7D%2CvAxis%3A%7Bformat%3A%5C%22%23%23%2C%23%23%23%20mb%5C%22%7D%2Cseries%3A%7B1%3A%7BcurveType%3A'function'%7D%2C0%3A%7Bcolor%3A'orange'%7D%7D%2Clegend%3A%7Bposition%3A%20'none'%7D%2Ctitle%3A'Pressure%20in%20Millibars'%20%7D%3B%5Cn%22)%3B%0A%20%20%20%20htmlContent%20%2B%3D%20(%22var%20optionsCelsius%20%3D%20%7B'height'%3A%20320%2CchartArea%3A%7Btop%3A20%2C%20%20height%3A%5C%2260%25%5C%22%7D%2ChAxis%3A%7Bgridlines%3A%7Bcolor%3A'transparent'%7D%2Cticks%3AxTicks%2CslantedText%3A%20true%2CslantedTextAngle%20%3A70%2CtextStyle%3A%7BfontSize%3A%2011%7D%20%7D%2CvAxis%3A%7Bformat%3A%5C%22%23%23.%23%23%20%22%20%2B%20String((char)176)%20%2B%20%22C%5C%22%7D%2Cseries%3A%7B1%3A%7BcurveType%3A'function'%7D%2C0%3A%7Bcolor%3A'red'%7D%7D%2Clegend%3A%7Bposition%3A%20'none'%7D%2Ctitle%3A'Temperature%20in%20Celsius'%20%7D%3B%5Cn%22)%3B%0A%20%20%20%20htmlContent%20%2B%3D%20(%22var%20optionsFahrenheit%20%3D%20%7B'height'%3A%20320%2CchartArea%3A%7Btop%3A20%2C%20height%3A%5C%2260%25%5C%22%7D%2ChAxis%3A%7Bgridlines%3A%7Bcolor%3A'transparent'%7D%2Cticks%3AxTicks%2CslantedText%3A%20true%2CslantedTextAngle%20%3A70%2CtextStyle%3A%7BfontSize%3A%2011%7D%20%7D%2CvAxis%3A%7Bformat%3A%5C%22%23%23.%23%23%20%22%20%2B%20String((char)176)%20%2B%20%22F%5C%22%7D%2Cseries%3A%7B0%3A%7BcurveType%3A%20'function'%7D%2C0%3A%7Bcolor%3A'Blue'%7D%7D%2Clegend%3A%7Bposition%3A%20'none'%7D%2Ctitle%3A%20'Temperature%20in%20Fahrenheit'%7D%3B%5Cn%22)%3B%0A%20%20%20%20client.print(htmlContent)%3B%20%20%20%20%0A%20%20%0A%20%20%20%20%2F%2FDraw%20chart%20%0A%20%20%20%20htmlContent%20%3D%20(%22var%20chart%20%3D%20new%20google.visualization.LineChart(document.getElementById('curve_chart'))%3BdrawChartCelsius()%3B%7D%5Cn%22)%3B%0A%20%20%20%20htmlContent%20%2B%3D%20(%22%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<script>" title="<script>" />\n"); //Page heading htmlContent += ("<font color=\"#000000\"><body><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\">\n <h1>Temperature and Pressure Chart</h1> <a href=\"/\">back</a><BR><BR>\n"); htmlContent += (" <div id=\"curve_chart\" style=\"width: 800px; height: 300px\"></div> <BR><BR>Number of readings=" + String(count) + "<BR>Max allowed readings=" + String(numberOfRows) + "<BR>"); //Display the data and time for first and last reading htmlContent += ("<BR><BR>First reading at : "); timeAndDate(timeStamp[0],htmlContent); htmlContent += ("<BR>Most recent reading : "); timeAndDate(timeStamp[count-1],htmlContent); htmlContent += ("<BR></body></html>\n"); client.print(htmlContent); }//End chart
Here are some helpful tips for building the page;
- Use the developer tools (F12) in your browser to troubleshoot.
- "\n" is not needed, however it makes the source for the html file more readable.
- Quotes used within html have to be escaped out with backslash "\".
- Variables have to be converted to strings using String()
- Dont make the the string htmlContent too large as it will crash the ESP.
[wp_ad_camp_1]
hello wher is the full code?
there is a link at the top for the full code
Can you write a guide for reading temperature and pressure with BerryGPS-IMUv3 – GPS and Arduino MEGA 2560?