This blog sequence is also available as PDF document.
Wherever you celebrate, it is also drunk, certainly not only shower and juice, but also completely different things. But before you get behind the wheel, as a precaution you should keep the blood alcohol mirror in sight, otherwise the fun has a hole if the fine flutters into the house, due to drunkenness in traffic. This post from the series shows how you can check your alcohol consumption
Micropython on the ESP32 and ESP8266
today
An alkometer with the ESP8266 and an MQ-3 sensor
In this episode, we will first deal with the technical backgrounds before it goes to episode 2. How do I oked the ADC of an ESP8266 or an ESP32? How do I find out whether the MQ-3 has reached its working temperature? The present post answers these questions. The follow-up fee will then be particularly concerned with programming the alcohol tester.
Admittedly, a precise measurement that also contains a comparison with the official alkomat of the police cannot be carried out with the simple means that are available to us. It starts with the calibration of the alcohol and alleys MQ-3. After searching through the data sheet and the Internet several times, I have not found any useful procedures for this. Nevertheless, a lot is possible with the simple circuit.
But let's start with the basics for the measurements. The MQ-3 bob (Break Out Board) delivers a digital and analogous output signal. The latter consists of a DC voltage, the more the alcohol concentration in the breathing air is of DC voltage. A diagram (Image 2) in datasheet Unfortunately, the MQ-3 now does not reveal an absolute connection between concentration and voltage value but only provides that Resistance relationship Between the sensor resistance RS to the measured value RO at a concentration of 0.4mg of alcohol per liter of air for different concentrations. In addition, the load resistance RL on the module has the value 1 kΩ, instead of a value between 100kΩ and 500kΩ, as the datasheet specifies. So "youth research" is required.
I presented the part of the module that was used in the article in Image 1. I left out the digital part with the Comparator LM393.
Image 1: sensor circuit
The sensor resistance RS between the points - AB can be calculated if you know the operating voltage of 5V and the voltage UAO at the analog output AO, as well as the value of the load resistance RL. RS and RL form a voltage divider. The voltage 5V - UAO is on RS, and the output voltage is UAO on RL. The partial voltages behave like the partial resistors.
Image 2: Calculation of the sensor resistance RS
So that wouldn't be a problem. But the determination of the resistance value RS at a concentration of 0.4mg of alcohol in 1 liter of air for the value is a problem. Already 0.4mg of alcohol, which is 0.5mm³ to measure with household aids, is impossible.
Therefore, only the self-experiment remains. After a look in the Catalog of fines Could it look that way? If you double the value of the breathing air concentration, you get the blood alcohol level. In other words, I get the breathing air concentration by halving the blood value. The latter can be calculated. An example:
A man with 80kg body mass has 80kg • 0.7 = 56kg body fluid. The reduction factor in women is 0.6. It drinks 1l of beer = a measure of alcohol content of 5.5 % vol. Pure alcohol has a density of 0.79g/cm³, so the 55cm³ have a mass of 55cm³ • 0.79g/cm³ = 43.45g. In relation to 56kg body fluid, this is 43.45g / 56kg = 0.78 per thousand. And that would result in a breathing air concentration of almost 0.4 per thousand or 0.4 mg/liter. However, the measure would have to be drunk on one train if possible, because the liver reduces by 0.15 per thousand per hour. However, whether you are still able to calibrate your structure after enjoying a 1-liter beer can only decide for yourself, and you should no longer drive a car.
Hardware
I chose an ESP8266 as a controller because it has a reasonably linear ADC characteristic. Basically, an ESP32 would also be useful, but it causes a higher effort for the straightening of the characteristic line. I'll come back to it later.
The first three ESP8266 models in the partial list have the advantage that the voltage of the USB connection is available on the connection VIN / 5V. This saves an additional power supply during development. A power supply is recommended for later operation as a stand-alone device, because batteries or a battery is quickly sucked, after all, the heating of the MQ-3 150 MA and the ESP8266 pulls another 20 mA.
1 |
D1 Mini Nodemcu with ESP8266-12F WLAN module or D1 Mini V3 Nodemcu with ESP8266-12F or |
1 |
|
1 |
|
1 |
MOREGAND ROTARY POTENITOMENT with protective resistance 3590s 10k ohm |
1 |
|
1 |
|
1 |
|
|
Various jumper cable |
1 |
Resistance 4K7 |
2 |
Resistance 10k |
2 |
Resistance 390 Ohm |
1 |
The software
For flashing and the programming of the ESP32:
Thonny or
Used firmware for the ESP8266:
Used firmware for the ESP32:
The micropython programs for the project:
SSD1306.PY Hardware driver for the OLED display
oled.py API for the OLED display
Calibrate.py Program for calibrating the ADC
Heat. Py Program for determining the preheat duration of the MQ-3
Micropython - Language - Modules and Programs
To install Thonny you will find one here Detailed instructions (English version). There is also a description of how the Micropython firmware (As of 18.06.2022) on the ESP chip burned becomes.
Micropython is an interpreter language. The main difference to the Arduino IDE, where you always flash entire programs, is that you only have to flash the Micropython firmware once on the ESP32 so that the controller understands Micropython instructions. You can use Thonny, µpycraft, or ESPTOOL.PY. For Thonny I have the process here described.
As soon as the firmware has flashed, you can easily talk to your controller in a dialogue, test individual commands and see the answer immediately without having to compile and transmit an entire program beforehand. That is exactly what bothers me about the Arduino IDE. You simply save an enormous time if you can check simple tests of the syntax and hardware to try out and refine functions and entire program parts via the command line before knitting a program from it. For this purpose, I always like to create small test programs. As a kind of macro, they summarize recurring commands. Whole applications then develop from such program fragments.
Autostart
If the program is to start autonomously by switching on the controller, copy the program text into a newly created blank tile. Save this file under boot.py in WorkSpace and upload it to the ESP chip. The program starts automatically the next time the reset or switching is on.
Test programs
Programs from the current editor window in the Thonny-IDE are started manually via the F5 button. This can be done faster than the mouse click on the start button, or via the menu run. Only the modules used in the program must be in the flash of the ESP32.
In between, Arduino id again?
Should you later use the controller together with the Arduino IDE, just flash the program in the usual way. However, the ESP32/ESP8266 then forgot that it has ever spoken Micropython. Conversely, any espressif chip that contains a compiled program from the Arduino IDE or AT-Firmware or Lua or ... can be easily provided with the Micropython firmware. The process is always here described.
Calibration of the ADC
Before it deals with the use of the MQ-3 sensor, we have to deal with the ADC characteristic of the ESP8266, because this affects the accuracy of the voltage measurement on the alley. The ADC (Analogous Digital Converter) converts tensions in the range from 0V to 3.3V into integer values from 0 to 1023. First of all, it is simply a matter of determining how the connection between the ADC values and the readings on a DVM (digital voltmeter). The goal is then to develop a method that leads to both values (almost) being the same. The circuit for the investigation shows in Image 3.
Image 3: Alcomat - Calibration of the ADC
With the 10-speed pot, I present a precisely adjustable voltage (2), which is led via the voltage divider from the resistors 4K7 and 10K to the analog input A0 of the ESP8266. Connection 1 lies at 3.3V, connection 3 to GND. The V-input of the DVM is also connected to the grinding contact (2) of the potentiometer, the COM connection is due to GND. The PIN assignment of the potentiometer takes some getting used to because the handle (2) is not the medium connection!
Image 4: circuit of the ten-speed potentiometer
Now I set a tension, write down the value of the DVM, and write the ADC value (counts) and the tension calculated from it. I repeat the process in small steps until I arrived at 3.2V. I use the program for this Calibrate.py. I present the result of the measurements after the program has been discussed.
# Calibrate.py
# After flashing the firmware on the ESP8266:
# Import WebRepl_Setup
#> D FUR DISABLE
# Then RST; Restart!
# Pintranslator for ESP8266 boards
# Lua-Pins D0 D1 D2 D3 D4 D5 D6 D7 D8
# ESP8266 Pins 16 5 4 0 2 14 12 13 15
# SC SD
From machine import Pin code, ADC, Softi2c
From time import sleep,Ticks_MS, Sleep_ms
From OLED import OLED
import sys
From OneWire import OneWire
From DS18x20 import DS18x20
The modules oled.py and SSD1306.PY must have been uploaded to the ESP8266 before you can import them. All others are part of the Micropython kernel.
IF sys.platform == "ESP8266":
I2C=Softi2c(scl=Pin code(5),sda=Pin code(4))
ADC=ADC(0)
maxcnt=1023
elif sys.platform == "ESP32":
I2C=Softi2c(scl=Pin code(22),sda=Pin code(21))
ADC=ADC(Pin code(36))
ADC.attack(ADC.Attn_11db)
ADC.Width(ADC.Width_12bit)
maxcnt=4095
Else:
raise Runtime("Unknown Port")
The IF construct checks the supported microcontroller ESP32 and ESP8266. Other boards lead to a runtime exception and thus to the program. The i2C bus is initialized controller-specifically, as is the ADC with settings.
ds_pin = Pin code(14) # D5 @ ESP8266
DS = DS18x20(OneWire(ds_pin))
chip = DS.scan()[0]
print(chip)
I create a one-wire object to PIN 14 (D5) and hand it over directly to the constructor of the class DS18x20. Ds.Scan() Looking for a DS18B20 chip on the bus and giving its ROM detection in the form of a list return.
R1=4600 # Ohm
R2=10000 # Ohm
U0=3.2 # Volt on the sensor
RL=1000 # Ohm load resistance
scale=(R1+R2)/R2
The voltage of the MQ-3 sensor is later placed on the voltage divider as well as now as the tap (2) of the potentiometer. The values should be measured using DMM (digital multimeter) and entered for R1 and R2. The scaling factor scale allows the COUNTS to be extrapolated by the ADC on the tension at the entrance of the voltage divider.
D=OLED(I2C,Heightw=32)
D.clearall()
D.writer("Alkomat 1.0",3,0)
An OLED object is created. I hand over the I2C object and the height of the display in pixels. The default width is 128 and does not have to be specified. Delete the display and output the heading.
def Time-out(T):
begin=Ticks_MS()
def compare():
return intimately(Ticks_MS()-begin) >= T
return compare
The Closure Time-out() When calling, produces a method that represents a non -blocking software -timer. During this, other things can be done by the program.
def waitheating(Val=90000):
hot=Time-out(Val)
D.writer("PLEASE WAIT",0,1)
rest=intimately(Val/1000)
while need hot():
D.writer("Still {} seconds".format(rest),0,2)
sleep(1)
rest-=1
D.clearft(0,1)
The function waitheating() takes a value in milliseconds. I hereby put it with Time-out() A timer that spends the remaining duration on the display during the expiry. The timer is via the function hot() queried which Time-out() returned. hot() is nothing more than a Alias for the function compare() that inside Time-out() is defined.
def getval(n):
sum=0
for I in range(n):
sum = sum + ADC.read()
AVG = sum/n
volt=U0/maxcnt*AVG*scale
# Volt = (volt*1000-63.5567) /0.99
return (AVG, volt)
To the function getval() I hand over n The number of individual measurements of the ADC to be made. The mean value is determined as the most likely result from the sum of the measured values. This procedure largely eliminates the noise on the line. The extrapolation follows the input level of the voltage divider. The quotient from the maximum voltage U0 = 3.2V and the maximum converter value maxcnt= 1023 delivers the smallest tension quantity that corresponds to a count of the ADC. Multiplied by counting the ADC, I get the voltage on the A0 connection. This value must now be entered into the input of the voltage divider by multiplication with the scaling factor scale be extrapolated. The previous previous line later corrects the measurement errors of the ESP8266. The explanation follows below.
def getting temperature(C):
DS.convert_temp()
Sleep_ms(750)
return DS.read_temp(C)
The temperature of the MQ-3 housing is a measure of the usability of the measured value on the alley. It must be at least 25 ° C. The function getting temperature() I hand over the ROM code of the DS18B20. A measurement is triggered. After 750 milliseconds, the measured value is available and can be called up and returned.
def prayer(U):
return ((U0*1000 - U) * RL ) / U
From the output voltage U The function can prayer(), according to Image 2, the sensor resistance RS Calculate the value of which it releases. U0 was given in Volt at the beginning, but U is calculated in Millivolt. Therefore, U0 must be multiplied by 1000 to obtain MV.
To this end, it was about program parts that are also used for other programs around the MQ-3. The following very short main program provides the ADC raw value in counts and the extrapolated value at the entrance of the voltage divider in the display. getval() delivers both values as Tupel Back that in the variables CNT and volt is unpacked.
D.clearft(0,1)
while 1:
CNT,volt=getval(50)
D.writer("{} cnt".format(intimately(CNT)),0,1)
D.writer("{0: 2.3f} MV".format(volt),0,2)
sleep(1)
Here is the result of the series of measurements.
DVM |
ESP8266 |
ESP8266 |
MV |
CNT |
MV |
102 |
36 |
169 |
149 |
46 |
210 |
200 |
57 |
260 |
249 |
67 |
310 |
309 |
80 |
365 |
406 |
101 |
463 |
499 |
122 |
557 |
609 |
146 |
671 |
750 |
177 |
809 |
906 |
210 |
962 |
1114 |
255 |
1169 |
1317 |
300 |
1374 |
1552 |
350 |
1601 |
1998 |
449 |
2051 |
2472 |
551 |
2518 |
3019 |
667 |
3047 |
3189 |
706 |
3227 |
All measured values of the ESP8266 are clearly too large. The connection provides a diagram that I get from the column in Libre Office Calc DVM MV and ESP8266 MV created.
Image 5: DVM-ESP8266 diagram
The relation coefficient R² confirms with a value of quasi 1 that the measuring points are very precise on the trend line (straight). So there is a linear connection between the DVM and ADC values. However, this does not go through the origin of the coordinate system. This is also what the Functionsterm F (X) says.
In order to obtain the DVM value X from the extrapolated ADC voltage value = functional value F (x), I have to dissolve the equation according to X.
Image 6: ADC error correction
With this formula, I have the corrected values calculated from the ESP8266 MV values. The table shows the effect. With the exception of two values, all corrected values are within a fault limit of +/- 1%. This formula can also be found in the function getval() again. If I now remove the comment sign and start the program again, DVM and ESP8266 MV values are approximately the same. Due to the exemplary scattering, you should always carry out the calibration yourself.
DVM |
Corrected |
delta |
delta |
MV |
MV |
MV |
% |
102 |
107 |
5 |
4,4 |
149 |
148 |
-1 |
-0,7 |
200 |
198 |
-2 |
-0,8 |
249 |
249 |
0 |
0,0 |
309 |
304 |
-5 |
-1,5 |
406 |
403 |
-3 |
-0,6 |
499 |
498 |
-1 |
-0,1 |
609 |
613 |
4 |
0,7 |
750 |
753 |
3 |
0,4 |
906 |
908 |
2 |
0,2 |
1114 |
1117 |
3 |
0,2 |
1317 |
1324 |
7 |
0,5 |
1552 |
1553 |
1 |
0,1 |
1998 |
2008 |
10 |
0,5 |
2472 |
2479 |
7 |
0,3 |
3019 |
3014 |
-5 |
-0,2 |
3189 |
3195 |
6 |
0,2 |
Preheat MQ-3-how long does it take?
The MQ-3 only works reliably if the sensor has reached its working temperature. The program finds when the entry Evil.py is out of here.
The only difference to Calibrate.py Exists in the main program.
D.clearall()
time = 0
delta=2
while 1:
CNT,volt=getval(50)
tempo=getting temperature(chip)
D.writer("{} sec".format(time),0,0)
D.writer("{0: 2.2f} *c".format(tempo),0,1)
D.writer("{0: 2.3f} MV".format(volt),0,2)
print(time,tempo,volt)
time+=delta
sleep(delta-1)
I get ADC, voltage value, and temperature on the MQ-3 case. The DS18B20 provides the temperature value, which I attached to the housing with a clamping rubber.
Image 7: DS18B20 on the MQ-3 case
The data line is drawn to the operating voltage of 3.3V with a pullup resistor of 10kΩ. Instead of the potentiometer, the output AO of the MQ-3 is now on the voltage divider. Image 8 shows the circuit diagram.
Image 8: Alkomat - circuit
Since the sensor is +5V, tensions of more than 3.3V at the AO output could theoretically occur. For this reason, I used the voltage divider. It delivers two-thirds of the input voltage on the middle grip. At 5V input voltage, this is 3.33V and we have the circuit in dry cloths.
The program now lists time temperature and voltage values in the terminal at the same time intervals. If the latter no longer differs significantly from the previous value, the time is reached. The time specified for this can then apply to the function waitheating() handed over, which is called up when a user program is started. During my preliminary tests, there was a time of about 5 minutes that should be waited for before the first test blowing. Incidentally, a thicker drink or a 10 to 15-cm long, thin tube piece has proven to be cheap to blow up the sensor.
This static procedure with a fixed waiting period makes sense for an absolutely cold start. A dynamic working method makes more sense in continuous operation. I put them with the program alcohol test.py in the next episode.
Stay tuned!
1 commento
Bruno
Genau danach habe ich gesucht! Vielen Dank :)