update
This commit is contained in:
@@ -2,9 +2,11 @@
|
|||||||
"loop_activation": true,
|
"loop_activation": true,
|
||||||
"loop_log": true,
|
"loop_log": true,
|
||||||
"boot_log": true,
|
"boot_log": true,
|
||||||
|
"modem_config_mode": false,
|
||||||
"NPM/get_data_v2.py": true,
|
"NPM/get_data_v2.py": true,
|
||||||
"loop/SARA_send_data_v2.py": true,
|
"loop/SARA_send_data_v2.py": true,
|
||||||
"RTC/save_to_db.py": true,
|
"RTC/save_to_db.py": true,
|
||||||
|
"BME280/get_data_v2.py": true,
|
||||||
"deviceID": "XXXX",
|
"deviceID": "XXXX",
|
||||||
"deviceName": "NebuleAir-proXXX",
|
"deviceName": "NebuleAir-proXXX",
|
||||||
"SaraR4_baudrate": 115200,
|
"SaraR4_baudrate": 115200,
|
||||||
|
|||||||
@@ -57,12 +57,9 @@
|
|||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<div class="card text-dark bg-light">
|
<div class="card text-dark bg-light">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Consulter Base de donnée</h5>
|
<h5 class="card-title">Consulter la base de donnée</h5>
|
||||||
<p class="card-text">General information.</p>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM',10,false)">Mesures PM</button>
|
||||||
<button class="btn btn-primary" onclick="get_internet()">Get Data</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_BME280',10,false)">Mesures Temp/Hum</button>
|
||||||
<table class="table table-striped-columns">
|
|
||||||
<tbody id="data-table-body_internet_general"></tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,19 +68,19 @@
|
|||||||
<div class="card text-dark bg-light">
|
<div class="card text-dark bg-light">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Télécharger les données</h5>
|
<h5 class="card-title">Télécharger les données</h5>
|
||||||
<p class="card-text">Scan des réseaux WIFI disponibles.</p>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM',10,true)">Mesures PM</button>
|
||||||
<button class="btn btn-primary" onclick="wifi_scan()">Download</button>
|
<button class="btn btn-primary" onclick="get_data_sqlite('data_BME280',10,true)">Mesures Temp/Hum</button>
|
||||||
<table class="table">
|
|
||||||
<tbody id="data-table-body_wifi_scan"></tbody>
|
|
||||||
</table>
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
|
<div class="row mt-2">
|
||||||
|
<div id="table_data"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
@@ -164,11 +161,125 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(error => console.error('Error loading config.json:', error));
|
.catch(error => console.error('Error loading config.json:', error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TABLE PM
|
||||||
|
function get_data_sqlite(table, limit, download) {
|
||||||
|
console.log("Getting data for table mesure PM");
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: 'launcher.php?type=table_mesurePM&table='+table+'&limit='+limit+'&download='+download,
|
||||||
|
dataType: 'text', // Specify that you expect a JSON response
|
||||||
|
method: 'GET', // Use GET or POST depending on your needs
|
||||||
|
success: function(response) {
|
||||||
|
console.log(response);
|
||||||
|
|
||||||
|
// If download is true, generate and trigger CSV download
|
||||||
|
if (download) {
|
||||||
|
downloadCSV(response, table);
|
||||||
|
return; // Exit function after triggering download
|
||||||
|
}
|
||||||
|
|
||||||
|
let rows = response.trim().split("\n");
|
||||||
|
// Generate Bootstrap table
|
||||||
|
|
||||||
|
let tableHTML = `<table class="table table-striped table-bordered">
|
||||||
|
<thead class="table-dark"><tr>`;
|
||||||
|
|
||||||
|
// Define column headers dynamically based on the table type
|
||||||
|
if (table === "data_NPM") {
|
||||||
|
tableHTML += `
|
||||||
|
<th>Timestamp</th>
|
||||||
|
<th>PM1</th>
|
||||||
|
<th>PM2.5</th>
|
||||||
|
<th>PM10</th>
|
||||||
|
<th>Temperature (°C)</th>
|
||||||
|
<th>Humidity (%)</th>
|
||||||
|
`;
|
||||||
|
} else if (table === "data_BME280") {
|
||||||
|
tableHTML += `
|
||||||
|
<th>Timestamp</th>
|
||||||
|
<th>Temperature (°C)</th>
|
||||||
|
<th>Humidity (%)</th>
|
||||||
|
<th>Pressure (hPa)</th>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
tableHTML += `</tr></thead><tbody>`;
|
||||||
|
|
||||||
|
// Loop through rows and create table rows
|
||||||
|
rows.forEach(row => {
|
||||||
|
let columns = row.replace(/[()]/g, "").split(", "); // Remove parentheses and split
|
||||||
|
tableHTML += "<tr>";
|
||||||
|
|
||||||
|
if (table === "data_NPM") {
|
||||||
|
tableHTML += `
|
||||||
|
<td>${columns[0]}</td>
|
||||||
|
<td>${columns[1]}</td>
|
||||||
|
<td>${columns[2]}</td>
|
||||||
|
<td>${columns[3]}</td>
|
||||||
|
<td>${columns[4]}</td>
|
||||||
|
<td>${columns[5]}</td>
|
||||||
|
`;
|
||||||
|
} else if (table === "data_BME280") {
|
||||||
|
tableHTML += `
|
||||||
|
<td>${columns[0]}</td>
|
||||||
|
<td>${columns[1]}</td>
|
||||||
|
<td>${columns[2]}</td>
|
||||||
|
<td>${columns[3]}</td>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
tableHTML += "</tr>";
|
||||||
|
});
|
||||||
|
|
||||||
|
tableHTML += `</tbody></table>`;
|
||||||
|
|
||||||
|
// Update the #table_data div with the generated table
|
||||||
|
document.getElementById("table_data").innerHTML = tableHTML;
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
console.error('AJAX request failed:', status, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadCSV(response, table) {
|
||||||
|
let rows = response.trim().split("\n");
|
||||||
|
|
||||||
|
let csvContent = "";
|
||||||
|
|
||||||
|
// Add headers based on table type
|
||||||
|
if (table === "data_NPM") {
|
||||||
|
csvContent += "Timestamp,PM1,PM2.5,PM10,Temperature (°C),Humidity (%)\n";
|
||||||
|
} else if (table === "data_BME280") {
|
||||||
|
csvContent += "Timestamp,Temperature (°C),Humidity (%),Pressure (hPa)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format rows as CSV
|
||||||
|
rows.forEach(row => {
|
||||||
|
let columns = row.replace(/[()]/g, "").split(", ");
|
||||||
|
csvContent += columns.join(",") + "\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a downloadable file
|
||||||
|
let blob = new Blob([csvContent], { type: "text/csv" });
|
||||||
|
let url = window.URL.createObjectURL(blob);
|
||||||
|
let a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.download = table + "_data.csv"; // File name
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -84,9 +84,9 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
|
|
||||||
<!-- Card BME values -->
|
|
||||||
<div class="col-sm-4 mt-2">
|
<div class="col-sm-4 mt-2">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -202,6 +202,17 @@ if ($type == "BME280") {
|
|||||||
echo $output;
|
echo $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($type == "table_mesurePM") {
|
||||||
|
$table=$_GET['table'];
|
||||||
|
$limit=$_GET['limit'];
|
||||||
|
$download=$_GET['download'];
|
||||||
|
|
||||||
|
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/read.py '.$table.' '.$limit;
|
||||||
|
$output = shell_exec($command);
|
||||||
|
echo $output;
|
||||||
|
}
|
||||||
|
|
||||||
# SARA R4 COMMANDS
|
# SARA R4 COMMANDS
|
||||||
if ($type == "sara") {
|
if ($type == "sara") {
|
||||||
$port=$_GET['port'];
|
$port=$_GET['port'];
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ function clear_loopLogs(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getModem_busy_status() {
|
function getModem_busy_status() {
|
||||||
console.log("Getting modem busy status");
|
//console.log("Getting modem busy status");
|
||||||
|
|
||||||
const script_is_running = document.getElementById("script_running");
|
const script_is_running = document.getElementById("script_running");
|
||||||
|
|
||||||
@@ -235,7 +235,7 @@ function getModem_busy_status() {
|
|||||||
dataType: 'json', // Expecting JSON response
|
dataType: 'json', // Expecting JSON response
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
console.log(response);
|
//console.log(response);
|
||||||
|
|
||||||
if (response.running) {
|
if (response.running) {
|
||||||
// Script is running → Show the Bootstrap spinner
|
// Script is running → Show the Bootstrap spinner
|
||||||
|
|||||||
@@ -51,6 +51,12 @@
|
|||||||
<main class="col-md-10 ms-sm-auto col-lg-11 offset-md-2 offset-lg-1 px-md-4">
|
<main class="col-md-10 ms-sm-auto col-lg-11 offset-md-2 offset-lg-1 px-md-4">
|
||||||
<h1 class="mt-4">Modem 4G</h1>
|
<h1 class="mt-4">Modem 4G</h1>
|
||||||
<p>Votre capteur est équipé d'un modem 4G et d'une carte SIM afin d'envoyer les mesures sur internet.</p>
|
<p>Votre capteur est équipé d'un modem 4G et d'une carte SIM afin d'envoyer les mesures sur internet.</p>
|
||||||
|
|
||||||
|
<div class="form-check form-switch mb-2">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch" id="check_modem_configMode" onchange="update_modem_configMode('modem_config_mode',this.checked)">
|
||||||
|
<label class="form-check-label" for="check_modem_configMode">Mode configuration</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<span id="modem_status_message"></span>
|
<span id="modem_status_message"></span>
|
||||||
<h3>
|
<h3>
|
||||||
Status
|
Status
|
||||||
@@ -298,6 +304,17 @@
|
|||||||
})
|
})
|
||||||
.catch(error => console.error(`Error loading ${file}:`, error));
|
.catch(error => console.error(`Error loading ${file}:`, error));
|
||||||
});
|
});
|
||||||
|
fetch('../config.json') // Replace 'deviceID.txt' with 'config.json'
|
||||||
|
.then(response => response.json()) // Parse response as JSON
|
||||||
|
.then(data => {
|
||||||
|
console.log("Getting config file (onload)");
|
||||||
|
//modem config mode
|
||||||
|
const check_modem_configMode = document.getElementById("check_modem_configMode");
|
||||||
|
check_modem_configMode.checked = data.modem_config_mode;
|
||||||
|
console.log("Modem configuration: " + data.modem_config_mode);
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function getData_saraR4(port, command, timeout){
|
function getData_saraR4(port, command, timeout){
|
||||||
@@ -595,7 +612,7 @@ function connectAPN_saraR4(port, APN_address, timeout){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getModem_busy_status() {
|
function getModem_busy_status() {
|
||||||
console.log("Getting modem busy status");
|
//console.log("Getting modem busy status");
|
||||||
|
|
||||||
const SARA_busy_message = document.getElementById("modem_status_message");
|
const SARA_busy_message = document.getElementById("modem_status_message");
|
||||||
|
|
||||||
@@ -604,19 +621,20 @@ function getModem_busy_status() {
|
|||||||
dataType: 'json', // Expecting JSON response
|
dataType: 'json', // Expecting JSON response
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
console.log(response);
|
//console.log(response);
|
||||||
|
|
||||||
if (response.running) {
|
if (response.running) {
|
||||||
// Script is running → Red button, "Modem is busy"
|
// Script is running → Red button, "Modem is busy"
|
||||||
|
|
||||||
SARA_busy_message.innerHTML= ` <div class="alert alert-warning" role="alert">
|
SARA_busy_message.innerHTML= ` <div class="alert alert-warning" role="alert">
|
||||||
Le modem 4G est en cours d'utilisation. L'utilisation des boutons ci-dessous peut entrainer des erreurs. Veuillez désactiver le modem avant de continuer.
|
Le modem 4G est en cours d'utilisation! L'utilisation des boutons ci-dessous peut entrainer des erreurs. Veuillez mettre le modem en mode configuration.
|
||||||
</div>`
|
</div>`
|
||||||
} else {
|
} else {
|
||||||
// Script is NOT running → Green button, "Modem is available"
|
// Script is NOT running → Green button, "Modem is available"
|
||||||
|
|
||||||
SARA_busy_message.innerHTML= ` <div class="alert alert-primary" role="alert">
|
SARA_busy_message.innerHTML= ` <div class="alert alert-primary" role="alert">
|
||||||
Le modem 4G est disponible.
|
Veuillez vous assurer de mettre le modem en mode configuration avant de cliquer sur les boutons ci-dessous. <br>
|
||||||
|
Une fois terminé veillez à bien désactiver le mode configuration.
|
||||||
</div>`
|
</div>`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -628,6 +646,23 @@ function getModem_busy_status() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function update_modem_configMode(param, checked){
|
||||||
|
console.log("updating modem config mode to :" + checked);
|
||||||
|
$.ajax({
|
||||||
|
url: 'launcher.php?type=update_config¶m='+param+'&value='+checked,
|
||||||
|
dataType: 'text', // Specify that you expect a JSON response
|
||||||
|
method: 'GET', // Use GET or POST depending on your needs
|
||||||
|
cache: false, // Prevent AJAX from caching
|
||||||
|
success: function(response) {
|
||||||
|
console.log(response);
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
console.error('AJAX request failed:', status, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
getModem_busy_status();
|
getModem_busy_status();
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ import sys
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import RPi.GPIO as GPIO
|
import RPi.GPIO as GPIO
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
# Record the start time of the script
|
# Record the start time of the script
|
||||||
start_time_script = time.time()
|
start_time_script = time.time()
|
||||||
@@ -205,10 +206,16 @@ need_to_log = config.get('loop_log', False) #inscription des logs
|
|||||||
send_aircarto = config.get('send_aircarto', True) #envoi sur AirCarto (data.nebuleair.fr)
|
send_aircarto = config.get('send_aircarto', True) #envoi sur AirCarto (data.nebuleair.fr)
|
||||||
send_uSpot = config.get('send_uSpot', False) #envoi sur MicroSpot ()
|
send_uSpot = config.get('send_uSpot', False) #envoi sur MicroSpot ()
|
||||||
selected_networkID = config.get('SARA_R4_neworkID', '')
|
selected_networkID = config.get('SARA_R4_neworkID', '')
|
||||||
|
modem_config_mode = config.get('modem_config_mode', False) #modem 4G en mode configuration
|
||||||
|
|
||||||
#update device id in the payload json
|
#update device id in the payload json
|
||||||
payload_json["nebuleairid"] = device_id
|
payload_json["nebuleairid"] = device_id
|
||||||
|
|
||||||
|
# Skip execution if modem_config_mode is true
|
||||||
|
if modem_config_mode:
|
||||||
|
print("Modem 4G (SARA R4) is in config mode -> EXIT")
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
ser_sara = serial.Serial(
|
ser_sara = serial.Serial(
|
||||||
port='/dev/ttyAMA2',
|
port='/dev/ttyAMA2',
|
||||||
baudrate=baudrate, #115200 ou 9600
|
baudrate=baudrate, #115200 ou 9600
|
||||||
@@ -279,6 +286,13 @@ try:
|
|||||||
# Display the result
|
# Display the result
|
||||||
if last_row:
|
if last_row:
|
||||||
print("SQLite DB last available row:", last_row)
|
print("SQLite DB last available row:", last_row)
|
||||||
|
|
||||||
|
datetime_measure_PM = last_row[0] #on récupère le datetime de la table data_NPM
|
||||||
|
# Convert to a datetime object
|
||||||
|
dt_object = datetime.strptime(datetime_measure_PM, '%Y-%m-%d %H:%M:%S')
|
||||||
|
# Convert to InfluxDB RFC3339 format with UTC 'Z' suffix
|
||||||
|
influx_timestamp = dt_object.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
|
||||||
PM1 = last_row[1]
|
PM1 = last_row[1]
|
||||||
PM25 = last_row[2]
|
PM25 = last_row[2]
|
||||||
PM10 = last_row[3]
|
PM10 = last_row[3]
|
||||||
@@ -358,7 +372,7 @@ try:
|
|||||||
|
|
||||||
#3. Send to endpoint (with device ID)
|
#3. Send to endpoint (with device ID)
|
||||||
print("Send data (POST REQUEST):")
|
print("Send data (POST REQUEST):")
|
||||||
command= f'AT+UHTTPC={aircarto_profile_id},4,"/pro_4G/data.php?sensor_id={device_id}&datetime=000","server_response.txt","sensordata_csv.json",4\r'
|
command= f'AT+UHTTPC={aircarto_profile_id},4,"/pro_4G/data.php?sensor_id={device_id}&datetime={influx_timestamp}","server_response.txt","sensordata_csv.json",4\r'
|
||||||
ser_sara.write(command.encode('utf-8'))
|
ser_sara.write(command.encode('utf-8'))
|
||||||
|
|
||||||
response_SARA_3 = read_complete_response(ser_sara, timeout=5, end_of_response_timeout=120, wait_for_lines=["+UUHTTPCR", "+CME ERROR"], debug=True)
|
response_SARA_3 = read_complete_response(ser_sara, timeout=5, end_of_response_timeout=120, wait_for_lines=["+UUHTTPCR", "+CME ERROR"], debug=True)
|
||||||
|
|||||||
@@ -6,11 +6,16 @@
|
|||||||
|____/ \__\_\_____|_|\__\___|
|
|____/ \__\_\_____|_|\__\___|
|
||||||
|
|
||||||
Script to read data from a sqlite database
|
Script to read data from a sqlite database
|
||||||
/usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/read.py
|
/usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/read.py data_NPM 10
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import sys
|
||||||
|
parameter = sys.argv[1:] # Exclude the script name
|
||||||
|
#print("Parameters received:")
|
||||||
|
table_name=parameter[0]
|
||||||
|
limit_num=parameter[1]
|
||||||
|
|
||||||
# Connect to the SQLite database
|
# Connect to the SQLite database
|
||||||
conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db")
|
conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db")
|
||||||
@@ -18,8 +23,15 @@ cursor = conn.cursor()
|
|||||||
|
|
||||||
# Retrieve the last 10 sensor readings
|
# Retrieve the last 10 sensor readings
|
||||||
#cursor.execute("SELECT * FROM data_NPM ORDER BY timestamp DESC LIMIT 10")
|
#cursor.execute("SELECT * FROM data_NPM ORDER BY timestamp DESC LIMIT 10")
|
||||||
cursor.execute("SELECT * FROM data_BME280 ORDER BY timestamp DESC LIMIT 10")
|
#cursor.execute("SELECT * FROM data_BME280 ORDER BY timestamp DESC LIMIT 10")
|
||||||
#cursor.execute("SELECT * FROM timestamp_table")
|
#cursor.execute("SELECT * FROM timestamp_table")
|
||||||
|
if table_name == "timestamp_table":
|
||||||
|
cursor.execute("SELECT * FROM timestamp_table")
|
||||||
|
|
||||||
|
else:
|
||||||
|
query = f"SELECT * FROM {table_name} ORDER BY timestamp DESC LIMIT ?"
|
||||||
|
cursor.execute(query, (limit_num,))
|
||||||
|
|
||||||
|
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
rows.reverse() # Reverse the order in Python (to get ascending order)
|
rows.reverse() # Reverse the order in Python (to get ascending order)
|
||||||
|
|||||||
Reference in New Issue
Block a user