diff --git a/stmhal/led.c b/stmhal/led.c
index 80c1e145f149bfb5d4cfad240f40c19a13d6fc91..e9a97d1debd76bf349abb26eeadbc47a1b42c47e 100644
--- a/stmhal/led.c
+++ b/stmhal/led.c
@@ -1,10 +1,13 @@
 #include <stdio.h>
 #include <stm32f4xx_hal.h>
+#include "usbd_cdc_msc.h"
+#include "usbd_cdc_interface.h"
 
 #include "misc.h"
 #include "mpconfig.h"
 #include "qstr.h"
 #include "obj.h"
+#include "runtime.h"
 #include "led.h"
 #include "pin.h"
 #include "build/pins.h"
@@ -38,12 +41,43 @@ void led_init(void) {
         GPIO_InitStructure.Pin = gLed[led]->pin_mask;
         HAL_GPIO_Init(gLed[led]->gpio, &GPIO_InitStructure);
     }
+
+    // LED4 (blue) is on PB4 which is TIM3_CH1
+    // we use PWM on this channel to fade the LED
+
+    // GPIO configuration
+    GPIO_InitStructure.Pin = GPIO_PIN_4;
+    GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+    GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
+    GPIO_InitStructure.Pull = GPIO_NOPULL;
+    GPIO_InitStructure.Alternate = GPIO_AF2_TIM3;
+    HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+    // PWM mode configuration
+    TIM_OC_InitTypeDef oc_init;
+    oc_init.OCMode = TIM_OCMODE_PWM1;
+    oc_init.Pulse = 0; // off
+    oc_init.OCPolarity = TIM_OCPOLARITY_HIGH;
+    oc_init.OCFastMode = TIM_OCFAST_DISABLE;
+    HAL_TIM_PWM_ConfigChannel(&TIM3_Handle, &oc_init, TIM_CHANNEL_1);
+
+    // start PWM
+    TIM_CCxChannelCmd(TIM3, TIM_CHANNEL_1, TIM_CCx_ENABLE);
+    //HAL_TIM_PWM_Start(&USBD_CDC_TIM3_Handle, TIM_CHANNEL_1);
 }
 
 void led_state(pyb_led_t led, int state) {
     if (led < 1 || led > NUM_LEDS) {
         return;
     }
+    if (led == 4) {
+        if (state) {
+            TIM3->CCR1 = 0xffff;
+        } else {
+            TIM3->CCR1 = 0;
+        }
+        return;
+    }
     const pin_obj_t *led_pin = gLed[led - 1];
 //printf("led_state(%d,%d)\n", led, state);
     if (state == 0) {
@@ -73,6 +107,23 @@ void led_toggle(pyb_led_t led) {
     }
 }
 
+int led_get_state(pyb_led_t led) {
+    if (led < 1 || led > NUM_LEDS) {
+        return 0;
+    }
+    const pin_obj_t *led_pin = gLed[led - 1];
+    GPIO_TypeDef *gpio = led_pin->gpio;
+
+    // TODO convert high/low to on/off depending on board
+    if (gpio->ODR & led_pin->pin_mask) {
+        // pin is high
+        return 1;
+    } else {
+        // pin is low
+        return 0;
+    }
+}
+
 void led_debug(int n, int delay) {
     led_state(1, n & 1);
     led_state(2, n & 2);
@@ -112,25 +163,47 @@ mp_obj_t led_obj_toggle(mp_obj_t self_in) {
     return mp_const_none;
 }
 
-static MP_DEFINE_CONST_FUN_OBJ_1(led_obj_on_obj, led_obj_on);
-static MP_DEFINE_CONST_FUN_OBJ_1(led_obj_off_obj, led_obj_off);
-static MP_DEFINE_CONST_FUN_OBJ_1(led_obj_toggle_obj, led_obj_toggle);
+mp_obj_t led_obj_state(uint n_args, const mp_obj_t *args) {
+    pyb_led_obj_t *self = args[0];
+    if (n_args == 0) {
+        return MP_BOOL(led_get_state(self->led_id));
+    } else {
+        led_state(self->led_id, rt_is_true(args[1]));
+        return mp_const_none;
+    }
+}
+
+mp_obj_t led_obj_intensity(mp_obj_t self_in, mp_obj_t intensity) {
+    pyb_led_obj_t *self = self_in;
+    if (self->led_id == 4) {
+        TIM3->CCR1 = mp_obj_get_int(intensity);
+    }
+    return mp_const_none;
+}
+
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(led_obj_on_obj, led_obj_on);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(led_obj_off_obj, led_obj_off);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(led_obj_toggle_obj, led_obj_toggle);
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(led_obj_state_obj, 1, 2, led_obj_state);
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(led_obj_intensity_obj, led_obj_intensity);
 
-static const mp_method_t led_methods[] = {
+STATIC const mp_method_t led_methods[] = {
     { "on", &led_obj_on_obj },
     { "off", &led_obj_off_obj },
     { "toggle", &led_obj_toggle_obj },
+    { "state", &led_obj_state_obj },
+    { "intensity", &led_obj_intensity_obj },
     { NULL, NULL },
 };
 
-static const mp_obj_type_t led_obj_type = {
+STATIC const mp_obj_type_t led_obj_type = {
     { &mp_type_type },
     .name = MP_QSTR_Led,
     .print = led_obj_print,
     .methods = led_methods,
 };
 
-static mp_obj_t pyb_Led(mp_obj_t led_id) {
+STATIC mp_obj_t pyb_Led(mp_obj_t led_id) {
     pyb_led_obj_t *o = m_new_obj(pyb_led_obj_t);
     o->base.type = &led_obj_type;
     o->led_id = mp_obj_get_int(led_id);
diff --git a/stmhal/main.c b/stmhal/main.c
index 6511891a1ba821e4937cf0ee57c07b27d5ac90bd..9d882069784f7979e4c95550fcaf5c97c626be00 100644
--- a/stmhal/main.c
+++ b/stmhal/main.c
@@ -37,10 +37,10 @@
 #include "lcd.h"
 #include "accel.h"
 #include "servo.h"
+#include "pin.h"
 #if 0
 #include "timer.h"
 #include "pybwlan.h"
-#include "pin.h"
 #endif
 
 void SystemClock_Config(void);
@@ -266,9 +266,7 @@ soft_reset:
 #endif
 #endif
 
-#if 0
     pin_map_init();
-#endif
 
     // add some functions to the builtin Python namespace
     rt_store_name(MP_QSTR_help, rt_make_function_n(0, pyb_help));
diff --git a/stmhal/stm32f4xx_hal_msp.c b/stmhal/stm32f4xx_hal_msp.c
index c64f37795930d3c2930bec4fbdd133af46a9c53a..a8587c384a8b8eb47500b8d501260994294125a3 100644
--- a/stmhal/stm32f4xx_hal_msp.c
+++ b/stmhal/stm32f4xx_hal_msp.c
@@ -5,16 +5,7 @@
   * @version V1.0.1
   * @date    26-February-2014
   * @brief   HAL MSP module.
-  *         
-  @verbatim
- ===============================================================================
-                     ##### How to use this driver #####
- ===============================================================================
-    [..]
-    This file is generated automatically by MicroXplorer and eventually modified 
-    by the user
-
-  @endverbatim
+  *
   ******************************************************************************
   * @attention
   *
@@ -56,25 +47,7 @@
 #include "obj.h"
 #include "servo.h"
 
-/** @addtogroup STM32F4xx_HAL_Driver
-  * @{
-  */
-
-/** @defgroup HAL_MSP
-  * @brief HAL MSP module.
-  * @{
-  */
-
-/* Private typedef -----------------------------------------------------------*/
-/* Private define ------------------------------------------------------------*/
-/* Private macro -------------------------------------------------------------*/
-/* Private variables ---------------------------------------------------------*/
-/* Private function prototypes -----------------------------------------------*/
-/* Private functions ---------------------------------------------------------*/
-
-/** @defgroup HAL_MSP_Private_Functions
-  * @{
-  */
+TIM_HandleTypeDef TIM3_Handle;
 
 /**
   * @brief  Initializes the Global MSP.
@@ -83,9 +56,21 @@
   */
 void HAL_MspInit(void) {
     // set up the timer for USBD CDC
-    USBD_CDC_TIMx_CLK_ENABLE();
-    HAL_NVIC_SetPriority(USBD_CDC_TIMx_IRQn, 6, 0);
-    HAL_NVIC_EnableIRQ(USBD_CDC_TIMx_IRQn);
+    __TIM3_CLK_ENABLE();
+
+    TIM3_Handle.Instance = TIM3;
+    TIM3_Handle.Init.Period = (USBD_CDC_POLLING_INTERVAL*1000) - 1;
+    TIM3_Handle.Init.Prescaler = 84-1;
+    TIM3_Handle.Init.ClockDivision = 0;
+    TIM3_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
+    HAL_TIM_Base_Init(&TIM3_Handle);
+
+    HAL_NVIC_SetPriority(TIM3_IRQn, 6, 0);
+    HAL_NVIC_EnableIRQ(TIM3_IRQn);
+
+    if (HAL_TIM_Base_Start(&TIM3_Handle) != HAL_OK) {
+        /* Starting Error */
+    }
 }
 
 /**
@@ -94,9 +79,9 @@ void HAL_MspInit(void) {
   * @retval None
   */
 void HAL_MspDeInit(void) {
-    // reset USBD CDC timer
-    USBD_CDC_TIMx_FORCE_RESET();
-    USBD_CDC_TIMx_RELEASE_RESET();
+    // reset TIM3 timer
+    __TIM3_FORCE_RESET();
+    __TIM3_RELEASE_RESET();
 }
 
 /**
@@ -162,7 +147,7 @@ void HAL_RTC_MspDeInit(RTC_HandleTypeDef *hrtc)
 }
 
 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
-    if (htim == &USBD_CDC_TIM3_Handle) {
+    if (htim == &TIM3_Handle) {
         USBD_CDC_HAL_TIM_PeriodElapsedCallback();
     } else if (htim == &servo_TIM2_Handle) {
         servo_timer_irq_callback();
diff --git a/stmhal/stm32f4xx_it.c b/stmhal/stm32f4xx_it.c
index 30ded5f7b5d5a0439597654128f120a82e2daa6e..cfd24dd3313d1fc2e673ab5d38fc563b466ce368 100644
--- a/stmhal/stm32f4xx_it.c
+++ b/stmhal/stm32f4xx_it.c
@@ -357,8 +357,7 @@ void TIM2_IRQHandler(void) {
 }
 
 void TIM3_IRQHandler(void) {
-    // USBD CDC timer is TIM3
-    HAL_TIM_IRQHandler(&USBD_CDC_TIM3_Handle);
+    HAL_TIM_IRQHandler(&TIM3_Handle);
 }
 
 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/stmhal/usbd_cdc_interface.c b/stmhal/usbd_cdc_interface.c
index e190e34793394977dae27ec2ac054b3e09a073f3..8a2470c853631a06d4f05ad0d47111636172295d 100644
--- a/stmhal/usbd_cdc_interface.c
+++ b/stmhal/usbd_cdc_interface.c
@@ -65,8 +65,6 @@ static uint16_t UserTxBufPtrOut = 0; // increment this pointer modulo APP_TX_DAT
 static int user_interrupt_char = VCP_CHAR_NONE;
 static void *user_interrupt_data = NULL;
 
-/* TIM handler declaration */
-TIM_HandleTypeDef USBD_CDC_TIM3_Handle;
 /* USB handler declaration */
 extern USBD_HandleTypeDef hUSBDDevice;
 
@@ -76,8 +74,6 @@ static int8_t CDC_Itf_DeInit   (void);
 static int8_t CDC_Itf_Control  (uint8_t cmd, uint8_t* pbuf, uint16_t length);
 static int8_t CDC_Itf_Receive  (uint8_t* pbuf, uint32_t *Len);
 
-static void TIM_Config(void);
-
 const USBD_CDC_ItfTypeDef USBD_CDC_fops = {
     CDC_Itf_Init,
     CDC_Itf_DeInit,
@@ -125,21 +121,19 @@ static int8_t CDC_Itf_Init(void)
     /* Transfer error in reception process */
     Error_Handler();
   }
-#endif
   
   /*##-3- Configure the TIM Base generation  #################################*/
+  now done in HAL_MspInit
   TIM_Config();
+#endif
   
-  /*##-4- Start the TIM Base generation in interrupt mode ####################*/
-  /* Start Channel1 */
-  if(HAL_TIM_Base_Start_IT(&USBD_CDC_TIM3_Handle) != HAL_OK)
-  {
-    /* Starting Error */
-  }
+    /*##-4- Start the TIM Base generation in interrupt mode ####################*/
+    /* Start Channel1 */
+    __HAL_TIM_ENABLE_IT(&TIM3_Handle, TIM_IT_UPDATE);
   
-  /*##-5- Set Application Buffers ############################################*/
-  USBD_CDC_SetTxBuffer(&hUSBDDevice, UserTxBuffer, 0);
-  USBD_CDC_SetRxBuffer(&hUSBDDevice, UserRxBuffer);
+    /*##-5- Set Application Buffers ############################################*/
+    USBD_CDC_SetTxBuffer(&hUSBDDevice, UserTxBuffer, 0);
+    USBD_CDC_SetRxBuffer(&hUSBDDevice, UserRxBuffer);
 
     UserRxBufCur = 0;
     UserRxBufLen = 0;
@@ -147,7 +141,7 @@ static int8_t CDC_Itf_Init(void)
     user_interrupt_char = VCP_CHAR_NONE;
     user_interrupt_data = NULL;
 
-  return (USBD_OK);
+    return (USBD_OK);
 }
 
 /**
@@ -378,29 +372,3 @@ int USBD_CDC_RxGet(void) {
     }
     return c;
 }
-
-/**
-  * @brief  TIM_Config: Configure TIMx timer
-  * @param  None.
-  * @retval None.
-  */
-static void TIM_Config(void)
-{  
-  /* Set TIMx instance */
-  USBD_CDC_TIM3_Handle.Instance = USBD_CDC_TIMx;
-  
-  /* Initialize TIM3 peripheral as follow:
-       + Period = 10000 - 1
-       + Prescaler = ((SystemCoreClock/2)/10000) - 1
-       + ClockDivision = 0
-       + Counter direction = Up
-  */
-  USBD_CDC_TIM3_Handle.Init.Period = (USBD_CDC_POLLING_INTERVAL*1000) - 1;
-  USBD_CDC_TIM3_Handle.Init.Prescaler = 84-1;
-  USBD_CDC_TIM3_Handle.Init.ClockDivision = 0;
-  USBD_CDC_TIM3_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
-  if(HAL_TIM_Base_Init(&USBD_CDC_TIM3_Handle) != HAL_OK)
-  {
-    /* Initialization Error */
-  }
-}
diff --git a/stmhal/usbd_cdc_interface.h b/stmhal/usbd_cdc_interface.h
index f0c8404ff5aacef7c709c842353997f2fd78bc47..5ed5ecef46bc73d42032faf392567d1eb7ace539 100644
--- a/stmhal/usbd_cdc_interface.h
+++ b/stmhal/usbd_cdc_interface.h
@@ -33,21 +33,11 @@
 /* Exported types ------------------------------------------------------------*/
 /* Exported constants --------------------------------------------------------*/
 
-/* Definition for TIMx clock resources */
-#define USBD_CDC_TIMx                             TIM3
-#define USBD_CDC_TIMx_CLK_ENABLE                  __TIM3_CLK_ENABLE
-#define USBD_CDC_TIMx_FORCE_RESET()               __TIM3_FORCE_RESET()
-#define USBD_CDC_TIMx_RELEASE_RESET()             __TIM3_RELEASE_RESET()
-
-/* Definition for TIMx's NVIC */
-#define USBD_CDC_TIMx_IRQn                        TIM3_IRQn
-//#define USBD_CDC_TIMx_IRQHandler                  TIM3_IRQHandler // this is hard coded in stm32f4xx_it.c
-
 /* Periodically, the state of the buffer "UserTxBuffer" is checked.
    The period depends on USBD_CDC_POLLING_INTERVAL */
 #define USBD_CDC_POLLING_INTERVAL             10 /* in ms. The max is 65 and the min is 1 */
 
-extern TIM_HandleTypeDef USBD_CDC_TIM3_Handle;
+extern TIM_HandleTypeDef TIM3_Handle;
 extern const USBD_CDC_ItfTypeDef USBD_CDC_fops;
 
 void USBD_CDC_HAL_TIM_PeriodElapsedCallback(void);