diff --git a/src/shared/radio/CC3135/CC3135.cpp b/src/shared/radio/CC3135/CC3135.cpp
index 5124be5ee064891a1cdb787e8da6e88bab6bd167..15680adf1f101411513e6758000405e7ccb6d5d5 100644
--- a/src/shared/radio/CC3135/CC3135.cpp
+++ b/src/shared/radio/CC3135/CC3135.cpp
@@ -25,6 +25,17 @@
 #include <kernel/scheduler/scheduler.h>
 #include <utils/Debug.h>
 
+#define TRY(expr)                                            \
+    do                                                       \
+    {                                                        \
+        Boardcore::CC3135::Error result = (expr);            \
+        if (result != Boardcore::CC3135::Error::NO_ERROR)    \
+        {                                                    \
+            TRACE("[cc3135] NWP error@ %s\n", __FUNCTION__); \
+            return result;                                   \
+        }                                                    \
+    } while (0)
+
 using namespace Boardcore::CC3135Defs;
 using namespace miosix;
 
@@ -33,20 +44,38 @@ namespace Boardcore
 
 CC3135::CC3135(std::unique_ptr<ICC3135Iface> &&iface) : iface(std::move(iface))
 {
-    // Start internal thread
-    this->start();
     irq_wait_thread = thread;
 }
 
-DeviceVersion CC3135::getVersion()
+CC3135::Error CC3135::init(bool wait_for_init)
 {
-    DeviceVersion result = {};
+    if (wait_for_init)
+    {
+        DeviceInitInfo init_info = {};
+        TRY(readPacketSync(OPCODE_DEVICE_INITCOMPLETE, Buffer::from(&init_info),
+                           Buffer::null()));
+
+        TRACE(
+            "[cc3135] Init completed:\n"
+            "- Status: %8x\n"
+            "- Chip Id: %lx\n"
+            "- More Data: %8x\n",
+            init_info.status, init_info.chip_id, init_info.more_data);
+    }
 
-    devigeGet(1, 12, Buffer::from(&result));
-    return result;
+    // Start internal thread
+    this->start();
+
+    return Error::NO_ERROR;
+}
+
+CC3135::Error CC3135::getVersion(DeviceVersion &version)
+{
+    version = {};
+    return devigeGet(1, 12, Buffer::from<DeviceVersion>(&version));
 }
 
-void CC3135::devigeGet(uint8_t set_id, uint8_t option, Buffer result)
+CC3135::Error CC3135::devigeGet(uint8_t set_id, uint8_t option, Buffer result)
 {
     DeviceSetGet tx_command  = {};
     tx_command.device_set_id = set_id;
@@ -54,21 +83,21 @@ void CC3135::devigeGet(uint8_t set_id, uint8_t option, Buffer result)
 
     DeviceSetGet rx_command = {};
 
-    inoutPacketSync(OPCODE_DEVICE_DEVICEGET, Buffer::from(&tx_command),
-                    Buffer::null(), OPCODE_DEVICE_DEVICEGETRESPONSE,
-                    Buffer::from(&rx_command), result);
+    return inoutPacketSync(OPCODE_DEVICE_DEVICEGET, Buffer::from(&tx_command),
+                           Buffer::null(), OPCODE_DEVICE_DEVICEGETRESPONSE,
+                           Buffer::from(&rx_command), result);
 }
 
-void CC3135::setMode(CC3135Defs::Mode mode)
+CC3135::Error CC3135::setMode(CC3135Defs::Mode mode)
 {
     WlanSetMode tx_command = {};
     tx_command.mode        = mode;
 
     BasicResponse rx_command = {};
 
-    inoutPacketSync(OPCODE_WLAN_SET_MODE, Buffer::from(&tx_command),
-                    Buffer::null(), OPCODE_WLAN_SET_MODE_RESPONSE,
-                    Buffer::from(&rx_command), Buffer::null());
+    return inoutPacketSync(OPCODE_WLAN_SET_MODE, Buffer::from(&tx_command),
+                           Buffer::null(), OPCODE_WLAN_SET_MODE_RESPONSE,
+                           Buffer::from(&rx_command), Buffer::null());
 }
 
 void CC3135::handleIrq()
@@ -136,28 +165,7 @@ void CC3135::defaultPacketHandler(CC3135Defs::ResponseHeader header)
 
     size_t len = header.inner.len;
 
-    switch (header.inner.opcode)
-    {
-        case OPCODE_DEVICE_INITCOMPLETE:
-        {
-            DeviceInitInfo result = {};
-            safeRead(len, Buffer::from(&result));
-
-            TRACE(
-                "[cc3135] Init completen:\n"
-                "- Status: %8x\n"
-                "- Chip Id: %lx\n"
-                "- More Data: %8x\n",
-                result.status, result.chip_id, result.more_data);
-            break;
-        }
-
-        default:
-            break;
-    }
-
     // Dummy read rest of the data
-    // TODO: Add async commands
     if (len > 0)
         dummyRead(len);
 }
@@ -165,7 +173,6 @@ void CC3135::defaultPacketHandler(CC3135Defs::ResponseHeader header)
 void CC3135::run()
 {
     // TODO: Implement a way to stop this thread
-
     while (true)
     {
         waitForIrq();
@@ -176,53 +183,68 @@ void CC3135::run()
             Lock<FastMutex> lock(iface_mutex);
 
             ResponseHeader header;
-            readHeader(&header);
+            Error result = readHeader(&header);
 
-            defaultPacketHandler(header);
+            if (result != Error::NO_ERROR)
+            {
+                TRACE("[cc3135] NWP error!\n");
+            }
+            else
+            {
+                defaultPacketHandler(header);
+            }
         }
     }
 }
 
-void CC3135::inoutPacketSync(CC3135Defs::OpCode tx_opcode,
-                             CC3135::Buffer tx_command,
-                             CC3135::Buffer tx_payload,
-                             CC3135Defs::OpCode rx_opcode,
-                             CC3135::Buffer rx_command,
-                             CC3135::Buffer rx_payload)
+CC3135::Error CC3135::inoutPacketSync(CC3135Defs::OpCode tx_opcode,
+                                      CC3135::Buffer tx_command,
+                                      CC3135::Buffer tx_payload,
+                                      CC3135Defs::OpCode rx_opcode,
+                                      CC3135::Buffer rx_command,
+                                      CC3135::Buffer rx_payload)
 {
     installAsServiceThread();
 
     // Lock the device interface
     Lock<FastMutex> lock(iface_mutex);
-    writePacket(tx_opcode, tx_command, tx_payload);
+    TRY(writePacket(tx_opcode, tx_command, tx_payload));
 
-    readPacket(rx_opcode, rx_command, rx_payload);
+    TRY(readPacket(rx_opcode, rx_command, rx_payload));
 
     restoreDefaultServiceThread();
+
+    return Error::NO_ERROR;
 }
 
-void CC3135::readPacketSync(CC3135Defs::OpCode opcode, CC3135::Buffer command,
-                            CC3135::Buffer payload)
+CC3135::Error CC3135::readPacketSync(CC3135Defs::OpCode opcode,
+                                     CC3135::Buffer command,
+                                     CC3135::Buffer payload)
 {
     installAsServiceThread();
 
     // Lock the device interface
     Lock<FastMutex> lock(iface_mutex);
-    readPacket(opcode, command, payload);
+    TRY(readPacket(opcode, command, payload));
 
     restoreDefaultServiceThread();
+
+    return Error::NO_ERROR;
 }
 
-void CC3135::writePacketSync(CC3135Defs::OpCode opcode, CC3135::Buffer command,
-                             CC3135::Buffer payload)
+CC3135::Error CC3135::writePacketSync(CC3135Defs::OpCode opcode,
+                                      CC3135::Buffer command,
+                                      CC3135::Buffer payload)
 {
     // Lock the device interface
     Lock<FastMutex> lock(iface_mutex);
-    writePacket(opcode, command, payload);
+    TRY(writePacket(opcode, command, payload));
+
+    return Error::NO_ERROR;
 }
 
-void CC3135::readPacket(OpCode opcode, CC3135::Buffer command,
-                        CC3135::Buffer payload)
+CC3135::Error CC3135::readPacket(OpCode opcode, CC3135::Buffer command,
+                                 CC3135::Buffer payload)
 {
     while (true)
     {
@@ -232,7 +254,7 @@ void CC3135::readPacket(OpCode opcode, CC3135::Buffer command,
         // Locking the interface is not needed
 
         ResponseHeader header = {};
-        readHeader(&header);
+        TRY(readHeader(&header));
 
         if (header.inner.opcode != opcode)
         {
@@ -253,25 +275,34 @@ void CC3135::readPacket(OpCode opcode, CC3135::Buffer command,
             break;
         }
     }
+
+    return Error::NO_ERROR;
 }
 
-void CC3135::writePacket(OpCode opcode, CC3135::Buffer command,
-                         CC3135::Buffer payload)
+CC3135::Error CC3135::writePacket(OpCode opcode, CC3135::Buffer command,
+                                  CC3135::Buffer payload)
 {
     RequestHeader header{opcode,
                          static_cast<uint16_t>(command.len + payload.len)};
 
-    writeHeader(&header);
+    TRY(writeHeader(&header));
 
     if (command.len > 0)
         iface->write(command.ptr, command.len);
 
     if (payload.len > 0)
         iface->write(payload.ptr, payload.len);
+
+    return Error::NO_ERROR;
 }
 
-void CC3135::readHeader(ResponseHeader *header)
+CC3135::Error CC3135::readHeader(ResponseHeader *header)
 {
+    // 20 kernel ticks, or 100ms
+    const int TIMEOUT = 2000;
+
+    long long start = getTick();
+
     // 1. Write CNYS pattern (only if SPI)
     if (iface->is_spi())
     {
@@ -293,8 +324,10 @@ void CC3135::readHeader(ResponseHeader *header)
     // 3. Scan for device SYNC
     while (true)
     {
-        // Reads in SPI are always more than 4 bytes
+        if ((getTick() - start) > TIMEOUT)
+            return Error::NWP_TIMEOUT;
 
+        // Reads in SPI are always more than 4 bytes
         bool found = false;
         int i;
         for (i = 0; i < 4; i++)
@@ -303,11 +336,6 @@ void CC3135::readHeader(ResponseHeader *header)
             uint32_t sync;
             memcpy(&sync, &buf[i], 4);
 
-            if (sync == 0x0a7b2d01)
-            {
-                // BRUH
-            }
-
             if (n2hSyncPatternMatch(sync, tx_seq_num))
             {
                 found = true;
@@ -334,6 +362,9 @@ void CC3135::readHeader(ResponseHeader *header)
     memcpy(&sync, &buf[0], 4);
     while (n2hSyncPatternMatch(sync, tx_seq_num))
     {
+        if ((getTick() - start) > TIMEOUT)
+            return Error::NWP_TIMEOUT;
+
         memmove(&buf[0], &buf[4], 4);
         iface->read(&buf[4], 4);
 
@@ -348,9 +379,11 @@ void CC3135::readHeader(ResponseHeader *header)
 
     // 6. Adjust for bigger response header
     header->inner.len -= sizeof(ResponseHeader) - sizeof(GenericHeader);
+
+    return Error::NO_ERROR;
 }
 
-void CC3135::writeHeader(RequestHeader *header)
+CC3135::Error CC3135::writeHeader(RequestHeader *header)
 {
     // 1. Write SYNC pattern
     if (iface->is_spi())
@@ -370,17 +403,25 @@ void CC3135::writeHeader(RequestHeader *header)
 
     // 2. Write body
     iface->write(reinterpret_cast<uint8_t *>(header), sizeof(RequestHeader));
+
+    return Error::NO_ERROR;
 }
 
 void CC3135::dummyRead(size_t n)
 {
-    uint8_t dummy[n];
-    iface->read(dummy, n);
+    // Avoid HUGE stack allocations
+    uint8_t dummy[256];
+
+    while (n > 0)
+    {
+        iface->read(dummy, std::min(n, sizeof(dummy)));
+        n -= std::min(n, sizeof(dummy));
+    }
 }
 
 void CC3135::safeRead(size_t &len, Buffer buffer)
 {
-    if (buffer.len > 0)
+    if (buffer.len > 0 && len > 0)
     {
         iface->read(buffer.ptr, std::min(len, buffer.len));
         len -= std::min(len, buffer.len);
diff --git a/src/shared/radio/CC3135/CC3135.h b/src/shared/radio/CC3135/CC3135.h
index 31d18cdaa3b486aee8fac758837a7da1bde890db..8a569d72efd2ebcc98cb84429ffacbc2cba9595c 100644
--- a/src/shared/radio/CC3135/CC3135.h
+++ b/src/shared/radio/CC3135/CC3135.h
@@ -43,13 +43,22 @@ namespace Boardcore
 class CC3135 : ActiveObject
 {
 public:
+    enum class Error
+    {
+        NO_ERROR,             //< No error occured.
+        NWP_TIMEOUT,          //< The NWP did not respond.
+        NWP_STATUS_NON_ZERO,  //< The NWD returned a non-zero status code.
+    };
+
     explicit CC3135(std::unique_ptr<ICC3135Iface> &&iface);
 
+    CC3135::Error init(bool wait_for_init);
+
     void handleIrq();
 
-    CC3135Defs::DeviceVersion getVersion();
+    CC3135::Error getVersion(CC3135Defs::DeviceVersion &version);
 
-    void setMode(CC3135Defs::Mode mode);
+    CC3135::Error setMode(CC3135Defs::Mode mode);
 
 private:
     //! Simple buffer for scatter/gather IO
@@ -72,7 +81,7 @@ private:
 
     void defaultPacketHandler(CC3135Defs::ResponseHeader header);
 
-    void devigeGet(uint8_t set_id, uint8_t option, Buffer result);
+    CC3135::Error devigeGet(uint8_t set_id, uint8_t option, Buffer result);
 
     // Functions dedicated to interrupt servicing
 
@@ -86,26 +95,29 @@ private:
     // Functions for high level IO
 
     //! Write a packet in output and wait for a packet in input
-    void inoutPacketSync(CC3135Defs::OpCode tx_opcode, Buffer tx_command,
-                         Buffer tx_payload, CC3135Defs::OpCode rx_opcode,
-                         Buffer rx_command, Buffer rx_payload);
+    CC3135::Error inoutPacketSync(CC3135Defs::OpCode tx_opcode,
+                                  Buffer tx_command, Buffer tx_payload,
+                                  CC3135Defs::OpCode rx_opcode,
+                                  Buffer rx_command, Buffer rx_payload);
     //! Read packet in input, with proper synchronization.
-    void readPacketSync(CC3135Defs::OpCode opcode, Buffer command,
-                        Buffer payload);
+    CC3135::Error readPacketSync(CC3135Defs::OpCode opcode, Buffer command,
+                                 Buffer payload);
     //! Write a packet in output, with proper synchronization.
-    void writePacketSync(CC3135Defs::OpCode opcode, Buffer command,
-                         Buffer payload);
+    CC3135::Error writePacketSync(CC3135Defs::OpCode opcode, Buffer command,
+                                  Buffer payload);
 
     // Functions for low level IO
 
     //! Read a single packet.
-    void readPacket(CC3135Defs::OpCode opcode, Buffer command, Buffer payload);
+    CC3135::Error readPacket(CC3135Defs::OpCode opcode, Buffer command,
+                             Buffer payload);
     //! Write a single packet.
-    void writePacket(CC3135Defs::OpCode opcode, Buffer command, Buffer payload);
+    CC3135::Error writePacket(CC3135Defs::OpCode opcode, Buffer command,
+                              Buffer payload);
     //! Read a packet header.
-    void readHeader(CC3135Defs::ResponseHeader *header);
+    CC3135::Error readHeader(CC3135Defs::ResponseHeader *header);
     //! Write a packet header.
-    void writeHeader(CC3135Defs::RequestHeader *header);
+    CC3135::Error writeHeader(CC3135Defs::RequestHeader *header);
 
     //! Read dummy n bytes.
     void dummyRead(size_t n);
diff --git a/src/tests/drivers/CC3135/test-cc3135.cpp b/src/tests/drivers/CC3135/test-cc3135.cpp
index 47e575d3c4bce62e6f42dadd116e4bd3c9a8a6a4..f3e2e034b3a3185bf2b3387b70db1f2e6287bd98 100644
--- a/src/tests/drivers/CC3135/test-cc3135.cpp
+++ b/src/tests/drivers/CC3135/test-cc3135.cpp
@@ -28,6 +28,14 @@
 using namespace Boardcore;
 using namespace miosix;
 
+#define CHECK(expr)                                       \
+    do                                                    \
+    {                                                     \
+        Boardcore::CC3135::Error result = (expr);         \
+        if (result != Boardcore::CC3135::Error::NO_ERROR) \
+            printf("[cc3135] NWP error!\n");              \
+    } while (0)
+
 /*
 Connection diagram:
 cc3135:CC_SPI_CS   -> stm32:pa1
@@ -170,6 +178,13 @@ int main()
 
     initBoard();
 
+#ifdef CC3135_HIB
+    // Reset CC3135
+    hib::low();
+    Thread::sleep(100);
+    hib::high();
+#endif
+
 #ifdef CC3135_SPI
     SPIBus bus(CC3135_SPI);
     GpioPin cs_pin = cs::getPin();
@@ -186,22 +201,29 @@ int main()
     std::unique_ptr<ICC3135Iface> iface(new CC3135Uart(CC3135_UART));
 #endif
 
+    Thread::sleep(5000);
+
     printf("[cc3135] Initializing...\n");
     cc3135 = new CC3135(std::move(iface));
-    printf("[cc3135] Initialization complete!\n");
 
-#ifdef CC3135_HIB
-    // Reset CC3135
-    hib::low();
-    Thread::sleep(100);
-    hib::high();
-#endif
+    cc3135->handleIrq();
+
+    if (cc3135->init(true) != CC3135::Error::NO_ERROR)
+    {
+        printf("[cc3135] Failed to start cc3135, retrying...\n");
+        Thread::sleep(2000);
+        return;
+
+        // miosix::reboot();
+    }
+
+    printf("[cc3135] Initialization complete!\n");
 
-    Thread::sleep(500);
-    // cc3135->setMode(CC3135Defs::ROLE_AP);
+    // CHECK(cc3135->setMode(CC3135Defs::ROLE_STA));
     // Thread::sleep(500);
 
-    auto version = cc3135->getVersion();
+    CC3135Defs::DeviceVersion version = {};
+    CHECK(cc3135->getVersion(version));
     printf(
         "[cc3135] Chip Id: %lx\n"
         "[cc3135] Fw version: %u.%u.%u.%u\n"
@@ -216,4 +238,6 @@ int main()
 
     while (true)
         ;
+    // Thread::sleep(3000);
+    // miosix::reboot();
 }