Dev prototype #1

Merged
david merged 7 commits from dev into main 2025-04-24 11:34:49 -07:00
6 changed files with 113 additions and 23 deletions
Showing only changes of commit 22aeed01be - Show all commits

16
sketch.c Normal file
View File

@ -0,0 +1,16 @@
void setup() {
Serial.begin(9600);
}
void loop() {
int moisture0 = analogRead(A0);
delayMicroseconds(100);
analogRead(A1);
int moisture1 = analogRead(A1);
Serial.print("{\"A0\":");
Serial.print(moisture0);
Serial.print(",\"A1\":");
Serial.print(moisture1);
Serial.println("}");
delay(1000);
}

View File

@ -21,9 +21,11 @@ export default class IO implements ISensors {
gpio: pigpio.Gpio
serial: Serial;
power: boolean;
getMoisture : ()=>number;
public constructor(POWER_GPIO = 17) {
const serial = new Serial()
serial.getMoisture.bind(this);
this.getMoisture = serial.getMoisture.bind(serial);
this.serial = serial;
// Initialize pigpio and the GPIO pin
const read = new pigpio.Gpio(POWER_GPIO, {mode: pigpio.Gpio.INPUT});
@ -35,9 +37,6 @@ export default class IO implements ISensors {
// this.serial.getMoisture.bind(this)
}
getMoisture() {
return
}
// getPower() {
// // Read the current state of the GPIO pin
@ -52,16 +51,15 @@ export default class IO implements ISensors {
public togglePower() {
// Toggle the power state (turn the pin on or off)
console.log("toggle")
this.power = !this.power;
this.gpio.digitalWrite(this.power ? ON : OFF);
}
public getSensors(): ISensors {
console.log("serial ", this.serial)
// console.log("serial ", )
return {
power: this.power,
moisture: 0,
moisture: this.getMoisture(),
temperature: this.temperature,
humidity: this.humidity
}

View File

@ -6,16 +6,25 @@ import * as serialport from 'serialport';
// }
export default class Serial {
private moisture: number;
private moisture0: number;
private moisture1: number;
public constructor(path = '/dev/ttyUSB0', baudRate = 9600, delimiter = '\n') {
const port = new serialport.SerialPort({ path, baudRate });
const readline = new serialport.ReadlineParser({ delimiter });
const parser = port.pipe(readline);
parser.on('data', line => {
const moisture = parseInt(line.trim(), 10);
if (!isNaN(moisture)) {
this.moisture = moisture
// console.log("data line ", line)
const data = JSON.parse(line)
const moisture0 = parseInt(data['A0'], 10);
const moisture1 = parseInt(data['A1'], 10);
if (!isNaN(moisture0) && !isNaN(moisture1)) {
// console.log("m ", this.moisture)
this.moisture0 = moisture0;
this.moisture1 = moisture1;
}
else{
console.log("data ",data)
}
});
port.on('error', err => {
@ -23,7 +32,7 @@ export default class Serial {
});
}
public getMoisture() {
return this.moisture;
return this.moisture0;
}

View File

@ -1,5 +1,5 @@
import HttpServer from './http';
import IO from './io';
import IO, { ISensors } from './io';
import VideoSocket from './ws';
const HTTP_PORT = process.env.HTTP_PORT ? parseInt(process.env.HTTP_PORT, 10) : 8080;
@ -26,7 +26,7 @@ const TV_DEV_0 = process.env.TV_DEV_0 ?? '/dev/video0'
// zap.getSignal(adapter)
const io = new IO();
// const getSensors = io.getSensors.bind(this);
const getSensors: ()=>ISensors = io.getSensors.bind(io);
// setTimeout(()=>{
// console.log("sen ",io.getSensors())
// console.log("sen2 ",getSensors())
@ -34,7 +34,7 @@ const io = new IO();
// const setPower = io.setPower.bind(this, true)
// const togglePower = io.togglePower.bind(this)
const httpServer = new HttpServer(HTTP_PORT, STATIC_ROOT, io);
const videoSocket = new VideoSocket(WS_PORT, TV_DEV_0);
const videoSocket = new VideoSocket(WS_PORT, TV_DEV_0, getSensors);
httpServer.start();
@ -42,7 +42,7 @@ httpServer.start();
process.stdin.setEncoding("utf8");
process.stdin.resume();
console.log("Menu:\n1) Power off\n2)Power on\n3) Power flop\n4)Read moisture");
console.log("Menu:\n1) Power off\n2)Power on\n3) Power flop\n4)Read Sensors");
process.stdin.on("data", async (data: string) => {
const input = data.trim();
@ -53,7 +53,7 @@ process.stdin.on("data", async (data: string) => {
break;
case 1:
io.setPower(false);// console.log(io.getMoisture());
io.setPower(false);
break;
case 2:
io.setPower(true);
@ -62,7 +62,8 @@ process.stdin.on("data", async (data: string) => {
io.togglePower();
break;
case 4:
io.getSensors()
console.log("a ", getSensors())
break;
default:
console.log("No option for "+input)

View File

@ -11,7 +11,7 @@ const config = {
}
]
};
const pc0 = new RTCPeerConnection(config);
const pc0 = new RTCPeerConnection(isSecure ? config: {});
const video0 = document.getElementById('video0') as HTMLVideoElement;
@ -26,6 +26,23 @@ pc0.onicecandidate = ({ candidate }) => {
}
};
// Create the data channel (client initiates)
const dataChannel = pc0.createDataChannel('sensors');
console.log("📡 Data channel created by client");
dataChannel.onopen = () => {
console.log('📬 Client: Data channel opened');
};
dataChannel.onmessage = (event) => {
console.log("📦 Client received message:", event.data);
};
dataChannel.onclose = () => {
console.log("📴 Client: Data channel closed");
};
ws0.onopen = async () => {
pc0.addTransceiver('video', { direction: 'recvonly' });
pc0.addTransceiver('audio', { direction: 'recvonly' })
@ -37,6 +54,7 @@ ws0.onopen = async () => {
ws0.onmessage = async (message) => {
const msg = JSON.parse(message.data);
if (msg.type === 'answer') {
// pc0.data
await pc0.setRemoteDescription(msg.data);
}
else if (msg.type === 'ice-candidate') {
@ -44,3 +62,4 @@ ws0.onmessage = async (message) => {
}
};

View File

@ -1,6 +1,7 @@
import { MediaStream, MediaStreamTrack, nonstandard, RTCPeerConnection } from '@roamhq/wrtc';
import { MediaStream, MediaStreamTrack, nonstandard, RTCPeerConnection, RTCDataChannel } from '@roamhq/wrtc';
import { ChildProcessWithoutNullStreams, spawn } from 'child_process';
import * as ws from 'ws';
import { ISensors } from './io';
// Constants
const WIDTH = 640; // Video width
@ -9,7 +10,7 @@ const FRAME_SIZE = WIDTH * HEIGHT * 1.5; // YUV420P frame size
export default class VideoSocket {
videoDevice: string;
public constructor(port: number, videoDevice) {
public constructor(port: number, videoDevice, getSensors: () => ISensors) {
this.videoDevice = videoDevice
const ffmpegProcess = this.startFFmpeg();
const videoTrack = this.createVideoTrack(ffmpegProcess);
@ -25,11 +26,35 @@ export default class VideoSocket {
wss.on('connection', async (ws: ws.WebSocket) => {
const peerConnection: RTCPeerConnection = this.createPeerConnection(videoTrack, audioTrack);
// The client created the data channel. The server should access it as follows:
peerConnection.ondatachannel = (event) => {
const dataChannel = event.channel; // This is the data channel created by the client
dataChannel.onopen = () => {
console.log('📬 Server: Data channel opened');
// Now you can send data through the channel
setInterval(() => {
const sensorData = getSensors(); // Example function to fetch data
dataChannel.send(JSON.stringify(sensorData)); // Send data to the client
}, 1000);
};
dataChannel.onmessage = (event) => {
console.log("📦 Server received message:", event.data);
};
dataChannel.onclose = () => {
console.log("📴 Server: Data channel closed");
};
};
ws.on('message', async (message: Buffer) => {
const { type, data } = JSON.parse(message.toString());
if (type == 'offer') {
await peerConnection.setRemoteDescription(data);
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
@ -181,6 +206,28 @@ export default class VideoSocket {
return audioSource.createTrack();
}
createDataChannel = (peerConnection: RTCPeerConnection, getSensors: () => any) => {
const dataChannel = peerConnection.createDataChannel('sensors')
console.log("create data channel");
dataChannel.onopen = () => {
console.log('✅ Data channel is open');
// Send dummy JSON for testing
setInterval(() => {
const sensorData = getSensors(); // Assuming getSensors returns JSON
dataChannel.send(JSON.stringify(sensorData));
}, 1000);
};
dataChannel.onerror = (error) => {
console.error('❌ DataChannel error:', error);
};
dataChannel.onclose = () => {
console.log('❎ DataChannel closed');
};
return peerConnection;
}
createPeerConnection = (videoTrack: MediaStreamTrack, audioTrack: MediaStreamTrack): RTCPeerConnection => {
const peerConnection = new RTCPeerConnection({
iceServers: [