Skip to content

Commit 31e61e7

Browse files
Improve camera
1 parent 7e4585e commit 31e61e7

File tree

3 files changed

+64
-146
lines changed

3 files changed

+64
-146
lines changed

c_mpos/src/webcam.c

Lines changed: 59 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ static const mp_obj_type_t webcam_type;
2525
typedef struct _webcam_obj_t {
2626
mp_obj_base_t base;
2727
int fd;
28+
char device[64]; // Device path (e.g., "/dev/video0")
2829
void *buffers[NUM_BUFFERS];
2930
size_t buffer_length;
3031
int frame_count;
@@ -147,8 +148,11 @@ static void save_raw_rgb565(const char *filename, uint16_t *data, int width, int
147148
fclose(fp);
148149
}
149150

150-
static int init_webcam(webcam_obj_t *self, const char *device) {
151-
//WEBCAM_DEBUG_PRINT("webcam.c: init_webcam\n");
151+
static int init_webcam(webcam_obj_t *self, const char *device, int width, int height) {
152+
// Store device path for later use (e.g., reconfigure)
153+
strncpy(self->device, device, sizeof(self->device) - 1);
154+
self->device[sizeof(self->device) - 1] = '\0';
155+
152156
self->fd = open(device, O_RDWR);
153157
if (self->fd < 0) {
154158
WEBCAM_DEBUG_PRINT("Cannot open device: %s\n", strerror(errno));
@@ -157,8 +161,8 @@ static int init_webcam(webcam_obj_t *self, const char *device) {
157161

158162
struct v4l2_format fmt = {0};
159163
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
160-
fmt.fmt.pix.width = WIDTH;
161-
fmt.fmt.pix.height = HEIGHT;
164+
fmt.fmt.pix.width = width;
165+
fmt.fmt.pix.height = height;
162166
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
163167
fmt.fmt.pix.field = V4L2_FIELD_ANY;
164168
if (ioctl(self->fd, VIDIOC_S_FMT, &fmt) < 0) {
@@ -167,6 +171,10 @@ static int init_webcam(webcam_obj_t *self, const char *device) {
167171
return -errno;
168172
}
169173

174+
// Store actual format (driver may adjust dimensions)
175+
width = fmt.fmt.pix.width;
176+
height = fmt.fmt.pix.height;
177+
170178
struct v4l2_requestbuffers req = {0};
171179
req.count = NUM_BUFFERS;
172180
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -215,9 +223,9 @@ static int init_webcam(webcam_obj_t *self, const char *device) {
215223

216224
self->frame_count = 0;
217225

218-
// Store the input dimensions from V4L2 format
219-
self->input_width = WIDTH;
220-
self->input_height = HEIGHT;
226+
// Store the input dimensions (actual values from V4L2, may be adjusted by driver)
227+
self->input_width = width;
228+
self->input_height = height;
221229

222230
// Initialize output dimensions with defaults if not already set
223231
if (self->output_width == 0) self->output_width = OUTPUT_WIDTH;
@@ -323,13 +331,25 @@ static mp_obj_t capture_frame(mp_obj_t self_in, mp_obj_t format) {
323331
}
324332
}
325333

326-
static mp_obj_t webcam_init(size_t n_args, const mp_obj_t *args) {
327-
mp_arg_check_num(n_args, 0, 0, 1, false);
334+
static mp_obj_t webcam_init(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
335+
enum { ARG_device, ARG_width, ARG_height };
336+
static const mp_arg_t allowed_args[] = {
337+
{ MP_QSTR_device, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
338+
{ MP_QSTR_width, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = WIDTH} },
339+
{ MP_QSTR_height, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = HEIGHT} },
340+
};
341+
342+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
343+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
344+
328345
const char *device = "/dev/video0";
329-
if (n_args == 1) {
330-
device = mp_obj_str_get_str(args[0]);
346+
if (args[ARG_device].u_obj != MP_OBJ_NULL) {
347+
device = mp_obj_str_get_str(args[ARG_device].u_obj);
331348
}
332349

350+
int width = args[ARG_width].u_int;
351+
int height = args[ARG_height].u_int;
352+
333353
webcam_obj_t *self = m_new_obj(webcam_obj_t);
334354
self->base.type = &webcam_type;
335355
self->fd = -1;
@@ -340,14 +360,14 @@ static mp_obj_t webcam_init(size_t n_args, const mp_obj_t *args) {
340360
self->output_width = 0; // Will use default OUTPUT_WIDTH in init_webcam
341361
self->output_height = 0; // Will use default OUTPUT_HEIGHT in init_webcam
342362

343-
int res = init_webcam(self, device);
363+
int res = init_webcam(self, device, width, height);
344364
if (res < 0) {
345365
mp_raise_OSError(-res);
346366
}
347367

348368
return MP_OBJ_FROM_PTR(self);
349369
}
350-
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(webcam_init_obj, 0, 1, webcam_init);
370+
MP_DEFINE_CONST_FUN_OBJ_KW(webcam_init_obj, 0, webcam_init);
351371

352372
static mp_obj_t webcam_deinit(mp_obj_t self_in) {
353373
webcam_obj_t *self = MP_OBJ_TO_PTR(self_in);
@@ -373,11 +393,10 @@ MP_DEFINE_CONST_FUN_OBJ_2(webcam_capture_frame_obj, webcam_capture_frame);
373393

374394
static mp_obj_t webcam_reconfigure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
375395
/*
376-
* Reconfigure webcam resolution.
396+
* Reconfigure webcam resolution by reinitializing.
377397
*
378-
* Supports changing both INPUT resolution (V4L2 capture format) and
379-
* OUTPUT resolution (conversion buffers). If input resolution changes,
380-
* this will stop streaming, reconfigure V4L2, and restart streaming.
398+
* This elegantly reuses deinit_webcam() and init_webcam() instead of
399+
* duplicating V4L2 setup code.
381400
*
382401
* Parameters:
383402
* input_width, input_height: V4L2 capture resolution (optional)
@@ -412,141 +431,40 @@ static mp_obj_t webcam_reconfigure(size_t n_args, const mp_obj_t *pos_args, mp_m
412431
if (new_output_height == 0) new_output_height = self->output_height;
413432

414433
// Validate dimensions
415-
if (new_input_width <= 0 || new_input_height <= 0 || new_input_width > 1920 || new_input_height > 1920) {
434+
if (new_input_width <= 0 || new_input_height <= 0 || new_input_width > 3840 || new_input_height > 2160) {
416435
mp_raise_ValueError(MP_ERROR_TEXT("Invalid input dimensions"));
417436
}
418-
if (new_output_width <= 0 || new_output_height <= 0 || new_output_width > 1920 || new_output_height > 1920) {
437+
if (new_output_width <= 0 || new_output_height <= 0 || new_output_width > 3840 || new_output_height > 2160) {
419438
mp_raise_ValueError(MP_ERROR_TEXT("Invalid output dimensions"));
420439
}
421440

422-
bool input_changed = (new_input_width != self->input_width || new_input_height != self->input_height);
423-
bool output_changed = (new_output_width != self->output_width || new_output_height != self->output_height);
424-
425-
// If input resolution changed, need to reconfigure V4L2
426-
if (input_changed) {
427-
WEBCAM_DEBUG_PRINT("Reconfiguring V4L2: %dx%d -> %dx%d\n",
428-
self->input_width, self->input_height,
429-
new_input_width, new_input_height);
430-
431-
// 1. Stop streaming
432-
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
433-
if (ioctl(self->fd, VIDIOC_STREAMOFF, &type) < 0) {
434-
WEBCAM_DEBUG_PRINT("STREAMOFF failed: %s\n", strerror(errno));
435-
mp_raise_OSError(errno);
436-
}
437-
438-
// 2. Unmap old buffers
439-
for (int i = 0; i < NUM_BUFFERS; i++) {
440-
if (self->buffers[i] != MAP_FAILED && self->buffers[i] != NULL) {
441-
munmap(self->buffers[i], self->buffer_length);
442-
self->buffers[i] = MAP_FAILED;
443-
}
444-
}
445-
446-
// 3. Set new V4L2 format
447-
struct v4l2_format fmt = {0};
448-
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
449-
fmt.fmt.pix.width = new_input_width;
450-
fmt.fmt.pix.height = new_input_height;
451-
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
452-
fmt.fmt.pix.field = V4L2_FIELD_ANY;
453-
454-
if (ioctl(self->fd, VIDIOC_S_FMT, &fmt) < 0) {
455-
WEBCAM_DEBUG_PRINT("S_FMT failed: %s\n", strerror(errno));
456-
mp_raise_OSError(errno);
457-
}
458-
459-
// Verify format was set (driver may adjust dimensions)
460-
if (fmt.fmt.pix.width != new_input_width || fmt.fmt.pix.height != new_input_height) {
461-
WEBCAM_DEBUG_PRINT("Warning: Driver adjusted format to %dx%d\n",
462-
fmt.fmt.pix.width, fmt.fmt.pix.height);
463-
new_input_width = fmt.fmt.pix.width;
464-
new_input_height = fmt.fmt.pix.height;
465-
}
466-
467-
// 4. Request new buffers
468-
struct v4l2_requestbuffers req = {0};
469-
req.count = NUM_BUFFERS;
470-
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
471-
req.memory = V4L2_MEMORY_MMAP;
472-
473-
if (ioctl(self->fd, VIDIOC_REQBUFS, &req) < 0) {
474-
WEBCAM_DEBUG_PRINT("REQBUFS failed: %s\n", strerror(errno));
475-
mp_raise_OSError(errno);
476-
}
477-
478-
// 5. Map new buffers
479-
for (int i = 0; i < NUM_BUFFERS; i++) {
480-
struct v4l2_buffer buf = {0};
481-
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
482-
buf.memory = V4L2_MEMORY_MMAP;
483-
buf.index = i;
484-
485-
if (ioctl(self->fd, VIDIOC_QUERYBUF, &buf) < 0) {
486-
WEBCAM_DEBUG_PRINT("QUERYBUF failed: %s\n", strerror(errno));
487-
mp_raise_OSError(errno);
488-
}
489-
490-
self->buffer_length = buf.length;
491-
self->buffers[i] = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
492-
MAP_SHARED, self->fd, buf.m.offset);
493-
494-
if (self->buffers[i] == MAP_FAILED) {
495-
WEBCAM_DEBUG_PRINT("mmap failed: %s\n", strerror(errno));
496-
mp_raise_OSError(errno);
497-
}
498-
}
499-
500-
// 6. Queue buffers
501-
for (int i = 0; i < NUM_BUFFERS; i++) {
502-
struct v4l2_buffer buf = {0};
503-
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
504-
buf.memory = V4L2_MEMORY_MMAP;
505-
buf.index = i;
506-
507-
if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) {
508-
WEBCAM_DEBUG_PRINT("QBUF failed: %s\n", strerror(errno));
509-
mp_raise_OSError(errno);
510-
}
511-
}
512-
513-
// 7. Restart streaming
514-
if (ioctl(self->fd, VIDIOC_STREAMON, &type) < 0) {
515-
WEBCAM_DEBUG_PRINT("STREAMON failed: %s\n", strerror(errno));
516-
mp_raise_OSError(errno);
517-
}
518-
519-
// Update stored input dimensions
520-
self->input_width = new_input_width;
521-
self->input_height = new_input_height;
441+
// Check if anything changed
442+
if (new_input_width == self->input_width &&
443+
new_input_height == self->input_height &&
444+
new_output_width == self->output_width &&
445+
new_output_height == self->output_height) {
446+
return mp_const_none; // Nothing to do
522447
}
523448

524-
// If output resolution changed (or input changed which may affect output), reallocate output buffers
525-
if (output_changed || input_changed) {
526-
// Free old buffers
527-
free(self->gray_buffer);
528-
free(self->rgb565_buffer);
449+
WEBCAM_DEBUG_PRINT("Reconfiguring webcam: %dx%d -> %dx%d (input), %dx%d -> %dx%d (output)\n",
450+
self->input_width, self->input_height, new_input_width, new_input_height,
451+
self->output_width, self->output_height, new_output_width, new_output_height);
529452

530-
// Update dimensions
531-
self->output_width = new_output_width;
532-
self->output_height = new_output_height;
453+
// Remember device path before deinit (which closes fd)
454+
char device[64];
455+
strncpy(device, self->device, sizeof(device));
533456

534-
// Allocate new buffers
535-
self->gray_buffer = (unsigned char *)malloc(self->output_width * self->output_height * sizeof(unsigned char));
536-
self->rgb565_buffer = (uint16_t *)malloc(self->output_width * self->output_height * sizeof(uint16_t));
457+
// Set desired output dimensions before reinit
458+
self->output_width = new_output_width;
459+
self->output_height = new_output_height;
537460

538-
if (!self->gray_buffer || !self->rgb565_buffer) {
539-
free(self->gray_buffer);
540-
free(self->rgb565_buffer);
541-
self->gray_buffer = NULL;
542-
self->rgb565_buffer = NULL;
543-
mp_raise_OSError(MP_ENOMEM);
544-
}
545-
}
461+
// Clean shutdown and reinitialize with new input dimensions
462+
deinit_webcam(self);
463+
int res = init_webcam(self, device, new_input_width, new_input_height);
546464

547-
WEBCAM_DEBUG_PRINT("Webcam reconfigured: input %dx%d, output %dx%d\n",
548-
self->input_width, self->input_height,
549-
self->output_width, self->output_height);
465+
if (res < 0) {
466+
mp_raise_OSError(-res);
467+
}
550468

551469
return mp_const_none;
552470
}

internal_filesystem/apps/com.micropythonos.camera/assets/camera_app.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,10 @@ def onResume(self, screen):
144144
else:
145145
print("camera app: no internal camera found, trying webcam on /dev/video0")
146146
try:
147-
self.cam = webcam.init("/dev/video0")
147+
# Initialize webcam with desired resolution directly
148+
print(f"Initializing webcam at {self.width}x{self.height}")
149+
self.cam = webcam.init("/dev/video0", width=self.width, height=self.height)
148150
self.use_webcam = True
149-
# Reconfigure webcam to use saved resolution
150-
print(f"Reconfiguring webcam to {self.width}x{self.height}")
151-
webcam.reconfigure(self.cam, output_width=self.width, output_height=self.height)
152151
except Exception as e:
153152
print(f"camera app: webcam exception: {e}")
154153
if self.cam:

internal_filesystem/lib/mpos/board/fri3d_2024.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ def keypad_read_cb(indev, data):
266266
2482 is 4.180
267267
2470 is 4.170
268268
2457 is 4.147
269+
# 2444 is 4.12
269270
2433 is 4.109
270271
2429 is 4.102
271272
2393 is 4.044
@@ -280,7 +281,7 @@ def adc_to_voltage(adc_value):
280281
Calibration data shows linear relationship: voltage = -0.0016237 * adc + 8.2035
281282
This is ~10x more accurate than simple scaling (error ~0.01V vs ~0.1V).
282283
"""
283-
return (-0.0016237 * adc_value + 8.2035)
284+
return (0.001651* adc_value + 0.08709)
284285

285286
mpos.battery_voltage.init_adc(13, adc_to_voltage)
286287

0 commit comments

Comments
 (0)