Compare commits
15 Commits
4b70f8d67b
...
master
Author | SHA1 | Date | |
---|---|---|---|
16ea6dafe3 | |||
2e580b44d2 | |||
4068b8bb01 | |||
a9a8cf2745 | |||
85e6ab01ae | |||
31585c4b6b | |||
1de19b208a | |||
72800546a8 | |||
287a28d065 | |||
efc56aae40 | |||
a127bcfbbd | |||
f5e1d73988 | |||
90456f5800 | |||
00f11f8c08 | |||
3e06856c70 |
1152
package-lock.json
generated
Normal file
1152
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -5,11 +5,12 @@
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node src/main.js"
|
||||
"apollo": "node src/main.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"@ztimson/utils": "^0.21.6",
|
||||
"express": "^4.21.1",
|
||||
"i2c-bus": "^5.2.3",
|
||||
|
21
src/bme.js
21
src/bme.js
@ -1,21 +0,0 @@
|
||||
import i2c from 'i2c-bus';
|
||||
|
||||
export async function bme(address = 0x76) {
|
||||
const i2cBus = await i2c.openPromisified(1);
|
||||
const data = await Promise.all([
|
||||
i2cBus.readByte(address, 0xFA),
|
||||
i2cBus.readByte(address, 0xFB),
|
||||
i2cBus.readByte(address, 0xFC),
|
||||
i2cBus.readByte(address, 0xF7),
|
||||
i2cBus.readByte(address, 0xF8),
|
||||
i2cBus.readByte(address, 0xF9),
|
||||
i2cBus.readByte(address, 0xFD),
|
||||
i2cBus.readByte(address, 0xFE),
|
||||
]);
|
||||
return {
|
||||
temperature: (((data[0] << 12) | (data[1] << 4) | (data[2] >> 4)) / 16384.0 - 5120.0) / 100,
|
||||
pressure: ((data[3] << 12) | (data[4] << 4) | (data[5] >> 4)) / 25600,
|
||||
humidity: ((data[6] << 8) | data[7]) / 1024.0,
|
||||
};
|
||||
}
|
||||
console.log(await bme());
|
@ -3,17 +3,32 @@ import i2c from 'i2c-bus';
|
||||
export async function bme(address = 0x76) {
|
||||
const i2cBus = await i2c.openPromisified(1);
|
||||
const data = await Promise.all([
|
||||
i2cBus.readByte(address, 0xFA),
|
||||
i2cBus.readByte(address, 0xFB),
|
||||
i2cBus.readByte(address, 0xFC),
|
||||
i2cBus.readByte(address, 0xF7),
|
||||
i2cBus.readByte(address, 0xF8),
|
||||
i2cBus.readByte(address, 0xF9),
|
||||
i2cBus.readByte(address, 0xFD),
|
||||
i2cBus.readByte(address, 0xFE),
|
||||
i2cBus.readByte(address, 0xFA), // Temp MSB
|
||||
i2cBus.readByte(address, 0xFB), // Temp LSB
|
||||
i2cBus.readByte(address, 0xFC), // Temp XLSB
|
||||
i2cBus.readByte(address, 0xF7), // Pressure MSB
|
||||
i2cBus.readByte(address, 0xF8), // Pressure LSB
|
||||
i2cBus.readByte(address, 0xF9), // Pressure XLSB
|
||||
i2cBus.readByte(address, 0xFD), // Humidity MSB
|
||||
i2cBus.readByte(address, 0xFE), // Humidity LSB
|
||||
]);
|
||||
|
||||
// Calibration registers
|
||||
const CALIBRATION_LENGTH = 24;
|
||||
const calibBuffer = Buffer.alloc(CALIBRATION_LENGTH);
|
||||
await i2cBus.readI2cBlock(address, 0x88, CALIBRATION_LENGTH, calibBuffer);
|
||||
const T1 = calibBuffer.readUInt16LE(0);
|
||||
const T2 = calibBuffer.readInt16LE(2);
|
||||
const T3 = calibBuffer.readInt16LE(4);
|
||||
|
||||
const rawTemp = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
|
||||
const var1 = (((rawTemp >> 3) - (T1 << 1)) * T2) / 2048;
|
||||
const var2 = (((((rawTemp >> 4) - T1) * ((rawTemp >> 4) - T1)) >> 12) * T3) / 16384;
|
||||
const temp = (var1 + var2) / 5120.0;
|
||||
|
||||
await i2cBus.close();
|
||||
return {
|
||||
temperature: (((data[0] << 12) | (data[1] << 4) | (data[2] >> 4)) / 16384.0 - 5120.0) / 100,
|
||||
temperature: temp,
|
||||
pressure: ((data[3] << 12) | (data[4] << 4) | (data[5] >> 4)) / 25600,
|
||||
humidity: ((data[6] << 8) | data[7]) / 1024.0,
|
||||
};
|
||||
|
@ -9,6 +9,7 @@ export async function bms(address = 0x57) {
|
||||
i2cBus.readByte(address, 0x23),
|
||||
i2cBus.readByte(address, 0x2a),
|
||||
]);
|
||||
await i2cBus.close();
|
||||
return {
|
||||
charging: !!((data[0] >> 7) & 1),
|
||||
percentage: data[4] / 100,
|
||||
|
71
src/bno080.js
Normal file
71
src/bno080.js
Normal file
@ -0,0 +1,71 @@
|
||||
import i2c from 'i2c-bus';
|
||||
|
||||
export async function imu(address = 0x4B) { // BNO080 default I2C address
|
||||
const i2cBus = await i2c.openPromisified(1);
|
||||
|
||||
// Register addresses for accelerometer, gyroscope, and magnetometer data (assumes configuration is done)
|
||||
const ACCEL_X_LSB = 0x08;
|
||||
const ACCEL_X_MSB = 0x09;
|
||||
const ACCEL_Y_LSB = 0x0A;
|
||||
const ACCEL_Y_MSB = 0x0B;
|
||||
const ACCEL_Z_LSB = 0x0C;
|
||||
const ACCEL_Z_MSB = 0x0D;
|
||||
|
||||
const GYRO_X_LSB = 0x10;
|
||||
const GYRO_X_MSB = 0x11;
|
||||
const GYRO_Y_LSB = 0x12;
|
||||
const GYRO_Y_MSB = 0x13;
|
||||
const GYRO_Z_LSB = 0x14;
|
||||
const GYRO_Z_MSB = 0x15;
|
||||
|
||||
const MAG_X_LSB = 0x16; // Hypothetical register for magnetometer X LSB
|
||||
const MAG_X_MSB = 0x17; // Hypothetical register for magnetometer X MSB
|
||||
const MAG_Y_LSB = 0x18; // Hypothetical register for magnetometer Y LSB
|
||||
const MAG_Y_MSB = 0x19; // Hypothetical register for magnetometer Y MSB
|
||||
const MAG_Z_LSB = 0x1A; // Hypothetical register for magnetometer Z LSB
|
||||
const MAG_Z_MSB = 0x1B; // Hypothetical register for magnetometer Z MSB
|
||||
|
||||
// Read data from the sensor registers
|
||||
const data = await Promise.all([
|
||||
i2cBus.readByte(address, ACCEL_X_LSB),
|
||||
i2cBus.readByte(address, ACCEL_X_MSB),
|
||||
i2cBus.readByte(address, ACCEL_Y_LSB),
|
||||
i2cBus.readByte(address, ACCEL_Y_MSB),
|
||||
i2cBus.readByte(address, ACCEL_Z_LSB),
|
||||
i2cBus.readByte(address, ACCEL_Z_MSB),
|
||||
i2cBus.readByte(address, GYRO_X_LSB),
|
||||
i2cBus.readByte(address, GYRO_X_MSB),
|
||||
i2cBus.readByte(address, GYRO_Y_LSB),
|
||||
i2cBus.readByte(address, GYRO_Y_MSB),
|
||||
i2cBus.readByte(address, GYRO_Z_LSB),
|
||||
i2cBus.readByte(address, GYRO_Z_MSB),
|
||||
i2cBus.readByte(address, MAG_X_LSB),
|
||||
i2cBus.readByte(address, MAG_X_MSB),
|
||||
i2cBus.readByte(address, MAG_Y_LSB),
|
||||
i2cBus.readByte(address, MAG_Y_MSB),
|
||||
i2cBus.readByte(address, MAG_Z_LSB),
|
||||
i2cBus.readByte(address, MAG_Z_MSB),
|
||||
]);
|
||||
|
||||
// Convert accelerometer data
|
||||
const accelX = ((data[1] << 8) | data[0]) / 1000;
|
||||
const accelY = ((data[3] << 8) | data[2]) / 1000;
|
||||
const accelZ = ((data[5] << 8) | data[4]) / 1000;
|
||||
|
||||
// Convert gyroscope data
|
||||
const gyroX = ((data[7] << 8) | data[6]) / 1000;
|
||||
const gyroY = ((data[9] << 8) | data[8]) / 1000;
|
||||
const gyroZ = ((data[11] << 8) | data[10]) / 1000;
|
||||
|
||||
// Convert magnetometer data
|
||||
const magX = ((data[13] << 8) | data[12]) / 1000; // Hypothetical conversion factor
|
||||
const magY = ((data[15] << 8) | data[14]) / 1000;
|
||||
const magZ = ((data[17] << 8) | data[16]) / 1000;
|
||||
|
||||
await i2cBus.close();
|
||||
return {
|
||||
acceleration: { x: accelX, y: accelY, z: accelZ },
|
||||
gyroscope: { x: gyroX, y: gyroY, z: gyroZ },
|
||||
magnetometer: { x: magX, y: magY, z: magZ },
|
||||
};
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
import cors from 'cors';
|
||||
import express from 'express';
|
||||
import path from 'path';
|
||||
import APOLLO from './apollo.js';
|
||||
@ -13,6 +14,8 @@ export default class Daemon {
|
||||
|
||||
this.express = express();
|
||||
|
||||
this.express.use(cors('*'));
|
||||
|
||||
this.express.get('/api/*', async (req, res) => {
|
||||
const cmd = req.params['0'];
|
||||
res.json(await this.run(cmd));
|
||||
|
13
src/main.js
13
src/main.js
@ -30,8 +30,11 @@ function run(cmd) {
|
||||
else if(cmd.startsWith('remote')) {
|
||||
remote = cmd.split(' ').pop();
|
||||
return `Remote Set: ${remote}`;
|
||||
} else if(cmd.startsWith('start')) new Daemon(+cmd.split(' ').pop() || 1969);
|
||||
else return fetch(`${remote.startsWith('http') ? '' : 'http://'}${remote}/api/${cmd}`).then(resp => {
|
||||
} else if(cmd.startsWith('start')) {
|
||||
const port = +cmd.split(' ').pop() || 1969;
|
||||
new Daemon(port);
|
||||
return `Listening on: http://localhost:${port}`;
|
||||
} else return fetch(`${remote.startsWith('http') ? '' : 'http://'}${remote}/api/${cmd}`).then(resp => {
|
||||
if(resp.ok && resp.headers['Content-Type']?.includes('json'))
|
||||
return resp.json();
|
||||
else return resp.text();
|
||||
@ -42,8 +45,10 @@ function run(cmd) {
|
||||
}
|
||||
|
||||
if(command) console.log(run(command));
|
||||
else console.log(help());
|
||||
while(true) {
|
||||
else {
|
||||
console.log(help());
|
||||
while(true) {
|
||||
const cmd = await ask('> ');
|
||||
console.log(await run(cmd), '\n');
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {adjustedInterval, sleep} from './misc.js';
|
||||
import {adjustedInterval, sleep} from '@ztimson/utils';
|
||||
import {bms} from './bms.js';
|
||||
import {bme} from './bme280.js';
|
||||
import {imu} from './bno080.js';
|
||||
|
||||
export default class SensorSuite {
|
||||
data = {
|
||||
@ -25,11 +26,15 @@ export default class SensorSuite {
|
||||
}
|
||||
|
||||
async start() {
|
||||
// Movement
|
||||
this.intervals.push(adjustedInterval(async () => {
|
||||
this.data.movement = await this.statusWrapper(imu(), 'imu');
|
||||
}, 1000));
|
||||
// Environment
|
||||
this.intervals.push(adjustedInterval(async () => {
|
||||
this.data.environment = await this.statusWrapper(bme(), 'bme280');
|
||||
if(this.data.environment?.pressure != null)
|
||||
this.data.environment.altitude = SensorSuite.#hPaToAltitude(this.data.environment.pressure);
|
||||
}, 1000));
|
||||
// Battery
|
||||
await sleep(500); // Offset reading sensor data
|
||||
this.intervals.push(adjustedInterval(async () => {
|
||||
this.data.battery = await this.statusWrapper(bms(), 'bms');
|
||||
|
BIN
ui/assets/navball-cursor.png
Normal file
BIN
ui/assets/navball-cursor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 665 B |
BIN
ui/assets/navball.png
Normal file
BIN
ui/assets/navball.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 200 KiB |
BIN
ui/assets/navball2.png
Normal file
BIN
ui/assets/navball2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 79 KiB |
@ -141,7 +141,7 @@
|
||||
<div class="status status-bat">BAT</div>
|
||||
<div class="status status-env">ENV</div>
|
||||
<div class="status status-gps">GPS</div>
|
||||
<div class="status status-agm">IMU</div>
|
||||
<div class="status status-imu">IMU</div>
|
||||
<div class="status status-tel">TEL</div>
|
||||
</div>
|
||||
|
||||
@ -180,10 +180,10 @@
|
||||
</dialog>
|
||||
|
||||
<script>
|
||||
const remote = localStorage.getItem('remote') || 'localhost:1969';
|
||||
const remote = localStorage.getItem('remote') || '';
|
||||
|
||||
function run(cmd) {
|
||||
return fetch(`${remote.startsWith('http') ? '' : 'http://'}${remote}/api/${cmd}`).then(async resp => {
|
||||
return fetch(`${remote}/api/${cmd}`).then(async resp => {
|
||||
const value = await resp.text();
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
@ -196,12 +196,15 @@
|
||||
const battery = document.querySelectorAll('.battery');
|
||||
const batteryTemp = document.querySelectorAll('.battery-temperature');
|
||||
const batteryIcon = document.querySelectorAll('.battery-icon');
|
||||
const humidity = document.querySelectorAll('.humidity');
|
||||
const navball = document.querySelectorAll('.navball');
|
||||
const statusAgm = document.querySelectorAll('.status-agm');
|
||||
const pressure = document.querySelectorAll('.pressure');
|
||||
const statusBat = document.querySelectorAll('.status-bat');
|
||||
const statusEnv = document.querySelectorAll('.status-env');
|
||||
const statusImu = document.querySelectorAll('.status-imu');
|
||||
const statusGps = document.querySelectorAll('.status-gps');
|
||||
const statusTel = document.querySelectorAll('.status-tel');
|
||||
const temperature = document.querySelectorAll('.temperature');
|
||||
const time = document.querySelectorAll('.time');
|
||||
const tPlus = document.querySelectorAll('.t-plus');
|
||||
const voltage = document.querySelectorAll('.voltage');
|
||||
@ -217,12 +220,16 @@
|
||||
battery.forEach(b => b.innerHTML = (sensors.battery?.percentage || 0) * 100);
|
||||
batteryTemp.forEach(bt => bt.innerHTML = sensors.battery?.temperature || 0);
|
||||
batteryIcon.forEach(bi => bi.className = sensors.battery?.charging ? 'fa fa-bolt' : 'fa fa-battery');
|
||||
humidity.forEach(h => h.innerHTML = sensors.environment?.humidity ?? 0);
|
||||
pressure.forEach(p => p.innerHTML = sensors.environment?.pressure ?? 0);
|
||||
temperature.forEach(t => t.innerHTML = sensors.environment?.temperature ?? 0);
|
||||
voltage.forEach(v => v.innerHTML = sensors.battery?.voltage || 0);
|
||||
});
|
||||
|
||||
run('status').then(status => {
|
||||
statusEnv.forEach(se => se.style.background = status['bme'] === 'ok' ? 'green' : 'red');
|
||||
statusBat.forEach(sb => sb.style.background = status['bms'] === 'ok' ? 'green' : 'red');
|
||||
statusEnv.forEach(se => se.style.background = status['bme280'] === 'ok' ? 'green' : 'red');
|
||||
statusImu.forEach(sb => sb.style.background = status['imu'] === 'ok' ? 'green' : 'red');
|
||||
});
|
||||
|
||||
navball.forEach(n => n.style.transform = `rotate(${count * 5}deg)`);
|
||||
|
Reference in New Issue
Block a user