Apply modifications to mass-produced boards
- 3 channel power sensor ina3221 - io expander pca9557 - Some gpio moves - ... Signed-off-by: YoungSoo Shin <shinys000114@gmail.com>
This commit is contained in:
@@ -71,7 +71,7 @@
|
||||
<div class="mt-4">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center control-list-item">
|
||||
Main Power (12V)
|
||||
Main Power
|
||||
<div class="control-wrapper">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="main-power-toggle">
|
||||
@@ -79,7 +79,7 @@
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center control-list-item">
|
||||
USB Power (5V)
|
||||
USB Power
|
||||
<div class="control-wrapper">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="usb-power-toggle">
|
||||
@@ -111,7 +111,7 @@
|
||||
<div class="d-flex justify-content-end mb-3">
|
||||
<a href="/datalog.csv" class="btn btn-primary" download="datalog.csv"><i class="bi bi-download me-1"></i> Download CSV</a>
|
||||
</div>
|
||||
<h5 class="card-title text-center mb-3">Power Input</h5>
|
||||
<h5 class="card-title text-center mb-3">Power Metrics</h5>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3 mb-md-0">
|
||||
<canvas id="powerChart" class="chart-canvas"></canvas>
|
||||
@@ -251,7 +251,7 @@
|
||||
<div id="ap-mode-config" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label for="ap-ssid" class="form-label">AP SSID</label>
|
||||
<input type="text" class="form-control" id="ap-ssid" placeholder="ODROID-Remote-AP">
|
||||
<input type="text" class="form-control" id="ap-ssid" placeholder="odroid-pm">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="ap-password" class="form-label">AP Password</label>
|
||||
@@ -278,6 +278,12 @@
|
||||
<option value="1500000" selected>1500000</option>
|
||||
</select>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">System Reboot</label>
|
||||
<p class="text-muted small">This will restart the device. The reboot will occur after 3 seconds.</p>
|
||||
<button type="button" class="btn btn-danger" id="reboot-button">Reboot Now</button>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end pt-3 border-top mt-3">
|
||||
<button type="button" class="btn btn-primary me-2" id="baud-rate-apply-button">Apply</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
@@ -314,6 +320,38 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const rebootButton = document.getElementById('reboot-button');
|
||||
if (rebootButton) {
|
||||
rebootButton.addEventListener('click', () => {
|
||||
if (confirm('Are you sure you want to reboot the device?')) {
|
||||
fetch('/api/reboot', {
|
||||
method: 'POST',
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('Reboot command sent:', data);
|
||||
const settingsModalEl = document.getElementById('settingsModal');
|
||||
const settingsModal = bootstrap.Modal.getInstance(settingsModalEl);
|
||||
if (settingsModal) {
|
||||
settingsModal.hide();
|
||||
}
|
||||
alert('Reboot command sent. The device will restart in 3 seconds.');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error sending reboot command:', error);
|
||||
alert('Failed to send reboot command.');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -17,6 +17,7 @@ export const charts = {
|
||||
current: null
|
||||
};
|
||||
|
||||
const channelKeys = ['USB', 'MAIN', 'VIN'];
|
||||
const CHART_DATA_POINTS = 30; // Number of data points to display on the chart
|
||||
|
||||
/**
|
||||
@@ -53,10 +54,10 @@ function createChartOptions(title, minValue, maxValue) {
|
||||
},
|
||||
scales: {
|
||||
x: { ticks: { autoSkipPadding: 10, maxRotation: 0, minRotation: 0 } },
|
||||
y: {
|
||||
y: {
|
||||
min: minValue,
|
||||
max: maxValue,
|
||||
beginAtZero: false,
|
||||
beginAtZero: true, // Start at zero for better comparison
|
||||
ticks: {
|
||||
stepSize: (maxValue - minValue) / 8
|
||||
}
|
||||
@@ -65,6 +66,23 @@ function createChartOptions(title, minValue, maxValue) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the dataset objects for a chart.
|
||||
* @param {string} unit - The unit for the dataset label (e.g., 'W', 'V', 'A').
|
||||
* @returns {Array<Object>} An array of Chart.js dataset objects.
|
||||
*/
|
||||
function createDatasets(unit) {
|
||||
return channelKeys.map(channel => ({
|
||||
label: `${channel} (${unit})`,
|
||||
data: initialData(),
|
||||
borderWidth: 2,
|
||||
fill: false,
|
||||
tension: 0.2,
|
||||
pointRadius: 2
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes all three charts (Power, Voltage, Current).
|
||||
* If chart instances already exist, they are destroyed and new ones are created.
|
||||
@@ -79,15 +97,12 @@ export function initCharts() {
|
||||
|
||||
// Create Power Chart
|
||||
if (powerChartCtx) {
|
||||
const powerOptions = createChartOptions('Power', 0, 120);
|
||||
const powerOptions = createChartOptions('Power', 0, 50); // Adjusted max value
|
||||
charts.power = new Chart(powerChartCtx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: initialLabels(),
|
||||
datasets: [
|
||||
{ label: 'Power (W)', data: initialData(), borderWidth: 2, fill: false, tension: 0.2, pointRadius: 2 },
|
||||
{ label: 'Avg Power', data: initialData(), borderWidth: 1.5, borderDash: [10, 5], fill: false, tension: 0, pointRadius: 0 }
|
||||
]
|
||||
datasets: createDatasets('W')
|
||||
},
|
||||
options: powerOptions
|
||||
});
|
||||
@@ -100,10 +115,7 @@ export function initCharts() {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: initialLabels(),
|
||||
datasets: [
|
||||
{ label: 'Voltage (V)', data: initialData(), borderWidth: 2, fill: false, tension: 0.2, pointRadius: 2 },
|
||||
{ label: 'Avg Voltage', data: initialData(), borderWidth: 1.5, borderDash: [10, 5], fill: false, tension: 0, pointRadius: 0 }
|
||||
]
|
||||
datasets: createDatasets('V')
|
||||
},
|
||||
options: voltageOptions
|
||||
});
|
||||
@@ -111,15 +123,12 @@ export function initCharts() {
|
||||
|
||||
// Create Current Chart
|
||||
if (currentChartCtx) {
|
||||
const currentOptions = createChartOptions('Current', 0, 7);
|
||||
const currentOptions = createChartOptions('Current', 0, 5); // Adjusted max value
|
||||
charts.current = new Chart(currentChartCtx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: initialLabels(),
|
||||
datasets: [
|
||||
{ label: 'Current (A)', data: initialData(), borderWidth: 2, fill: false, tension: 0.2, pointRadius: 2 },
|
||||
{ label: 'Avg Current', data: initialData(), borderWidth: 1.5, borderDash: [10, 5], fill: false, tension: 0, pointRadius: 0 }
|
||||
]
|
||||
datasets: createDatasets('A')
|
||||
},
|
||||
options: currentOptions
|
||||
});
|
||||
@@ -135,11 +144,14 @@ export function applyChartsTheme(themeName) {
|
||||
const gridColor = isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';
|
||||
const labelColor = isDark ? '#dee2e6' : '#212529';
|
||||
|
||||
const powerColor = getComputedStyle(htmlEl).getPropertyValue('--chart-power-color');
|
||||
const voltageColor = getComputedStyle(htmlEl).getPropertyValue('--chart-voltage-color');
|
||||
const currentColor = getComputedStyle(htmlEl).getPropertyValue('--chart-current-color');
|
||||
// Define colors for each channel. These could be from CSS variables.
|
||||
const channelColors = [
|
||||
getComputedStyle(htmlEl).getPropertyValue('--chart-usb-color').trim() || '#0d6efd', // Blue
|
||||
getComputedStyle(htmlEl).getPropertyValue('--chart-main-color').trim() || '#198754', // Green
|
||||
getComputedStyle(htmlEl).getPropertyValue('--chart-vin-color').trim() || '#dc3545' // Red
|
||||
];
|
||||
|
||||
const updateThemeForChart = (chart, color) => {
|
||||
const updateThemeForChart = (chart) => {
|
||||
if (!chart) return;
|
||||
chart.options.scales.x.grid.color = gridColor;
|
||||
chart.options.scales.y.grid.color = gridColor;
|
||||
@@ -147,15 +159,17 @@ export function applyChartsTheme(themeName) {
|
||||
chart.options.scales.y.ticks.color = labelColor;
|
||||
chart.options.plugins.legend.labels.color = labelColor;
|
||||
chart.options.plugins.title.color = labelColor;
|
||||
chart.data.datasets[0].borderColor = color;
|
||||
chart.data.datasets[1].borderColor = color;
|
||||
chart.data.datasets[1].borderDash = [10, 5];
|
||||
|
||||
chart.data.datasets.forEach((dataset, index) => {
|
||||
dataset.borderColor = channelColors[index];
|
||||
});
|
||||
|
||||
chart.update('none');
|
||||
};
|
||||
|
||||
updateThemeForChart(charts.power, powerColor);
|
||||
updateThemeForChart(charts.voltage, voltageColor);
|
||||
updateThemeForChart(charts.current, currentColor);
|
||||
updateThemeForChart(charts.power);
|
||||
updateThemeForChart(charts.voltage);
|
||||
updateThemeForChart(charts.current);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,7 +179,7 @@ export function applyChartsTheme(themeName) {
|
||||
export function updateCharts(data) {
|
||||
const timeLabel = new Date(data.timestamp * 1000).toLocaleTimeString();
|
||||
|
||||
const updateSingleChart = (chart, value) => {
|
||||
const updateSingleChart = (chart, metric) => {
|
||||
if (!chart) return;
|
||||
|
||||
// Shift old data
|
||||
@@ -174,18 +188,15 @@ export function updateCharts(data) {
|
||||
|
||||
// Push new data
|
||||
chart.data.labels.push(timeLabel);
|
||||
chart.data.datasets[0].data.push(value.toFixed(2));
|
||||
|
||||
// Calculate average
|
||||
const dataArray = chart.data.datasets[0].data.filter(v => v !== null).map(v => parseFloat(v));
|
||||
if (dataArray.length > 0) {
|
||||
const sum = dataArray.reduce((acc, val) => acc + val, 0);
|
||||
const avg = (sum / dataArray.length).toFixed(2);
|
||||
chart.data.datasets[1].data.push(avg);
|
||||
|
||||
} else {
|
||||
chart.data.datasets[1].data.push(null);
|
||||
}
|
||||
channelKeys.forEach((key, index) => {
|
||||
if (data[key] && data[key][metric] !== undefined) {
|
||||
const value = data[key][metric];
|
||||
chart.data.datasets[index].data.push(value.toFixed(2));
|
||||
} else {
|
||||
chart.data.datasets[index].data.push(null); // Push null if data for a channel is missing
|
||||
}
|
||||
});
|
||||
|
||||
// Only update the chart if the tab is visible
|
||||
if (graphTabPane.classList.contains('show')) {
|
||||
@@ -193,9 +204,9 @@ export function updateCharts(data) {
|
||||
}
|
||||
};
|
||||
|
||||
updateSingleChart(charts.power, data.power);
|
||||
updateSingleChart(charts.voltage, data.voltage);
|
||||
updateSingleChart(charts.current, data.current);
|
||||
updateSingleChart(charts.power, 'power');
|
||||
updateSingleChart(charts.voltage, 'voltage');
|
||||
updateSingleChart(charts.current, 'current');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,7 +34,6 @@ function onWsOpen() {
|
||||
if (term) {
|
||||
term.write('\x1b[32mConnected to WebSocket Server\x1b[0m\r\n');
|
||||
}
|
||||
updateControlStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,6 +91,9 @@ function initialize() {
|
||||
const savedTheme = localStorage.getItem('theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
applyTheme(savedTheme);
|
||||
|
||||
// Fetch initial status on page load
|
||||
updateControlStatus();
|
||||
|
||||
// Establish the WebSocket connection with the defined handlers
|
||||
initWebSocket({
|
||||
onOpen: onWsOpen,
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
|
||||
:root {
|
||||
--bs-body-font-family: 'Courier New', Courier, monospace;
|
||||
--chart-power-color: #007bff;
|
||||
--chart-voltage-color: #28a745;
|
||||
--chart-current-color: #ffc107;
|
||||
/* Chart Channel Colors */
|
||||
--chart-usb-color: #0d6efd; /* Bootstrap Blue */
|
||||
--chart-main-color: #198754; /* Bootstrap Green */
|
||||
--chart-vin-color: #dc3545; /* Bootstrap Red */
|
||||
}
|
||||
|
||||
[data-bs-theme="dark"] {
|
||||
--chart-power-color: #569cd6;
|
||||
--chart-voltage-color: #4ec9b0;
|
||||
--chart-current-color: #dcdcaa;
|
||||
/* Chart Channel Colors for Dark Theme */
|
||||
--chart-usb-color: #569cd6; /* A lighter blue for dark backgrounds */
|
||||
--chart-main-color: #4ec9b0; /* A teal/cyan for dark backgrounds */
|
||||
--chart-vin-color: #d16969; /* A softer red for dark backgrounds */
|
||||
}
|
||||
|
||||
body, .card, .modal-content, .list-group-item, .nav-tabs .nav-link {
|
||||
|
||||
@@ -40,12 +40,18 @@ export function applyTheme(themeName) {
|
||||
* @param {Object} data - The sensor data object from the WebSocket.
|
||||
*/
|
||||
export function updateSensorUI(data) {
|
||||
dom.voltageDisplay.textContent = `${data.voltage.toFixed(2)} V`;
|
||||
dom.currentDisplay.textContent = `${data.current.toFixed(2)} A`;
|
||||
dom.powerDisplay.textContent = `${data.power.toFixed(2)} W`;
|
||||
// Display VIN channel data in the header as a primary overview
|
||||
if (data.VIN) {
|
||||
dom.voltageDisplay.textContent = `${data.VIN.voltage.toFixed(2)} V`;
|
||||
dom.currentDisplay.textContent = `${data.VIN.current.toFixed(2)} A`;
|
||||
dom.powerDisplay.textContent = `${data.VIN.power.toFixed(2)} W`;
|
||||
}
|
||||
|
||||
if (data.uptime_sec !== undefined) {
|
||||
dom.uptimeDisplay.textContent = formatUptime(data.uptime_sec);
|
||||
}
|
||||
|
||||
// Pass the entire multi-channel data object to the charts
|
||||
updateCharts(data);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user