From a102bd9895e1413c08e1698fa1ee184fcc012e80 Mon Sep 17 00:00:00 2001 From: MayureshMore Date: Sat, 8 Feb 2025 18:59:11 -0800 Subject: [PATCH 01/13] Implemented sensor connection --- .idea/.gitignore | 8 ++ .idea/PlantWaterSystem.iml | 9 ++ .idea/misc.xml | 6 ++ .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 ++ embedded/README.md | 192 +++++++++++++++++++++++++++++++++++++ embedded/plant_monitor.py | 184 +++++++++++++++++++++++++++++++++++ embedded/send_data_api.py | 101 +++++++++++++++++++ 8 files changed, 514 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/PlantWaterSystem.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 embedded/plant_monitor.py create mode 100644 embedded/send_data_api.py diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/PlantWaterSystem.iml b/.idea/PlantWaterSystem.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/PlantWaterSystem.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..e0844bc --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..f50ffa0 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/embedded/README.md b/embedded/README.md index e69de29..4f0ddaf 100644 --- a/embedded/README.md +++ b/embedded/README.md @@ -0,0 +1,192 @@ +# Setting Up PlantWaterSystem on a New Raspberry Pi + +This guide will walk you through setting up a new Raspberry Pi for your Soil Moisture Monitoring System using: +- Raspberry Pi +- Capacitive Soil Moisture Sensor +- ADS1115 Analog-to-Digital Converter (ADC) +- SQLite Database +- Python (for data processing and logging) + +--- +## 1. Hardware Requirements +### Components Needed +1. Raspberry Pi (any model with GPIO support) +2. Capacitive Soil Moisture Sensor +3. ADS1115 ADC Module +4. Jumper Wires +5. Power Supply for Raspberry Pi + +--- +## 2. Wiring Connections +Follow the wiring diagram you provided to correctly connect the components. + +### ADS1115 (ADC) to Raspberry Pi +| ADS1115 Pin | Raspberry Pi GPIO Pin | +|-------------|-----------------| +| VDD | 3.3V (Pin 1) | +| GND | GND (Pin 9) | +| SCL | GPIO3 (SCL, Pin 5) | +| SDA | GPIO2 (SDA, Pin 3) | +| ADDR | GPIO7 (Pin 7) | +| ALRT | GPIO0 (Pin 11) | + + +### Capacitive Soil Moisture Sensor to ADS1115 +| Sensor Pin | ADS1115 Pin | +|------------|-------------| +| VCC | 5V (Pin 2) | +| GND | GND (Pin 14) | +| Sensor 1 AO | A0 | +| Sensor 2 AO | A1 | +| Sensor 3 AO | A2 | +| Sensor 4 AO | A3 | + + +### Capacitive Soil Moisture Sensor Digital Output to Raspberry Pi +| Sensor DO Pin | Raspberry Pi GPIO | +|---------------|-------------------| +| Sensor 1 AO | GPIO8 (Pin 8). | +| Sensor 2 AO | GPI15 (Pin 10). | +| Sensor 3 AO | GPI18 (Pin 12). | +| Sensor 4 AO | GPI23 (Pin 16). | + +--- + +## 3. Setting Up the Raspberry Pi +### Step 1: Update Raspberry Pi OS +Run the following commands to update your system: +sudo apt update && sudo apt upgrade -y + +### Step 2: Enable I2C Communication +Check if I2C is enabled: + +ls /dev/i2c-* + +If you don’t see a response, enable it using: + +sudo raspi-config + +- Go to **Interfacing Options** → I2C → Enable. + +### **Step 3: Install Required Packages** + +Install Python + +sudo apt install pip + +Install necessary Python libraries: + +sudo pip install RPi.GPIO adafruit-circuitpython-ads1x15 --break-system-packages +sudo apt install sqlite3 + +Ensure I2C tools are installed: + +sudo apt install -y python3-smbus i2c-tools + + +### Step 4: Verify I2C Devices +Check if the ADS1115 is detected: + +i2cdetect -y 1 + +You should see an address like **0x48**, confirming that ADS1115 is correctly connected. + +--- +## 4. Deploying the Python Script +### Step 1: Create a Working Directory + +mkdir -p ~/PlantWaterSystem && cd ~/PlantWaterSystem + + +### **Step 2: Create the Python Script** +Create a file: + +nano plant_monitor.py +nano send_data_api.py + +Paste the updated Python script that reads analog and digital data from the sensor. + +--- + +## 5. Running the Python Script +Make the script executable: + +chmod +x plant_monitor.py + +Run it: + +python3 plant_monitor.py + +It should display: +plaintext +Starting Plant Sensor Monitoring... +Raw ADC Value: 18345, Moisture Level: 54.23%, Digital Status: Dry + +To check stored data in SQLite: + +sqlite3 plant_sensor_data.db "SELECT * FROM moisture_data;" + +--- + +## 6. Auto-Start on Boot +To ensure the script runs at startup: + +### Option 1: Using systemd (Recommended) +1. Create a new service file: + + sudo nano /etc/systemd/system/plant_monitor.service + +2. Paste the following: + + [Unit] + Description=Plant Moisture Monitoring Service + After=multi-user.target + + [Service] + ExecStart=/usr/bin/python3 /home/pi/PlantWaterSystem/plant_monitor.py + WorkingDirectory=/home/pi/plant_monitor + StandardOutput=inherit + StandardError=inherit + Restart=always + User=pi + + [Install] + WantedBy=multi-user.target + +3. Enable and start the service: + + sudo systemctl daemon-reload + sudo systemctl enable plant_monitor.service + sudo systemctl start plant_monitor.service + +4. Check if it's running: + + sudo systemctl status plant_monitor.service + + +### Option 2: Using Cron +1. Open crontab: + + crontab -e + +2. Add this line at the end: + + @reboot /usr/bin/python3 /home/pi/PlantWaterSystem/plant_monitor.py & + +3. Save and exit. + +--- + +## 7. Testing & Debugging +### To View Logs + +journalctl -u plant_monitor.service --follow + + +### To Restart Service + +sudo systemctl restart plant_monitor.service + +### **To Manually View Data** + +sqlite3 plant_sensor_data.db "SELECT * FROM moisture_data;" diff --git a/embedded/plant_monitor.py b/embedded/plant_monitor.py new file mode 100644 index 0000000..367e70e --- /dev/null +++ b/embedded/plant_monitor.py @@ -0,0 +1,184 @@ +import time +import sqlite3 +import board +import busio +import RPi.GPIO as GPIO +import adafruit_ads1x15.ads1115 as ADS +from adafruit_ads1x15.analog_in import AnalogIn +import logging +import argparse +import signal +import sys + +# Setup logging +logging.basicConfig(filename="sensor_log.log", level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s") + +# Argument parser for configurable parameters +parser = argparse.ArgumentParser() +parser.add_argument("--interval", type=int, default=10, help="Sensor read interval in seconds") +parser.add_argument("--retention_days", type=int, default=7, help="Data retention period in days") +args = parser.parse_args() + +# Database and Interval Setup +DB_NAME = "plant_sensor_data.db" +READ_INTERVAL = args.interval +RETENTION_DAYS = args.retention_days + +i2c = busio.I2C(board.SCL, board.SDA) +ads = ADS.ADS1115(i2c) + +# Sensor Configuration (only active sensors are initialized) +SENSORS = [ + {"analog": ADS.P0, "digital": 14, "active": True}, # Sensor 1 + {"analog": ADS.P1, "digital": 15, "active": True}, # Sensor 2 + {"analog": ADS.P2, "digital": 18, "active": True}, # Sensor 3 + {"analog": ADS.P3, "digital": 23, "active": True}, # Sensor 4 +] + +ADDR_PIN = 7 # GPIO7 for address configuration +ALRT_PIN = 0 # GPIO0 for alerts + +# GPIO Setup +GPIO.setmode(GPIO.BCM) +GPIO.setup(ADDR_PIN, GPIO.OUT) +GPIO.setup(ALRT_PIN, GPIO.IN) +for sensor in SENSORS: + if sensor["active"]: + GPIO.setup(sensor["digital"], GPIO.IN) + +# Global SQLite connection +conn = None + +def handle_shutdown(signum, frame): + """Handle shutdown signals gracefully.""" + print("Received shutdown signal...") + GPIO.cleanup() + logging.info("GPIO Cleanup Done.") + if conn: + conn.close() + sys.exit(0) + +signal.signal(signal.SIGTERM, handle_shutdown) +signal.signal(signal.SIGINT, handle_shutdown) + +def setup_database(): + """Create or update SQLite table with sensor ID column.""" + cursor = conn.cursor() + cursor.execute(""" + CREATE TABLE IF NOT EXISTS moisture_data ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, + sensor_id INTEGER, + moisture_level REAL, + digital_status TEXT + ) + """) + conn.commit() + +def save_to_database(sensor_id, moisture_level, digital_status): + """Save sensor data to SQLite.""" + try: + cursor = conn.cursor() + cursor.execute(""" + INSERT INTO moisture_data (sensor_id, moisture_level, digital_status) + VALUES (?, ?, ?) + """, (sensor_id, moisture_level, digital_status)) + conn.commit() + except sqlite3.Error as e: + logging.error(f"Database error: {e}") + +def convert_adc_to_moisture(adc_value): + """Convert raw ADC value to moisture percentage.""" + min_adc = 14000 + max_adc = 26000 + moisture_level = ((max_adc - adc_value) / (max_adc - min_adc)) * 100 + return max(0, min(100, moisture_level)) + +def read_sensor_channel(sensor): + """Read data from a single sensor channel.""" + try: + chan = AnalogIn(ads, sensor["analog"]) + adc_value = chan.value + if adc_value == 0 or adc_value > 32767: # Handle disconnected or floating pins + logging.warning(f"Sensor channel {sensor['analog']} may be disconnected.") + return adc_value, 0, "Disconnected" + moisture_level = convert_adc_to_moisture(adc_value) + digital_status = "Dry" if GPIO.input(sensor["digital"]) == GPIO.HIGH else "Wet" + return adc_value, moisture_level, digital_status + except OSError as e: + logging.error(f"I2C error on sensor channel {sensor['analog']}: {e}") + return 0, 0, "Error" + except Exception as e: + logging.error(f"Unexpected error on sensor channel {sensor['analog']}: {e}") + return 0, 0, "Error" + +def read_sensors(): + """Read data from all active sensors via ADS1115 and GPIO.""" + for index, sensor in enumerate(SENSORS, start=1): + if not sensor["active"]: + continue + + adc_value, moisture_level, digital_status = read_sensor_channel(sensor) + print(f"Sensor {index} - Raw ADC Value: {adc_value}, Moisture Level: {moisture_level:.2f}%, Digital Status: {digital_status}") + logging.info(f"Sensor {index} - Raw ADC Value: {adc_value}, Moisture Level: {moisture_level:.2f}%, Digital Status: {digital_status}") + save_to_database(index, moisture_level, digital_status) + + if GPIO.input(ALRT_PIN) == GPIO.HIGH: + print("Alert! Check sensor readings.") + logging.warning("Alert triggered on ALRT_PIN.") + +def manage_data_retention(): + """Delete records older than the retention period.""" + try: + cursor = conn.cursor() + cursor.execute(""" + DELETE FROM moisture_data WHERE timestamp < datetime('now', ?) LIMIT 1000 + """, (f'-{RETENTION_DAYS} days',)) + conn.commit() + except sqlite3.Error as e: + logging.error(f"Data retention error: {e}") + +def sensor_health_check(): + """Monitor sensors for consistent or missing values.""" + try: + cursor = conn.cursor() + for sensor_id in range(1, len(SENSORS) + 1): + if not SENSORS[sensor_id - 1]["active"]: + continue + + cursor.execute(""" + SELECT AVG(moisture_level) FROM moisture_data WHERE sensor_id = ? + """, (sensor_id,)) + avg_moisture = cursor.fetchone()[0] + if avg_moisture is None: + logging.warning(f"No data recorded for Sensor {sensor_id}.") + elif avg_moisture < 10: + logging.warning(f"Low average moisture level for Sensor {sensor_id}: {avg_moisture:.2f}%") + except sqlite3.Error as e: + logging.error(f"Health check error: {e}") + +def main(): + """Main function to read sensors and store data in SQLite.""" + global conn + conn = sqlite3.connect(DB_NAME) + setup_database() + print("Starting Multi-Sensor Plant Monitoring...") + GPIO.output(ADDR_PIN, GPIO.HIGH) + + while True: + read_sensors() + manage_data_retention() + sensor_health_check() + time.sleep(READ_INTERVAL) + +try: + main() +except KeyboardInterrupt: + print("Exiting...") +finally: + GPIO.cleanup() + if conn: + conn.close() + logging.info("GPIO Cleanup Done.") + print("GPIO Cleanup Done.") diff --git a/embedded/send_data_api.py b/embedded/send_data_api.py new file mode 100644 index 0000000..6e6751d --- /dev/null +++ b/embedded/send_data_api.py @@ -0,0 +1,101 @@ +from flask import Flask, jsonify, request +import sqlite3 +import requests +from datetime import datetime, timedelta +import schedule +import time +import threading +import os + +app = Flask(__name__) + +# Configuration +DB_NAME = "plant_sensor_data.db" +BACKEND_API_URL = os.getenv("BACKEND_API_URL", "https://backend.example.com/receive-data") +RETRY_ATTEMPTS = 3 + + +def fetch_recent_data(): + """Fetch all sensor readings from the last 12 hours.""" + try: + conn = sqlite3.connect(DB_NAME) + cursor = conn.cursor() + + # Get the current timestamp and 12 hours back + twelve_hours_ago = datetime.now() - timedelta(hours=12) + + # Query for data within the last 12 hours + cursor.execute(""" + SELECT id, timestamp, sensor_id, moisture_level, digital_status + FROM moisture_data + WHERE timestamp >= ? + """, (twelve_hours_ago.strftime("%Y-%m-%d %H:%M:%S"),)) + + data = cursor.fetchall() + conn.close() + + # Convert data to a list of dictionaries + return [ + { + "id": row[0], + "timestamp": row[1], + "sensor_id": row[2], + "moisture_level": row[3], + "digital_status": row[4] + } + for row in data + ] + except sqlite3.Error as e: + print(f"Database error: {e}") + return [] + + +def send_data_to_backend(): + """Send sensor data to the backend with retry logic.""" + data = fetch_recent_data() + + if not data: + print("No data to send.") + return + + for attempt in range(RETRY_ATTEMPTS): + try: + response = requests.post(BACKEND_API_URL, json={"sensor_data": data}) + + if response.status_code == 200: + print("Data sent successfully") + return + else: + print(f"Attempt {attempt + 1}: Failed to send data ({response.status_code}) - {response.text}") + except requests.RequestException as e: + print(f"Attempt {attempt + 1}: Error sending data - {e}") + + print("All retry attempts failed.") + + +@app.route("/send-data", methods=["POST"]) +def send_data(): + """Trigger data sending via an API request.""" + send_data_to_backend() + return jsonify({"message": "Data sending initiated"}), 200 + + +def schedule_data_sending(): + """Schedule data sending to occur at 12:00 AM and 12:00 PM daily.""" + schedule.every().day.at("00:00").do(send_data_to_backend) + schedule.every().day.at("12:00").do(send_data_to_backend) + + while True: + schedule.run_pending() + time.sleep(1) + + +def run_schedule_in_thread(): + """Run the scheduler in a separate thread.""" + thread = threading.Thread(target=schedule_data_sending) + thread.daemon = True + thread.start() + +if __name__ == "__main__": + run_schedule_in_thread() + app.run(host="0.0.0.0", port=5000) From aa1067812515aa6abb9dea9ec1a83ee2df0b8aff Mon Sep 17 00:00:00 2001 From: MayureshMore Date: Sun, 9 Feb 2025 08:13:46 -0800 Subject: [PATCH 02/13] Implemented sensor connection --- .DS_Store | Bin 0 -> 6148 bytes .idea/misc.xml | 4 ++ embedded/README.md | 162 +++++++++++++++++++++++---------------------- 3 files changed, 88 insertions(+), 78 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..6ffb5dcd40f06fd2c944acf593fed7c39016a9bc GIT binary patch literal 6148 zcmeHKK~LK-6n-vcb%iGNuu0niDblWpwPUJ;b_pFj>`Kr$z|!^Lw%U*|B3HQiDm_BkB;5hB7wVC>{`QXI+qn_pAby zeMd$mrJoCPoD>Vu;&_Y<@Z7a1p_q>8gzn6rNzlT}Vh6f;Wd#;5g^W&-kx_~VrJL{D z_}~AEPp0Fuq_FXq$yt$)t8Vv?YOFL@pS5&Luj#MRPcx0GxSEUy@#v6i`^FT>jU6Xn z@@zWny!dF!D$dJnWE1i?v5%jnE^NnD( z{jLl9{q}s`)-T_@{jfJYDNajsZih+=%i79wk2i1uV}XcY(Wop;`2%g7r|CP5$l#Nx z9wJt)Bjzr#sEP;!!hkTa90vU6rq`C!o%A9M2m?=u0p1@1C}Zfcc4)T_6!r=LY{G2? zHvcqoPT(>0SUW@uM7dO;OI7}gp^3@nv_Yi6T!sQ>@~ literal 0 HcmV?d00001 diff --git a/.idea/misc.xml b/.idea/misc.xml index e0844bc..6532ae7 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,9 @@ + + diff --git a/embedded/README.md b/embedded/README.md index 4f0ddaf..d65a63d 100644 --- a/embedded/README.md +++ b/embedded/README.md @@ -1,3 +1,4 @@ + # Setting Up PlantWaterSystem on a New Raspberry Pi This guide will walk you through setting up a new Raspberry Pi for your Soil Moisture Monitoring System using: @@ -8,6 +9,7 @@ This guide will walk you through setting up a new Raspberry Pi for your Soil Moi - Python (for data processing and logging) --- + ## 1. Hardware Requirements ### Components Needed 1. Raspberry Pi (any model with GPIO support) @@ -17,134 +19,131 @@ This guide will walk you through setting up a new Raspberry Pi for your Soil Moi 5. Power Supply for Raspberry Pi --- + ## 2. Wiring Connections -Follow the wiring diagram you provided to correctly connect the components. ### ADS1115 (ADC) to Raspberry Pi | ADS1115 Pin | Raspberry Pi GPIO Pin | -|-------------|-----------------| -| VDD | 3.3V (Pin 1) | -| GND | GND (Pin 9) | -| SCL | GPIO3 (SCL, Pin 5) | -| SDA | GPIO2 (SDA, Pin 3) | -| ADDR | GPIO7 (Pin 7) | -| ALRT | GPIO0 (Pin 11) | - +|-------------|------------------------| +| VDD | 3.3V (Pin 1) | +| GND | GND (Pin 9) | +| SCL | GPIO3 (SCL, Pin 5) | +| SDA | GPIO2 (SDA, Pin 3) | +| ADDR | GPIO7 (Pin 7) | +| ALRT | GPIO0 (Pin 11) | ### Capacitive Soil Moisture Sensor to ADS1115 | Sensor Pin | ADS1115 Pin | |------------|-------------| -| VCC | 5V (Pin 2) | -| GND | GND (Pin 14) | -| Sensor 1 AO | A0 | -| Sensor 2 AO | A1 | -| Sensor 3 AO | A2 | -| Sensor 4 AO | A3 | - +| VCC | 5V (Pin 2) | +| GND | GND (Pin 14)| +| Sensor 1 AO| A0 | +| Sensor 2 AO| A1 | +| Sensor 3 AO| A2 | +| Sensor 4 AO| A3 | ### Capacitive Soil Moisture Sensor Digital Output to Raspberry Pi -| Sensor DO Pin | Raspberry Pi GPIO | -|---------------|-------------------| -| Sensor 1 AO | GPIO8 (Pin 8). | -| Sensor 2 AO | GPI15 (Pin 10). | -| Sensor 3 AO | GPI18 (Pin 12). | -| Sensor 4 AO | GPI23 (Pin 16). | +| Sensor DO Pin | Raspberry Pi GPIO | +|---------------|-------------------------| +| Sensor 1 AO | GPIO8 (Pin 8) | +| Sensor 2 AO | GPIO15 (Pin 10) | +| Sensor 3 AO | GPIO18 (Pin 12) | +| Sensor 4 AO | GPIO23 (Pin 16) | --- ## 3. Setting Up the Raspberry Pi + ### Step 1: Update Raspberry Pi OS Run the following commands to update your system: +```bash sudo apt update && sudo apt upgrade -y +``` ### Step 2: Enable I2C Communication Check if I2C is enabled: - +```bash ls /dev/i2c-* +``` -If you don’t see a response, enable it using: - +If no response is returned, enable I2C using: +```bash sudo raspi-config +``` +Navigate to **Interfacing Options** → **I2C** → Enable. -- Go to **Interfacing Options** → I2C → Enable. - -### **Step 3: Install Required Packages** - -Install Python - +### Step 3: Install Required Packages +Install Python and necessary libraries: +```bash sudo apt install pip - -Install necessary Python libraries: - sudo pip install RPi.GPIO adafruit-circuitpython-ads1x15 --break-system-packages sudo apt install sqlite3 - -Ensure I2C tools are installed: - sudo apt install -y python3-smbus i2c-tools - +``` ### Step 4: Verify I2C Devices Check if the ADS1115 is detected: - +```bash i2cdetect -y 1 - -You should see an address like **0x48**, confirming that ADS1115 is correctly connected. +``` +You should see an address like `0x48`, confirming proper connection. --- -## 4. Deploying the Python Script -### Step 1: Create a Working Directory - -mkdir -p ~/PlantWaterSystem && cd ~/PlantWaterSystem - -### **Step 2: Create the Python Script** -Create a file: +## 4. Deploying the Python Script -nano plant_monitor.py -nano send_data_api.py +### Step 1: Clone the Repository +Clone the repository and navigate into the directory: +```bash +git clone git@github.com:SE4CPS/PlantWaterSystem.git +cd PlantWaterSystem/Embedded +``` -Paste the updated Python script that reads analog and digital data from the sensor. +The `plant_monitor.py` and `send_data_api.py` scripts are already present in this directory. --- ## 5. Running the Python Script Make the script executable: - +```bash chmod +x plant_monitor.py +``` -Run it: - +Run the script: +```bash python3 plant_monitor.py +``` -It should display: -plaintext +The output should display something like: +``` Starting Plant Sensor Monitoring... Raw ADC Value: 18345, Moisture Level: 54.23%, Digital Status: Dry +``` To check stored data in SQLite: - +```bash sqlite3 plant_sensor_data.db "SELECT * FROM moisture_data;" +``` --- ## 6. Auto-Start on Boot -To ensure the script runs at startup: ### Option 1: Using systemd (Recommended) -1. Create a new service file: - +1. Create a service file: + ```bash sudo nano /etc/systemd/system/plant_monitor.service + ``` -2. Paste the following: - +2. Paste the following configuration: + ```ini [Unit] Description=Plant Moisture Monitoring Service After=multi-user.target [Service] - ExecStart=/usr/bin/python3 /home/pi/PlantWaterSystem/plant_monitor.py - WorkingDirectory=/home/pi/plant_monitor + ExecStart=/usr/bin/python3 /home/pi/PlantWaterSystem/Embedded/plant_monitor.py + WorkingDirectory=/home/pi/PlantWaterSystem/Embedded StandardOutput=inherit StandardError=inherit Restart=always @@ -152,41 +151,48 @@ To ensure the script runs at startup: [Install] WantedBy=multi-user.target + ``` 3. Enable and start the service: - + ```bash sudo systemctl daemon-reload sudo systemctl enable plant_monitor.service sudo systemctl start plant_monitor.service + ``` -4. Check if it's running: - +4. Check if the service is running: + ```bash sudo systemctl status plant_monitor.service - + ``` ### Option 2: Using Cron 1. Open crontab: - + ```bash crontab -e + ``` -2. Add this line at the end: - - @reboot /usr/bin/python3 /home/pi/PlantWaterSystem/plant_monitor.py & +2. Add the following line at the end: + ```bash + @reboot /usr/bin/python3 /home/pi/PlantWaterSystem/Embedded/plant_monitor.py & + ``` 3. Save and exit. --- ## 7. Testing & Debugging -### To View Logs +### To View Logs: +```bash journalctl -u plant_monitor.service --follow +``` - -### To Restart Service - +### To Restart the Service: +```bash sudo systemctl restart plant_monitor.service +``` -### **To Manually View Data** - +### To Manually View Data: +```bash sqlite3 plant_sensor_data.db "SELECT * FROM moisture_data;" +``` \ No newline at end of file From e7fb60d94555fc7623a21c40313b2200bc031625 Mon Sep 17 00:00:00 2001 From: MayureshMore Date: Sun, 9 Feb 2025 08:31:24 -0800 Subject: [PATCH 03/13] Implemented sensor connection --- embedded/setup.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 embedded/setup.sh diff --git a/embedded/setup.sh b/embedded/setup.sh new file mode 100644 index 0000000..e69de29 From 0c6ac505b812696a2c995a7c77a589358d46aefb Mon Sep 17 00:00:00 2001 From: MayureshMore Date: Sun, 9 Feb 2025 08:41:58 -0800 Subject: [PATCH 04/13] Automated Setup for Raspberry Pi --- embedded/README.md | 119 +++++++++++++++++++++++---------------------- embedded/setup.sh | 67 +++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 58 deletions(-) diff --git a/embedded/README.md b/embedded/README.md index d65a63d..17ecf52 100644 --- a/embedded/README.md +++ b/embedded/README.md @@ -10,8 +10,8 @@ This guide will walk you through setting up a new Raspberry Pi for your Soil Moi --- -## 1. Hardware Requirements -### Components Needed +## **1. Hardware Requirements** +### **Components Needed** 1. Raspberry Pi (any model with GPIO support) 2. Capacitive Soil Moisture Sensor 3. ADS1115 ADC Module @@ -20,9 +20,9 @@ This guide will walk you through setting up a new Raspberry Pi for your Soil Moi --- -## 2. Wiring Connections +## **2. Wiring Connections** -### ADS1115 (ADC) to Raspberry Pi +### **ADS1115 (ADC) to Raspberry Pi** | ADS1115 Pin | Raspberry Pi GPIO Pin | |-------------|------------------------| | VDD | 3.3V (Pin 1) | @@ -32,7 +32,7 @@ This guide will walk you through setting up a new Raspberry Pi for your Soil Moi | ADDR | GPIO7 (Pin 7) | | ALRT | GPIO0 (Pin 11) | -### Capacitive Soil Moisture Sensor to ADS1115 +### **Capacitive Soil Moisture Sensor to ADS1115** | Sensor Pin | ADS1115 Pin | |------------|-------------| | VCC | 5V (Pin 2) | @@ -42,7 +42,7 @@ This guide will walk you through setting up a new Raspberry Pi for your Soil Moi | Sensor 3 AO| A2 | | Sensor 4 AO| A3 | -### Capacitive Soil Moisture Sensor Digital Output to Raspberry Pi +### **Capacitive Soil Moisture Sensor Digital Output to Raspberry Pi** | Sensor DO Pin | Raspberry Pi GPIO | |---------------|-------------------------| | Sensor 1 AO | GPIO8 (Pin 8) | @@ -52,65 +52,73 @@ This guide will walk you through setting up a new Raspberry Pi for your Soil Moi --- -## 3. Setting Up the Raspberry Pi +## **3. Setup Options** -### Step 1: Update Raspberry Pi OS -Run the following commands to update your system: -```bash -sudo apt update && sudo apt upgrade -y -``` +### **Automated Setup** +Run the following command to clone the repository, install dependencies, and configure the system automatically: -### Step 2: Enable I2C Communication -Check if I2C is enabled: ```bash -ls /dev/i2c-* +bash <(curl -L https://raw.githubusercontent.com/SE4CPS/PlantWaterSystem/embedded-code/Embedded/setup.sh) ``` -If no response is returned, enable I2C using: -```bash -sudo raspi-config -``` -Navigate to **Interfacing Options** → **I2C** → Enable. +This command performs the following tasks: +- Clones the PlantWaterSystem repository. +- Updates your Raspberry Pi OS. +- Installs required packages and Python libraries. +- Verifies I2C connection. +- Configures auto-start for the `plant_monitor.py` script using `systemd`. -### Step 3: Install Required Packages -Install Python and necessary libraries: -```bash -sudo apt install pip -sudo pip install RPi.GPIO adafruit-circuitpython-ads1x15 --break-system-packages -sudo apt install sqlite3 -sudo apt install -y python3-smbus i2c-tools -``` +Check the service status after setup: -### Step 4: Verify I2C Devices -Check if the ADS1115 is detected: ```bash -i2cdetect -y 1 +sudo systemctl status plant_monitor.service ``` -You should see an address like `0x48`, confirming proper connection. --- -## 4. Deploying the Python Script +### **Manual Setup (or for Debugging)** + +If you prefer a manual setup or need to debug, follow these steps: -### Step 1: Clone the Repository -Clone the repository and navigate into the directory: +### **Step 1: Clone the Repository** ```bash git clone git@github.com:SE4CPS/PlantWaterSystem.git cd PlantWaterSystem/Embedded ``` -The `plant_monitor.py` and `send_data_api.py` scripts are already present in this directory. +### **Step 2: Update Raspberry Pi OS** +```bash +sudo apt update && sudo apt upgrade -y +``` ---- +### **Step 3: Enable I2C Communication** -## 5. Running the Python Script -Make the script executable: +Check if I2C is enabled: ```bash -chmod +x plant_monitor.py +ls /dev/i2c-* ``` -Run the script: +If you don’t see a response, enable I2C: ```bash +sudo raspi-config +``` +Navigate to **Interfacing Options** → **I2C** → Enable. + +### **Step 4: Install Required Packages** +```bash +sudo apt install -y python3-pip python3-smbus i2c-tools sqlite3 +sudo pip3 install RPi.GPIO adafruit-circuitpython-ads1x15 --break-system-packages +``` + +### **Step 5: Verify I2C Devices** +```bash +i2cdetect -y 1 +``` +Ensure the address `0x48` appears to confirm the connection. + +### **Step 6: Set Permissions and Run the Script** +```bash +chmod +x plant_monitor.py python3 plant_monitor.py ``` @@ -120,22 +128,16 @@ Starting Plant Sensor Monitoring... Raw ADC Value: 18345, Moisture Level: 54.23%, Digital Status: Dry ``` -To check stored data in SQLite: -```bash -sqlite3 plant_sensor_data.db "SELECT * FROM moisture_data;" -``` - ---- +### **Step 7: Set Up Auto-Start (Optional)** -## 6. Auto-Start on Boot +#### **Option 1: Using systemd (Recommended)** -### Option 1: Using systemd (Recommended) 1. Create a service file: ```bash sudo nano /etc/systemd/system/plant_monitor.service ``` -2. Paste the following configuration: +2. Paste the following: ```ini [Unit] Description=Plant Moisture Monitoring Service @@ -160,18 +162,19 @@ sqlite3 plant_sensor_data.db "SELECT * FROM moisture_data;" sudo systemctl start plant_monitor.service ``` -4. Check if the service is running: +4. Check the service status: ```bash sudo systemctl status plant_monitor.service ``` -### Option 2: Using Cron +#### **Option 2: Using Cron** + 1. Open crontab: ```bash crontab -e ``` -2. Add the following line at the end: +2. Add this line: ```bash @reboot /usr/bin/python3 /home/pi/PlantWaterSystem/Embedded/plant_monitor.py & ``` @@ -180,19 +183,19 @@ sqlite3 plant_sensor_data.db "SELECT * FROM moisture_data;" --- -## 7. Testing & Debugging +## **4. Testing & Debugging** -### To View Logs: +### **To View Logs:** ```bash journalctl -u plant_monitor.service --follow ``` -### To Restart the Service: +### **To Restart the Service:** ```bash sudo systemctl restart plant_monitor.service ``` -### To Manually View Data: +### **To Manually View Data:** ```bash sqlite3 plant_sensor_data.db "SELECT * FROM moisture_data;" -``` \ No newline at end of file +``` diff --git a/embedded/setup.sh b/embedded/setup.sh index e69de29..153ff68 100644 --- a/embedded/setup.sh +++ b/embedded/setup.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +echo "Starting PlantWaterSystem setup..." + +# Step 1: Clone the repository (only if not already cloned) +if [ ! -d "PlantWaterSystem" ]; then + echo "Cloning the PlantWaterSystem repository..." + git clone git@github.com:SE4CPS/PlantWaterSystem.git +fi + +# Navigate to the directory +cd PlantWaterSystem/Embedded || exit + +# Step 2: Update Raspberry Pi OS +echo "Updating Raspberry Pi OS..." +sudo apt update && sudo apt upgrade -y + +# Step 3: Enable I2C Communication (manual step reminder) +echo "Please ensure I2C is enabled by running: sudo raspi-config (Interfacing Options -> I2C -> Enable)" +echo "Press Enter to continue after enabling I2C or if it is already enabled." +read -p "" + +# Step 4: Install Required Packages +echo "Installing required packages..." +sudo apt install -y python3-pip python3-smbus i2c-tools sqlite3 + +echo "Installing necessary Python libraries..." +sudo pip3 install RPi.GPIO adafruit-circuitpython-ads1x15 --break-system-packages + +# Step 5: Verify I2C Devices +echo "Verifying I2C connection..." +i2cdetect -y 1 + +# Step 6: Set Permissions and Run Python Script +echo "Setting executable permission for the plant_monitor.py script..." +chmod +x plant_monitor.py + +# Step 7: Setup Auto-Start with systemd +echo "Setting up auto-start using systemd..." +SERVICE_FILE="/etc/systemd/system/plant_monitor.service" + +sudo bash -c "cat > $SERVICE_FILE" < Date: Sun, 9 Feb 2025 08:55:26 -0800 Subject: [PATCH 05/13] Automated Setup for Raspberry Pi --- embedded/setup.sh | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/embedded/setup.sh b/embedded/setup.sh index 153ff68..6829e88 100644 --- a/embedded/setup.sh +++ b/embedded/setup.sh @@ -15,14 +15,28 @@ cd PlantWaterSystem/Embedded || exit echo "Updating Raspberry Pi OS..." sudo apt update && sudo apt upgrade -y -# Step 3: Enable I2C Communication (manual step reminder) -echo "Please ensure I2C is enabled by running: sudo raspi-config (Interfacing Options -> I2C -> Enable)" -echo "Press Enter to continue after enabling I2C or if it is already enabled." -read -p "" +# Step 3: Enable I2C Communication Automatically +echo "Enabling I2C communication..." +CONFIG_FILE="/boot/config.txt" +I2C_LINE="dtparam=i2c_arm=on" +REBOOT_REQUIRED=false + +# Check if I2C is already enabled +if ! grep -q "$I2C_LINE" "$CONFIG_FILE"; then + echo "I2C not enabled. Adding the configuration to $CONFIG_FILE..." + sudo bash -c "echo '$I2C_LINE' >> $CONFIG_FILE" + REBOOT_REQUIRED=true +else + echo "I2C is already enabled." +fi + +# Install I2C tools (if not installed) +echo "Installing I2C tools..." +sudo apt install -y i2c-tools python3-smbus # Step 4: Install Required Packages echo "Installing required packages..." -sudo apt install -y python3-pip python3-smbus i2c-tools sqlite3 +sudo apt install -y python3-pip sqlite3 echo "Installing necessary Python libraries..." sudo pip3 install RPi.GPIO adafruit-circuitpython-ads1x15 --break-system-packages @@ -63,5 +77,22 @@ echo "Enabling and starting the plant_monitor service..." sudo systemctl enable plant_monitor.service sudo systemctl start plant_monitor.service +# Step 8: Reboot if required +if [ "$REBOOT_REQUIRED" = true ]; then + echo "The system needs to reboot to apply I2C configuration changes." + read -p "Do you want to reboot now? (y/n): " REBOOT_ANSWER + if [[ "$REBOOT_ANSWER" == "y" || "$REBOOT_ANSWER" == "Y" ]]; then + echo "Rebooting now..." + sudo reboot + else + echo "Please remember to reboot later to apply I2C configuration changes." + fi +else + echo "No reboot required." +fi + echo "Setup is complete. You can now check the service status with:" echo "sudo systemctl status plant_monitor.service" + +echo "If you want to manually run the script, use:" +echo "python3 plant_monitor.py" \ No newline at end of file From 23dd46dda52c36c84af95ccfaba330ce0e2c66dd Mon Sep 17 00:00:00 2001 From: MayureshMore Date: Sun, 9 Feb 2025 16:00:21 -0800 Subject: [PATCH 06/13] Implemented sensor connection --- embedded/setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded/setup.sh b/embedded/setup.sh index 6829e88..dd90c61 100644 --- a/embedded/setup.sh +++ b/embedded/setup.sh @@ -5,7 +5,7 @@ echo "Starting PlantWaterSystem setup..." # Step 1: Clone the repository (only if not already cloned) if [ ! -d "PlantWaterSystem" ]; then echo "Cloning the PlantWaterSystem repository..." - git clone git@github.com:SE4CPS/PlantWaterSystem.git + git git clone https://github.com/SE4CPS/PlantWaterSystem.git fi # Navigate to the directory From 7d1889a1c563aa4903c8471f7e109c5df291fe1d Mon Sep 17 00:00:00 2001 From: MayureshMore Date: Sun, 9 Feb 2025 16:00:53 -0800 Subject: [PATCH 07/13] Implemented sensor connection --- embedded/setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded/setup.sh b/embedded/setup.sh index dd90c61..55319f5 100644 --- a/embedded/setup.sh +++ b/embedded/setup.sh @@ -5,7 +5,7 @@ echo "Starting PlantWaterSystem setup..." # Step 1: Clone the repository (only if not already cloned) if [ ! -d "PlantWaterSystem" ]; then echo "Cloning the PlantWaterSystem repository..." - git git clone https://github.com/SE4CPS/PlantWaterSystem.git + git clone https://github.com/SE4CPS/PlantWaterSystem.git fi # Navigate to the directory From 50ec72be67cf5a0149d4c7aadbaca90afd35e874 Mon Sep 17 00:00:00 2001 From: MayureshMore Date: Sun, 9 Feb 2025 16:03:11 -0800 Subject: [PATCH 08/13] Implemented sensor connection --- embedded/setup.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embedded/setup.sh b/embedded/setup.sh index 55319f5..bb241a2 100644 --- a/embedded/setup.sh +++ b/embedded/setup.sh @@ -9,7 +9,7 @@ if [ ! -d "PlantWaterSystem" ]; then fi # Navigate to the directory -cd PlantWaterSystem/Embedded || exit +cd PlantWaterSystem/embedded || exit # Step 2: Update Raspberry Pi OS echo "Updating Raspberry Pi OS..." @@ -59,8 +59,8 @@ Description=Plant Moisture Monitoring Service After=multi-user.target [Service] -ExecStart=/usr/bin/python3 /home/pi/PlantWaterSystem/Embedded/plant_monitor.py -WorkingDirectory=/home/pi/PlantWaterSystem/Embedded +ExecStart=/usr/bin/python3 /home/pi/PlantWaterSystem/embedded/plant_monitor.py +WorkingDirectory=/home/pi/PlantWaterSystem/embedded StandardOutput=inherit StandardError=inherit Restart=always From c6f2dab9ef4d162426396be8ff6eefac7f313000 Mon Sep 17 00:00:00 2001 From: MayureshMore Date: Sun, 9 Feb 2025 16:06:08 -0800 Subject: [PATCH 09/13] Implemented sensor connection --- embedded/PlantWaterSystem | 1 + 1 file changed, 1 insertion(+) create mode 160000 embedded/PlantWaterSystem diff --git a/embedded/PlantWaterSystem b/embedded/PlantWaterSystem new file mode 160000 index 0000000..84b7923 --- /dev/null +++ b/embedded/PlantWaterSystem @@ -0,0 +1 @@ +Subproject commit 84b7923675f9d6964df313599d3abed0c3eac0d9 From 9808d6350313337e780f1d9dab45975003d2dcf4 Mon Sep 17 00:00:00 2001 From: MayureshMore Date: Sun, 9 Feb 2025 16:33:15 -0800 Subject: [PATCH 10/13] Implemented sensor connection --- embedded/PlantWaterSystem | 1 - embedded/setup.sh | 16 +++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) delete mode 160000 embedded/PlantWaterSystem diff --git a/embedded/PlantWaterSystem b/embedded/PlantWaterSystem deleted file mode 160000 index 84b7923..0000000 --- a/embedded/PlantWaterSystem +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 84b7923675f9d6964df313599d3abed0c3eac0d9 diff --git a/embedded/setup.sh b/embedded/setup.sh index bb241a2..ddf6501 100644 --- a/embedded/setup.sh +++ b/embedded/setup.sh @@ -5,7 +5,7 @@ echo "Starting PlantWaterSystem setup..." # Step 1: Clone the repository (only if not already cloned) if [ ! -d "PlantWaterSystem" ]; then echo "Cloning the PlantWaterSystem repository..." - git clone https://github.com/SE4CPS/PlantWaterSystem.git + git clone -b embedded-code https://github.com/SE4CPS/PlantWaterSystem.git fi # Navigate to the directory @@ -44,10 +44,20 @@ sudo pip3 install RPi.GPIO adafruit-circuitpython-ads1x15 --break-system-package # Step 5: Verify I2C Devices echo "Verifying I2C connection..." i2cdetect -y 1 +if i2cdetect -y 1 | grep -q "48"; then + echo "I2C device detected successfully at address 0x48." +else + echo "Warning: No I2C device detected. Please check your connections." +fi # Step 6: Set Permissions and Run Python Script -echo "Setting executable permission for the plant_monitor.py script..." -chmod +x plant_monitor.py + +if [ -f "plant_monitor.py" ]; then + echo "Setting executable permission for the plant_monitor.py script..." + chmod +x plant_monitor.py +else + echo "Warning: plant_monitor.py not found!" +fi # Step 7: Setup Auto-Start with systemd echo "Setting up auto-start using systemd..." From 0d16a43f7c2843b2be3d22a0a104433a0067798e Mon Sep 17 00:00:00 2001 From: MayureshMore Date: Sun, 9 Feb 2025 17:19:13 -0800 Subject: [PATCH 11/13] Implemented sensor connection --- embedded/README.md | 55 ++++++++++++++++++++++++++++++++------- embedded/plant_monitor.py | 14 +++++++--- embedded/setup.sh | 46 +++++++++++++++++++++++++++----- 3 files changed, 96 insertions(+), 19 deletions(-) diff --git a/embedded/README.md b/embedded/README.md index 17ecf52..9defa20 100644 --- a/embedded/README.md +++ b/embedded/README.md @@ -1,4 +1,3 @@ - # Setting Up PlantWaterSystem on a New Raspberry Pi This guide will walk you through setting up a new Raspberry Pi for your Soil Moisture Monitoring System using: @@ -74,6 +73,12 @@ Check the service status after setup: sudo systemctl status plant_monitor.service ``` +If you choose to set up the `send_data_api.py` service, check its status with: + +```bash +sudo systemctl status send_data_api.service +``` + --- ### **Manual Setup (or for Debugging)** @@ -132,7 +137,7 @@ Raw ADC Value: 18345, Moisture Level: 54.23%, Digital Status: Dry #### **Option 1: Using systemd (Recommended)** -1. Create a service file: +1. Create a service file for `plant_monitor.py`: ```bash sudo nano /etc/systemd/system/plant_monitor.service ``` @@ -167,19 +172,44 @@ Raw ADC Value: 18345, Moisture Level: 54.23%, Digital Status: Dry sudo systemctl status plant_monitor.service ``` -#### **Option 2: Using Cron** +#### **Option 2: Set Up Auto-Start for `send_data_api.py`** + +If you want to run `send_data_api.py` automatically, follow these steps: -1. Open crontab: +1. Create a service file for `send_data_api.py`: ```bash - crontab -e + sudo nano /etc/systemd/system/send_data_api.service + ``` + +2. Paste the following: + ```ini + [Unit] + Description=Send Data API Service + After=multi-user.target + + [Service] + ExecStart=/usr/bin/python3 /home/pi/PlantWaterSystem/Embedded/send_data_api.py + WorkingDirectory=/home/pi/PlantWaterSystem/Embedded + StandardOutput=inherit + StandardError=inherit + Restart=always + User=pi + + [Install] + WantedBy=multi-user.target ``` -2. Add this line: +3. Enable and start the service: ```bash - @reboot /usr/bin/python3 /home/pi/PlantWaterSystem/Embedded/plant_monitor.py & + sudo systemctl daemon-reload + sudo systemctl enable send_data_api.service + sudo systemctl start send_data_api.service ``` -3. Save and exit. +4. Check the service status: + ```bash + sudo systemctl status send_data_api.service + ``` --- @@ -189,13 +219,20 @@ Raw ADC Value: 18345, Moisture Level: 54.23%, Digital Status: Dry ```bash journalctl -u plant_monitor.service --follow ``` +For `send_data_api.py`: +```bash +journalctl -u send_data_api.service --follow +``` ### **To Restart the Service:** ```bash sudo systemctl restart plant_monitor.service ``` +For `send_data_api.py`: +```bash +sudo systemctl restart send_data_api.service +``` ### **To Manually View Data:** ```bash sqlite3 plant_sensor_data.db "SELECT * FROM moisture_data;" -``` diff --git a/embedded/plant_monitor.py b/embedded/plant_monitor.py index 367e70e..f4cd3c9 100644 --- a/embedded/plant_monitor.py +++ b/embedded/plant_monitor.py @@ -1,3 +1,4 @@ +import subprocess # Import subprocess to manage the API script import time import sqlite3 import board @@ -9,6 +10,7 @@ import argparse import signal import sys +from datetime import datetime, timedelta # Setup logging logging.basicConfig(filename="sensor_log.log", level=logging.INFO, @@ -28,12 +30,15 @@ i2c = busio.I2C(board.SCL, board.SDA) ads = ADS.ADS1115(i2c) +# Start `send_data_api.py` subprocess +api_process = subprocess.Popen(["python3", "send_data_api.py"]) + # Sensor Configuration (only active sensors are initialized) SENSORS = [ {"analog": ADS.P0, "digital": 14, "active": True}, # Sensor 1 {"analog": ADS.P1, "digital": 15, "active": True}, # Sensor 2 {"analog": ADS.P2, "digital": 18, "active": True}, # Sensor 3 - {"analog": ADS.P3, "digital": 23, "active": True}, # Sensor 4 + {"analog": ADS.P3, "digital": 23, "active": True}, # Sensor 4 ] ADDR_PIN = 7 # GPIO7 for address configuration @@ -57,6 +62,7 @@ def handle_shutdown(signum, frame): logging.info("GPIO Cleanup Done.") if conn: conn.close() + api_process.terminate() # Terminate the API subprocess sys.exit(0) signal.signal(signal.SIGTERM, handle_shutdown) @@ -131,11 +137,13 @@ def read_sensors(): def manage_data_retention(): """Delete records older than the retention period.""" try: + cutoff_date = datetime.now() - timedelta(days=RETENTION_DAYS) cursor = conn.cursor() cursor.execute(""" - DELETE FROM moisture_data WHERE timestamp < datetime('now', ?) LIMIT 1000 - """, (f'-{RETENTION_DAYS} days',)) + DELETE FROM moisture_data WHERE timestamp < ? + """, (cutoff_date.strftime("%Y-%m-%d %H:%M:%S"),)) conn.commit() + logging.info(f"Old data deleted up to {cutoff_date}.") except sqlite3.Error as e: logging.error(f"Data retention error: {e}") diff --git a/embedded/setup.sh b/embedded/setup.sh index ddf6501..e0d6491 100644 --- a/embedded/setup.sh +++ b/embedded/setup.sh @@ -59,10 +59,10 @@ else echo "Warning: plant_monitor.py not found!" fi -# Step 7: Setup Auto-Start with systemd -echo "Setting up auto-start using systemd..." +# Step 7: Setup Auto-Start with systemd for plant_monitor SERVICE_FILE="/etc/systemd/system/plant_monitor.service" +echo "Setting up auto-start for plant_monitor using systemd..." sudo bash -c "cat > $SERVICE_FILE" < $SEND_API_SERVICE_FILE" < Date: Mon, 10 Feb 2025 13:11:21 -0800 Subject: [PATCH 12/13] Automated Setup for Raspberry Pi --- embedded/plant_monitor.py | 32 ++++++++++-- embedded/send_data_api.py | 104 ++++++++++++++++++++++++++------------ embedded/setup.sh | 37 ++++++++++---- 3 files changed, 124 insertions(+), 49 deletions(-) diff --git a/embedded/plant_monitor.py b/embedded/plant_monitor.py index f4cd3c9..2b6cb38 100644 --- a/embedded/plant_monitor.py +++ b/embedded/plant_monitor.py @@ -10,6 +10,7 @@ import argparse import signal import sys +import os from datetime import datetime, timedelta # Setup logging @@ -23,15 +24,20 @@ args = parser.parse_args() # Database and Interval Setup -DB_NAME = "plant_sensor_data.db" +DB_NAME = os.getenv("DB_NAME", "plant_sensor_data.db") READ_INTERVAL = args.interval RETENTION_DAYS = args.retention_days +# I2C Setup i2c = busio.I2C(board.SCL, board.SDA) ads = ADS.ADS1115(i2c) -# Start `send_data_api.py` subprocess -api_process = subprocess.Popen(["python3", "send_data_api.py"]) +# Start `send_data_api.py` subprocess with error handling +try: + api_process = subprocess.Popen(["python3", "send_data_api.py"]) +except Exception as e: + logging.error(f"Failed to start send_data_api.py subprocess: {e}") + sys.exit(1) # Sensor Configuration (only active sensors are initialized) SENSORS = [ @@ -55,6 +61,18 @@ # Global SQLite connection conn = None +# Retry logic for sensor reading +MAX_RETRIES = 3 +def read_sensor_with_retries(sensor): + for attempt in range(MAX_RETRIES): + try: + return read_sensor_channel(sensor) + except Exception as e: + logging.warning(f"Attempt {attempt + 1}: Retrying sensor read for {sensor['analog']} due to error: {e}") + time.sleep(1) # Small delay between retries + logging.error(f"Failed to read sensor {sensor['analog']} after {MAX_RETRIES} retries.") + return 0, 0, "Error" + def handle_shutdown(signum, frame): """Handle shutdown signals gracefully.""" print("Received shutdown signal...") @@ -125,7 +143,7 @@ def read_sensors(): if not sensor["active"]: continue - adc_value, moisture_level, digital_status = read_sensor_channel(sensor) + adc_value, moisture_level, digital_status = read_sensor_with_retries(sensor) print(f"Sensor {index} - Raw ADC Value: {adc_value}, Moisture Level: {moisture_level:.2f}%, Digital Status: {digital_status}") logging.info(f"Sensor {index} - Raw ADC Value: {adc_value}, Moisture Level: {moisture_level:.2f}%, Digital Status: {digital_status}") save_to_database(index, moisture_level, digital_status) @@ -169,7 +187,11 @@ def sensor_health_check(): def main(): """Main function to read sensors and store data in SQLite.""" global conn - conn = sqlite3.connect(DB_NAME) + try: + conn = sqlite3.connect(DB_NAME) + except sqlite3.Error as e: + logging.error(f"Failed to connect to the database: {e}") + sys.exit(1) setup_database() print("Starting Multi-Sensor Plant Monitoring...") GPIO.output(ADDR_PIN, GPIO.HIGH) diff --git a/embedded/send_data_api.py b/embedded/send_data_api.py index 6e6751d..1f9048e 100644 --- a/embedded/send_data_api.py +++ b/embedded/send_data_api.py @@ -1,24 +1,34 @@ from flask import Flask, jsonify, request import sqlite3 import requests -from datetime import datetime, timedelta import schedule import time import threading +import logging import os +from datetime import datetime, timedelta -app = Flask(__name__) +# Setup logging +logging.basicConfig(filename="api_log.log", level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s") # Configuration -DB_NAME = "plant_sensor_data.db" +DB_NAME = os.getenv("DB_NAME", "plant_sensor_data.db") BACKEND_API_URL = os.getenv("BACKEND_API_URL", "https://backend.example.com/receive-data") RETRY_ATTEMPTS = 3 +BASE_DELAY = 2 # Base delay for exponential backoff +app = Flask(__name__) def fetch_recent_data(): """Fetch all sensor readings from the last 12 hours.""" try: conn = sqlite3.connect(DB_NAME) + except sqlite3.Error as e: + logging.error(f"Failed to connect to the database: {e}") + return [] + + try: cursor = conn.cursor() # Get the current timestamp and 12 hours back @@ -32,64 +42,92 @@ def fetch_recent_data(): """, (twelve_hours_ago.strftime("%Y-%m-%d %H:%M:%S"),)) data = cursor.fetchall() - conn.close() - - # Convert data to a list of dictionaries - return [ - { - "id": row[0], - "timestamp": row[1], - "sensor_id": row[2], - "moisture_level": row[3], - "digital_status": row[4] - } - for row in data - ] except sqlite3.Error as e: - print(f"Database error: {e}") + logging.error(f"Database query error: {e}") return [] + finally: + conn.close() + # Convert data to a list of dictionaries + return [ + { + "id": row[0], + "timestamp": row[1], + "sensor_id": row[2], + "moisture_level": row[3], + "digital_status": row[4] + } + for row in data + ] + +def retry_with_backoff(func, max_attempts=3, base_delay=2): + """Retry a function with exponential backoff.""" + for attempt in range(max_attempts): + if func(): + return True + delay = base_delay * (2 ** attempt) + logging.warning(f"Retrying after {delay} seconds...") + time.sleep(delay) + logging.error("All retry attempts failed.") + return False def send_data_to_backend(): """Send sensor data to the backend with retry logic.""" data = fetch_recent_data() if not data: - print("No data to send.") - return + logging.info("No data to send.") + return False - for attempt in range(RETRY_ATTEMPTS): + def send_request(): try: - response = requests.post(BACKEND_API_URL, json={"sensor_data": data}) - + response = requests.post(BACKEND_API_URL, json={"sensor_data": data}, timeout=10) if response.status_code == 200: - print("Data sent successfully") - return + logging.info("Data sent successfully") + return True else: - print(f"Attempt {attempt + 1}: Failed to send data ({response.status_code}) - {response.text}") + logging.error(f"Failed to send data ({response.status_code}) - {response.text}") + return False except requests.RequestException as e: - print(f"Attempt {attempt + 1}: Error sending data - {e}") - - print("All retry attempts failed.") + logging.error(f"Error sending data - {e}") + return False + return retry_with_backoff(send_request, max_attempts=RETRY_ATTEMPTS, base_delay=BASE_DELAY) @app.route("/send-data", methods=["POST"]) def send_data(): """Trigger data sending via an API request.""" - send_data_to_backend() - return jsonify({"message": "Data sending initiated"}), 200 + if send_data_to_backend(): + return jsonify({"message": "Data sent successfully"}), 200 + else: + return jsonify({"message": "Failed to send data"}), 500 +def safe_task_execution(task): + """Execute a task safely, catching any exceptions.""" + try: + task() + except Exception as e: + logging.error(f"Scheduled task failed: {e}") + +def verify_schedule_registration(job): + """Verify if the schedule is successfully registered.""" + if job is None: + logging.error("Failed to register scheduled job.") + else: + logging.info("Scheduled job registered successfully.") def schedule_data_sending(): """Schedule data sending to occur at 12:00 AM and 12:00 PM daily.""" - schedule.every().day.at("00:00").do(send_data_to_backend) - schedule.every().day.at("12:00").do(send_data_to_backend) + job1 = schedule.every().day.at("00:00").do(lambda: safe_task_execution(send_data_to_backend)) + job2 = schedule.every().day.at("12:00").do(lambda: safe_task_execution(send_data_to_backend)) + + verify_schedule_registration(job1) + verify_schedule_registration(job2) while True: schedule.run_pending() time.sleep(1) - def run_schedule_in_thread(): """Run the scheduler in a separate thread.""" thread = threading.Thread(target=schedule_data_sending) diff --git a/embedded/setup.sh b/embedded/setup.sh index e0d6491..b709b70 100644 --- a/embedded/setup.sh +++ b/embedded/setup.sh @@ -51,7 +51,6 @@ else fi # Step 6: Set Permissions and Run Python Script - if [ -f "plant_monitor.py" ]; then echo "Setting executable permission for the plant_monitor.py script..." chmod +x plant_monitor.py @@ -62,8 +61,7 @@ fi # Step 7: Setup Auto-Start with systemd for plant_monitor SERVICE_FILE="/etc/systemd/system/plant_monitor.service" -echo "Setting up auto-start for plant_monitor using systemd..." -sudo bash -c "cat > $SERVICE_FILE" < $SEND_API_SERVICE_FILE" < Date: Wed, 12 Feb 2025 13:39:51 -0800 Subject: [PATCH 13/13] Revert 140 main (#141) Co-authored-by: Mayuresh More