Alkomat - Alkoholtester mit ESP8266 und MQ-3 in MicroPython - Teil 1 - AZ-Delivery

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.

Figure 1: sensor circuit

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.

Figure 2: Calculation of the sensor resistance RS

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

Nodemcu Lua Amica Module V2 ESP8266 ESP-12F WiFi or

Nodemcu Lua Lolin V3 Module ESP8266 ESP-12F WIFI

1

0.91 inch OLED I2C Display 128 x 32 pixels

1

Alcohol Gas Sensor DC 5V MQ-3 with signal output

1

MOREGAND ROTARY POTENITOMENT with protective resistance 3590s 10k ohm

1

KY-011 BI-Color LED module 5mm

1

DS18B20 digital temperature sensor to92

1

Mini Breadboard 400 PIN with 4 current rails


Various jumper cable

1

Resistance 4K7

2

Resistance 10k

2

Resistance 390 Ohm

1

Battery Expansion Shield 18650 V3 including USB cable

The software

For flashing and the programming of the ESP32:

Thonny or

µpycraft

Used firmware for the ESP8266:

V1.19.1 (2022-06-18).

Used firmware for the ESP32:

V1.19.1 (2022-06-18).

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.

Figure 3: Alcomat - Calibration of the ADC

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!

Figure 4: circuit of the ten-speed potentiometer

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.

Figure 5: DVM-ESP8266 diagram

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.

Figure 6: ADC error correction

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.

Figure 7: DS18B20 on the MQ-3 case

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.

Figure 8: Alkomat - circuit

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.

 Figure 9: MQ-3 + ESP8266 = alkomat

Image 9: MQ-3 + ESP8266 = alkomat

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!

DisplaysEsp-8266Projekte für fortgeschritteneSensoren

1 comment

Bruno

Bruno

Genau danach habe ich gesucht! Vielen Dank :)

Leave a comment

All comments are moderated before being published

Recommended blog posts

  1. ESP32 jetzt über den Boardverwalter installieren - AZ-Delivery
  2. Internet-Radio mit dem ESP32 - UPDATE - AZ-Delivery
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1 - AZ-Delivery
  4. ESP32 - das Multitalent - AZ-Delivery