Aug 202016
Power Usage of Wemos D1 mini + WS2812B RGB Shield

Using the Wemos D1 mini + WS2812B RGB shield + Battery shield and a 750 mAh single cell LiPo from my Walkera Dragonfly V120D02S , I wondered how long it’ll last to run. It’s not super-low power (use Bluetooth for that, not Wifi), but anything from 1h to 24h was possible according to my estimates and absolute power rating limits. So it’s time to measure!

The conditions:

  • ws-to-tcp bridge in my PC bridges the raw TCP port with a WebSocket port which is what the JavaScript web page uses which can change the LED color.
  • Lua program runs listening on a TCP socket. Nothing else runs actively.
  • When a command comes in via the TCP socket, analyze the command and execute it. Estimated run time: less than 10ms.
  • To confirm all keeps on working, I changed the LED color about 4 times per hour.
  • The LED was set to run at 20% R, G and B (brightness 55 out of 256).
  • The set of battery shield, D1 mini and LED shield and the battery had no other connections to the outside world.


  • Run time: 14h (750mAh LiPo from fully charged via battery shield, to 3.45V)
  • Average power draw for the LiPo: 700mAh/14h=50mA (assuming 700mAh used)
  • Assuming 90% efficiency of the DC-DC circuit (see TP5410 datasheet), that’s about 30mA@5V coming out of the battery shield (it only feeds +5V)
  • Average power consumption by LED: 55/256*3*20mA=13mA@5V (plus whatever the LED controller chip uses)
  • Leaving 17mA for the D1 mini. The LDO RT9013 drops that to 3.3V (still 17mA)
  • No sleep mode was used here since the device is listening to a TCP port.

The efficiencly is obviously improvable (going from >3.3V to 5V and then to 3.3V again for the ESP8266).

Side note: Charging is taking about 1h until the call has 4.195V. Very nice. I’ll see what the cell voltage is when the TP5410 shuts down. According to the data sheet, it’s likely 2.7V (undervoltage protection).

Update a day later:

With the WS2812 LED being on with low intensity, here the result:

  • Run time: 20h
  • Voltage of LiPo: 2.7V at the end
  • The TP5410 of the battery shield turned off 5V, but the 5V rail still had voltage to make the red and green LEDs of the WS2812 glow (blue turned off)
  • The ESP8266 was still rsponsive via WiFi. Its blue WiFi LED got significantly fainter compared to before when the LiPo was full.
  • Using the same assumptions as above:
    • 21mA@5V out of the TP5410
    • LED board: <1mA @ 5V, for the LEDs
    • About 20mA for the D1 mini module

This is consistent with the first measurement and good enough for me. For a day of being online, you need 1Ah single cell LiPo for the CPU/battery module.

This list shows 15mA for Modem Sleep State and 50mA and more for active WiFi connections. The conclusion would be that NodeMCU uses Modem Sleep when listening to incoming TCP packets.

Aug 142016

When you have a microcontroller which can connect to the network and it has a RGB LED, the logical step is to make this LED controllable via a web browser.

The list of issues faced is numerous:

  • NodeMCU does not handle websockets natively
  • Thus a bridge between normal TCP sockets used by NodeMCU and WebSockets used by the web browser is needed: ws-tcp-bridge does that
  • ws-tcp-bridge defaults to binary blobs which the browser cannot handle. Switching the websocket’s binaryType to arraybuffer fixes this
  • Sending data too fast to a just created websocket which has its binaryType not yet changed to arraybuffer breaks ws-tcp-bridge with a fatal error:
    TypeError: “list” argument must be an Array of Buffers
  • NodeMCU uses Lua, the browser JavaScript. The langages are quite similar! See below for an example.

Code is at

Interesting is the comparison of Lua and JavaScript variable names are the same):

JavaScript Lua
for (c of s.split('')) {

  if (c>='0' && c<='9') {
    if (state==1)

    } else {
      if (state==1) {
        setOneColor(color, value);
      if (c=='R' || c=='G' || c=='B') {
for n=1,s:len(),1 do
   c=s:sub(n, n)
   if c>='0' and c<='9' then
     if state==1 then
     if state==1 then
       setOneColor(color, value)
     if c=='R' or c=='G' or c=='B' then


Aug 132016

2 more I/O shields: Temperature and humity sensor (DHT22 AKA AM2302) and here the super-simple example:

0 26 1 800 0

That’s 26.8 °C and 1.0% humidity. I somehow don’t trust the humidity…

And here the push button module example, which shows well how much the contact bounce is:

 local pin=3
 gpio.mode(pin, gpio.INT, gpio.PULLUP)
 local pulse1=0

 local function pushed(level)
 local pulse2 =
 print("Level: ", level, "since last change: ", pulse2-pulse1, " ns")
 gpio.trig(pin, "down", pushed)



Aug 132016

It was surprisingly difficult to find some working nodeMCU examples. Thus here my findings and sample code:

First the WS2812 shield: It’s using D2 (NodeMCU numbering, which is printed on the board). The ws2812 module for NodeMCU uses D4 though. Here what it looks like:


With this change, this sample program works:

ws2812.write(string.char(100, 20, 5))

Green 100, Red 20, Blue 5. GRB.

The OLED shield needs no hardware modification, but it’s using non-standard I²C pins it seems. Here a working example (using a very slightly modified

I removed the SPI parts, adjusted the pins. The rest is unmodified:

-- ***************************************************************************
-- Rotation Test
-- This script executes the rotation features of u8glib to test their Lua
-- integration.
-- Note: It is prepared for SSD1306-based displays. Select your connectivity
-- type by calling either init_i2c_display() or init_spi_display() at
-- the bottom of this file.
-- ***************************************************************************

-- setup I2c and connect display
function init_i2c_display()
 -- SDA and SCL can be assigned freely to available GPIOs
 local sda = 2 -- 5 -- GPIO14
 local scl = 1 -- 6 -- GPIO12
 local sla = 0x3c
 i2c.setup(0, sda, scl, i2c.SLOW)
 disp = u8g.ssd1306_64x48_i2c(sla)

-- the draw() routine
function draw()
 disp:drawStr( 0+0, 20+0, "Hello!")
 disp:drawStr( 0+2, 20+16, "Hello!")

 disp:drawBox(0, 0, 3, 3)
 disp:drawBox(disp:getWidth()-6, 0, 6, 6)
 disp:drawBox(disp:getWidth()-9, disp:getHeight()-9, 9, 9)
 disp:drawBox(0, disp:getHeight()-12, 12, 12)

function rotate()
 if (next_rotation < / 1000) then
  if (dir == 0) then
  elseif (dir == 1) then
  elseif (dir == 2) then
  elseif (dir == 3) then

  dir = dir + 1
  dir =, 3)
  -- schedule next rotation step in 1000ms
  next_rotation = / 1000 + 1000

function rotation_test()
 print("--- Starting Rotation Test ---")
 dir = 0
 next_rotation = 0
 print("size:", disp:getWidth(), disp:getHeight())
 local loopcnt
 for loopcnt = 1, 100, 1 do

  until disp:nextPage() == false


 print("--- Rotation Test done ---")


The important bits: SDA=2, SCL=1, ADDR=0x3C (there’s a jumper to change it to 0x3D if needed)


Aug 132016
Wemos D1

Got some new NodeMCU modules: Wemos D1. Very neat small modules with USB and “shields”, one of them being a 0.66″ I²C OLED display with 64×48 pixel. Very cute.

I needed some extra NodeMCU modules (specifically 1-wire and u8g), so a new firmware was in order. Best place for that is Highly recommended.

One odd problem I saw though: when using the usual

./ --port /dev/ttyUSB0 --baud 115200 write_flash 0x0 

to program the FLASH, all I got is a stream of data on the serial port after a reset and not the usual prompt. Power cycle did not change that behavior. Neither did waiting. Even re-programming esp_data_init_default.bin did not help. What did help is adding the -fm and -fs parameters:

./ --port /dev/ttyUSB0 --baud 115200 write_flash -fm dio -fs 32m 0x0 

The interesting bit is that programming an older image from about a month ago worked fine without -fm and -fs. Which made me think there’s something buggy in the creation of the image (e.g. the new modules I added are broken).