element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet & Tria Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
    About the element14 Community
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Japan
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      •  Vietnam
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
EZ-EV Challenge
  • Challenges & Projects
  • Design Challenges
  • EZ-EV Challenge
  • More
  • Cancel
EZ-EV Challenge
Forum hall-w-EV - Post 4 - Giving it eyes
  • News
  • Projects
  • Forum
  • DC
  • Leaderboard
  • Files
  • Members
  • More
  • Cancel
  • New
Join EZ-EV Challenge to participate - click to join for free!
Actions
  • Share
  • More
  • Cancel
Forum Thread Details
  • Replies 0 replies
  • Subscribers 59 subscribers
  • Views 20 views
  • Users 0 members are here
  • usb
  • camera
  • uno q
Related

hall-w-EV - Post 4 - Giving it eyes

tamadillo
tamadillo 3 hours ago

Hi again, it’s Hambreros and Tamadillo. Last post ended with a promise: the joystick makes driving nicer, but you’re still driving blind. So a USB webcam went on, and the control page now shows what the robot sees.

What we’ve actually built

A live video feed right on the same page as the joystick — the “puppy on a leash from your phone” post the original plan called for, minus the actual leash. Point the robot somewhere without needing to be in the room with it.

Checking what the framework already gives you

Same move as the TTS detour: before writing any camera code, went and read arduino/app-bricks-py to see what Arduino already ships. Turned out app_peripherals/camera is a whole unified abstraction — one Camera class covering CSI, USB (V4L), IP, and even WebSocket sources, same family as the Speaker peripheral the sound system already uses:

from arduino.app_peripherals.camera import Camera

camera = Camera("usb:0", resolution=(640, 480), fps=15)
camera.start()
frame = camera.capture()   # numpy array, or None
# or: for frame in camera.stream(): ...

Since Speaker was already confirmed bundled in this app’s base container with zero extra install, betting Camera is too — no new brick, no sidecar container, just a normal peripheral call from our own code. The only actual new dependency is opencv-python-headless, for turning each frame into a JPEG.

The dumbest frontend that works

The tempting-but-overbuilt version of this involves a <canvas>, a WebSocket, and a JS render loop pulling frames off it. Skipped all of that:

<img id="cameraFeed" src="/api/camera/stream" alt="Live camera feed">

That’s the entire client-side video pipeline. GET /api/camera/stream returns multipart/x-mixed-replace — a boundary-delimited stream of JPEG frames — and browsers have known how to render that straight into an <img> tag since basically forever. No JS needed for the video itself, just a listener on the error event for when there’s no camera to show:

cameraFeed.addEventListener('error', () => {
  cameraFeed.style.display = 'none';
  fetch('/api/camera/status').then(r => r.json()).then(data => {
    cameraError.hidden = false;
    cameraError.textContent = data.error || 'camera stream unavailable';
  });
});

Same “surface the real reason, not a cryptic broken icon” instinct as the sound system’s error banner from a couple of posts back.

Starting the camera without blocking everything else

Camera.start() has its own connection retry loop with exponential backoff — reasonable for “give the USB device a moment to enumerate,” bad if it’s sitting on the same startup path as the wheel and sound APIs. A slow or missing camera shouldn’t hold up driving the robot.

So python/camera.py kicks off Camera(...).start() on a background thread at import time, mirroring sounds.py’s general shape:

def _start():
    global _camera, _error
    try:
        cam = Camera(SOURCE, resolution=RESOLUTION, fps=FPS)
        cam.start()
        _camera = cam
    except Exception as e:
        _error = str(e)

threading.Thread(target=_start, daemon=True).start()

status() reports (ok, error) off that shared state, and the stream route just checks it before handing back the actual multipart response:

@ web.route('/api/camera/stream') # space added not to look like email address
def camera_stream():              # which flags the post as inappropriate
    ok, error = camera.status()
    if not ok:
        return jsonify({'ok': False, 'error': error}), 503
    return Response(camera.mjpeg_frames(),
                     mimetype='multipart/x-mixed-replace; boundary=frame')

mjpeg_frames() itself is a small generator wrapping Camera.stream(), JPEG-encoding (cv2.imencode) each frame as it comes.

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

What’s next

Camera’s on, joystick works — next logical step is doing something with the two together: point-and-drive, or finally trying that on-device object detection now that we know UNO Q supports in it’s demo bricks.

The codes

  • https://github.com/tamadillo/hall-w-EV

— Hambreros (and Tamadillo)

  • Sign in to reply
  • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2026 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube