Skip to content

Instantly share code, notes, and snippets.

@eloquentarduino
Created March 8, 2020 10:36
Show Gist options
  • Save eloquentarduino/6bb0b26a3900d7fac68b2f3cc7b2c688 to your computer and use it in GitHub Desktop.
Save eloquentarduino/6bb0b26a3900d7fac68b2f3cc7b2c688 to your computer and use it in GitHub Desktop.
ESP32 cam motion debug tool

ESP32 motion detection debug tool

This gist setups a simple debug tool for your pure ESP32 camera motion detection project. It lets you visualize the video streaming from the camera and highlights the differences from a frame to the previous.

To make it work you need to:

  1. upload the ESP32 naive motion detection sketch to your ESP32 camera
  2. put the two files below in a folder
  3. connect your ESP32 to your PC, set the serial port on the Python script accordingly and run the script
  4. start a webserver in the folder and open the motion_visualizer.html file in your browser

You should be able to see a grid on top of the video streaming highlighting the cells that triggered motion.

Please, before asking for help, try to debug for yourself, the whole project is only few lines of code you should be able to understand on your own.

import json
from time import time, sleep
import numpy as np
from PIL import Image
from serial import SerialException, Serial
def serial_to_file(port=None, baudrate=115200):
"""Read serial and generate files required for the visualization frontend"""
SHAPE = (48, 64)
with Serial(port, baudrate, timeout=3) as ser:
changed = []
while True:
try:
line = ser.readline().decode('utf-8').strip()
# raw image
if line == 'Current frame:':
pixels = []
# read each line of pixels
for i in range(SHAPE[0]):
l = ser.readline().decode('utf-8').strip()
l = [int(x) for x in l.split('\t')]
pixels.append(l)
mat = np.array(pixels).astype(np.uint8).reshape(SHAPE)
image = Image.fromarray(mat)
# save to disk to update frontend
image.save('images/capture.jpg')
# different pixels
if line.startswith('diff'):
# each line has a pair (x, y) for changed tile
y, x = line.split('\t')[1:3]
changed.append((int(y), int(x)))
if line.startswith('======'):
# end of different pixels
with open('images/diff.json', 'w') as out:
json.dump(changed, out)
changed = []
except (SerialException, KeyboardInterrupt):
break
if __name__ == '__main__':
serial_to_file(port='/dev/ttyUSB0')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Image viewer</title>
<style>
* {
box-sizing: border-box;
}
#grid {
position: absolute;
top: 8px;
left: 8px;
width: 640px;
height: 480px;
}
#grid table {
width: 100%;
border-collapse: collapse;
}
#grid td {
/* set as per your need */
width: 20px;
height: 10px;
border: 1px solid #ffa5002e;
}
h1 {
font-family: monospace;
}
</style>
<style id="style"></style>
</head>
<body>
<div>
<img id="img" style="width: 640px" />
<div id="grid"></div>
<h1 id="motion"></h1>
</div>
<script>
const SHAPE = [48, 64]
// update frame at given interval
setInterval(() => {
const v = +(new Date())
const src = `/images/capture.jpg?v=${v}`
const img = document.getElementById('img')
img.src = src
}, 300)
// highlight changed tiles
setInterval(() => {
const v = +(new Date())
fetch(`/images/diff.json?v=${v}`)
.then(res => res.json())
.then(changed => {
// generate a CSS rule for each changed tile
const style = changed.map(([y, x]) => `#c-${y}-${x} { background: #ffe5131c }`).join('')
// minimum number of changed tiles to be detected as motion
const threshold = 100
document.getElementById('motion').innerText = changed.length > threshold ? 'Motion' : ''
document.getElementById('style').innerHTML = style
})
}, 300)
// display grid
setTimeout(() => {
const grid = document.getElementById('grid')
const html = [...new Array(SHAPE[0])].map((y, i) => {
return '<tr>' + [...new Array(SHAPE[1])].map((x, j) => `<td id="c-${i}-${j}"></td>`).join('') + '</tr>'
}).join('')
grid.innerHTML = `<table>${html}</table>`
}, 3000)
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment