This post explores how to create your own WiFi camera, for realtime video streaming over the web, using a Raspberry Pi Zero W, a camera module, the Go programming language.
Vladimir Vivien · Follow
Published in · 6 min read · Nov 13, 2022
In a previous post, I explored how to use the Go for Video Linux (Go4VL) package to create Go programs to capture video data from a Linux machine with an attached USB web camera gadget. This post shows how to build your own WiFi camera using the Raspberry Pi Zero W, a Pi camera module, and a Go program that serves the captured video using an HTTP web server.
For this project you will need the followings:
- Raspberry Pi Zero W (running headless)
- Camera module (HQ camera, Pi Cam 2, or compatible modules)
- The Go programming environment
- Go for Video Linux package (go4vl)
- SSH client (to be able to SSH into the Raspberry PI)
Before you plug the Raspberry Pi to a power source, attach the camera module. Regardless of your Pi model or camera model, you will attach your camera similarly to what is shown in the animation below (from these instructions).
Note: remember the instructions here are for a headless setup (SSH only). After you follow the instruction to attach your camera to the board, return here to continue.
If you do not already have a Pi configured with a camera module, use the steps below to help with your Pi and camera configuration. The following steps assume that you will setup your Raspberry Pi without a desktop environment (or headless).
Flash the OS using the Pi Imager tool
Use the Raspberry Pi imager tool to flash the Raspberry Pi OS Lite on your SSD for headless use. Follow the advanced instructions to set up the followings:
- Enable support for SSH
- Specify a host name, if needed
- Enable and configure Wifi
After flashing the SSD, boot up your Pi and continue to the next section.
Enable the camera module
Assuming your Raspberry Pi device is on the same network as your workstation or laptop, use these instructions to log into your Pi using SSH. Upon login, use the raspi-config
tool to enable the camera.
sudo raspi-config
Use theInterface Options
, to enable the camera interface as shown below.
Once done, reboot the Raspberry Pi.
Validate your setup
Since this is a headless setup, it is a good idea to ensure that the camera is attached and working properly. SSH back into the Pi and install the v4l2-utils
package
sudo apt-get install v4l-utils
Next, use the v4l2-ctl
command (from the installed package) to list your attached devices:
v4l2-ctl --list-devices
You should see a list of several devices including a Broadcom BCM235 V4L2 device driver as shown below:
mmal service 16.1 (platform:bcm2835-v4l2-0):
/dev/video0
The /dev/video0
is the device name for the camera device that will be used for video capture. You can get more information about the device with the following v4l2-ctl
command:
v4l2-ctl --info -d /dev/video0
Driver Info:
Driver name : bm2835 mmal
Card type : mmal service 16.1
Bus info : platform:bcm2835-v4l2-0
Driver version : 5.15.65
Capabilities : 0x85200005
Video Capture
Video Overlay
Read/Write
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x05200005
Video Capture
Video Overlay
Read/Write
Streaming
Extended Pix Format
Now that you have your Pi properly configured, it is time to write a simple program that will allow you to stream M-JPEG formatted videos to your web browser.
The source code
The following code is based on example Go4VL/examples/simplecam. First, let us examine the main
function which is used to setup the camera device and start an HTTP server.
import(
"github.com/vladimirvivien/go4vl/device"
"github.com/vladimirvivien/go4vl/v4l2"
)var (
frames <-chan []byte
)
func main() {
port := ":9090"
devName := "/dev/video0"
camera, err := device.Open(
devName,
device.WithPixFormat(v4l2.PixFormat{PixelFormat: v4l2.PixelFmtMJPEG, Width: 640, Height: 480}),
)
if err != nil {
log.Fatalf("failed to open device: %s", err)
}
defer camera.Close()
if err := camera.Start(context.TODO()); err != nil {
log.Fatalf("camera start: %s", err)
}
frames = camera.GetOutput()
http.HandleFunc("/stream", imageServ)
log.Fatal(http.ListenAndServe(port, nil))
}
In the previous snippet, note that function device.Open
, from the go4vl
package, is used to open device /dev/video0
and configure with the desired video format v4l2.PixFormat{PixelFormat: v4l2.PixelFmtMJPEG}
. Once the device is opened, it is assigned to variable camera
.
Method call camera.GetOutput()
returns a channel of byte slices, assigned to variable frames
, that contain the video frames captured from the camera device. Lastly, the code registers HTTP handler imageServ
for path /stream
.
The next code snippet defines HTTP handler function imageServ
which is used to serve the video stream to a web client (i.e. your browser). The code sets up a for-range
loop to continuously retrieve video frame data from the frames
channel variable (setup earlier). Each retrieved frame is returned as a separate JPEG image back to the web client using a multi-part mime writer as shown in the snippet below.
func imageServ(w http.ResponseWriter, req *http.Request) {
mimeWriter := multipart.NewWriter(w)
w.Header().Set("Content-Type", fmt.Sprintf("multipart/x-mixed-replace; boundary=%s", mimeWriter.Boundary()))
partHeader := make(textproto.MIMEHeader)
partHeader.Add("Content-Type", "image/jpeg") var frame []byte
for frame = range frames {
partWriter, err := mimeWriter.CreatePart(partHeader)
if err != nil {
log.Printf("failed to create multi-part writer: %s", err)
return
}
if _, err := partWriter.Write(frame); err != nil {
log.Printf("failed to write image: %s", err)
}
}
}
The Raspberry Pi Zero is a 32-bit ARM machine. There are several ways to build the Go code to create a 32-bit ARM binary, including cross-compilation.
Building on the Raspberry Pi Zero
The easiest way to build and run the program is to build it on theRaspberry Pi itself. First, download the Go tools for the ARMv6 architecture (i.e. go1.19.3.linux-armv6l.tar.gz). Then install the files with the following commands:
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.19.3.linux-armv6l.tar.gz
Then you are ready to compile your source code:
go build ./simplecam
Cross compiling for the Raspberry Pi
Cross compiling Go programs (from a different architecture and/or OS with CGo included) to target the Raspberry Pi Zero is more involved (read complex). The steps vary depending on the host OS and architecture from which you will cross-compile your code to target the Raspberry Pi. In the interest of brevity, that exercise will be reserved for a future write up the subject.
After the source code is compiled successfully, the camera program can be started as shown below (output is from the example simplecam program):
./simplecam
2022/11/13 14:05:38 Serving images: [:9090/stream]
Accessing via a web browser
Next point your browser to the IP address (or host name) of the Raspberry Pi at http://<host-ip>:9090/stream
to see the video feed as shown below.
Accessing via VLC
You can also access the stream via any other client that can stream MJPEG over HTTP. For instance, if you have VLC, create new network source as shown below.
Once connected, you will see your video stream displayed using the VLC application as shown.
This writeup explores how to turn a Raspberry Pi Zero W, with an attached camera module, into a working WiFi camera using Go. The program uses the Go4VL package to create a simple, but functional, program that serves captured video buffers, from the camera, using an HTTP web server making the video accessible via a web browser.
In a future write up, we will explore how to extend this program to add camera control functionalities such as brightness and contrast to your Go camera project.
- The Go4vl Project
- Previous post about Go4VL
- Linux for Video API
- GopherCon presentation (Github files)