This commit is contained in:
Your Name
2025-02-26 10:39:00 +01:00
parent 105e439199
commit d0e1ad18e9
10 changed files with 331 additions and 41 deletions

View File

@@ -5,9 +5,8 @@
| |__| |_| / __/ | |__| |_| / __/
\____\___/_____| \____\___/_____|
Script to get CO2 values and write it to text file Script to get CO2 values and write it to the database
need parameter: CO2_port /usr/bin/python3 /var/www/moduleair_pro_4g/MH-Z19/write_data.py
/usr/bin/python3 /var/www/moduleair_pro_4g/MH-Z19/write_data.py ttyAMA4
''' '''
import serial import serial
@@ -16,13 +15,32 @@ import json
import sys import sys
import subprocess import subprocess
import time import time
import sqlite3
parameter = sys.argv[1:] # Exclude the script name
#print("Parameters received:")
port='/dev/'+parameter[0]
# Connect to the SQLite database
conn = sqlite3.connect("/var/www/moduleair_pro_4g/sqlite/sensors.db")
cursor = conn.cursor()
def load_config(config_file):
try:
with open(config_file, 'r') as file:
config_data = json.load(file)
return config_data
except Exception as e:
print(f"Error loading config file: {e}")
return {}
# Load the configuration data
config_file = '/var/www/moduleair_pro_4g/config.json'
config = load_config(config_file)
mh_z19_port = config.get('MH-Z19_port', '') #port du NPM solo
ser = serial.Serial( ser = serial.Serial(
port=port, port=mh_z19_port,
baudrate=9600, baudrate=9600,
parity=serial.PARITY_NONE, parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE, stopbits=serial.STOPBITS_ONE,
@@ -78,10 +96,23 @@ def main():
co2 = read_co2() co2 = read_co2()
if co2 is not None: if co2 is not None:
print(f"CO2 Concentration: {co2} ppm") print(f"CO2 Concentration: {co2} ppm")
#save to file
output_file = "/var/www/moduleair_pro_4g/matrix/input_MHZ16.txt" output_file = "/var/www/moduleair_pro_4g/matrix/input_MHZ16.txt"
with open(output_file, "w") as file: with open(output_file, "w") as file:
file.write(f"{co2} \n") file.write(f"{co2} \n")
print(f"Data written to {output_file}") print(f"Data written to {output_file}")
#GET RTC TIME from SQlite
cursor.execute("SELECT * FROM timestamp_table LIMIT 1")
row = cursor.fetchone() # Get the first (and only) row
rtc_time_str = row[1] # '2025-02-07 12:30:45'
#save to sqlite
cursor.execute('''
INSERT INTO data_CO2 (timestamp,CO2) VALUES (?,?)'''
, (rtc_time_str,co2))
# Commit and close the connection
conn.commit()
else: else:
print("Failed to get CO2 data.") print("Failed to get CO2 data.")
@@ -89,6 +120,7 @@ def main():
print("Program terminated.") print("Program terminated.")
finally: finally:
ser.close() ser.close()
conn.close()
if __name__ == '__main__': if __name__ == '__main__':
main() main()

190
SARA/reboot/start.py Normal file
View File

@@ -0,0 +1,190 @@
'''
____ _ ____ _
/ ___| / \ | _ \ / \
\___ \ / _ \ | |_) | / _ \
___) / ___ \| _ < / ___ \
|____/_/ \_\_| \_\/_/ \_\
Script that starts at the boot of the RPI (with cron)
@reboot sleep 30 && /usr/bin/python3 /var/www/moduleair_pro_4g/SARA/reboot/start.py >> /var/www/moduleair_pro_4g/logs/app.log 2>&1
/usr/bin/python3 /var/www/moduleair_pro_4g/SARA/reboot/start.py
'''
import serial
import time
import sys
import json
import re
#get data from config
def load_config(config_file):
try:
with open(config_file, 'r') as file:
config_data = json.load(file)
return config_data
except Exception as e:
print(f"Error loading config file: {e}")
return {}
#Fonction pour mettre à jour le JSON de configuration
def update_json_key(file_path, key, value):
"""
Updates a specific key in a JSON file with a new value.
:param file_path: Path to the JSON file.
:param key: The key to update in the JSON file.
:param value: The new value to assign to the key.
"""
try:
# Load the existing data
with open(file_path, "r") as file:
data = json.load(file)
# Check if the key exists in the JSON file
if key in data:
data[key] = value # Update the key with the new value
else:
print(f"Key '{key}' not found in the JSON file.")
return
# Write the updated data back to the file
with open(file_path, "w") as file:
json.dump(data, file, indent=2) # Use indent for pretty printing
print(f"💾 updating '{key}' to '{value}'.")
except Exception as e:
print(f"Error updating the JSON file: {e}")
# Define the config file path
config_file = '/var/www/moduleair_pro_4g/config.json'
# Load the configuration data
config = load_config(config_file)
baudrate = config.get('SaraR4_baudrate', 115200) #baudrate du sara R4
device_id = config.get('deviceID', '').upper() #device ID en maj
ser_sara = serial.Serial(
port='/dev/ttyAMA2',
baudrate=baudrate, #115200 ou 9600
parity=serial.PARITY_NONE, #PARITY_NONE, PARITY_EVEN or PARITY_ODD
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout = 2
)
def read_complete_response(serial_connection, timeout=2, end_of_response_timeout=2, wait_for_lines=None, debug=True):
'''
Fonction très importante !!!
Reads the complete response from a serial connection and waits for specific lines.
'''
if wait_for_lines is None:
wait_for_lines = [] # Default to an empty list if not provided
response = bytearray()
serial_connection.timeout = timeout
end_time = time.time() + end_of_response_timeout
start_time = time.time()
while True:
elapsed_time = time.time() - start_time # Time since function start
if serial_connection.in_waiting > 0:
data = serial_connection.read(serial_connection.in_waiting)
response.extend(data)
end_time = time.time() + end_of_response_timeout # Reset timeout on new data
# Decode and check for any target line
decoded_response = response.decode('utf-8', errors='replace')
for target_line in wait_for_lines:
if target_line in decoded_response:
if debug:
print(f"[DEBUG] 🔎 Found target line: {target_line} (in {elapsed_time:.2f}s)")
return decoded_response # Return response immediately if a target line is found
elif time.time() > end_time:
if debug:
print(f"[DEBUG] Timeout reached. No more data received.")
break
time.sleep(0.1) # Short sleep to prevent busy waiting
# Final response and debug output
total_elapsed_time = time.time() - start_time
if debug:
print(f"[DEBUG] ⏱️ elapsed time: {total_elapsed_time:.2f}s. ⏱️")
# Check if the elapsed time exceeded 10 seconds
if total_elapsed_time > 10 and debug:
print(f"[ALERT] 🚨 The operation took too long 🚨")
print(f'<span style="color: red;font-weight: bold;">[ALERT] ⚠️{total_elapsed_time:.2f}s⚠</span>')
return response.decode('utf-8', errors='replace') # Return the full response if no target line is found
try:
print('<h3>Start reboot python script</h3>')
#check modem status
print("Check SARA Status")
command = f'ATI\r'
ser_sara.write(command.encode('utf-8'))
response_SARA_ATI = read_complete_response(ser_sara, wait_for_lines=["IMEI"])
print(response_SARA_ATI)
match = re.search(r"Model:\s*(.+)", response_SARA_ATI)
model = match.group(1).strip() if match else "Unknown" # Strip unwanted characters
print(f" Model: {model}")
update_json_key(config_file, "modem_version", model)
time.sleep(1)
# 1. Set AIRCARTO URL
print('Set aircarto URL')
aircarto_profile_id = 0
aircarto_url="data.nebuleair.fr"
command = f'AT+UHTTP={aircarto_profile_id},1,"{aircarto_url}"\r'
ser_sara.write(command.encode('utf-8'))
response_SARA_1 = read_complete_response(ser_sara, wait_for_lines=["OK"])
print(response_SARA_1)
time.sleep(1)
#2. Set uSpot URL
print('Set uSpot URL')
uSpot_profile_id = 1
uSpot_url="api-prod.uspot.probesys.net"
command = f'AT+UHTTP={uSpot_profile_id},1,"{uSpot_url}"\r'
ser_sara.write(command.encode('utf-8'))
response_SARA_2 = read_complete_response(ser_sara, wait_for_lines=["OK"])
print(response_SARA_2)
time.sleep(1)
print("set port 81")
command = f'AT+UHTTP={uSpot_profile_id},5,81\r'
ser_sara.write((command + '\r').encode('utf-8'))
response_SARA_55 = read_complete_response(ser_sara, wait_for_lines=["OK"])
print(response_SARA_55)
time.sleep(1)
#3. Get localisation (CellLocate)
mode = 2
sensor = 2
response_type = 0
timeout_s = 2
accuracy_m = 1
command = f'AT+ULOC={mode},{sensor},{response_type},{timeout_s},{accuracy_m}\r'
ser_sara.write((command + '\r').encode('utf-8'))
response_SARA_3 = read_complete_response(ser_sara, wait_for_lines=["+UULOC"])
print(response_SARA_3)
match = re.search(r"\+UULOC: \d{2}/\d{2}/\d{4},\d{2}:\d{2}:\d{2}\.\d{3},([-+]?\d+\.\d+),([-+]?\d+\.\d+)", response_SARA_3)
if match:
latitude = match.group(1)
longitude = match.group(2)
print(f"📍 Latitude: {latitude}, Longitude: {longitude}")
else:
print("❌ Failed to extract coordinates.")
#update config.json
update_json_key(config_file, "latitude_raw", float(latitude))
update_json_key(config_file, "longitude_raw", float(longitude))
time.sleep(1)
except Exception as e:
print("An error occurred:", e)
traceback.print_exc() # This prints the full traceback

View File

@@ -8,25 +8,11 @@
OUTPUT_FILE="/var/www/moduleair_pro_4g/wifi_list.csv" OUTPUT_FILE="/var/www/moduleair_pro_4g/wifi_list.csv"
JSON_FILE="/var/www/moduleair_pro_4g/config.json" JSON_FILE="/var/www/moduleair_pro_4g/config.json"
echo "-------------------" echo "-------------------"
echo "-------------------" echo "-------------------"
echo "NebuleAir pro started at $(date)" echo "NebuleAir pro started at $(date)"
# Blink GPIO 23 and 24 five times
for i in {1..5}; do
# Turn GPIO 23 and 24 ON
gpioset gpiochip0 23=1 24=1
#echo "LEDs ON"
sleep 1
# Turn GPIO 23 and 24 OFF
gpioset gpiochip0 23=0 24=0
#echo "LEDs OFF"
sleep 1
done
echo "getting SARA R4 serial number" echo "getting SARA R4 serial number"
# Get the last 8 characters of the serial number and write to text file # Get the last 8 characters of the serial number and write to text file
@@ -34,6 +20,7 @@ serial_number=$(cat /proc/cpuinfo | grep Serial | awk '{print substr($3, length(
# Define the JSON file path # Define the JSON file path
# Use jq to update the "deviceID" in the JSON file # Use jq to update the "deviceID" in the JSON file
jq --arg serial_number "$serial_number" '.deviceID = $serial_number' "$JSON_FILE" > temp.json && mv temp.json "$JSON_FILE" jq --arg serial_number "$serial_number" '.deviceID = $serial_number' "$JSON_FILE" > temp.json && mv temp.json "$JSON_FILE"
echo "id: $serial_number" echo "id: $serial_number"
#get the SSH port for tunneling #get the SSH port for tunneling
SSH_TUNNEL_PORT=$(jq -r '.sshTunnel_port' "$JSON_FILE") SSH_TUNNEL_PORT=$(jq -r '.sshTunnel_port' "$JSON_FILE")
@@ -59,7 +46,7 @@ if [ "$STATE" == "30 (disconnected)" ]; then
else else
echo "Success: wlan0 is connected!" echo "🛜Success: wlan0 is connected!🛜"
CONN_SSID=$(nmcli -g GENERAL.CONNECTION device show wlan0) CONN_SSID=$(nmcli -g GENERAL.CONNECTION device show wlan0)
echo "Connection: $CONN_SSID" echo "Connection: $CONN_SSID"
@@ -69,27 +56,27 @@ else
sudo chmod 777 "$JSON_FILE" sudo chmod 777 "$JSON_FILE"
# Lancer le tunnel SSH # Lancer le tunnel SSH
echo "Démarrage du tunnel SSH sur le port $SSH_TUNNEL_PORT..." #echo "Démarrage du tunnel SSH sur le port $SSH_TUNNEL_PORT..."
# Start the SSH agent if it's not already running # Start the SSH agent if it's not already running
eval "$(ssh-agent -s)" #eval "$(ssh-agent -s)"
# Add your SSH private key # Add your SSH private key
ssh-add /home/airlab/.ssh/id_rsa #ssh-add /home/airlab/.ssh/id_rsa
#connections details #connections details
REMOTE_USER="airlab_server1" # Remplacez par votre nom d'utilisateur distant #REMOTE_USER="airlab_server1" # Remplacez par votre nom d'utilisateur distant
REMOTE_SERVER="aircarto.fr" # Remplacez par l'adresse de votre serveur #REMOTE_SERVER="aircarto.fr" # Remplacez par l'adresse de votre serveur
LOCAL_PORT=22 # Port local à rediriger #LOCAL_PORT=22 # Port local à rediriger
MONITOR_PORT=0 # Désactive la surveillance de connexion autossh #MONITOR_PORT=0 # Désactive la surveillance de connexion autossh
#autossh -M "$MONITOR_PORT" -f -N -R "$SSH_TUNNEL_PORT:localhost:$LOCAL_PORT" "$REMOTE_USER@$REMOTE_SERVER" -p 50221 #autossh -M "$MONITOR_PORT" -f -N -R "$SSH_TUNNEL_PORT:localhost:$LOCAL_PORT" "$REMOTE_USER@$REMOTE_SERVER" -p 50221
# ssh -f -N -R 52221:localhost:22 -p 50221 airlab_server1@aircarto.fr # ssh -f -N -R 52221:localhost:22 -p 50221 airlab_server1@aircarto.fr
ssh -i /var/www/.ssh/id_rsa -f -N -R "$SSH_TUNNEL_PORT:localhost:$LOCAL_PORT" -p 50221 -o StrictHostKeyChecking=no "$REMOTE_USER@$REMOTE_SERVER" #ssh -i /var/www/.ssh/id_rsa -f -N -R "$SSH_TUNNEL_PORT:localhost:$LOCAL_PORT" -p 50221 -o StrictHostKeyChecking=no "$REMOTE_USER@$REMOTE_SERVER"
#Check if the tunnel was created successfully #Check if the tunnel was created successfully
if [ $? -eq 0 ]; then #if [ $? -eq 0 ]; then
echo "Tunnel started successfully!" # echo "Tunnel started successfully!"
else #else
echo "Error: Unable to start the tunnel!" # echo "Error: Unable to start the tunnel!"
exit 1 # exit 1
fi #fi
fi fi
echo "-------------------" echo "-------------------"

View File

@@ -5,10 +5,18 @@
"RTC/save_to_db.py": true, "RTC/save_to_db.py": true,
"BME280/get_data_v2.py": true, "BME280/get_data_v2.py": true,
"envea/read_value_v2.py": true, "envea/read_value_v2.py": true,
"MH-Z19/write_data.py": true,
"sqlite/flush_old_data.py": true, "sqlite/flush_old_data.py": true,
"deviceName": "ModuleAir-proXXX",
"deviceID": "XXXXX", "deviceID": "XXXXX",
"modem_version": "XXX",
"latitude_raw": 0,
"longitude_raw":0,
"latitude_precision": 0,
"longitude_precision": 0,
"SaraR4_baudrate": 115200, "SaraR4_baudrate": 115200,
"NPM_solo_port": "/dev/ttyAMA3", "NPM_solo_port": "/dev/ttyAMA3",
"MH-Z19_port": "/dev/ttyAMA4",
"NextPM_ports": ["ttyAMA3"], "NextPM_ports": ["ttyAMA3"],
"CO2_serial": true, "CO2_serial": true,
"sensirion_SFA30": false, "sensirion_SFA30": false,
@@ -19,5 +27,8 @@
"SARA_R4_general_status" : "connected", "SARA_R4_general_status" : "connected",
"SARA_R4_SIM_status" : "connected", "SARA_R4_SIM_status" : "connected",
"SARA_R4_network_status": "connected", "SARA_R4_network_status": "connected",
"SARA_R4_neworkID": 0,
"send_aircarto": true,
"send_uSpot": false,
"WIFI_status": "connected" "WIFI_status": "connected"
} }

View File

@@ -2,7 +2,7 @@
@reboot /var/www/moduleair_pro_4g/boot_hotspot.sh >> /var/www/moduleair_pro_4g/logs/app.log 2>&1 @reboot /var/www/moduleair_pro_4g/boot_hotspot.sh >> /var/www/moduleair_pro_4g/logs/app.log 2>&1
@reboot sleep 30 && /usr/bin/python3 /var/www/moduleair_pro_4g/SARA/sara_setURL.py ttyAMA2 data.moduleair.fr 0 >> /var/www/moduleair_pro_4g/logs/app.log 2>&1 @reboot sleep 30 && /usr/bin/python3 /var/www/moduleair_pro_4g/SARA/reboot/start.py >> /var/www/moduleair_pro_4g/logs/app.log 2>&1
@reboot /var/www/moduleair_pro_4g/run_every_10_seconds.sh @reboot /var/www/moduleair_pro_4g/run_every_10_seconds.sh

View File

@@ -68,6 +68,8 @@
</select> </select>
</div> </div>
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM',getSelectedLimit(),false)">Mesures PM</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_NPM',getSelectedLimit(),false)">Mesures PM</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_CO2',getSelectedLimit(),false)">Mesures CO2</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_BME280',getSelectedLimit(),false)">Mesures Temp/Hum</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_BME280',getSelectedLimit(),false)">Mesures Temp/Hum</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM_5channels',getSelectedLimit(),false)">Mesures PM (5 canaux)</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_NPM_5channels',getSelectedLimit(),false)">Mesures PM (5 canaux)</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_envea',getSelectedLimit(),false)">Sonde Cairsens</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_envea',getSelectedLimit(),false)">Sonde Cairsens</button>
@@ -90,6 +92,8 @@
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM',10,true, getStartDate(), getEndDate())">Mesures PM</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_NPM',10,true, getStartDate(), getEndDate())">Mesures PM</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_CO2',10,true, getStartDate(), getEndDate())">Mesures CO2</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_BME280',10,true, getStartDate(), getEndDate())">Mesures Temp/Hum</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_BME280',10,true, getStartDate(), getEndDate())">Mesures Temp/Hum</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM_5channels',10,true, getStartDate(), getEndDate())">Mesures PM (5 canaux)</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_NPM_5channels',10,true, getStartDate(), getEndDate())">Mesures PM (5 canaux)</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_envea',10,true, getStartDate(), getEndDate())">Sonde Cairsens</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_envea',10,true, getStartDate(), getEndDate())">Sonde Cairsens</button>
@@ -255,6 +259,11 @@ function get_data_sqlite(table, limit, download , startDate = "", endDate = "")
<th>O3</th> <th>O3</th>
`; `;
}else if (table === "data_CO2") {
tableHTML += `
<th>Timestamp</th>
<th>CO2</th>
`;
} }
tableHTML += `</tr></thead><tbody>`; tableHTML += `</tr></thead><tbody>`;
@@ -302,6 +311,12 @@ function get_data_sqlite(table, limit, download , startDate = "", endDate = "")
`; `;
} }
else if (table === "data_CO2") {
tableHTML += `
<td>${columns[0]}</td>
<td>${columns[1]}</td>
`;
}
tableHTML += "</tr>"; tableHTML += "</tr>";
}); });

View File

@@ -83,8 +83,9 @@ def run_script(script_name, interval, delay=0):
# Define scripts and their execution intervals (seconds) # Define scripts and their execution intervals (seconds)
SCRIPTS = [ SCRIPTS = [
("RTC/save_to_db.py", 1, 0), # SAVE RTC time every 1 second, no delay ("RTC/save_to_db.py", 1, 0), # SAVE RTC time every 1 second, no delay
("NPM/get_data_modbus_v3.py", 10, 0), # Get NPM data (modbus 5 channels) every 10s, with 2s delay ("NPM/get_data_modbus_v3.py", 10, 0), # Get NPM data (modbus 5 channels) every 10s,
("envea/read_value_v2.py", 10, 0), # Get NPM data (modbus 5 channels) every 10s, with 2s delay ("envea/read_value_v2.py", 10, 0), # Get Envea data every 10s,
("MH-Z19/write_data.py", 10, 0), # Get CO2 values every 10s,
("loop/SARA_send_data_v2.py", 60, 1), # Send data every 60 seconds, with 2s delay ("loop/SARA_send_data_v2.py", 60, 1), # Send data every 60 seconds, with 2s delay
("BME280/get_data_v2.py", 120, 0), # Get BME280 data every 120 seconds, no delay ("BME280/get_data_v2.py", 120, 0), # Get BME280 data every 120 seconds, no delay
("sqlite/flush_old_data.py", 86400, 0) # flush old data inside db every day () ("sqlite/flush_old_data.py", 86400, 0) # flush old data inside db every day ()

View File

@@ -26,7 +26,8 @@ CREATE TABLE IF NOT EXISTS timestamp_table (
) )
""") """)
cursor.execute(""" cursor.execute("""
INSERT INTO timestamp_table (id, last_updated) VALUES (1, CURRENT_TIMESTAMP); INSERT OR REPLACE INTO timestamp_table (id, last_updated)
VALUES (1, CURRENT_TIMESTAMP);
""") """)
@@ -42,6 +43,14 @@ CREATE TABLE IF NOT EXISTS data_NPM (
) )
""") """)
# Create a table CO2
cursor.execute("""
CREATE TABLE IF NOT EXISTS data_CO2 (
timestamp TEXT,
CO2 INTEGER
)
""")
# Create a table BME280 # Create a table BME280
cursor.execute(""" cursor.execute("""
CREATE TABLE IF NOT EXISTS data_BME280 ( CREATE TABLE IF NOT EXISTS data_BME280 (

View File

@@ -16,6 +16,7 @@ data_NPM_5channels
data_BME280 data_BME280
data_envea data_envea
timestamp_table timestamp_table
data_CO2
''' '''
@@ -45,7 +46,7 @@ if row:
print(f"[INFO] Deleting records older than: {cutoff_date_str}") print(f"[INFO] Deleting records older than: {cutoff_date_str}")
# List of tables to delete old data from # List of tables to delete old data from
tables_to_clean = ["data_NPM", "data_NPM_5channels", "data_BME280", "data_envea"] tables_to_clean = ["data_NPM", "data_NPM_5channels", "data_BME280", "data_envea", "data_CO2"]
# Loop through each table and delete old data # Loop through each table and delete old data
for table in tables_to_clean: for table in tables_to_clean:

44
touch/test.py Normal file
View File

@@ -0,0 +1,44 @@
'''
_____ ___ _ _ ____ _ _
|_ _/ _ \| | | |/ ___| | | |
| || | | | | | | | | |_| |
| || |_| | |_| | |___| _ |
|_| \___/ \___/ \____|_| |_|
script to test the TTP223 sensor
/usr/bin/python3 /var/www/moduleair_pro_4g/touch/test.py
'''
import RPi.GPIO as GPIO
import time
# Setup GPIO
TOUCH_PIN = 6 # GPIO06 (BCM numbering)
GPIO.setmode(GPIO.BCM)
GPIO.setup(TOUCH_PIN, GPIO.IN)
print("Touch sensor ready. Waiting for touch...")
last_state = False # Track previous state (False = not touched)
try:
while True:
current_state = GPIO.input(TOUCH_PIN) == GPIO.HIGH
# If touch state has changed
if current_state != last_state:
if current_state:
print("👋 Sensor touched!")
else:
print("🖐️ Sensor released!")
last_state = current_state # Update last state
time.sleep(0.1) # Polling interval
except KeyboardInterrupt:
print("\nExiting...")
finally:
GPIO.cleanup()