Example #1
0
static mp_obj_t py_select(uint n_args, const mp_obj_t *args)
{
    int nfds=0; //highest-numbered fd plus 1
    timeval tv={0};
    fd_set rfds, wfds, xfds;

    mp_obj_t *rlist, *wlist, *xlist;
    uint rlist_len, wlist_len, xlist_len;

    /* read args */
    mp_obj_get_array(args[0], &rlist_len, &rlist);
    mp_obj_get_array(args[1], &wlist_len, &wlist);
    mp_obj_get_array(args[2], &xlist_len, &xlist);

    if (n_args == 4) {
        float timeout = mp_obj_get_float(args[3]);
        tv.tv_sec = (int)timeout;
        tv.tv_usec = (timeout-(int)timeout)*1000*1000;
    }

    // add fds to their respective sets
    set_fds(&nfds, rlist, rlist_len, &rfds);
    set_fds(&nfds, wlist, wlist_len, &wfds);
    set_fds(&nfds, xlist, xlist_len, &xfds);

    // call select
    nfds = select(nfds+1, &rfds, &wfds, &xfds, &tv);

    // if any of the read sockets is closed, we add it to the read fd set,
    // a subsequent call to recv() returns 0. This behavior is consistent with BSD.
    for (int i=0; i<rlist_len; i++) {
        socket_t *s = rlist[i];
        if (wlan_get_fd_state(s->fd)) {
            FD_SET(s->fd, &rfds);
            nfds = max(nfds, s->fd);
        }
    }

    // return value; a tuple of 3 lists
    mp_obj_t fds[3] = {
        mp_obj_new_list(0, NULL),
        mp_obj_new_list(0, NULL),
        mp_obj_new_list(0, NULL)
    };

    // On success, select() returns the number of file descriptors contained
    // in the three returned descriptor sets which may be zero if the timeout
    // expires before anything interesting happens, -1 is returned on error.
    if (nfds == -1) {   // select failed
        nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "select failed"));
    } else if (nfds) {  // an fd is ready
        get_fds(rlist, rlist_len, fds[0], &rfds);
        get_fds(wlist, wlist_len, fds[1], &wfds);
        get_fds(xlist, xlist_len, fds[2], &xfds);
    } // select timedout

    return mp_obj_new_tuple(3, fds);
}
Example #2
0
int _socket_getaddrinfo(const mp_obj_t addrtuple, struct addrinfo **resp) {
    mp_uint_t len = 0;
    mp_obj_t *elem;
    mp_obj_get_array(addrtuple, &len, &elem);
    if (len != 2) return -1;
    return _socket_getaddrinfo2(elem[0], elem[1], resp);
}
Example #3
0
/// \method alarm_datetime([datetimetuple,alarmMask])
STATIC mp_obj_t rtc_alarm_datetime(mp_uint_t n_args, const mp_obj_t *args) {
    //pyb_rtc_obj_t *self = args[0];

    if (n_args == 1) {
        // get
        uint32_t yr;
		uint32_t mon;
		uint32_t day;
		uint32_t wd;
		uint32_t hr;
		uint32_t min;
		uint32_t sec; 
		
		mp_obj_t tuple[7];
		mp_hal_getAlarmTimeRTC(&hr, &min, &sec, &day, &mon, &yr, &wd);
		
		tuple[0] = MP_OBJ_NEW_SMALL_INT(yr);
		tuple[1] = MP_OBJ_NEW_SMALL_INT(mon);
		tuple[2] = MP_OBJ_NEW_SMALL_INT(day);
		tuple[3] = MP_OBJ_NEW_SMALL_INT(wd);
		tuple[4] = MP_OBJ_NEW_SMALL_INT(hr);
		tuple[5] = MP_OBJ_NEW_SMALL_INT(min);
		tuple[6] = MP_OBJ_NEW_SMALL_INT(sec);
					
		return mp_obj_new_tuple(7,tuple);
		
    } else if(n_args == 3) {
        // set 
        //(year, month, day, weekday, hours, minutes, seconds)
		mp_buffer_info_t bufinfo;
		mp_obj_t *items;
		mp_obj_get_array(args[1], &bufinfo.len, &items);
		uint32_t alarmMask = mp_obj_get_int(args[2]);
		
		if(bufinfo.len==7)
		{
			uint32_t yr = mp_obj_get_int(items[0]);
			uint32_t mon = mp_obj_get_int(items[1]);
			uint32_t day = mp_obj_get_int(items[2]);
			uint32_t wd = mp_obj_get_int(items[3]);
			uint32_t hr = mp_obj_get_int(items[4]);
			uint32_t min = mp_obj_get_int(items[5]);
			uint32_t sec = mp_obj_get_int(items[6]);					
			mp_hal_setAlarmTimeRTC(hr,min,sec,day,mon,yr,wd,alarmMask);
			
		}
		else{
			nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "7 arguments are required in tuple"));
		}
		
        return mp_const_none;	
    }
    else {
		nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "2 arguments are required"));
	}
    
}
Example #4
0
STATIC mp_obj_t mod_thread_start_new_thread(size_t n_args, const mp_obj_t *args) {
    // This structure holds the Python function and arguments for thread entry.
    // We copy all arguments into this structure to keep ownership of them.
    // We must be very careful about root pointers because this pointer may
    // disappear from our address space before the thread is created.
    thread_entry_args_t *th_args;

    // get positional arguments
    size_t pos_args_len;
    mp_obj_t *pos_args_items;
    mp_obj_get_array(args[1], &pos_args_len, &pos_args_items);

    // check for keyword arguments
    if (n_args == 2) {
        // just position arguments
        th_args = m_new_obj_var(thread_entry_args_t, mp_obj_t, pos_args_len);
        th_args->n_kw = 0;
    } else {
        // positional and keyword arguments
        if (mp_obj_get_type(args[2]) != &mp_type_dict) {
            mp_raise_TypeError("expecting a dict for keyword args");
        }
        mp_map_t *map = &((mp_obj_dict_t*)MP_OBJ_TO_PTR(args[2]))->map;
        th_args = m_new_obj_var(thread_entry_args_t, mp_obj_t, pos_args_len + 2 * map->used);
        th_args->n_kw = map->used;
        // copy across the keyword arguments
        for (size_t i = 0, n = pos_args_len; i < map->alloc; ++i) {
            if (mp_map_slot_is_filled(map, i)) {
                th_args->args[n++] = map->table[i].key;
                th_args->args[n++] = map->table[i].value;
            }
        }
    }

    // copy agross the positional arguments
    th_args->n_args = pos_args_len;
    memcpy(th_args->args, pos_args_items, pos_args_len * sizeof(mp_obj_t));

    // pass our locals and globals into the new thread
    th_args->dict_locals = mp_locals_get();
    th_args->dict_globals = mp_globals_get();

    // set the stack size to use
    th_args->stack_size = thread_stack_size;

    // set the function for thread entry
    th_args->fun = args[0];

    // spawn the thread!
    mp_thread_create(thread_entry, th_args, &th_args->stack_size);

    return mp_const_none;
}
Example #5
0
STATIC mp_obj_t new_namedtuple_type(mp_obj_t name_in, mp_obj_t fields_in) {
    qstr name = mp_obj_str_get_qstr(name_in);
    mp_uint_t n_fields;
    mp_obj_t *fields;
    #if MICROPY_CPYTHON_COMPAT
    if (MP_OBJ_IS_STR(fields_in)) {
        fields_in = mp_obj_str_split(1, &fields_in);
    }
    #endif
    mp_obj_get_array(fields_in, &n_fields, &fields);
    return mp_obj_new_namedtuple_type(name, n_fields, fields);
}
Example #6
0
// note: returned value in *items may point to the interior of a GC block
void mp_obj_get_array_fixed_n(mp_obj_t o, size_t len, mp_obj_t **items) {
    size_t seq_len;
    mp_obj_get_array(o, &seq_len, items);
    if (seq_len != len) {
        if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
            mp_raise_ValueError("tuple/list has wrong length");
        } else {
            nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
                "requested length %d but object has length %d", (int)len, (int)seq_len));
        }
    }
}
Example #7
0
STATIC mp_obj_t time_mktime(mp_obj_t tuple) {
    mp_uint_t len;
    mp_obj_t *elem;

    mp_obj_get_array(tuple, &len, &elem);

    // localtime generates a tuple of len 8. CPython uses 9, so we accept both.
    if (len < 8 || len > 9) {
        nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, mpexception_num_type_invalid_arguments));
    }

    return mp_obj_new_int_from_uint(timeutils_mktime(mp_obj_get_int(elem[0]), mp_obj_get_int(elem[1]), mp_obj_get_int(elem[2]),
                                                     mp_obj_get_int(elem[3]), mp_obj_get_int(elem[4]), mp_obj_get_int(elem[5])));
}
Example #8
0
/// \function mktime()
/// This is inverse function of localtime. It's argument is a full 8-tuple
/// which expresses a time as per localtime. It returns an integer which is
/// the number of seconds since Jan 1, 2000.
STATIC mp_obj_t time_mktime(mp_obj_t tuple) {
    size_t len;
    mp_obj_t *elem;
    mp_obj_get_array(tuple, &len, &elem);

    // localtime generates a tuple of len 8. CPython uses 9, so we accept both.
    if (len < 8 || len > 9) {
        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "mktime needs a tuple of length 8 or 9 (%d given)", len));
    }

    return mp_obj_new_int_from_uint(timeutils_mktime(mp_obj_get_int(elem[0]),
            mp_obj_get_int(elem[1]), mp_obj_get_int(elem[2]), mp_obj_get_int(elem[3]),
            mp_obj_get_int(elem[4]), mp_obj_get_int(elem[5])));
}
Example #9
0
/// \function select(rlist, wlist, xlist[, timeout])
STATIC mp_obj_t select_select(uint n_args, const mp_obj_t *args) {
    // get array data from tuple/list arguments
    mp_uint_t rwx_len[3];
    mp_obj_t *r_array, *w_array, *x_array;
    mp_obj_get_array(args[0], &rwx_len[0], &r_array);
    mp_obj_get_array(args[1], &rwx_len[1], &w_array);
    mp_obj_get_array(args[2], &rwx_len[2], &x_array);

    // get timeout
    mp_uint_t timeout = -1;
    if (n_args == 4) {
        if (args[3] != mp_const_none) {
            #if MICROPY_PY_BUILTINS_FLOAT
            float timeout_f = mp_obj_get_float(args[3]);
            if (timeout_f >= 0) {
                timeout = (mp_uint_t)(timeout_f * 1000);
            }
            #else
            timeout = mp_obj_get_int(args[3]) * 1000;
            #endif
        }
    }

    // merge separate lists and get the ioctl function for each object
    mp_map_t poll_map;
    mp_map_init(&poll_map, rwx_len[0] + rwx_len[1] + rwx_len[2]);
    poll_map_add(&poll_map, r_array, rwx_len[0], MP_IOCTL_POLL_RD, true);
    poll_map_add(&poll_map, w_array, rwx_len[1], MP_IOCTL_POLL_WR, true);
    poll_map_add(&poll_map, x_array, rwx_len[2], MP_IOCTL_POLL_ERR | MP_IOCTL_POLL_HUP, true);

    mp_uint_t start_tick = HAL_GetTick();
    rwx_len[0] = rwx_len[1] = rwx_len[2] = 0;
    for (;;) {
        // poll the objects
        mp_uint_t n_ready = poll_map_poll(&poll_map, rwx_len);

        if (n_ready > 0 || (timeout != -1 && HAL_GetTick() - start_tick >= timeout)) {
            // one or more objects are ready, or we had a timeout
            mp_obj_t list_array[3];
            list_array[0] = mp_obj_new_list(rwx_len[0], NULL);
            list_array[1] = mp_obj_new_list(rwx_len[1], NULL);
            list_array[2] = mp_obj_new_list(rwx_len[2], NULL);
            rwx_len[0] = rwx_len[1] = rwx_len[2] = 0;
            for (mp_uint_t i = 0; i < poll_map.alloc; ++i) {
                if (!MP_MAP_SLOT_IS_FILLED(&poll_map, i)) {
                    continue;
                }
                poll_obj_t *poll_obj = (poll_obj_t*)poll_map.table[i].value;
                if (poll_obj->flags_ret & MP_IOCTL_POLL_RD) {
                    ((mp_obj_list_t*)list_array[0])->items[rwx_len[0]++] = poll_obj->obj;
                }
                if (poll_obj->flags_ret & MP_IOCTL_POLL_WR) {
                    ((mp_obj_list_t*)list_array[1])->items[rwx_len[1]++] = poll_obj->obj;
                }
                if ((poll_obj->flags_ret & ~(MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR)) != 0) {
                    ((mp_obj_list_t*)list_array[2])->items[rwx_len[2]++] = poll_obj->obj;
                }
            }
            mp_map_deinit(&poll_map);
            return mp_obj_new_tuple(3, list_array);
        }
        __WFI();
    }
}
Example #10
0
static mp_obj_t py_image_threshold(mp_obj_t image_obj, mp_obj_t color_list_obj, mp_obj_t threshold)
{
    color_t *color;
    image_t *image;

    /* sanity checks */
    PY_ASSERT_TRUE(sensor.pixformat == PIXFORMAT_RGB565);
    PY_ASSERT_TRUE(sensor.framesize <= FRAMESIZE_QCIF);

    /* read arguments */
    image = py_image_cobj(image_obj);
    int thresh = mp_obj_get_int(threshold);

    /* returned image */
    image_t bimage = {
        .w=image->w,
        .h=image->h,
        .bpp=1,
        .pixels=image->data+(image->w*image->h*image->bpp)
    };

    /* copy color list */
    uint len;
    mp_obj_t *color_arr;
    mp_obj_get_array(color_list_obj, &len, &color_arr);

    color = xalloc(len*sizeof*color);

    for (int i=0; i<len; i++) {
        mp_obj_t *color_obj;
        mp_obj_get_array_fixed_n(color_arr[i], 3, &color_obj);
        color[i].r = mp_obj_get_int(color_obj[0]);
        color[i].g = mp_obj_get_int(color_obj[1]);
        color[i].b = mp_obj_get_int(color_obj[2]);
    }

    /* Threshold image using reference color */
    imlib_threshold(image, &bimage, color, len, thresh);

    return py_image_from_struct(&bimage);
}

static mp_obj_t py_image_rainbow(mp_obj_t src_image_obj)
{
    image_t *src_image = NULL;

    /* get C image pointer */
    src_image = py_image_cobj(src_image_obj);
    /* sanity checks */
    PY_ASSERT_TRUE(src_image->bpp==1);

    image_t dst_image = {
        .w=src_image->w,
        .h=src_image->h,
        .bpp=2,
        .pixels=xalloc(src_image->w*src_image->h*2)
    };

    imlib_rainbow(src_image, &dst_image);
    *src_image = dst_image;
    return src_image_obj;
}

static mp_obj_t py_image_compress(mp_obj_t image_obj, mp_obj_t quality)
{
    image_t *image = py_image_cobj(image_obj);

    image_t cimage = {
        .w=image->w,
        .h=image->h,
        .bpp=0,
        .pixels= NULL
    };

    jpeg_compress(image, &cimage, mp_obj_get_int(quality));
    return py_image_from_struct(&cimage);
}

static mp_obj_t py_image_draw_line(mp_obj_t image_obj, mp_obj_t line_obj)
{
    /* get image pointer */
    struct image *image;
    image = py_image_cobj(image_obj);

    mp_obj_t *array;
    mp_obj_get_array_fixed_n(line_obj, 4, &array);
    int x0 = mp_obj_get_int(array[0]);
    int y0 = mp_obj_get_int(array[1]);
    int x1 = mp_obj_get_int(array[2]);
    int y1 = mp_obj_get_int(array[3]);

    imlib_draw_line(image, x0, y0, x1, y1);
    return mp_const_none;
}

static mp_obj_t py_image_draw_circle(mp_obj_t image_obj, mp_obj_t c_obj, mp_obj_t r_obj)
{
    int cx, cy, r;
    mp_obj_t *array;

    struct image *image;
    color_t c = {.r=0xFF, .g=0xFF, .b=0xFF};

    /* get image pointer */
    image = py_image_cobj(image_obj);

    /* center */
    mp_obj_get_array_fixed_n(c_obj, 2, &array);
    cx = mp_obj_get_int(array[0]);
    cy = mp_obj_get_int(array[1]);

    /* radius */
    r = mp_obj_get_int(r_obj);
    imlib_draw_circle(image, cx, cy, r, &c);
    return mp_const_none;
}

static mp_obj_t py_image_draw_string(uint n_args, const mp_obj_t *args)
{
    int x = mp_obj_get_int(args[1]);
    int y = mp_obj_get_int(args[2]);
    image_t *image =py_image_cobj(args[0]);
    const char *str = mp_obj_str_get_str(args[3]);
    color_t c = {.r=0xFF, .g=0xFF, .b=0xFF};

    if (n_args == 5) {
        // get color
        mp_obj_t *array;
        mp_obj_get_array_fixed_n(args[4], 3, &array);
        c.r = mp_obj_get_int(array[0]);
        c.g = mp_obj_get_int(array[1]);
        c.b = mp_obj_get_int(array[2]);
    }
    imlib_draw_string(image, x, y, str, &c);
    return mp_const_none;
}


static mp_obj_t py_image_erode(mp_obj_t image_obj, mp_obj_t ksize_obj)
{
    image_t *image = NULL;
    image = py_image_cobj(image_obj);

    /* sanity checks */
    PY_ASSERT_TRUE(image->bpp==1);

    imlib_erode(image, mp_obj_get_int(ksize_obj));
    return mp_const_none;
}
Example #11
0
STATIC mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
    if (value == MP_OBJ_NULL) {
        // delete
#if MICROPY_PY_BUILTINS_SLICE
        if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) {
            mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in);
            mp_bound_slice_t slice;
            if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice)) {
                mp_raise_NotImplementedError(NULL);
            }

            mp_int_t len_adj = slice.start - slice.stop;
            //printf("Len adj: %d\n", len_adj);
            assert(len_adj <= 0);
            mp_seq_replace_slice_no_grow(self->items, self->len, slice.start, slice.stop, self->items/*NULL*/, 0, sizeof(*self->items));
            // Clear "freed" elements at the end of list
            mp_seq_clear(self->items, self->len + len_adj, self->len, sizeof(*self->items));
            self->len += len_adj;
            return mp_const_none;
        }
#endif
        mp_obj_t args[2] = {self_in, index};
        list_pop(2, args);
        return mp_const_none;
    } else if (value == MP_OBJ_SENTINEL) {
        // load
        mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in);
#if MICROPY_PY_BUILTINS_SLICE
        if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) {
            mp_bound_slice_t slice;
            if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice)) {
                return mp_seq_extract_slice(self->len, self->items, &slice);
            }
            mp_obj_list_t *res = list_new(slice.stop - slice.start);
            mp_seq_copy(res->items, self->items + slice.start, res->len, mp_obj_t);
            return MP_OBJ_FROM_PTR(res);
        }
#endif
        size_t index_val = mp_get_index(self->base.type, self->len, index, false);
        return self->items[index_val];
    } else {
#if MICROPY_PY_BUILTINS_SLICE
        if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) {
            mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in);
            size_t value_len; mp_obj_t *value_items;
            mp_obj_get_array(value, &value_len, &value_items);
            mp_bound_slice_t slice_out;
            if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice_out)) {
                mp_raise_NotImplementedError(NULL);
            }
            mp_int_t len_adj = value_len - (slice_out.stop - slice_out.start);
            //printf("Len adj: %d\n", len_adj);
            if (len_adj > 0) {
                if (self->len + len_adj > self->alloc) {
                    // TODO: Might optimize memory copies here by checking if block can
                    // be grown inplace or not
                    self->items = m_renew(mp_obj_t, self->items, self->alloc, self->len + len_adj);
                    self->alloc = self->len + len_adj;
                }
                mp_seq_replace_slice_grow_inplace(self->items, self->len,
                    slice_out.start, slice_out.stop, value_items, value_len, len_adj, sizeof(*self->items));
            } else {
                mp_seq_replace_slice_no_grow(self->items, self->len,
                    slice_out.start, slice_out.stop, value_items, value_len, sizeof(*self->items));
                // Clear "freed" elements at the end of list
                mp_seq_clear(self->items, self->len + len_adj, self->len, sizeof(*self->items));
                // TODO: apply allocation policy re: alloc_size
            }
            self->len += len_adj;
            return mp_const_none;
        }
#endif
        mp_obj_list_store(self_in, index, value);
        return mp_const_none;
    }
}
Example #12
0
static mp_obj_t py_image_threshold(mp_obj_t image_obj, mp_obj_t color_list_obj, mp_obj_t threshold)
{
    color_t *color;
    image_t *image;

    /* sanity checks */
    PY_ASSERT_TRUE_MSG(sensor.pixformat == PIXFORMAT_RGB565,
            "This function is only supported on RGB565 images");

    PY_ASSERT_TRUE_MSG(sensor.framesize <= OMV_MAX_BLOB_FRAME,
            "This function is only supported on "OMV_MAX_BLOB_FRAME_STR" and smaller frames");


    /* read arguments */
    image = py_image_cobj(image_obj);
    int thresh = mp_obj_get_int(threshold);

    /* returned image */
    image_t bimage = {
        .w=image->w,
        .h=image->h,
        .bpp=1,
        .pixels=image->data+(image->w*image->h*image->bpp)
    };

    /* copy color list */
    uint len;
    mp_obj_t *color_arr;
    mp_obj_get_array(color_list_obj, &len, &color_arr);

    color = xalloc(len*sizeof*color);

    for (int i=0; i<len; i++) {
        mp_obj_t *color_obj;
        mp_obj_get_array_fixed_n(color_arr[i], 3, &color_obj);
        color[i].r = mp_obj_get_int(color_obj[0]);
        color[i].g = mp_obj_get_int(color_obj[1]);
        color[i].b = mp_obj_get_int(color_obj[2]);
    }

    /* Threshold image using reference color */
    imlib_threshold(image, &bimage, color, len, thresh);

    return py_image_from_struct(&bimage);
}

static mp_obj_t py_image_rainbow(mp_obj_t src_image_obj)
{
    image_t *src_image = NULL;

    /* get C image pointer */
    src_image = py_image_cobj(src_image_obj);

    /* sanity checks */
    PY_ASSERT_TRUE_MSG(src_image->bpp == 1,
            "This function is only supported on GRAYSCALE images");

    image_t dst_image = {
        .w=src_image->w,
        .h=src_image->h,
        .bpp=2,
        .pixels=xalloc(src_image->w*src_image->h*2)
    };

    imlib_rainbow(src_image, &dst_image);
    *src_image = dst_image;
    return src_image_obj;
}

static mp_obj_t py_image_compress(mp_obj_t image_obj, mp_obj_t quality)
{
    image_t *image = py_image_cobj(image_obj);

    image_t cimage = {
        .w=image->w,
        .h=image->h,
        .bpp = JPEG_INIT_BUF,
        .pixels = xalloc(JPEG_INIT_BUF)
    };

    jpeg_compress(image, &cimage, mp_obj_get_int(quality));

    return py_image_from_struct(&cimage);
}
Example #13
0
/// \function mktime()
/// This is inverse function of localtime. It's argument is a full 8-tuple
/// which expresses a time as per localtime. It returns an integer which is
/// the number of seconds since Jan 1, 2000.
STATIC mp_obj_t time_mktime(mp_obj_t tuple) {

    mp_uint_t len;
    mp_obj_t *elem;

    mp_obj_get_array(tuple, &len, &elem);

    // localtime generates a tuple of len 8. CPython uses 9, so we accept both.
    if (len < 8 || len > 9) {
        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "mktime needs a tuple of length 8 or 9 (%d given)", len));
    }

    mp_int_t year    = mp_obj_get_int(elem[0]);
    mp_int_t month   = mp_obj_get_int(elem[1]);
    mp_int_t mday    = mp_obj_get_int(elem[2]);
    mp_int_t hours   = mp_obj_get_int(elem[3]);
    mp_int_t minutes = mp_obj_get_int(elem[4]);
    mp_int_t seconds = mp_obj_get_int(elem[5]);

    // Normalise the tuple. This allows things like:
    //
    // tm_tomorrow = list(time.localtime())
    // tm_tomorrow[2] += 1 # Adds 1 to mday
    // tomorrow = time.mktime(tm_tommorrow)
    //
    // And not have to worry about all the weird overflows.
    //
    // You can subtract dates/times this way as well.

    minutes += seconds / 60;
    if ((seconds = seconds % 60) < 0) {
        seconds += 60;
        minutes--;
    }

    hours += minutes / 60;
    if ((minutes = minutes % 60) < 0) {
        minutes += 60;
        hours--;
    }

    mday += hours / 24;
    if ((hours = hours % 24) < 0) {
        hours += 24;
        mday--;
    }

    month--; // make month zero based
    year += month / 12;
    if ((month = month % 12) < 0) {
        month += 12;
        year--;
    }
    month++; // back to one based

    while (mday < 1) {
        if (--month == 0) {
            month = 12;
            year--;
        }
        mday += mod_time_days_in_month(year, month);
    }
    while (mday > mod_time_days_in_month(year, month)) {
        mday -= mod_time_days_in_month(year, month);
        if (++month == 13) {
            month = 1;
            year++;
        }
    }
    return mp_obj_new_int_from_uint(mod_time_seconds_since_2000(year, month, mday, hours, minutes, seconds));
}