diff --git a/esp8266/modpybhspi.c b/esp8266/modpybhspi.c
index f80fbae00ace44bd887702f960387a3eea85d138..c4d4dcee8d9525d4455d4703d6617a4959ec3e19 100644
--- a/esp8266/modpybhspi.c
+++ b/esp8266/modpybhspi.c
@@ -35,6 +35,7 @@
 #include "py/runtime.h"
 #include "py/stream.h"
 #include "py/mphal.h"
+#include "extmod/machine_spi.h"
 
 #include "hspi.h"
 
@@ -47,6 +48,58 @@ typedef struct _pyb_hspi_obj_t {
 } pyb_hspi_obj_t;
 
 
+STATIC void hspi_transfer(mp_obj_base_t *self_in, size_t src_len, const uint8_t *src_buf, size_t dest_len, uint8_t *dest_buf) {
+    (void)self_in;
+
+    if (dest_len == 0) {
+        // fast case when we only need to write data
+        size_t chunk_size = 1024;
+        size_t count = src_len / chunk_size;
+        size_t i = 0;
+        for (size_t j = 0; j < count; ++j) {
+            for (size_t k = 0; k < chunk_size; ++k) {
+                spi_tx8fast(HSPI, src_buf[i]);
+                ++i;
+            }
+            ets_loop_iter();
+        }
+        while (i < src_len) {
+            spi_tx8fast(HSPI, src_buf[i]);
+            ++i;
+        }
+    } else {
+        // we need to read and write data
+
+        // Process data in chunks, let the pending tasks run in between
+        size_t chunk_size = 1024; // TODO this should depend on baudrate
+        size_t count = dest_len / chunk_size;
+        size_t i = 0;
+        for (size_t j = 0; j < count; ++j) {
+            for (size_t k = 0; k < chunk_size; ++k) {
+                uint32_t data_out;
+                if (src_len == 1) {
+                    data_out = src_buf[0];
+                } else {
+                    data_out = src_buf[i];
+                }
+                dest_buf[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8, data_out, 8, 0);
+                ++i;
+            }
+            ets_loop_iter();
+        }
+        while (i < dest_len) {
+            uint32_t data_out;
+            if (src_len == 1) {
+                data_out = src_buf[0];
+            } else {
+                data_out = src_buf[i];
+            }
+            dest_buf[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8, data_out, 8, 0);
+            ++i;
+        }
+    }
+}
+
 /******************************************************************************/
 // MicroPython bindings for HSPI
 
@@ -126,133 +179,25 @@ STATIC mp_obj_t pyb_hspi_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_
 }
 MP_DEFINE_CONST_FUN_OBJ_KW(pyb_hspi_init_obj, 1, pyb_hspi_init);
 
-
-STATIC mp_obj_t pyb_hspi_read(size_t n_args, const mp_obj_t *args) {
-    vstr_t dest_buf;
-    vstr_init_len(&dest_buf, mp_obj_get_int(args[1]));
-    uint8_t write_byte = 0;
-    if (n_args == 3) {
-        write_byte = mp_obj_get_int(args[2]);
-    }
-    // Process data in chunks, let the pending tasks run in between
-    size_t chunk_size = 1024; // TODO this should depend on baudrate
-    size_t count = dest_buf.len / chunk_size;
-    size_t i = 0;
-    for (size_t j = 0; j < count; ++j) {
-        for (size_t k = 0; k < chunk_size; ++k) {
-            ((uint8_t*)dest_buf.buf)[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8,
-                (uint32_t)write_byte, 8, 0);
-            ++i;
-        }
-        ets_loop_iter();
-    }
-    while (i < dest_buf.len) {
-        ((uint8_t*)dest_buf.buf)[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8,
-            (uint32_t)write_byte, 8, 0);
-        ++i;
-    }
-    return mp_obj_new_str_from_vstr(&mp_type_bytes, &dest_buf);
-}
-MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_hspi_read_obj, 2, 3, pyb_hspi_read);
-
-
-STATIC mp_obj_t pyb_hspi_readinto(size_t n_args, const mp_obj_t *args) {
-    mp_buffer_info_t dest_buf;
-    mp_get_buffer_raise(args[1], &dest_buf, MP_BUFFER_WRITE);
-    uint8_t write_byte = 0;
-    if (n_args == 3) {
-        write_byte = mp_obj_get_int(args[2]);
-    }
-
-    size_t chunk_size = 1024;
-    size_t count = dest_buf.len / chunk_size;
-    size_t i = 0;
-    for (size_t j = 0; j < count; ++j) {
-        for (size_t k = 0; k < chunk_size; ++k) {
-            ((uint8_t*)dest_buf.buf)[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8,
-                (uint32_t)write_byte, 8, 0);
-            ++i;
-        }
-        ets_loop_iter();
-    }
-    while (i < dest_buf.len) {
-        ((uint8_t*)dest_buf.buf)[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8,
-            (uint32_t)write_byte, 8, 0);
-        ++i;
-    }
-    return mp_const_none;
-}
-MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_hspi_readinto_obj, 2, 3, pyb_hspi_readinto);
-
-
-STATIC mp_obj_t pyb_hspi_write(mp_obj_t self_in, mp_obj_t wr_buf_in) {
-    mp_buffer_info_t src_buf;
-    mp_get_buffer_raise(wr_buf_in, &src_buf, MP_BUFFER_READ);
-
-    size_t chunk_size = 1024;
-    size_t count = src_buf.len / chunk_size;
-    size_t i = 0;
-    for (size_t j = 0; j < count; ++j) {
-        for (size_t k = 0; k < chunk_size; ++k) {
-            spi_tx8fast(HSPI, ((const uint8_t*)src_buf.buf)[i]);
-            ++i;
-        }
-        ets_loop_iter();
-    }
-    while (i < src_buf.len) {
-        spi_tx8fast(HSPI, ((const uint8_t*)src_buf.buf)[i]);
-        ++i;
-    }
-    return mp_const_none;
-}
-MP_DEFINE_CONST_FUN_OBJ_2(pyb_hspi_write_obj, pyb_hspi_write);
-
-
-STATIC mp_obj_t pyb_hspi_write_readinto(mp_obj_t self_in, mp_obj_t wr_buf_in, mp_obj_t rd_buf_in) {
-    mp_buffer_info_t src_buf;
-    mp_get_buffer_raise(wr_buf_in, &src_buf, MP_BUFFER_READ);
-    mp_buffer_info_t dest_buf;
-    mp_get_buffer_raise(rd_buf_in, &dest_buf, MP_BUFFER_WRITE);
-    if (src_buf.len != dest_buf.len) {
-        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
-            "buffers must be the same length"));
-    }
-
-    size_t chunk_size = 1024;
-    size_t count = src_buf.len / chunk_size;
-    size_t i = 0;
-    for (size_t j = 0; j < count; ++j) {
-        for (size_t k = 0; k < chunk_size; ++k) {
-            ((uint8_t*)dest_buf.buf)[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8,
-                (uint32_t)(((const uint8_t*)src_buf.buf)[i]), 8, 0);
-            ++i;
-        }
-        ets_loop_iter();
-    }
-    while (i < src_buf.len) {
-        ((uint8_t*)dest_buf.buf)[i] = spi_transaction(HSPI, 0, 0, 0, 0, 8,
-            (uint32_t)(((const uint8_t*)src_buf.buf)[i]), 8, 0);
-        ++i;
-    }
-    return mp_const_none;
-}
-MP_DEFINE_CONST_FUN_OBJ_3(pyb_hspi_write_readinto_obj, pyb_hspi_write_readinto);
-
-
 STATIC const mp_rom_map_elem_t pyb_hspi_locals_dict_table[] = {
     { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_hspi_init_obj) },
-    { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&pyb_hspi_read_obj) },
-    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&pyb_hspi_readinto_obj) },
-    { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&pyb_hspi_write_obj) },
-    { MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&pyb_hspi_write_readinto_obj) },
+    { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_machine_spi_read_obj) },
+    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_machine_spi_readinto_obj) },
+    { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_machine_spi_write_obj) },
+    { MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&mp_machine_spi_write_readinto_obj) },
 };
 
 STATIC MP_DEFINE_CONST_DICT(pyb_hspi_locals_dict, pyb_hspi_locals_dict_table);
 
+STATIC const mp_machine_spi_p_t pyb_hspi_p = {
+    .transfer = hspi_transfer,
+};
+
 const mp_obj_type_t pyb_hspi_type = {
     { &mp_type_type },
     .name = MP_QSTR_HSPI,
     .print = pyb_hspi_print,
     .make_new = pyb_hspi_make_new,
+    .protocol = &pyb_hspi_p,
     .locals_dict = (mp_obj_dict_t*)&pyb_hspi_locals_dict,
 };