diff --git a/html/saraR4.html b/html/saraR4.html index e8b42bd..df0009e 100755 --- a/html/saraR4.html +++ b/html/saraR4.html @@ -419,6 +419,14 @@ Pending + + + + + +
@@ -1603,6 +1611,10 @@ function resetSelfTestUI() { document.getElementById('test_network_status').textContent = 'Pending'; document.getElementById('test_network_detail').textContent = 'Waiting...'; + // Reset sensor tests + document.getElementById('sensor_tests_separator').style.display = 'none'; + document.getElementById('sensor_tests_container').innerHTML = ''; + // Reset logs document.getElementById('selftest_logs').innerHTML = ''; @@ -1725,6 +1737,7 @@ async function selfTestSequence() { selfTestReport.modemVersion = configResponse.modem_version || 'Unknown'; selfTestReport.latitude = configResponse.latitude_raw || 'N/A'; selfTestReport.longitude = configResponse.longitude_raw || 'N/A'; + selfTestReport.config = configResponse; // Get RTC time try { @@ -2011,6 +2024,186 @@ async function selfTestSequence() { testsFailed++; } + // ═══════════════════════════════════════════════════════ + // SENSOR TESTS - Test enabled sensors based on config + // ═══════════════════════════════════════════════════════ + const config = selfTestReport.config || {}; + const sensorTests = []; + + // NPM is always present + sensorTests.push({ id: 'npm', name: 'NextPM (Particles)', type: 'npm', port: 'ttyAMA5' }); + + // BME280 if enabled + if (config.BME280) { + sensorTests.push({ id: 'bme280', name: 'BME280 (Temp/Hum)', type: 'BME280' }); + } + + // Noise if enabled + if (config.NOISE) { + sensorTests.push({ id: 'noise', name: 'Noise Sensor', type: 'noise' }); + } + + // Envea if enabled + if (config.envea) { + sensorTests.push({ id: 'envea', name: 'Envea (Gas Sensors)', type: 'envea' }); + } + + // Create sensor test UI entries dynamically + if (sensorTests.length > 0) { + document.getElementById('sensor_tests_separator').style.display = ''; + const sensorContainer = document.getElementById('sensor_tests_container'); + sensorContainer.innerHTML = ''; + + sensorTests.forEach(sensor => { + sensorContainer.innerHTML += ` +
+
+ ${sensor.name} +
Waiting...
+
+ Pending +
`; + }); + } + + addSelfTestLog(''); + addSelfTestLog('────────────────────────────────────────────────────────'); + addSelfTestLog('SENSOR TESTS'); + addSelfTestLog('────────────────────────────────────────────────────────'); + + // Run each sensor test + for (const sensor of sensorTests) { + await delay(500); + + document.getElementById('selftest_status').innerHTML = ` +
+
+ Testing ${sensor.name}... +
`; + + updateTestStatus(sensor.id, 'Testing...', 'Reading sensor data...', 'bg-info'); + addSelfTestLog(`Testing ${sensor.name}...`); + + try { + if (sensor.type === 'npm') { + // NPM sensor test + const npmResult = await new Promise((resolve, reject) => { + $.ajax({ + url: 'launcher.php?type=npm&port=' + sensor.port, + dataType: 'json', + method: 'GET', + timeout: 15000, + success: function(data) { resolve(data); }, + error: function(xhr, status, error) { reject(new Error(error || status)); } + }); + }); + + selfTestReport.rawResponses['NPM Sensor'] = JSON.stringify(npmResult, null, 2); + addSelfTestLog(`NPM response: PM1=${npmResult.PM1}, PM2.5=${npmResult.PM25}, PM10=${npmResult.PM10}`); + + // Check for errors + const npmErrors = ['notReady', 'fanError', 'laserError', 'heatError', 't_rhError', 'memoryError', 'degradedState']; + const activeErrors = npmErrors.filter(e => npmResult[e] === 1); + + if (activeErrors.length > 0) { + updateTestStatus(sensor.id, 'Warning', `Errors: ${activeErrors.join(', ')}`, 'bg-warning'); + testsFailed++; + } else if (npmResult.PM1 !== undefined && npmResult.PM25 !== undefined && npmResult.PM10 !== undefined) { + updateTestStatus(sensor.id, 'Passed', `PM1: ${npmResult.PM1} | PM2.5: ${npmResult.PM25} | PM10: ${npmResult.PM10} ug/m3`, 'bg-success'); + testsPassed++; + } else { + updateTestStatus(sensor.id, 'Warning', 'Incomplete data received', 'bg-warning'); + testsFailed++; + } + + } else if (sensor.type === 'BME280') { + // BME280 sensor test + const bme280Result = await new Promise((resolve, reject) => { + $.ajax({ + url: 'launcher.php?type=BME280', + dataType: 'text', + method: 'GET', + timeout: 15000, + success: function(data) { resolve(data); }, + error: function(xhr, status, error) { reject(new Error(error || status)); } + }); + }); + + const bmeData = JSON.parse(bme280Result); + selfTestReport.rawResponses['BME280 Sensor'] = JSON.stringify(bmeData, null, 2); + addSelfTestLog(`BME280 response: temp=${bmeData.temp}, hum=${bmeData.hum}, press=${bmeData.press}`); + + if (bmeData.temp !== undefined && bmeData.hum !== undefined && bmeData.press !== undefined) { + updateTestStatus(sensor.id, 'Passed', `${bmeData.temp}°C | ${bmeData.hum}% | ${bmeData.press} hPa`, 'bg-success'); + testsPassed++; + } else { + updateTestStatus(sensor.id, 'Warning', 'Incomplete data received', 'bg-warning'); + testsFailed++; + } + + } else if (sensor.type === 'noise') { + // Noise sensor test + const noiseResult = await new Promise((resolve, reject) => { + $.ajax({ + url: 'launcher.php?type=noise', + dataType: 'text', + method: 'GET', + timeout: 15000, + success: function(data) { resolve(data); }, + error: function(xhr, status, error) { reject(new Error(error || status)); } + }); + }); + + selfTestReport.rawResponses['Noise Sensor'] = noiseResult; + addSelfTestLog(`Noise response: ${noiseResult.trim()}`); + + const noiseValue = parseFloat(noiseResult.trim()); + if (!isNaN(noiseValue) && noiseValue > 0) { + updateTestStatus(sensor.id, 'Passed', `${noiseValue} dB`, 'bg-success'); + testsPassed++; + } else if (noiseResult.trim() !== '') { + updateTestStatus(sensor.id, 'Warning', `Unexpected value: ${noiseResult.trim()}`, 'bg-warning'); + testsFailed++; + } else { + updateTestStatus(sensor.id, 'Failed', 'No data received', 'bg-danger'); + testsFailed++; + } + + } else if (sensor.type === 'envea') { + // Envea sensor test - use the debug endpoint for all sensors + const enveaResult = await new Promise((resolve, reject) => { + $.ajax({ + url: 'launcher.php?type=envea_debug', + dataType: 'text', + method: 'GET', + timeout: 30000, + success: function(data) { resolve(data); }, + error: function(xhr, status, error) { reject(new Error(error || status)); } + }); + }); + + selfTestReport.rawResponses['Envea Sensors'] = enveaResult; + addSelfTestLog(`Envea response: ${enveaResult.trim().substring(0, 200)}`); + + if (enveaResult.trim() !== '' && !enveaResult.toLowerCase().includes('error')) { + updateTestStatus(sensor.id, 'Passed', 'Sensors responding', 'bg-success'); + testsPassed++; + } else if (enveaResult.toLowerCase().includes('error')) { + updateTestStatus(sensor.id, 'Failed', 'Sensor error detected', 'bg-danger'); + testsFailed++; + } else { + updateTestStatus(sensor.id, 'Failed', 'No data received', 'bg-danger'); + testsFailed++; + } + } + } catch (error) { + addSelfTestLog(`${sensor.name} test error: ${error.message}`); + updateTestStatus(sensor.id, 'Failed', error.message, 'bg-danger'); + selfTestReport.rawResponses[`${sensor.name}`] = `ERROR: ${error.message}`; + testsFailed++; + } + } + } catch (error) { addSelfTestLog(`Test sequence error: ${error.message}`); } finally { @@ -2101,7 +2294,11 @@ GPS Location: ${selfTestReport.latitude || 'N/A'}, ${selfTestReport.longitude modem: 'Modem Connection', sim: 'SIM Card', signal: 'Signal Strength', - network: 'Network Connection' + network: 'Network Connection', + npm: 'NextPM (Particles)', + bme280: 'BME280 (Temp/Hum)', + noise: 'Noise Sensor', + envea: 'Envea (Gas Sensors)' }; for (const [testId, name] of Object.entries(testNames)) {