diff --git a/components/flow3r_bsp/flow3r_bsp_i2c.c b/components/flow3r_bsp/flow3r_bsp_i2c.c
index c4baae0aa17a5864e0b9fd66e00598593c3d381e..981b22ba80f178af31a598d93c299e400ecae616 100644
--- a/components/flow3r_bsp/flow3r_bsp_i2c.c
+++ b/components/flow3r_bsp/flow3r_bsp_i2c.c
@@ -5,6 +5,8 @@
 #include "freertos/semphr.h"
 #include "esp_log.h"
 
+#include <string.h>
+
 static SemaphoreHandle_t mutex;
 
 static const char *TAG = "flow3r-bsp-i2c";
@@ -74,7 +76,7 @@ void flow3r_bsp_i2c_init(void) {
     assert(i2c_param_config(I2C_NUM_0, &i2c_conf) == ESP_OK);
 	assert(i2c_driver_install(I2C_NUM_0, i2c_conf.mode, 0, 0, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED) == ESP_OK);
 
-	flow3r_bsp_i2c_scan();
+	flow3r_bsp_i2c_scan(NULL);
 }
 
 // Take I2C bus lock.
@@ -101,7 +103,10 @@ esp_err_t flow3r_bsp_i2c_write_read_device(uint8_t address, const uint8_t *write
 	return res;
 }
 
-void flow3r_bsp_i2c_scan(void) {
+void flow3r_bsp_i2c_scan(flow3r_bsp_i2c_scan_result_t *res) {
+	if (res != NULL) {
+		memset(res, 0, sizeof(flow3r_bsp_i2c_scan_result_t));
+	}
 	ESP_LOGI(TAG, "Scan: starting...");
 	for (uint8_t i = 1; i < 127; i++) {
 		i2c_cmd_handle_t cmd = i2c_cmd_link_create();
@@ -112,6 +117,11 @@ void flow3r_bsp_i2c_scan(void) {
         i2c_cmd_link_delete(cmd);
 		if (ret == ESP_OK) {
 			ESP_LOGI(TAG, "Scan: detected %02x", i);
+			if (res != NULL) {
+				size_t ix = i / 32;
+				size_t offs = i % 32;
+				res->res[ix] |= (1 << offs);
+			}
 		}
 	}
 	ESP_LOGI(TAG, "Scan: done.");
diff --git a/components/flow3r_bsp/flow3r_bsp_i2c.h b/components/flow3r_bsp/flow3r_bsp_i2c.h
index 49a14fdbae4395ae7f10a671902245a2afb43663..37d0c3b8453f618b78453ebe357d1d0c923c7f42 100644
--- a/components/flow3r_bsp/flow3r_bsp_i2c.h
+++ b/components/flow3r_bsp/flow3r_bsp_i2c.h
@@ -37,4 +37,8 @@ esp_err_t flow3r_bsp_i2c_write_to_device(flow3r_i2c_address address, const uint8
 // This can be called concurrently from different tassks.
 esp_err_t flow3r_bsp_i2c_write_read_device(flow3r_i2c_address address, const uint8_t *write_buffer, size_t write_size, uint8_t *read_buffer, size_t read_size, TickType_t ticks_to_wait);
 
-void flow3r_bsp_i2c_scan(void);
\ No newline at end of file
+typedef struct {
+	uint32_t res[4]; // Bitfield of addresses from 0 to 127.
+} flow3r_bsp_i2c_scan_result_t;
+
+void flow3r_bsp_i2c_scan(flow3r_bsp_i2c_scan_result_t *res);
\ No newline at end of file
diff --git a/usermodule/mp_hardware.c b/usermodule/mp_hardware.c
index 0f26f937b2e182312d4f1628063a9bd54588b648..cb5418254589df2c62d4c9b477edf6c75965d305 100644
--- a/usermodule/mp_hardware.c
+++ b/usermodule/mp_hardware.c
@@ -197,6 +197,23 @@ STATIC mp_obj_t mp_scope_draw(mp_obj_t ctx_in) {
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_scope_draw_obj, mp_scope_draw);
 
+STATIC mp_obj_t mp_i2c_scan(void) {
+    flow3r_bsp_i2c_scan_result_t scan;
+    flow3r_bsp_i2c_scan(&scan);
+
+	mp_obj_t res = mp_obj_new_list(0, NULL);
+    for (int i = 0; i < 127; i++) {
+        size_t ix = i / 32;
+        size_t offs = i % 32;
+        if (scan.res[ix] & (1 << offs)) {
+            mp_obj_list_append(res, mp_obj_new_int_from_uint(i));
+        }
+    }
+    return res;
+}
+
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_i2c_scan_obj, mp_i2c_scan);
+
 STATIC const mp_rom_map_elem_t mp_module_hardware_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_hardware) },
 
@@ -224,6 +241,7 @@ STATIC const mp_rom_map_elem_t mp_module_hardware_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR_display_set_backlight), MP_ROM_PTR(&mp_display_set_backlight_obj) },
     { MP_ROM_QSTR(MP_QSTR_version), MP_ROM_PTR(&mp_version_obj) },
     { MP_ROM_QSTR(MP_QSTR_get_ctx), MP_ROM_PTR(&mp_get_ctx_obj) },
+    { MP_ROM_QSTR(MP_QSTR_i2c_scan), MP_ROM_PTR(&mp_i2c_scan_obj) },
 
     { MP_ROM_QSTR(MP_QSTR_BUTTON_PRESSED_LEFT), MP_ROM_INT(st3m_tripos_left) },
     { MP_ROM_QSTR(MP_QSTR_BUTTON_PRESSED_RIGHT), MP_ROM_INT(st3m_tripos_right) },