diff --git a/stm/Makefile b/stm/Makefile
index f48d6f55c970abb44795635be4da7f93df46d4cb..3d2dc4230208fdc00f409eb46305943026ba5244 100644
--- a/stm/Makefile
+++ b/stm/Makefile
@@ -54,6 +54,9 @@ PY_O = \
 	emitnthumb.o \
 	emitinlinethumb.o \
 	runtime.o \
+	map.o \
+	obj.o \
+	builtin.o \
 	vm.o \
 	repl.o \
 
diff --git a/stm/main.c b/stm/main.c
index e1c8df0508026e60c0597bb715d00ebb432a502b..fb8ef28b80225ad321a4c6c4b12c92a336d5d5f3 100644
--- a/stm/main.c
+++ b/stm/main.c
@@ -122,6 +122,7 @@ void __fatal_error(const char *msg) {
 #include "parse.h"
 #include "compile.h"
 #include "runtime.h"
+#include "obj.h"
 #include "repl.h"
 
 static qstr pyb_config_source_dir = 0;
@@ -153,9 +154,11 @@ py_obj_t pyb_led(py_obj_t state) {
     return state;
 }
 
-py_obj_t py_obj_new_user(const py_user_info_t *info, machine_uint_t data1, machine_uint_t data2);
-void py_user_get_data(py_obj_t o, machine_uint_t *data1, machine_uint_t *data2);
-void py_user_set_data(py_obj_t o, machine_uint_t data1, machine_uint_t data2);
+void led_obj_print(py_obj_t self) {
+    machine_uint_t led_id;
+    py_user_get_data(self, &led_id, NULL);
+    printf("<LED %lu>", led_id);
+}
 
 py_obj_t led_obj_on(py_obj_t self) {
     machine_uint_t led_id;
@@ -179,7 +182,7 @@ py_obj_t led_obj_off(py_obj_t self) {
 
 const py_user_info_t led_obj_info = {
     "Led",
-    NULL, // print
+    led_obj_print,
     {
         {"on", 0, led_obj_on},
         {"off", 0, led_obj_off},
@@ -199,6 +202,12 @@ py_obj_t pyb_sw(void) {
     }
 }
 
+void servo_obj_print(py_obj_t self) {
+    machine_uint_t servo_id;
+    py_user_get_data(self, &servo_id, NULL);
+    printf("<Servo %lu>", servo_id);
+}
+
 py_obj_t servo_obj_angle(py_obj_t self, py_obj_t angle) {
     machine_uint_t servo_id;
     py_user_get_data(self, &servo_id, NULL);
@@ -216,7 +225,7 @@ py_obj_t servo_obj_angle(py_obj_t self, py_obj_t angle) {
 
 const py_user_info_t servo_obj_info = {
     "Servo",
-    NULL, // print
+    servo_obj_print,
     {
         {"angle", 1, servo_obj_angle},
         {NULL, 0, NULL},
@@ -276,8 +285,40 @@ static const char fresh_boot_py[] =
 "#pyb.error_log('error.txt')\n"
 ;
 
+static const char fresh_main_py[] =
+"# main.py -- put your code here!\n"
+;
+
+static const char *help_text =
+"Welcome to Micro Python!\n\n"
+"This is a *very* early version of Micro Python and has minimal functionality.\n\n"
+"Specific commands for the board:\n"
+"    pyb.info()     -- print some general information\n"
+"    pyb.gc()       -- run the garbage collector\n"
+"    pyb.delay(<n>) -- wait for n milliseconds\n"
+"    pyb.Led(<n>)   -- create Led object for LED n (n=1,2)\n"
+"                      Led methods: on(), off()\n"
+"    pyb.Servo(<n>) -- create Servo object for servo n (n=1,2,3,4)\n"
+"                      Servo methods: angle(<x>)\n"
+"    pyb.switch()   -- return True/False if switch pressed or not\n"
+"    pyb.accel()    -- get accelerometer values\n"
+"    pyb.rand()     -- get a 16-bit random number\n"
+;
+
+// get some help about available functions
+static py_obj_t pyb_help(void) {
+    printf("%s", help_text);
+    return py_const_none;
+}
+
 // get lots of info about the board
 static py_obj_t pyb_info(void) {
+    // get and print unique id; 96 bits
+    {
+        byte *id = (byte*)0x1fff7a10;
+        printf("ID=%02x%02x%02x%02x:%02x%02x%02x%02x:%02x%02x%02x%02x\n", id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], id[9], id[10], id[11]);
+    }
+
     // get and print clock speeds
     // SYSCLK=168MHz, HCLK=168MHz, PCLK1=42MHz, PCLK2=84MHz
     {
@@ -478,8 +519,8 @@ int readline(vstr_t *line, const char *prompt) {
 }
 
 void do_repl(void) {
-    stdout_tx_str("Micro Python 0.5; STM32F405RG; PYBv2\r\n");
-    stdout_tx_str("Type \"help\" for more information.\r\n");
+    stdout_tx_str("Micro Python 0.1; STM32F405RG; PYBv3\r\n");
+    stdout_tx_str("Type \"help()\" for more information.\r\n");
 
     vstr_t line;
     vstr_init(&line);
@@ -523,8 +564,11 @@ void do_repl(void) {
                     if (nlr_push(&nlr) == 0) {
                         rt_call_function_0(module_fun);
                         nlr_pop();
-                        uint32_t ticks = sys_tick_counter - start; // TODO implement a function that does this properly
-                        //printf("(took %lu ms)\n", ticks);
+                        // optional timing
+                        if (0) {
+                            uint32_t ticks = sys_tick_counter - start; // TODO implement a function that does this properly
+                            printf("(took %lu ms)\n", ticks);
+                        }
                     } else {
                         // uncaught exception
                         py_obj_print((py_obj_t)nlr.ret_val);
@@ -592,12 +636,16 @@ void gc_collect(void) {
     gc_collect_root((void**)HEAP_END, (RAM_END - HEAP_END) / 4); // will trace regs since they now live in this function on the stack
     gc_collect_end();
     uint32_t ticks = sys_tick_counter - start; // TODO implement a function that does this properly
-    gc_info_t info;
-    gc_info(&info);
-    printf("GC@%lu %lums\n", start, ticks);
-    printf(" %lu total\n", info.total);
-    printf(" %lu : %lu\n", info.used, info.free);
-    printf(" 1=%lu 2=%lu m=%lu\n", info.num_1block, info.num_2block, info.max_block);
+
+    if (0) {
+        // print GC info
+        gc_info_t info;
+        gc_info(&info);
+        printf("GC@%lu %lums\n", start, ticks);
+        printf(" %lu total\n", info.total);
+        printf(" %lu : %lu\n", info.used, info.free);
+        printf(" 1=%lu 2=%lu m=%lu\n", info.num_1block, info.num_2block, info.max_block);
+    }
 }
 
 py_obj_t pyb_gc(void) {
@@ -951,13 +999,15 @@ soft_reset:
     timer_init();
 
     // RNG
-    if (0) {
+    if (1) {
         RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG, ENABLE);
         RNG_Cmd(ENABLE);
     }
 
     // add some functions to the python namespace
     {
+        rt_store_name(qstr_from_str_static("help"), rt_make_function_0(pyb_help));
+
         py_obj_t m = py_module_new();
         rt_store_attr(m, qstr_from_str_static("info"), rt_make_function_0(pyb_info));
         rt_store_attr(m, qstr_from_str_static("stop"), rt_make_function_0(pyb_stop));
@@ -968,16 +1018,16 @@ soft_reset:
         rt_store_attr(m, qstr_from_str_static("gc"), rt_make_function_0(pyb_gc));
         rt_store_attr(m, qstr_from_str_static("delay"), rt_make_function_1(pyb_delay));
         rt_store_attr(m, qstr_from_str_static("led"), rt_make_function_1(pyb_led));
-        rt_store_attr(m, qstr_from_str_static("sw"), rt_make_function_0(pyb_sw));
+        rt_store_attr(m, qstr_from_str_static("switch"), rt_make_function_0(pyb_sw));
         rt_store_attr(m, qstr_from_str_static("servo"), rt_make_function_2(pyb_servo_set));
         rt_store_attr(m, qstr_from_str_static("pwm"), rt_make_function_2(pyb_pwm_set));
-        rt_store_attr(m, qstr_from_str_static("mma"), rt_make_function_0(pyb_mma_read));
+        rt_store_attr(m, qstr_from_str_static("accel"), rt_make_function_0(pyb_mma_read));
         rt_store_attr(m, qstr_from_str_static("hid"), rt_make_function_1(pyb_hid_send_report));
         rt_store_attr(m, qstr_from_str_static("time"), rt_make_function_0(pyb_rtc_read));
         rt_store_attr(m, qstr_from_str_static("uout"), rt_make_function_1(pyb_usart_send));
         rt_store_attr(m, qstr_from_str_static("uin"), rt_make_function_0(pyb_usart_receive));
         rt_store_attr(m, qstr_from_str_static("ustat"), rt_make_function_0(pyb_usart_status));
-        rt_store_attr(m, qstr_from_str_static("rng"), rt_make_function_0(pyb_rng_get));
+        rt_store_attr(m, qstr_from_str_static("rand"), rt_make_function_0(pyb_rng_get));
         rt_store_attr(m, qstr_from_str_static("Led"), rt_make_function_1(pyb_Led));
         rt_store_attr(m, qstr_from_str_static("Servo"), rt_make_function_1(pyb_Servo));
         rt_store_name(qstr_from_str_static("pyb"), m);
@@ -1022,6 +1072,18 @@ soft_reset:
                 __fatal_error("could not create LFS");
             }
 
+            // create src directory
+            res = f_mkdir("0:/src");
+            // ignore result from mkdir
+
+            // create empty main.py
+            FIL fp;
+            f_open(&fp, "0:/src/main.py", FA_WRITE | FA_CREATE_ALWAYS);
+            UINT n;
+            f_write(&fp, fresh_main_py, sizeof(fresh_main_py) - 1 /* don't count null terminator */, &n);
+            // TODO check we could write n bytes
+            f_close(&fp);
+
             // keep LED on for at least 200ms
             sys_tick_wait_at_least(stc, 200);
             led_state(PYB_LED_R2, 0);
@@ -1343,9 +1405,9 @@ soft_reset:
     }
 
     // SD card testing
-    if (1) {
+    if (0) {
         extern void sdio_init(void);
-        //sdio_init();
+        sdio_init();
     }
 
     printf("PYB: sync filesystems\n");
diff --git a/stm/storage.c b/stm/storage.c
index ac0458d136970381d824cb686bf7461608a41432..576c83a560f6d55850f50db338dde0c202aec42b 100644
--- a/stm/storage.c
+++ b/stm/storage.c
@@ -152,6 +152,7 @@ bool storage_write_block(const uint8_t *src, uint32_t block) {
         uint8_t *dest = cache_get_addr_for_write(flash_addr);
         memcpy(dest, src, BLOCK_SIZE);
         sys_tick_counter_last_write = sys_tick_counter;
+        cache_flush(); // XXX hack for now so that the cache is always flushed
         return true;
 
     } else {