

The software part is developed based on the Python programming language, and achieves network communication and hardware control including the microcontroller (ESP32), the hydrogen sulfide sensor (MQ-136), the temperature and humidity sensor (SHT30), the pump and TFT-LCD screen (ST7735). It has implemented functions such as real-time monitoring, data display, data upload, and remote control. The whole program is divided into two parts: embedded part (handle hardware driving) and PC-side application software part (for data display on the computer).
1.Software modules
1.Display driver: ST773.py – TFT screen driver
2.Font library: sysfont.py – system font definition
3.User interface: gui.py – interface display logic
4.Main program: main.py – system main logic
5.Network communication: wifi.py – WiFi connection management/mqtt.py – MQTT client implementation
6.Sensor driver: sht.py – SHT30 temperature and humidity sensor driver
1.H₂S concentration detection: measure H₂S concentration using the MQ-136 sensor
2.Temperature and humidity monitoring: measure ambient temperature and humidity using the SHT30 sensor
3.Data display: real-time display of measured data on a TFT screen
4.Data upload: upload data to the server via the MQTT protocol
5.Remote control: receive server commands to control device status
1.One-shot measurement mode (OneShot): trigger a single measurement manually
2.Cyclic Measurement Mode (Start): automatically perform timed measurements and upload data
3.Pump control: manually or remotely control the on/off state of the pump.
1. Initialization:
- Import libraries and define global variables
- TFT screen initialization
- WiFi connection
- SHT30 and MQ-136 sensor initialization
- Buttons (control screen contents) and motor initialization
- MQTT connection
- update(): manages change in system state and renders the menu, controls the pump state
def update():
global command,menuID,state,prestate,pumpFlag
prestate=state
if state ==1:
if command == "OK":
if menuID==0:
state=21
if menuID==1:
state=22
if menuID==2:
pumpFlag =1-pumpFlag
if command == "Left":
menuID -=1
if command == "Right":
menuID +=1
if menuID==0:
tft.text((0,40),"OneShot",TFT.YELLOW,sysfont,2,nowrap=True)
tft.text((0,56),"Start",TFT.WHITE,sysfont,2,nowrap=True)
tft.fillrect((90,72),(15,16),TFT.BLACK)
if menuID==1:
tft.text((0,40),"OneShot",TFT.WHITE,sysfont,2,nowrap=True)
tft.text((0,56),"Start",TFT.YELLOW,sysfont,2,nowrap=True)
tft.fillrect((90,72),(15,16),TFT.BLACK)
if menuID==2:
tft.text((0,40),"OneShot",TFT.WHITE,sysfont,2,nowrap=True)
tft.text((0,56),"Start",TFT.WHITE,sysfont,2,nowrap=True)
tft.text((90,72),"<",TFT.YELLOW,sysfont,2)
if pumpFlag ==1:
tft.fillrect((0,72),(90,16),TFT.BLACK)
tft.text((0,72),"Pump-ON",TFT.MAROON,sysfont,2,nowrap=True)
else:
tft.fillrect((0,72),(90,16),TFT.BLACK)
tft.text((0,72),"Pump-OFF",TFT.GRAY,sysfont,2,nowrap=True)
command =
if state ==21:
if command == "OK":
do_one()
if command == "Back":
state=1
if state ==22:
if command == "OK":
do_cycle()
if command == "Back":
state=1
if pumpFlag==1:
pumpON()
else:
pumpOFF()
if prestate!=state:
if state ==1:
GUI.show(tft,GUI.welcome)
if menuID==0:
tft.text((0,40),"OneShot",TFT.YELLOW,sysfont,2,nowrap=True)
tft.text((0,56),"Start",TFT.WHITE,sysfont,2,nowrap=True)
if menuID==1:
tft.text((0,40),"OneShot",TFT.WHITE,sysfont,2,nowrap=True)
tft.text((0,56),"Start",TFT.YELLOW,sysfont,2,nowrap=True)
if state ==21:
GUI.show(tft,GUI.oneshot)
if state ==22:
GUI.show(tft,GUI.start)
- on_KeyDown(): maps buttons to commands and calls update() function
def on_KeyDown(event):
global command,menuID,state
time.sleep(0.1)
if event.value()==0:
if event==Pin(3):
command="OK"
if event==Pin(8):
command="Back"
if event==Pin(18):
command="Left"
if event==Pin(17):
command="Right"
update()
- do_command(): processes command and call different functions to execute operations
def do_command(cmd):
print(cmd)
global pumpFlag,state
tft.text((13,96),cmd,TFT.BLUE,sysfont,1,nowrap=True)
if cmd=="do_one":
state=21
GUI.show(tft,GUI.oneshot)
do_one()
if cmd=="do_cycle":
state=22
GUI.show(tft,GUI.start)
do_cycle()
if cmd=="pump_on":
pumpFlag=1
pumpON()
if cmd=="pump_off":
pumpFlag=0
pumpOFF()
update()
cmd=
- on_Timer_H2S(): periodic callbacks for sensor data collection
def on_Timer_H2S(timer):
global rt,temparrT,temparrH,temparrV
if rt>0:
tft.text((40,24),str(rt)+" seconds. ",TFT.RED,sysfont,1,nowrap=True)
x=4.5*(sysfont["Width"]*2)
global mq,sht,val,tem,hum
tem,t,hum,h=sht.measure_int()
val=mq.read_uv()/1000
if rt>0:
temparrT.append(tem)
temparrH.append(hum)
temparrV.append(val)
v=2.5*sysfont["Height"]*2
tft.fillrect((x,v),(sysfont["Width"]*16,sysfont["Height"]*2),TFT.BLACK)
tft.text((x,v),str(val),TFT.YELLOW,sysfont,2,nowrap=True)
v+=sysfont["Height"]*2
tft.fillrect((x,v),(sysfont["Width"]*16,sysfont["Height"]*2),TFT.BLACK)
tft.text((x,v),str(tem),TFT.YELLOW,sysfont,1,nowrap=True)
v+=sysfont["Height"]*2
tft.fillrect((x,v),(sysfont["Width"]*16,sysfont["Height"]*2),TFT.BLACK)
tft.text((x,v),str(hum),TFT.YELLOW,sysfont,1,nowrap=True)
if rt>0:
rt-=1
if(rt==0):do_send()
- on_Timer_Alarm(): stops the timer
def on_Timer_Alarm(timer):
global tmerH2S
tmerH2S.deinit()
tft.text((0,16), "MQ is ready! ",TFT.GREEN, sysfont, 1, nowrap=True)
GUI.show(tft,GUI.welcome)
state=1
prestate=state
while state == 0:
pass
- do_one(): triggers a single measurement
def do_one():
global rt
rt=-1
on_Timer_H2S(None)
- do_cycle(): triggers a 20-time cyclic measurements
def do_cycle():
global tmerAlarm,tmerH2S,rt,temparrT,temparrH,temparrV
rt=20
temparrT=[]
temparrH=[]
temparrV=[]
try:
tmerH2S.deinit()
tmerAlarm.deinit()
except AttributeError:
pass
tft.fillrect((40,16),(100,8),TFT.BLACK)
tft.text((40,16),"Measuring..",TFT.RED,sysfont,1,nowrap=True)
tmerH2S = Timer(0)
tmerAlarm = Timer(1)
tmerAlarm.init(period=20000,mode=Timer.ONE_SHOT,callback=on_Timer_Alarm)
tmerH2S.init(period=1000,mode=Timer.PERIODIC,callback=on_Timer_H2S)
- pumpON(): turns on the pump motor
def pumpON():
print("on")
motor.on()
pass
- pumpOFF(): turns off the pump motor
def pumpOFF():
print("off")
motor.off()
pass
- do_send(): sends data to MQTT
def do_send():
global mqttc,temparrT,temparrH,temparrV
tft.fillrect((40,16),(100,8),TFT.BLACK)
tft.text((40,16),"Sending data..",TFT.YELLOW,sysfont,1,nowrap=True)
msg_dict = {
'H2S': temparrV,
'Tem': temparrT,
'Hum': temparrH,
}
mqtt.send_one(mqttc,msg_dict)
tft.fillrect((40,16),(100,8),TFT.BLACK)
tft.text((40,16),"Done!",TFT.GREEN,sysfont,1,nowrap=True)
- wait for MQTT message
- process button interaction
- execute sensor measurements (single or cyclic)
- process remote instructions
- recover network anomaly
(Each step has corresponding contents displayed on the screen.)
- Communication module: MessageProcess.py - core for MQTT Communication
- WiFi configuration tool: setWiFi.py - WiFi configuration tool for ESP32
- Data visualization: DataVisual.py - 3D data visualization/showfig.py - segmented data visualization
- User interface: LogWindow.py - graphical log and control interface
Data reception: reception of H₂S concentration, temperature, and humidity data in real time
Data storage: automatic saving of data to a CSV file.
Visual analysis:
- 3D scatter plots for displaying multi-parameter relationships.
- Data fitting curves.
- Segmented viewing of historical data.
- Device control:
- Switching between single or continuous measurement modes.
- Control of the pump (ventilation system).
1. MQTT communication (MessageProcess.py)
def on_message(client, userdata, msg):
try:
# Parse JSON data
data = json.loads(msg.payload.decode())
# Process command
if all(key in data for key in ['command']):
print("Command received:", data['command'])
return
# Verify data format is correct
if not all(key in data for key in ['Hum', 'H2S', 'Tem']):
print("Received message format is incorrect, skipping processing")
print("Received data:", data)
return
# Verify data is of list type
if not all(isinstance(data[key], list) for key in ['Hum', 'H2S', 'Tem']):
print("Data is not of list type, skipping processing")
print("Received data:", data)
return
# Update global variables
global Hum, H2S, Tem
Hum = data['Hum']
H2S = data['H2S']
Tem = data['Tem']
# Print received data
print("\nNew data received:")
print("Humidity data:", Hum)
print("H2S concentration:", H2S)
print("Temperature data:", Tem)
# Save data to CSV file
save_to_csv(Hum, H2S, Tem)
except json.JSONDecodeError:
print("JSON parsing error")
print("Received raw data:", msg.payload.decode())
except Exception as e:
print(f"Error occurred while processing message: {e}")
print("Received data:", msg.payload.decode())
- TLS Encryption: using SSL encryption for MQTT communication.
- Authentication Mechanism: MQTT username and password authentication.
- Data Verification: strict input data validation