Now, there are many Arduino tutorials already available, some of which I referenced when putting this together, so why should you read this? Well, there are a few differences when using an ESP 32 instead of an Arduino, especially when using the form factor development board that I purchased. It sounds straight forward but I ran into a few issues that caused my implementation to take a bit longer to finish then I had hoped.
In addition to covering these issues I will also be covering ESP32 programming using the Deviot package for Sublime Text 3.
The code can be found on GitHub here, including a PDF of the circuit diagram.
Parts
No. | Name | Link | Price |
---|---|---|---|
1 | Photo resistor | eBay | £0.99 (10 pieces) |
1 | Thermistor | eBay | £1.85 (5 pieces) |
2 | 10k resistor | eBay | £0.99 (20 pieces) |
1 | OLED 0.96″ I2C | eBay | £4.19 |
1 | ESP32 | eBay | £5.99 |
As you can see from the image below, this ESP32 module has double stacked the GPIO pins to decrease its form factor, this means it’s not possible to directly plug this into a breadboard as you would bridge the pins. If you want one you can stick on a breadboard I would recommend getting this one for £11.59 or, if you don’t mind waiting, this one from China for £4.71.
For this I’m using my module in “dead bug” configuration and using breadboard wires to hook up the pins I need.
![ESP32](https://i0.wp.com/chrisholdt.com/wp-content/uploads/2017/12/IMG_1657-1.jpg?resize=2048%2C1536&ssl=1)
![ESP32 connected to thermistor, photoresistor and OLED](https://i0.wp.com/chrisholdt.com/wp-content/uploads/2017/12/IMG_1675-1.jpg?resize=2048%2C1536&ssl=1)
One of the lovely features of the ESP32 is the ability to use any GPIO pins as SDA and SCL for I2C. For this I used the two default I2C pins which are 21 for SDA and 22 for SCL. Another feature of the ESP32 is its 12bit ADC, this tripped me up a bit when doing the temperature and light conversions whilst following the Arduino tutorials, this is because most, if not all Atmel ATMega and ATTiny chips use a 10bit ADC. For a 10bit ADC the values it can read range from 0 to 1023, with the ESP32 the 12bit ADC has a range of 0 to 4095!
The thermistor is connected to GPIO 27 and the photo resistor is connected to GPIO 25. Below is the circuit diagram, it uses the ESP32S WROOM module layout, which is the ESP32S not mounted to any board.
(Click for a bigger image)
![Circuit diagram](https://i0.wp.com/chrisholdt.com/wp-content/uploads/2017/12/ESP32-OLED-LDR-Thermistor-1.png?resize=1181%2C833&ssl=1)
Sublime text, Deviot and Libraries
To program the ESP32 and upload the code I am writing in Sublime Text 3 with the Deviot plugin. Initially I wasn’t sure what board to select to program this ESP32 module but found that the Node32 board works perfectly.
Although compiling worked fine, when I tried to upload the code I got the following error:
*** Do not know how to make File target 'program' (C:UsersUserAppDataLocalTempDeviotmainprogram). Stop.
The end result was a lot of googling until I read that you should set the programmer to ‘None’ in the Deviot settings. After that, no more issues!
Reading the Thermistor
This is quite straightforward, all we need to do is read the analog input from the GPIO pin the thermistor is connected to.
//Pins
void setup() {}
void loop() {
double thermVal;
//Read the thermistor value
thermVal = analogRead(THERMISTORPIN);
// Print it to serial
Serial.print("Thermistor reading: ");
Serial.println(thermVal);
// Wait 2 seconds and do it again
delay(2000);
}
Sampling Thermistor values and converting to Celsius
Now, as we aren’t using the most accurate or expensive components if you were to output in a shorter times span in an environment that isn’t changing much you would notice that the value changes quite rapidly. Not to mention that this thermistors accuracy is really only 1 degree Celsius. How can this output be improved? Well for starters it would be nice if it was in degrees and fluctuated a bit less.
This can be achieved by taking a number of samples and averaging them, converting the value to resistance, then plumbing them through the simplified B Parameter equation of the Steinhart equation. This isn’t completely accurate but does the job well enough for the AI Society project.
One final noteworthy item, during usage the thermistor will heat up slightly, so if the reading seems a bit off it is because of this.
The Steinhart equation mentioned above, specifically the B parameter simplification is as follows:
T = 1/( 1/To + 1/B * ln(R/Ro) )
T = Temperature in Kelvin
R = Resistance measured
Ro = Resistance at nominal temperature
B = Coefficent of the thermistor
To = Nominal temperature in kelvin
I have rearranged it slightly for easier implementation, see the tutorials I’ve referenced at the end of this post for a more in depth look at this.
Instead of adding all the code to handle sampling and conversion in the main loop function, I created a getTemp() function. This will make it easier for me to migrate it to the AI Society code later on.
// Pins
// Series resistor value
// Number of samples to average
// Nominal resistance at 25C
// Nominal temperature in degrees
// Beta coefficient
void setup() {}
int getTemp() {
double thermalSamples[SAMPLERATE];
double average, kelvin, resistance, celsius;
int i;
// Collect SAMPLERATE (default 5) samples
for (i=0; i<SAMPLERATE; i++) {
thermalSamples[i] = analogRead(THERMISTORPIN);
delay(10);
}
// Calculate the average value of the samples
average = 0;
for (i=0; i<SAMPLERATE; i++) {
average += thermalSamples[i];
}
average /= SAMPLERATE;
// Convert to resistance
resistance = 4095 / average - 1;
resistance = SERIESRESISTOR / resistance;
/*
* Use Steinhart equation (simplified B parameter equation) to convert resistance to kelvin
* B param eq: T = 1/( 1/To + 1/B * ln(R/Ro) )
* T = Temperature in Kelvin
* R = Resistance measured
* Ro = Resistance at nominal temperature
* B = Coefficent of the thermistor
* To = Nominal temperature in kelvin
*/
kelvin = resistance/THERMISTORNOMINAL; // R/Ro
kelvin = log(kelvin); // ln(R/Ro)
kelvin = (1.0/BCOEFFICIENT) * kelvin; // 1/B * ln(R/Ro)
kelvin = (1.0/(TEMPERATURENOMINAL+273.15)) + kelvin; // 1/To + 1/B * ln(R/Ro)
kelvin = 1.0/kelvin; // 1/( 1/To + 1/B * ln(R/Ro) )
// Convert Kelvin to Celsius
celsius = kelvin - 273.15;
// Send the value back to be displayed
return celsius;
}
void loop() {
int temp;
// Call the function to get the temperature in degrees celsius
temp = getTemp();
// Output the temp to serial
Serial.print("Temp: ");
Serial.print(temp);
Serial.println(" C");
}
Reading the Photo Resistor
Like the thermistor this is also very easy.
//Pins
void setup() {}
void loop() {
double photoVal;
// Read the photo resistor value
photoVal = analogRead(PHOTORESISTORPIN);
// Print it to serial
Serial.print("Photo resistor reading: ");
Serial.println(thermVal);
// Wait 2 seconds and do it again
delay(2000);
}
Sampling the Photo Resistor and converting to a useful value
Similarly to the thermistor, I also put this code into its own function. This time there was nothing special about the implementation.
One thing that you should be aware of is that this value does not represent lux or any other light value reading. To actually read lux you need to build a lookup table that is specific to your circuit and component due to the exponential graph a photo resistor produces. This requires a pre calibrated lux meter and quite a bit of time compiling data. I don’t happen to have a lux meter, and really for the AI society I don’t care about having values like that right now. Mostly the nodes should know, that it’s dark, quite dark, light or very light.
//Pins
// Series resistor value
void setup() {}
int getLight() {
int ldrSamples[SAMPLERATE];
int i, light, average;
// Get a new sample every 10 milliseconds, get SAMPLERATE samples (default 5)
for (i=0; i<SAMPLERATE; i++) {
ldrSamples[i] = analogRead(PHOTORESISTORPIN);
delay(10);
}
// Calculate the average
average = 0;
for (i=0; i<SAMPLERATE; i++) {
average += ldrSamples[i];
}
average /= SAMPLERATE;
// Invert
average = 4095 - average;
// Map light to a value between 0-100
light = map(average, 0, 4095, 0, 100);
return light;
}
void loop() {
int photoVal;
// Call the function to get the light level
photoVal = getLight();
// Output the light to serial
Serial.print("Light: ");
Serial.println(photoVal);
}
Displaying the values on the OLED
Now, a bit of fun. I didn’t need to add an OLED, but added value is always good!
First things first, you need to install the right library. Following the OLED tutorial linked in the references below, I installed the Adafruit SSD1306 library and their GFX one. I uploaded the 128×64 example to make sure everything was working. If you do this, you will most likely need to change the header file to tell it to use the size 64 OLED. Installing libraries with Deviot is really nice and it tends to find the majority of them quite easily. Select the option “Find/Install Library” in the Deviot menu in Sublime and enter the search term for the library you want to use, select the right one and it installs it. Done!
As mentioned in the introduction I have the SDA connected to GPIO 21 and the SCL connected to GPIO 22.You may need to use the Arduino I2C scanner sketch to find out what the address of your display is, in my case it is 0x3C and it’s likely that yours will be the same if you are using the OLED I linked above. If that doesn’t work then, or if you want to check before hand anyway see here.
As you can see from the image below I’m displaying the temperature, light and a refresh count. Below that is a snippet of the code I’m using to show that.
![OLED displaying Temperature, Light and refresh count](https://i0.wp.com/chrisholdt.com/wp-content/uploads/2017/12/IMG_1670-1.jpg?resize=2048%2C1536&ssl=1)
Adafruit_SSD1306 display(OLED_RESET);
int refreshCount;
void setup() {
refreshCount = 0;
// Initialise the OLED
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.display();
// Setup the text display values
display.setTextSize(1);
display.setTextColor(WHITE);
}
void loop() {
int temp, photoVal;
photoVal = getLight();
temp = getTemp();
// Display thermal reading
display.setCursor(20, 20);
display.print("Temp: ");
display.print(temp);
display.println(" C");
// Display light reading
display.setCursor(20, 30);
display.print("Light: ");
display.println(photoVal);
// Display update count
display.setCursor(20, 40);
display.print("Refreshes: ");
display.println(refreshCount);
// Display photo values
display.display();
// Clear the display for new values after 2 seconds
delay(2000);
display.clearDisplay();
// Increment refresh counter
refreshCount++;
}
All together now!
And that is that. My main goal with this little project was to prepare the code I need to have the nodes in the AI society read temperature and light. I hope that others will find this information useful when creating projects with the ESP32 as there are some trip ups when converting arduino based tutorials for it.
I have made the complete code available on GitHub here, so feel free to improve it!
References
Putting this together I used the following for information:
- Thermistor 1: https://learn.adafruit.com/thermistor/using-a-thermistor
- Thermistor 2: https://www.allaboutcircuits.com/projects/measuring-temperature-with-an-ntc-thermistor/
- Thermistor datasheet: http://www.eaa.net.au/PDF/Hitech/MF52type.pdf
- Photo resistor 1: https://learn.adafruit.com/photocells/using-a-photocell
- OLED 1: http://www.instructables.com/id/Monochrome-096-i2c-OLED-display-with-arduino-SSD13/
Be First to Comment