By   July 3, 2022

As part of some tinkering I was doing with my robotics team, I was learning about AprilTags.  Coincidentally, I had also recently installed a gate opener at the front gate and was struggling with how to organize guest access.

I grabbed my OpenMV camera, a spare buck/boost converter, and an old Axis surveillance camera housing.  Put it all together and mounted it to a post in front of the gate:

 
Google Photos Refresh Token invalid. Please authenticate from Photonic → Authentication.
Error encountered during authentication:
{
  "error": "deleted_client",
  "error_description": "The OAuth client was deleted."
}
See here for documentation.

I played with the QR Code recognition and various families of AprilTag. While the QR Code recognition worked fine and allowed me some nice flexibility in terms of content, it was more fallible under varying light conditions. AprilTags are more robust and recognized pretty quickly from a few feet away.

The code is simple:

import sensor, image, time, math, omv, utime
import pyb
import machine


thresholds = ( 150,255 )
sensor.reset()                      # Reset and initialize the sensor.
sensor.set_pixformat(sensor.GRAYSCALE)
sensor.set_framesize(sensor.QQVGA)
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)
sensor.skip_frames(time = 2000)     # Wait for settings take effect.
clock = time.clock()                # Create a clock object to track the FPS.

tag_families = image.TAG36H11|image.TAG36H11

last_tag = 0
tag_repeat_cnt = 0
tag_repeat_threshold = 5
rtc = pyb.RTC()

def set_rtc(epoch):
    dt=time.localtime(int(epoch))
    rtc.datetime(tuple(dt))


P1_PIN="P4"
P2_PIN="P5"
p1 = 0
p2 = 0

STATE_NORMAL=0
STATE_STAY_OPEN=1
STATE_STAY_CLOSED=2

state = 0
rtc_valid = 0

red_led = pyb.LED(1)
green_led = pyb.LED(2)
blue_led = pyb.LED(3)
ir_leds = pyb.LED(4)

# micropython Epoch is 2000-01-01.  My timezone is UTC+8.
epoch_fixup=946728000


def set_p1(dur=0):
    p1.low()
    red_led.on()
    time.sleep_ms(dur)
    p1.high()
    red_led.off()


def set_p2(dur=0):
    p2.low()
    green_led.on()
    red_led.on()
    if (dur == 0):
        return
    time.sleep_ms(dur)
    p2.high()
    green_led.off()
    red_led.off()

def init_gpios():
    p1=pyb.Pin(P1_PIN, pyb.Pin.OUT_PP, pyb.Pin.PULL_DOWN)
    p2=pyb.Pin(P2_PIN, pyb.Pin.OUT_PP, pyb.Pin.PULL_DOWN)
    p1.high()
    p2.high()

    return p1,p2

p1,p2 = init_gpios()
blue_led.on()

i=0
while True:
    if i > 10:
        sensor.sleep(True)
        time.sleep_ms(500)
        sensor.sleep(False)
        i = 0
    i = i+1

    img = sensor.snapshot()         # Take a picture and return the image.
                                    # to the IDE. The FPS should increase once disconnected.
    tag_list = []

    try:
        tag_list.extend(img.find_apriltags(families=tag_families))
    except (MemoryError, OSError):
        pass

    for tag in tag_list:
        if tag.id() == last_tag:
            tag_repeat_cnt = tag_repeat_cnt + 1
        else:
            tag_repeat_cnt = 0
            last_tag = tag.id()

        if tag_repeat_cnt > tag_repeat_threshold:
            if tag.id() > 1 and tag.id() < 100 and state==STATE_NORMAL:
                print("Access granted (FRIEND): {}".format(tag.id()))
                set_p1(1000)

Obviously this isn’t super secure but most people who are breaking into houses aren’t sophisticated enough to know about Fiducials. Most people will assume the camera is for someone in the house to remotely activate a switch. I am actually using somewhat different code.

Because the only power I have out there is the deep cycle battery running the gate controller, I put the OpenMV to sleep for 1/2 second at a time. Seems to do ok and has gotten through an entire winter with temps that fell to -40C.