LambLisp 01 Red Fox Alpha
From AI to Actuator
Loading...
Searching...
No Matches

Macros

#define EYE_CAM_EARLY_DMA_RESERVE   0
#define EYE_CAM_XCLK   15
#define EYE_CAM_SIOD   4
#define EYE_CAM_SIOC   5
#define EYE_CAM_VSYNC   6
#define EYE_CAM_HREF   7
#define EYE_CAM_PCLK   13
#define EYE_CAM_D0   11
#define EYE_CAM_D1   9
#define EYE_CAM_D2   8
#define EYE_CAM_D3   10
#define EYE_CAM_D4   12
#define EYE_CAM_D5   18
#define EYE_CAM_D6   17
#define EYE_CAM_D7   16
#define LL_CAM_SET_I(FN)

Functions

Sexpr_t mop3_camera_init (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t mop3_camera_thumb (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t mop3_camera_set (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t mop3_camera_get (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t mop3_camera_capture (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t mop3_frame_brightness (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)
Sexpr_t EyeCam_install_mop3 (Lamb &lamb, Sexpr_t sexpr, Sexpr_t env_exec)

Detailed Description

LambLisp Camera (ESP32-S3-EYE) builtins.

Macro Definition Documentation

◆ EYE_CAM_EARLY_DMA_RESERVE

#define EYE_CAM_EARLY_DMA_RESERVE   0

THE PANEL LEFT THIS FILE. It is ll_xmop3_Panel.cpp now, behind the LL_Panel interface in ll_platform_generic.h, and this driver asks only ONE question of it: is there a panel to send a frame to? That is LL_PANEL.

What used to be here was LL_CAM_HAS_LCD, derived as LL_ESP32S3 – correct only by accident, because the one S3 board with a camera happened to be the one board with the ST7789. Worse, the panel could not exist WITHOUT this file: all 98 LCD references were inside #if LL_CAMERA, so a board with a panel and no camera could not compile a line of it. A camera driver is the wrong owner for a display.

◆ EYE_CAM_XCLK

#define EYE_CAM_XCLK   15

TWO BOARDS, TWO WIRINGS, AND THEY SHARE NO PIN. These are not interchangeable and a build that picks the wrong set configures GPIOs that either do not exist or belong to something else. The Freenove numbers are NOT guessed: they are exactly what scm/boards/pins/Freenove-4WD-Car-Kit-ESP32.scm already declares (pin-XCLK 21, pin-PCLK 22, pin-HREF 23, pin-CSI_VYSNC 25, pin-SIOD 26, pin-SIOC 27, pin-CSI_Y2..Y9 = 4 5 18 19 36 39 34 35), which is the Freenove WROVER-CAM mapping and differs from the AI-Thinker ESP32-CAM one that a web search hands you first. The Scheme map and this table must agree; if the board is ever re-wired, change BOTH.

◆ EYE_CAM_SIOD

#define EYE_CAM_SIOD   4

SCCB SDA.

◆ EYE_CAM_SIOC

#define EYE_CAM_SIOC   5

SCCB SCL.

◆ EYE_CAM_D0

#define EYE_CAM_D0   11

D0..D7 are the sensor's Y2..Y9.

◆ LL_CAM_SET_I

#define LL_CAM_SET_I ( FN)
Value:
do { if (s->FN) rc = s->FN(s, (int) val); else have = false; } while (0)

(camera-set key val) -> #t if the sensor took it, #f if it did not.

Tune the sensor through its control block. Keys, with the range each one accepts:

EXPOSURE AND GAIN – the controls that decide whether a dim scene is usable at all: exposure-ctrl 0/1 automatic exposure on (1) or off (0) aec-value 0..1200 manual exposure time, in sensor row periods; needs exposure-ctrl 0 aec2 0/1 the DSP's second-stage auto exposure ("night mode") ae-level -2..2 bias the automatic exposure darker or brighter gain-ctrl 0/1 automatic gain on (1) or off (0) agc-gain 0..30 manual gain; needs gain-ctrl 0 gainceiling 0..6 how far automatic gain may go: 0 is 2x, 6 is 128x

IMAGE: framesize, quality (10..63, LOWER is better), brightness/contrast/saturation (-2..2), sharpness, denoise, special-effect, wb-mode, whitebal, awb-gain, raw-gma, lenc, dcw, bpc, wpc, colorbar, hmirror, vflip.

RAISING THE GAIN CEILING IS THE LEVER FOR A DIM SCENE, AND THE TRADE IS NOISE AND MOTION BLUR. Automatic exposure will not use a gain it is not permitted, so a camera indoors at dusk typically sits at its ceiling with the image still under-exposed; the default ceiling is low because a moving camera cannot afford the blur a long exposure brings. A camera that does not move can: there is nothing in the frame to smear, and a noisy bright frame carries far more for a recogniser than a clean black one. So a fixed installation should expect to raise gainceiling, and may set aec-value directly once it knows its scene.

BRIGHTNESS IS NOT EXPOSURE. It is a digital offset applied to pixels the sensor has already captured, so it lifts the black level and the noise together and collects no extra light. Measured on an under-exposed indoor wall: brightness 2 with contrast 2 left the scene darker still, because the added contrast crushed what little shadow detail there was. Reach for the exposure and gain keys above instead.

IT REPORTS WHETHER IT WORKED, AND THE THREE WAYS IT CAN FAIL ARE DISTINGUISHED IN THE LOG: the camera is not initialised, the key is not one of the above, or this sensor does not implement that control. A control block carries a null pointer for anything the attached part cannot do – the OV2640 and the OV5640 do not implement the same set – so each call is checked before it is made; an unchecked call through a null entry is a reset, not an error. Returning a value rather than nothing matters because the three failures and a success used to be indistinguishable from the caller: a typo in a key silently did nothing.

DEFINED AT FILE SCOPE, NOT INSIDE THE GC FRAME BELOW, AND IT MUST STAY THERE. The gc frame macros are variadic, so the braced block passed to one is a macro ARGUMENT – and a #define written inside a macro argument is not in effect for that argument's own tokens. Move this back in and all ~20 uses below fail to compile with "'LL_CAM_SET_I' was not declared in this scope", naming a definition that is plainly visible above them. #if inside the same block DOES work (the preprocessor evaluates conditionals there), so a block can contain working directives alongside one that silently did nothing.

Function Documentation

◆ mop3_camera_init()

Sexpr_t mop3_camera_init ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

(camera-init [mode]) -> #t/#f. mode 'rgb565 (default, drives the LCD) or 'jpeg (for capture).

< 160x120

< 320x240

< 240x240 – the historical hardcoded default

< 480x320

< 640x480

< 800x600

< 1024x768

< 1600x1200, the OV2640's maximum

REFUSE AN UNKNOWN NAME RATHER THAN FALLING BACK TO THE DEFAULT. A typo that quietly captured at 240x240 would read as "the larger size made no difference" – a null result from an experiment that never ran, which this tree has paid for repeatedly.

◆ mop3_camera_thumb()

Sexpr_t mop3_camera_thumb ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

(camera->lcd) -> #t/#f. Grab one RGB565 frame and blit it to the LCD (the hot path).

◆ mop3_camera_set()

Sexpr_t mop3_camera_set ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

THE KEY CELL IS CONSUMED BEFORE THE VALUE IS FETCHED, AND THAT ORDER IS THE POINT. car and cdr ALLOCATE. A pair slot can hold a small integer or float EMBEDDED in the slot itself, so the accessor must BOX that value into a fresh cell on the way out – a cell the caller's argument list never contained, so the root the interpreter holds on that list does NOT cover it. Fetching the value is itself an accessor, hence itself an allocation: holding the key cell across it would let a collection cycle reclaim the box and hand the cell out again with a different type, after which the symbol test below passes on an unrelated object and the character read follows a pointer that is not a symbol. That is a fault with no Scheme error to catch, because the check that should have refused it has already run. So: test the key, take its characters, and only then fetch the value. key_cell MUST NOT be dereferenced after the val line below – it is named for the cell rather than for the key so that a reader looking for its uses finds all of them.

Interned symbol characters outlive the call; "" keeps the comparisons below well-defined on the rejected path rather than relying on the early return alone.

< the key is one of ours

< this sensor implements it (the entry is not null)

< what the sensor's own setter answered; 0 is success

ONE LINE PER CONTROL, EACH CHECKING ITS OWN ENTRY FIRST. The repetition is deliberate: the setters do not share a signature (framesize and gainceiling take enums), so a table would need a cast per row and would hide exactly the null check that must not be skipped.

◆ mop3_camera_get()

Sexpr_t mop3_camera_get ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

(camera-get) -> an alist of the sensor's CURRENT exposure and gain state. A fixed installation has to know whether automatic exposure has already run out of room before it decides to raise the ceiling or to ask for more light, and the status block carries what the sensor settled on.

Built by prepending and reversed once, so the list reads in the order written here.

◆ mop3_camera_capture()

Sexpr_t mop3_camera_capture ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

(camera-capture) -> bytevector. Return one JPEG frame (needs (camera-init 'jpeg)); returns #f if no frame is available.

◆ mop3_frame_brightness()

Sexpr_t mop3_frame_brightness ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

(frame-brightness) -> integer 0..255. Cheap mean luma over a downsampled RGB565 frame – a scalar for trigger-policy demos ("brighten past N -> fire").

◆ EyeCam_install_mop3()

Sexpr_t EyeCam_install_mop3 ( Lamb & lamb,
Sexpr_t sexpr,
Sexpr_t env_exec )

Installer – always compiles; registers the eye mops only under LL_CAMERA.

(lcd-backlight [percent]) -> void. Set backlight brightness 0..100 (default 100). Handy for demos: (lcd-backlight 0) blanks the screen without tearing down the panel, (lcd-backlight 100) restores it. Independent of panel content, so it also isolates a backlight fault from a draw fault when bringing up new hardware.

sym is read by dict_bind_bang with no allocation in between – step 1 stops it.

THE LCD BAND USED TO BE RESERVED HERE TOO, AND IS NOT ANY MORE: it belongs to the panel driver and is claimed lazily at (lcd-init). The two claims still COMPETE for the same internal-DMA pool, so whichever runs second can still come back NULL – that coupling is real and did not move with the code. If a blank screen returns after a camera change, this is the interaction to suspect.