Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Virus Fever Detection Using IoT</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/react@18/umd/react.development.js" crossorigin></script> | |
| <script src="https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.development.js" crossorigin></script> | |
| <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> | |
| <style> | |
| .chart-container { | |
| position: relative; | |
| height: 300px; | |
| width: 100%; | |
| max-width: 400px; | |
| margin: 0 auto; | |
| } | |
| .alert-badge { | |
| position: absolute; | |
| top: 10px; | |
| right: 10px; | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| background-color: #ef4444; | |
| color: white; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-weight: bold; | |
| font-size: 12px; | |
| z-index: 10; | |
| } | |
| .threshold-line { | |
| border-top: 2px dashed; | |
| position: absolute; | |
| width: 100%; | |
| } | |
| .threshold-label { | |
| position: absolute; | |
| right: 10px; | |
| font-size: 12px; | |
| background-color: rgba(255,255,255,0.8); | |
| padding: 2px 5px; | |
| border-radius: 3px; | |
| } | |
| .chart-toolbar { | |
| display: flex; | |
| gap: 10px; | |
| margin-bottom: 15px; | |
| flex-wrap: wrap; | |
| } | |
| .chart-wrapper { | |
| background-color: #f8fafc; | |
| border-radius: 0.5rem; | |
| padding: 1rem; | |
| box-shadow: 0 1px 3px rgba(0,0,0,0.1); | |
| transition: all 0.3s ease; | |
| } | |
| .chart-wrapper:hover { | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
| transform: translateY(-2px); | |
| } | |
| @media (max-width: 768px) { | |
| .chart-grid { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 font-sans"> | |
| <div id="root"></div> | |
| <script type="text/babel"> | |
| const { useState, useEffect, useRef } = React; | |
| function App() { | |
| // Initial data points (first 20 seconds) | |
| const initialTempData = [ | |
| {x: 0, y: 36.8}, {x: 2, y: 36.9}, {x: 4, y: 37.0}, | |
| {x: 6, y: 37.1}, {x: 8, y: 37.0}, {x: 10, y: 37.2}, | |
| {x: 12, y: 37.3}, {x: 14, y: 38.1}, {x: 16, y: 38.2}, | |
| {x: 18, y: 38.0} | |
| ]; | |
| const initialPulseData = [ | |
| {x: 0, y: 78}, {x: 2, y: 79}, {x: 4, y: 80}, | |
| {x: 6, y: 82}, {x: 8, y: 81}, {x: 10, y: 83}, | |
| {x: 12, y: 84}, {x: 14, y: 86}, {x: 16, y: 85}, | |
| {x: 18, y: 84} | |
| ]; | |
| const initialRespData = [ | |
| {x: 0, y: 14}, {x: 2, y: 15}, {x: 4, y: 15}, | |
| {x: 6, y: 14}, {x: 8, y: 15}, {x: 10, y: 16}, | |
| {x: 12, y: 15}, {x: 14, y: 16}, {x: 16, y: 15}, | |
| {x: 18, y: 14} | |
| ]; | |
| // State for data points | |
| const [temperatureData, setTemperatureData] = useState([...initialTempData]); | |
| const [pulseData, setPulseData] = useState([...initialPulseData]); | |
| const [respirationData, setRespirationData] = useState([...initialRespData]); | |
| const [isRunning, setIsRunning] = useState(false); | |
| const [showTemperature, setShowTemperature] = useState(true); | |
| const [showPulse, setShowPulse] = useState(true); | |
| const [showRespiration, setShowRespiration] = useState(true); | |
| const [currentTime, setCurrentTime] = useState(20); // Start at 20 seconds | |
| const [isInitialized, setIsInitialized] = useState(false); | |
| // Refs for chart instances | |
| const tempChartRef = useRef(null); | |
| const pulseChartRef = useRef(null); | |
| const respChartRef = useRef(null); | |
| const combinedChartRef = useRef(null); | |
| // Initialize data with initial points | |
| useEffect(() => { | |
| if (!isInitialized) { | |
| setIsInitialized(true); | |
| } | |
| }, []); | |
| // Data simulation effect | |
| useEffect(() => { | |
| let interval; | |
| if (isRunning) { | |
| interval = setInterval(() => { | |
| updateData(); | |
| setCurrentTime(prev => prev + 2); | |
| }, 2000); | |
| } | |
| return () => clearInterval(interval); | |
| }, [isRunning, temperatureData, pulseData, respirationData]); | |
| // Initialize charts when component mounts | |
| useEffect(() => { | |
| initCharts(); | |
| return () => { | |
| // Clean up charts when component unmounts | |
| if (tempChartRef.current) tempChartRef.current.destroy(); | |
| if (pulseChartRef.current) pulseChartRef.current.destroy(); | |
| if (respChartRef.current) respChartRef.current.destroy(); | |
| if (combinedChartRef.current) combinedChartRef.current.destroy(); | |
| }; | |
| }, []); | |
| // Update charts when data changes | |
| useEffect(() => { | |
| updateCharts(); | |
| }, [temperatureData, pulseData, respirationData, showTemperature, showPulse, showRespiration]); | |
| const resetData = () => { | |
| setTemperatureData([...initialTempData]); | |
| setPulseData([...initialPulseData]); | |
| setRespirationData([...initialRespData]); | |
| setCurrentTime(20); | |
| if (!isRunning) { | |
| updateCharts(); | |
| } | |
| }; | |
| const generateNewValue = (currentValue, min, max, maxChange) => { | |
| const change = (Math.random() * 2 - 1) * maxChange; | |
| let newValue = currentValue + change; | |
| // Ensure the new value stays within bounds | |
| newValue = Math.max(min, Math.min(max, newValue)); | |
| // Round to appropriate decimal places | |
| if (maxChange < 1) { | |
| return parseFloat(newValue.toFixed(1)); | |
| } | |
| return Math.round(newValue); | |
| }; | |
| const updateData = () => { | |
| // Get last values or use defaults if no data | |
| const lastTemp = temperatureData.length > 0 ? temperatureData[temperatureData.length - 1].y : 37.0; | |
| const lastPulse = pulseData.length > 0 ? pulseData[pulseData.length - 1].y : 80; | |
| const lastResp = respirationData.length > 0 ? respirationData[respirationData.length - 1].y : 15; | |
| // Generate new values with realistic fluctuations | |
| const newTemp = generateNewValue(lastTemp, 36.5, 38.5, 0.2); | |
| const newPulse = generateNewValue(lastPulse, 70, 100, 5); | |
| const newResp = generateNewValue(lastResp, 12, 18, 1); | |
| // Update data arrays (sliding window of 30 points) | |
| setTemperatureData(prev => { | |
| const newData = [...prev.slice(1), { x: currentTime, y: newTemp }]; | |
| return newData; | |
| }); | |
| setPulseData(prev => { | |
| const newData = [...prev.slice(1), { x: currentTime, y: newPulse }]; | |
| return newData; | |
| }); | |
| setRespirationData(prev => { | |
| const newData = [...prev.slice(1), { x: currentTime, y: newResp }]; | |
| return newData; | |
| }); | |
| }; | |
| const initCharts = () => { | |
| // Temperature Chart | |
| const tempCtx = document.getElementById('temperatureChart').getContext('2d'); | |
| tempChartRef.current = new Chart(tempCtx, { | |
| type: 'line', | |
| data: { | |
| datasets: [{ | |
| label: 'Temperature (Β°C)', | |
| data: temperatureData, | |
| borderColor: '#ef4444', | |
| backgroundColor: 'rgba(239, 68, 68, 0.1)', | |
| borderWidth: 2, | |
| tension: 0.4, | |
| fill: true, | |
| pointRadius: 0 | |
| }] | |
| }, | |
| options: getChartOptions('Temperature (Β°C)', 'Β°C', 34, 42, false, [ | |
| { value: 38, color: '#ef4444', label: 'Fever Threshold' } | |
| ]) | |
| }); | |
| // Pulse Chart | |
| const pulseCtx = document.getElementById('pulseChart').getContext('2d'); | |
| pulseChartRef.current = new Chart(pulseCtx, { | |
| type: 'line', | |
| data: { | |
| datasets: [{ | |
| label: 'Pulse Rate (bpm)', | |
| data: pulseData, | |
| borderColor: '#3b82f6', | |
| backgroundColor: 'rgba(59, 130, 246, 0.1)', | |
| borderWidth: 2, | |
| tension: 0.4, | |
| fill: true, | |
| pointRadius: 0 | |
| }] | |
| }, | |
| options: getChartOptions('Pulse Rate (bpm)', 'bpm', 50, 130, false, [ | |
| { value: 100, color: '#f59e0b', label: 'High Pulse' } | |
| ]) | |
| }); | |
| // Respiration Chart | |
| const respCtx = document.getElementById('respirationChart').getContext('2d'); | |
| respChartRef.current = new Chart(respCtx, { | |
| type: 'line', | |
| data: { | |
| datasets: [{ | |
| label: 'Respiration Rate (rpm)', | |
| data: respirationData, | |
| borderColor: '#10b981', | |
| backgroundColor: 'rgba(16, 185, 129, 0.1)', | |
| borderWidth: 2, | |
| tension: 0.4, | |
| fill: true, | |
| pointRadius: 0 | |
| }] | |
| }, | |
| options: getChartOptions('Respiration Rate (rpm)', 'rpm', 8, 24, false, [ | |
| { value: 20, color: '#f59e0b', label: 'High Respiration' } | |
| ]) | |
| }); | |
| // Combined Chart | |
| const combinedCtx = document.getElementById('combinedChart').getContext('2d'); | |
| combinedChartRef.current = new Chart(combinedCtx, { | |
| type: 'line', | |
| data: { | |
| datasets: [ | |
| { | |
| label: 'Temperature (Β°C)', | |
| data: temperatureData, | |
| borderColor: '#ef4444', | |
| backgroundColor: 'rgba(239, 68, 68, 0.1)', | |
| borderWidth: 2, | |
| tension: 0.4, | |
| fill: false, | |
| hidden: !showTemperature, | |
| pointRadius: 0 | |
| }, | |
| { | |
| label: 'Pulse Rate (bpm)', | |
| data: pulseData, | |
| borderColor: '#3b82f6', | |
| backgroundColor: 'rgba(59, 130, 246, 0.1)', | |
| borderWidth: 2, | |
| tension: 0.4, | |
| fill: false, | |
| hidden: !showPulse, | |
| pointRadius: 0 | |
| }, | |
| { | |
| label: 'Respiration Rate (rpm)', | |
| data: respirationData, | |
| borderColor: '#10b981', | |
| backgroundColor: 'rgba(16, 185, 129, 0.1)', | |
| borderWidth: 2, | |
| tension: 0.4, | |
| fill: false, | |
| hidden: !showRespiration, | |
| pointRadius: 0 | |
| } | |
| ] | |
| }, | |
| options: getChartOptions('Combined Health Metrics', 'Value', 30, 42, true, [ | |
| { value: 38, color: '#ef4444', label: 'Fever' }, | |
| { value: 100, color: '#f59e0b', label: 'High Pulse' }, | |
| { value: 20, color: '#f59e0b', label: 'High Respiration' } | |
| ]) | |
| }); | |
| }; | |
| const updateCharts = () => { | |
| if (tempChartRef.current) { | |
| tempChartRef.current.data.datasets[0].data = temperatureData; | |
| tempChartRef.current.update(); | |
| } | |
| if (pulseChartRef.current) { | |
| pulseChartRef.current.data.datasets[0].data = pulseData; | |
| pulseChartRef.current.update(); | |
| } | |
| if (respChartRef.current) { | |
| respChartRef.current.data.datasets[0].data = respirationData; | |
| respChartRef.current.update(); | |
| } | |
| if (combinedChartRef.current) { | |
| combinedChartRef.current.data.datasets[0].data = temperatureData; | |
| combinedChartRef.current.data.datasets[0].hidden = !showTemperature; | |
| combinedChartRef.current.data.datasets[1].data = pulseData; | |
| combinedChartRef.current.data.datasets[1].hidden = !showPulse; | |
| combinedChartRef.current.data.datasets[2].data = respirationData; | |
| combinedChartRef.current.data.datasets[2].hidden = !showRespiration; | |
| combinedChartRef.current.update(); | |
| } | |
| }; | |
| const getChartOptions = (title, yLabel, minY, maxY, isCombined = false, thresholds = []) => { | |
| const options = { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| animation: { | |
| duration: 0 | |
| }, | |
| plugins: { | |
| title: { | |
| display: true, | |
| text: title, | |
| font: { | |
| size: 16, | |
| weight: 'bold' | |
| }, | |
| padding: { | |
| top: 10, | |
| bottom: 10 | |
| } | |
| }, | |
| tooltip: { | |
| mode: 'index', | |
| intersect: false, | |
| callbacks: { | |
| label: function(context) { | |
| return `${context.dataset.label}: ${context.parsed.y.toFixed(1)}`; | |
| } | |
| } | |
| }, | |
| legend: { | |
| display: isCombined, | |
| position: 'top', | |
| labels: { | |
| boxWidth: 12, | |
| padding: 20 | |
| } | |
| }, | |
| zoom: { | |
| pan: { | |
| enabled: true, | |
| mode: 'xy' | |
| }, | |
| zoom: { | |
| wheel: { | |
| enabled: true, | |
| }, | |
| pinch: { | |
| enabled: true | |
| }, | |
| mode: 'xy', | |
| } | |
| } | |
| }, | |
| scales: { | |
| x: { | |
| type: 'linear', | |
| position: 'bottom', | |
| title: { | |
| display: true, | |
| text: 'Time (seconds)', | |
| font: { | |
| size: 12 | |
| } | |
| }, | |
| min: Math.max(0, currentTime - 60), | |
| max: currentTime, | |
| ticks: { | |
| stepSize: 10, | |
| callback: function(value) { | |
| return value + 's'; | |
| } | |
| }, | |
| grid: { | |
| color: 'rgba(0, 0, 0, 0.05)' | |
| } | |
| }, | |
| y: { | |
| title: { | |
| display: true, | |
| text: yLabel, | |
| font: { | |
| size: 12 | |
| } | |
| }, | |
| min: minY, | |
| max: maxY, | |
| ticks: { | |
| // For more precise ticks on temperature | |
| ...(yLabel === 'Β°C' ? { | |
| stepSize: 1, | |
| callback: function(value) { | |
| // Show minor ticks (0.2) as smaller ticks | |
| if (value % 1 === 0) return value; | |
| return ''; | |
| } | |
| } : {}), | |
| // For pulse and respiration | |
| ...(yLabel === 'bpm' ? { stepSize: 10 } : {}), | |
| ...(yLabel === 'rpm' ? { stepSize: 2 } : {}) | |
| }, | |
| grid: { | |
| color: function(context) { | |
| // For temperature chart, draw minor grid lines every 0.2Β°C | |
| if (yLabel === 'Β°C' && context.tick.value % 1 !== 0) { | |
| return 'rgba(0, 0, 0, 0.02)'; | |
| } | |
| return 'rgba(0, 0, 0, 0.05)'; | |
| } | |
| } | |
| } | |
| }, | |
| elements: { | |
| point: { | |
| radius: 0, | |
| hoverRadius: 5 | |
| }, | |
| line: { | |
| borderWidth: 2, | |
| tension: 0.4 | |
| } | |
| }, | |
| interaction: { | |
| mode: 'nearest', | |
| axis: 'x', | |
| intersect: false | |
| } | |
| }; | |
| // Add threshold annotations | |
| if (thresholds.length > 0) { | |
| options.plugins.annotation = { | |
| annotations: thresholds.map(threshold => ({ | |
| type: 'line', | |
| yMin: threshold.value, | |
| yMax: threshold.value, | |
| borderColor: threshold.color, | |
| borderWidth: 2, | |
| borderDash: [6, 6], | |
| label: { | |
| content: threshold.label, | |
| enabled: true, | |
| position: 'right', | |
| backgroundColor: 'rgba(255,255,255,0.8)', | |
| color: threshold.color, | |
| font: { | |
| weight: 'bold', | |
| size: 10 | |
| } | |
| } | |
| })) | |
| }; | |
| } | |
| return options; | |
| }; | |
| const toggleSimulation = () => { | |
| setIsRunning(!isRunning); | |
| }; | |
| const handleReset = () => { | |
| resetData(); | |
| if (!isRunning) { | |
| updateCharts(); | |
| } | |
| }; | |
| const getLatestValues = () => { | |
| const temp = temperatureData.length > 0 ? temperatureData[temperatureData.length - 1].y : null; | |
| const pulse = pulseData.length > 0 ? pulseData[pulseData.length - 1].y : null; | |
| const resp = respirationData.length > 0 ? respirationData[respirationData.length - 1].y : null; | |
| return { | |
| temp: temp !== null ? temp.toFixed(1) : '--', | |
| pulse: pulse !== null ? pulse.toFixed(0) : '--', | |
| resp: resp !== null ? resp.toFixed(0) : '--', | |
| time: currentTime + 's' | |
| }; | |
| }; | |
| const getStatusColor = (value, thresholds) => { | |
| if (value === null || value === undefined || value === '--') return 'gray'; | |
| const numValue = parseFloat(value); | |
| if (numValue > thresholds.high) return 'red'; | |
| if (numValue > thresholds.warning) return 'yellow'; | |
| return 'green'; | |
| }; | |
| const latest = getLatestValues(); | |
| const tempStatus = getStatusColor(latest.temp, { warning: 37.5, high: 38 }); | |
| const pulseStatus = getStatusColor(latest.pulse, { warning: 90, high: 100 }); | |
| const respStatus = getStatusColor(latest.resp, { warning: 18, high: 20 }); | |
| return ( | |
| <div className="container mx-auto px-4 py-8 max-w-6xl"> | |
| {/* Header Section */} | |
| <header className="text-center mb-8"> | |
| <h1 className="text-4xl md:text-5xl font-bold text-blue-800 mb-2"> | |
| Virus Fever Detection Using IoT | |
| </h1> | |
| <p className="text-gray-600 text-lg"> | |
| By Dhanashree (220301014), Harinee (220301027), Hema Rekha (220301030) | |
| </p> | |
| </header> | |
| {/* Current Readings Section */} | |
| <section className="mb-8 bg-white p-6 rounded-lg shadow-md"> | |
| <h2 className="text-2xl font-bold text-blue-700 mb-4">Current Health Readings</h2> | |
| <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6"> | |
| <div className={`bg-${tempStatus}-100 border-${tempStatus}-500 border-l-4 p-4 rounded-lg`}> | |
| <div className="flex justify-between items-center"> | |
| <h3 className="font-semibold text-gray-800">Temperature</h3> | |
| {tempStatus === 'red' && <span className="bg-red-500 text-white text-xs px-2 py-1 rounded-full">ALERT</span>} | |
| </div> | |
| <p className="text-3xl font-bold mt-2">{latest.temp} <span className="text-xl">Β°C</span></p> | |
| <p className="text-sm text-gray-600 mt-1">Last updated: {latest.time}</p> | |
| </div> | |
| <div className={`bg-${pulseStatus}-100 border-${pulseStatus}-500 border-l-4 p-4 rounded-lg`}> | |
| <div className="flex justify-between items-center"> | |
| <h3 className="font-semibold text-gray-800">Pulse Rate</h3> | |
| {pulseStatus === 'red' && <span className="bg-red-500 text-white text-xs px-2 py-1 rounded-full">ALERT</span>} | |
| </div> | |
| <p className="text-3xl font-bold mt-2">{latest.pulse} <span className="text-xl">bpm</span></p> | |
| <p className="text-sm text-gray-600 mt-1">Last updated: {latest.time}</p> | |
| </div> | |
| <div className={`bg-${respStatus}-100 border-${respStatus}-500 border-l-4 p-4 rounded-lg`}> | |
| <div className="flex justify-between items-center"> | |
| <h3 className="font-semibold text-gray-800">Respiration Rate</h3> | |
| {respStatus === 'red' && <span className="bg-red-500 text-white text-xs px-2 py-1 rounded-full">ALERT</span>} | |
| </div> | |
| <p className="text-3xl font-bold mt-2">{latest.resp} <span className="text-xl">rpm</span></p> | |
| <p className="text-sm text-gray-600 mt-1">Last updated: {latest.time}</p> | |
| </div> | |
| </div> | |
| <div className="flex flex-wrap gap-4 mb-4"> | |
| <button | |
| onClick={toggleSimulation} | |
| className={`px-4 py-2 rounded-lg font-medium ${isRunning ? 'bg-red-500 hover:bg-red-600' : 'bg-blue-500 hover:bg-blue-600'} text-white transition-colors`} | |
| > | |
| {isRunning ? 'Stop Simulation' : 'Start Simulation'} | |
| </button> | |
| <button | |
| onClick={handleReset} | |
| className="px-4 py-2 rounded-lg font-medium bg-gray-500 hover:bg-gray-600 text-white transition-colors" | |
| > | |
| Reset Graphs | |
| </button> | |
| <div className="flex items-center ml-auto text-sm text-gray-600"> | |
| <span className="mr-2">Simulation Time:</span> | |
| <span className="font-medium">{currentTime}s</span> | |
| </div> | |
| </div> | |
| </section> | |
| {/* Graph Visualization Section */} | |
| <section className="mb-8 bg-white p-6 rounded-lg shadow-md"> | |
| <h2 className="text-2xl font-bold text-blue-700 mb-4">Real-time Health Monitoring</h2> | |
| {/* Combined Graph Controls */} | |
| <div className="mb-6 bg-gray-50 p-4 rounded-lg"> | |
| <h3 className="font-semibold text-lg mb-2">Graph Controls</h3> | |
| <div className="flex flex-wrap gap-4"> | |
| <label className="inline-flex items-center"> | |
| <input | |
| type="checkbox" | |
| checked={showTemperature} | |
| onChange={() => setShowTemperature(!showTemperature)} | |
| className="form-checkbox h-5 w-5 text-red-500" | |
| /> | |
| <span className="ml-2 text-gray-700">Temperature</span> | |
| </label> | |
| <label className="inline-flex items-center"> | |
| <input | |
| type="checkbox" | |
| checked={showPulse} | |
| onChange={() => setShowPulse(!showPulse)} | |
| className="form-checkbox h-5 w-5 text-blue-500" | |
| /> | |
| <span className="ml-2 text-gray-700">Pulse Rate</span> | |
| </label> | |
| <label className="inline-flex items-center"> | |
| <input | |
| type="checkbox" | |
| checked={showRespiration} | |
| onChange={() => setShowRespiration(!showRespiration)} | |
| className="form-checkbox h-5 w-5 text-green-500" | |
| /> | |
| <span className="ml-2 text-gray-700">Respiration Rate</span> | |
| </label> | |
| </div> | |
| </div> | |
| {/* Combined Graph */} | |
| <div className="mb-8"> | |
| <h3 className="font-semibold text-lg mb-2">Combined Metrics</h3> | |
| <div className="chart-container"> | |
| <canvas id="combinedChart" aria-label="Combined health metrics graph"></canvas> | |
| {(latest.temp > 38 || latest.pulse > 100 || latest.resp > 20) && ( | |
| <div className="alert-badge">!</div> | |
| )} | |
| </div> | |
| </div> | |
| {/* Individual Graphs */} | |
| <h3 className="font-semibold text-lg mb-4">Individual Metrics</h3> | |
| <div className="grid chart-grid gap-6 md:grid-cols-2 lg:grid-cols-3"> | |
| <div className="chart-wrapper"> | |
| <div className="chart-container"> | |
| <canvas id="temperatureChart" aria-label="Temperature graph"></canvas> | |
| {latest.temp > 38 && ( | |
| <div className="alert-badge">!</div> | |
| )} | |
| </div> | |
| </div> | |
| <div className="chart-wrapper"> | |
| <div className="chart-container"> | |
| <canvas id="pulseChart" aria-label="Pulse rate graph"></canvas> | |
| {latest.pulse > 100 && ( | |
| <div className="alert-badge">!</div> | |
| )} | |
| </div> | |
| </div> | |
| <div className="chart-wrapper"> | |
| <div className="chart-container"> | |
| <canvas id="respirationChart" aria-label="Respiration rate graph"></canvas> | |
| {latest.resp > 20 && ( | |
| <div className="alert-badge">!</div> | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| {/* Data Table for Accessibility */} | |
| <section className="mb-8 bg-white p-6 rounded-lg shadow-md"> | |
| <h2 className="text-2xl font-bold text-blue-700 mb-4">Recent Data Points</h2> | |
| <div className="overflow-x-auto"> | |
| <table className="min-w-full bg-white"> | |
| <thead> | |
| <tr className="bg-gray-100"> | |
| <th className="py-2 px-4 border">Time (s)</th> | |
| <th className="py-2 px-4 border">Temperature (Β°C)</th> | |
| <th className="py-2 px-4 border">Pulse Rate (bpm)</th> | |
| <th className="py-2 px-4 border">Respiration Rate (rpm)</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {temperatureData.slice(-5).map((data, index) => ( | |
| <tr key={index} className="border-t"> | |
| <td className="py-2 px-4 border">{data.x}</td> | |
| <td className="py-2 px-4 border">{data.y?.toFixed(1) || '--'}</td> | |
| <td className="py-2 px-4 border">{pulseData[index + temperatureData.length - 5]?.y?.toFixed(0) || '--'}</td> | |
| <td className="py-2 px-4 border">{respirationData[index + temperatureData.length - 5]?.y?.toFixed(0) || '--'}</td> | |
| </tr> | |
| ))} | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| {/* System Description Sections */} | |
| <section className="mb-8 bg-white p-6 rounded-lg shadow-md"> | |
| <h2 className="text-2xl font-bold text-blue-700 mb-4">System Overview</h2> | |
| <div className="grid md:grid-cols-2 gap-8"> | |
| {/* System Architecture */} | |
| <div> | |
| <h3 className="text-xl font-semibold text-blue-600 mb-3">System Architecture</h3> | |
| <div className="bg-gray-50 p-4 rounded-lg"> | |
| <div className="flex flex-col items-center space-y-2"> | |
| <div className="bg-blue-100 p-3 rounded-lg w-full text-center font-medium">Sensors</div> | |
| <div className="text-2xl text-gray-500">β</div> | |
| <div className="bg-purple-100 p-3 rounded-lg w-full text-center font-medium">Microcontroller</div> | |
| <div className="text-2xl text-gray-500">β</div> | |
| <div className="bg-green-100 p-3 rounded-lg w-full text-center font-medium">Wi-Fi Module</div> | |
| <div className="text-2xl text-gray-500">β</div> | |
| <div className="bg-yellow-100 p-3 rounded-lg w-full text-center font-medium">Cloud & Mobile App</div> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Hardware Components */} | |
| <div> | |
| <h3 className="text-xl font-semibold text-blue-600 mb-3">Hardware Components</h3> | |
| <ul className="space-y-2"> | |
| <li className="flex items-start"> | |
| <span className="bg-blue-100 text-blue-800 rounded-full p-1 mr-2">π</span> | |
| <span><strong>Temperature Sensor:</strong> LM35/DS18B20 (Β±0.5Β°C accuracy)</span> | |
| </li> | |
| <li className="flex items-start"> | |
| <span className="bg-blue-100 text-blue-800 rounded-full p-1 mr-2">π</span> | |
| <span><strong>Pulse Sensor:</strong> Photoplethysmography (PPG) based</span> | |
| </li> | |
| <li className="flex items-start"> | |
| <span className="bg-blue-100 text-blue-800 rounded-full p-1 mr-2">π</span> | |
| <span><strong>Respiration Sensor:</strong> Chest movement detection</span> | |
| </li> | |
| <li className="flex items-start"> | |
| <span className="bg-blue-100 text-blue-800 rounded-full p-1 mr-2">π</span> | |
| <span><strong>Microcontroller:</strong> NodeMCU (ESP8266)</span> | |
| </li> | |
| <li className="flex items-start"> | |
| <span className="bg-blue-100 text-blue-800 rounded-full p-1 mr-2">π</span> | |
| <span><strong>Power Supply:</strong> 5V USB or battery</span> | |
| </li> | |
| </ul> | |
| </div> | |
| </div> | |
| </section> | |
| {/* Advantages Section */} | |
| <section className="mb-8 bg-white p-6 rounded-lg shadow-md"> | |
| <h2 className="text-2xl font-bold text-blue-700 mb-4">Advantages</h2> | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> | |
| {[ | |
| "Contactless monitoring reduces infection risk", | |
| "Real-time health data visualization", | |
| "Early fever detection alerts", | |
| "Cost-effective IoT solution", | |
| "Cloud connectivity for remote access", | |
| "User-friendly dashboard interface", | |
| "Scalable for multiple locations", | |
| "Continuous health monitoring", | |
| "Reduces healthcare worker exposure" | |
| ].map((advantage, index) => ( | |
| <div key={index} className="bg-green-50 p-4 rounded-lg border border-green-200 flex items-start"> | |
| <span className="text-green-600 mr-2">β</span> | |
| <span>{advantage}</span> | |
| </div> | |
| ))} | |
| </div> | |
| </section> | |
| {/* Conclusion Section */} | |
| <section className="bg-white p-6 rounded-lg shadow-md"> | |
| <h2 className="text-2xl font-bold text-blue-700 mb-4">Conclusion</h2> | |
| <p className="text-gray-700 mb-4"> | |
| The IoT-based Virus Fever Detection system provides an innovative, contactless solution for | |
| early fever detection in public spaces, healthcare facilities, and homes. By integrating | |
| multiple health parameters with real-time cloud monitoring, the system enables timely | |
| interventions while minimizing infection risks. | |
| </p> | |
| <p className="text-gray-700"> | |
| This project demonstrates the potential of IoT in transforming healthcare monitoring, | |
| offering a scalable and cost-effective approach to pandemic response and general health | |
| surveillance. Future enhancements could include AI-based predictive analytics and integration | |
| with hospital EHR systems for comprehensive patient care. | |
| </p> | |
| </section> | |
| </div> | |
| ); | |
| } | |
| ReactDOM.render(<App />, document.getElementById('root')); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 𧬠<a href="https://enzostvs-deepsite.hf.space?remix=Soundaryasos/hema" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |