PiCam on Pan & Tilt online with Blynk Video Widget

Thanks to some incentive, seeing another forum members similar topic, I finally got my PiCam online with Blynk :smiley:

Video stream by - https://elinux.org/RPi-Cam-Web-Interface

Video Widget URL (in my case) - http://10.10.3.140/html/cam_pic_new.php

Pan & Tilt code in js. (including BrightPi LED control).

const Gpio = require('pigpio').Gpio;

// Joystick with Pan & Tilt Servos
const PAN_PORT = 22;
const TILT_PORT = 23;
const pan = new Gpio(PAN_PORT, {mode: Gpio.OUTPUT});
const tilt = new Gpio(TILT_PORT, {mode: Gpio.OUTPUT});
var JoyX = new blynk.VirtualPin(22);  // Setup Joystick X on V22 MIN/MAX for my setup 2500-1400
var JoyY = new blynk.VirtualPin(23);  // Setup Joystick Y on V23 MIN/MAX for my setup 2000-800

JoyX.on('write', function(panValue) {  // Watches for Joystick X
  pan.servoWrite(panValue);  // Set Servo to value
});

JoyY.on('write', function(tiltValue) {  // Watches for Joystick y
  tilt.servoWrite(tiltValue);  // Set Servo to value
});



// ----- BrightPi LED -----
var BrightPiWhiteLED = new blynk.VirtualPin(8);  // Setup BrightPi White LED Button
var BrightPiIRLED = new blynk.VirtualPin(16);  // Setup BrightPi IR LED Button

// White
BrightPiWhiteLED.on('write', function(param) {  // Watches for virtual map button
  if (param == 0) {  // If Button OFF
    process.exec('sudo i2cset -y 1 0x70 0x00 0x00', function (err, stdout, stderr) {   // ALL LEDs OFF
      if (err) {
        console.log(stderr);
	  }
   })
  } else if (param == 1) {  // If Button ON 
    process.exec('sudo i2cset -y 1 0x70 0x09 0x0f', function (err, stdout, stderr) {   // ALL LEDs full Intensity
      if (err) {
        console.log(stderr);
      } 
   })
    process.exec('sudo i2cset -y 1 0x70 0x00 0x5a', function (err, stdout, stderr) {   // White LEDs ON
      if (err) {
        console.log(stderr);
      } 
   })
  }
});

// IR
BrightPiIRLED.on('write', function(param) {  // Watches for virtual map button
  if (param == 0) {  // If Button OFF
    process.exec('sudo i2cset -y 1 0x70 0x00 0x00', function (err, stdout, stderr) {  // ALL LEDs OFF
      if (err) {
        console.log("\n" + stderr);
      } 
   })
  } else if (param == 1) {  // If Button ON
    process.exec('sudo i2cset -y 1 0x70 0x09 0x0f', function (err, stdout, stderr) {  // ALL LEDs full Intensity
      if (err) {
        console.log("\n" + stderr);
      }
   })
    process.exec('sudo i2cset -y 1 0x70 0x00 0xa5', function (err, stdout, stderr) {  // IR LEDs ON
      if (err) {
        console.log("\n" + stderr);
      }
   })
  }
});
5 Likes

I got yet another idea with integrating IP camera with Blynk. Using webhook widget I want to control a functioning PTZ IP camera. Already tried with http and it works. Unfortunately more important tasks are pushing it back… :frowning:

Hi @Gunner, thanks for sharing!
Just one question about the video, do you observe delays between frames?
I’ve got a 15€ Wi-Fi camera (the cheapest I found it) attached at a car and it is complicated to control it due to the delays, I have to wait about one second or so to refresh the video and know where it is to move the car a little bit more.

There is about a second delay/lag that is only noticeable on the Blynk App. The web view is almost spot on. But the video flow itself is smooth in the App. I might add in a video of the video :stuck_out_tongue: later.

That particular web streaming library is only for the PiCamera (and way overkill for my needs)… but I suspect there are other options out there.

I think it is all about buffering… In IP cameras I’m using the buffer is dynamically assigned based on connection quality to achieve smooth playback. If however the buffer is assigned statically and there is no control about it, I think there is little you can do.

I just added in code to start and stop the video stream with a Button in switch mode…

Figuring out all these brackets in javascript makes C++ look simple :stuck_out_tongue:

// ----- Pi Video Stream -----
var PiCamStream = new blynk.VirtualPin(24);  // Setup PiCam Video Streaming Switch on V24

PiCamStream.on('write', function(param) {  // Watches for V24 Button/switch
  if (param == 1) {  // Runs this CLI command if the switch on V24 is ON
	process.exec('RPi_Cam_Web_Interface/start.sh', function (err, stdout, stderr) {  // Start Streaming
      if (err) {
        console.log("\n" + stderr);
      }
 })
 } else {  // Runs this CLI command if the switch on V24 is OFF
	 process.exec('RPi_Cam_Web_Interface/stop.sh', function (err, stdout, stderr) {  // Stop Streaming
      if (err) {
        console.log("\n" + stderr);
      }
  })
}
});

Such as I’m newbie on this things, should you, please, help me to get It working? I would like only to show the rpiwebcam interface onto the video widget on blynk app.

This project/topic was a long time ago… and I don’t hang out here much anymore, so unable to assist.

If viewing the simple streaming is all you want, then aside from the Video Widget, Blynk is not involved in the process and all you need to focus on are these two aspects I had already posted above.

And I recommend you Google for other RPi Camera streaming options (there are many out there) and try those URL’s in the Video Widget… such as …

https://picamera.readthedocs.io/en/release-1.13/recipes2.html#web-streaming

I finally found my simple Python sketch I used to just stream video from a PiCam to my Blue Iris DVR.

One of the URLs might work with the Video Widget

# Web streaming example
# Source code from the official PiCamera package
# http://picamera.readthedocs.io/en/latest/recipes2.html#web-streaming
#
# (Gunner) adaptations 10/29/2020
# Use http://10.10.3.13:8800/index.html for web browser
#
# Use http://10.10.3.13:8800/stream.mjpg for Blue Iris
# (Make: Generic/ONVIF  Model: *RTSP H.264/h.265/MJPG/MPEG4)

import io
import picamera
import logging
import socketserver
from threading import Condition
from http import server

PAGE="""\
<html>
<head>
<title>Raspberry Pi - Surveillance Camera</title>
</head>
<body>
<center><h1>Raspberry Pi - Surveillance Camera</h1></center>
<center><img src="stream.mjpg" width="640" height="480"></center>
</body>
</html>
"""

class StreamingOutput(object):
    def __init__(self):
        self.frame = None
        self.buffer = io.BytesIO()
        self.condition = Condition()

    def write(self, buf):
        if buf.startswith(b'\xff\xd8'):
            # New frame, copy the existing buffer's content and notify all
            # clients it's available
            self.buffer.truncate()
            with self.condition:
                self.frame = self.buffer.getvalue()
                self.condition.notify_all()
            self.buffer.seek(0)
        return self.buffer.write(buf)

class StreamingHandler(server.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/':
            self.send_response(301)
            self.send_header('Location', '/index.html')
            self.end_headers()
        elif self.path == '/index.html':
            content = PAGE.encode('utf-8')
            self.send_response(200)
            self.send_header('Content-Type', 'text/html')
            self.send_header('Content-Length', len(content))
            self.end_headers()
            self.wfile.write(content)
        elif self.path == '/stream.mjpg':
            self.send_response(200)
            self.send_header('Age', 0)
            self.send_header('Cache-Control', 'no-cache, private')
            self.send_header('Pragma', 'no-cache')
            self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=FRAME')
            self.end_headers()
            try:
                while True:
                    with output.condition:
                        output.condition.wait()
                        frame = output.frame
                    self.wfile.write(b'--FRAME\r\n')
                    self.send_header('Content-Type', 'image/jpeg')
                    self.send_header('Content-Length', len(frame))
                    self.end_headers()
                    self.wfile.write(frame)
                    self.wfile.write(b'\r\n')
            except Exception as e:
                logging.warning(
                    'Removed streaming client %s: %s',
                    self.client_address, str(e))
        else:
            self.send_error(404)
            self.end_headers()

class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer):
    allow_reuse_address = True
    daemon_threads = True

with picamera.PiCamera(resolution='640x480', framerate=24) as camera:
    output = StreamingOutput()
    #Uncomment the next line to change your Pi's Camera rotation (in degrees)
    #camera.rotation = 90
    camera.start_recording(output, format='mjpeg')
    try:
        address = ('', 8800)
        server = StreamingServer(address, StreamingHandler)
        server.serve_forever()
    finally:
        camera.stop_recording()
        camera.close()