Nov 192016
 
Orange Pi Zero - Neat

Got myself a (actually two) Orange Pi Zero: US$7, quad core ARM Cortex [email protected], 256MB RAM, WLAN, FastEthernet, 1 USB, USB-to-go for power. All in a (about) 5×5 cm² package. Add in a 8GB microSD card, and it’s a small capable little board.

While the memory looks on the small side, itś plenty to run one program. Armbian uses about 40MB itself when running:

harald@opz1:~$ free 
              total        used        free      shared  buff/cache   available 
Mem:         247012       36720      142740        2168       67552      193175 
Swap:        131068           0      131068

leaving >200MB left. Given that I can have a (small, limited) web server runnig on an ESP8266 with about 40KB RAM, 256MB is plenty for a single-purpose server/controller.

Ethernet works (as expected). WLAN works via simple nmtui command.

Controlling the 2 LED’s is easy too:

root@opz1:/# cd /sys/class/leds/red_led 
root@opz1:/sys/class/leds/red_led# ls 
brightness  device  max_brightness  power  subsystem  trigger  uevent 
root@opz1:/sys/class/leds/red_led# echo 1 >brightness  

That turns on the red LED. Similar for the green LED.

To find out what GPIO’s exist, use this:

root@opz1:/sys/class/leds/red_led# cat /sys/kernel/debug/gpio                                              
GPIOs 0-383, platform/sunxi-pinctrl, sunxi-pinctrl: 
 gpio-10  (?                   ) out hi 
 gpio-17  (red_led             ) out hi 
 gpio-202 (xradio_irq          ) in  lo 
 gpio-354 (?                   ) out hi 
 gpio-362 (green_led           ) out hi

To export a GPIO, do

# echo 15 >/sys/class/gpio/export

and then in /sys/class/gpio/gpio15/ you can see the standard Linux kernel GPIO things like direction (in/out) and value (0/1)

See also the schematics which show what port of the H2+ connects to what thing: orange-pi-zero-schanetics-v1_11 (sp!)

 

Oct 302016
 

Previously I made LEDs on/off via Lua. The pin-order was 4, 3, 2, 1, 5, 0, 6, 7, 8 in NodeMCU: 6 red LEDs, and a RGB LED.

Using Espruino (specifically version 1.87), the order turns out to be: D2, D0, D4, D5, D14, D16?, D12, D13, D15. D1 seems to turn the WiFi LED on/off, which is a bit odd.

 

Update:

Espruino has a nice feature to convert NodeMCU pins into Espruino pins:

for (var i=1; i<11; ++i) { console.log(NodeMCU["D"+i])}
D5
D4
D0
D2
D14
D12
D13
D15
D3
D1

Or easier, just use NodeMCU.D1 for what was D1 in NodeMCU. It also helps to know that the Espruino numbers are simply the GPIO numbers of the ESP8266 (Espruino D5 = ESP8266 GPIO 14 = NodeMCU D1)

Oct 302016
 

Lua is neat, but learning Lua and JavaScript and NodeJS. Although Lua and especially NodeMCU is similar (not only in name) to NodeJS, it would be nicer to use only one language.

Here the recipe:

  1. Download espruino_1v87.tve_master_b3dc05b_esp8266.tgz
  2. Write flash (note: might use 80m and qio, but my old one does dio):
    ./esptool.py --port /dev/ttyUSB0 --baud 115200 write_flash --flash_freq 40m --flash_mode dio --flash_size 32m \
    0x0000 ~/Downloads/espruino_1v87.tve_master_b3dc05b_esp8266/boot_v1.6.bin \
    0x1000 ~/Downloads/espruino_1v87.tve_master_b3dc05b_esp8266/espruino_esp8266_user1.bin \
    0x3FC000 ~/Downloads/espruino_1v87.tve_master_b3dc05b_esp8266/esp_init_data_default.bin \
    0x37E000 ~/Downloads/espruino_1v87.tve_master_b3dc05b_esp8266/blank.bin
  3. Verify it:
    ./esptool.py --port /dev/ttyUSB0 --baud 115200 verify_flash \
    0x1000 ~/Downloads/espruino_1v87.tve_master_b3dc05b_esp8266/espruino_esp8266_user1.bin \
    0x3FC000 ~/Downloads/espruino_1v87.tve_master_b3dc05b_esp8266/esp_init_data_default.bin \
    0x37E000 ~/Downloads/espruino_1v87.tve_master_b3dc05b_esp8266/blank.bin
  4. Connect at 115200bps.
  5. Connect to your AP:
    var wifi = require("Wifi");
    wifi.connect("your_sid", {password:"your_password"}, function(err){
     console.log("connected? err=", err, "info=", wifi.getIP());
    });
    wifi.stopAP();
    wifi.save();
  6. In the Espruino IDE add the IP address
  7. When you reconnect via Espruino IDE, you should now have 2 choices: serial or TCP/IP via WLAN

 

Sep 222016
 

I bought a small waterproof DS18B20 based temperature probe. Finally I had a reason to use it: to measure the temperature drop in the joeveo mug which I received 2 days ago. While the theory is sound, it’s one thing to just believe in it or to measure yourself. I pick the latter, especially if it it involves some programming!

Temperature over time for various things

x: time in seconds, y: temperature in degrees Celsius

Using flot (for now) to make a quick graph, I got a sense of temperature drop in my normal ceramic mug as well as an idea of the drop in an insulation mug. The first graph (ice melting in glass) was using integers for the temperature.  The DS18B20 can clearly do better, so the next measurements I did with the floating point NodeMCU firmware.

Here the trivial code:

ds18b20 = require("ds18b20")
gpio0 = 3
ds18b20.setup(gpio0)

# plot temp over time

mytime0 = tmr.time()
n = 0

function plot_temp()
 uart.write(0, "["..tmr.time()-mytime0..","..ds18b20.read().."],")
 n = n > 4 and 0 or n+1
 if n == 0 then
  print()
 end
end

tmr.alarm(0, 10000, tmr.ALARM_AUTO, plot_temp)

Integrate the output into a HTML et voilà: a simple yet good looking graph.

Step 2 is to actually measure the temperature in the joeveo mug.

 

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.

Results:

  • 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 https://github.com/haraldkubota/rgbled-websocket

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)
      value=10*value+c.charCodeAt()-48;

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

 

Aug 132016
 

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

print(dht.read(4))
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:

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

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

 

 

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:

Wemos-WS2812-modified

With this change, this sample program works:

ws2812.init()
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 https://github.com/nodemcu/nodemcu-firmware/blob/master/lua_examples/u8glib/u8g_graphics_test.lua).

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)
end


-- the draw() routine
function draw()
 disp:setFont(u8g.font_6x10)
 
 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)
end


function rotate()
 if (next_rotation < tmr.now() / 1000) then
  if (dir == 0) then
   disp:undoRotation()
  elseif (dir == 1) then
   disp:setRot90()
  elseif (dir == 2) then
   disp:setRot180()
  elseif (dir == 3) then
   disp:setRot270()
  end

  dir = dir + 1
  dir = bit.band(dir, 3)
  -- schedule next rotation step in 1000ms
  next_rotation = tmr.now() / 1000 + 1000
 end
end

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
  rotate()

  disp:firstPage()
  repeat
    draw()
  until disp:nextPage() == false

  tmr.delay(100000)
  tmr.wdclr()
  end

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

init_i2c_display()
rotation_test()

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 http://nodemcu-build.com/. Highly recommended.

One odd problem I saw though: when using the usual

./esptool.py --port /dev/ttyUSB0 --baud 115200 write_flash 0x0 
~/Downloads/nodemcu-master-20-modules-2016-08-13-01-27-33-integer.bin

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:

./esptool.py --port /dev/ttyUSB0 --baud 115200 write_flash -fm dio -fs 32m 0x0 
~/Downloads/nodemcu-master-20-modules-2016-08-13-01-27-33-integer.bin

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).