ESP8266 and the Micropython firmware

One of the alternative firmwares available for the ESP8266 is MicroPython Python interpreter. I’ve found by chance a great tutorial at Adafruit for building the Micropython firmware and I thought to give it a try.

Building the firmware:
The Adafruit tutorial uses a Vagrant based virtual machine to build the firmware, but since I’m already running Linux (Arch Linux to be more specific) and already have the Falcon open ESP8266 sdk installed (see here) and the esptool.py also available since I’m using the Sming firmware, I’ve just downloaded only the latest Micropython source code from the Github repository to a local directory.

cd ~
git clone https://github.com/micropython/micropython
cd ~/micropython
git submodule update --init
cd ~/micropython/esp8266
export PATH=/opt/esp-open-sdk/xtensa-lx106-elf/bin:$PATH
make axtls
make

So far nothing different from the Adafruit tutorial except that I’m not using the vagrant VM. Also make sure that you first execute the command make axtls otherwise the main make command will compiling about not finding a version.h file. Also make sure that the export command that adds the path to the Xtensa compiler points to the right location.

After compiling, which was fast, I’ve just flashed the firmware on my Wemos mini D1 board. Again I had trouble flashing this board with other speeds than the default 115200 bps.

So:

cd ~/micropython/esp8266/build
esptool.py -p /dev/ttyUSB0 --baud 115200 write_flash 0 firmware-combined.bin

And after flashing, we can connect through the serial terminal, and pressing CTRL-D we should be greeted with the following message:

PYB: soft reboot
could not open file 'main.py' for reading
MicroPython v1.8-157-g480159c on 2016-05-29; ESP module with ESP8266
Type "help()" for more information.

Some basic tests:
When doing some tests I’ve found out that most information is outdated regarding the version of Micropython that I’ve flashed. For example:

>>> import pyb
Traceback (most recent call last):
  File "", line 1, in 
ImportError: no module named 'pyb'
>>> 

The common refered module pyb doesn’t exist, because it’s now machine:

>>> import machine
>>> dir(machine)
['__name__', 'mem8', 'mem16', 'mem32', 'freq', 'reset', 'reset_cause', 'unique_id', 'deepsleep', 'disable_irq', 'enable_irq', 'RTC', 'Timer', 'Pin', 'PWM', 'ADC', 'UART', 'I2C', 'SPI', 'DEEPSLEEP', 'PWR_ON_RESET', 'HARD_RESET', 'DEEPSLEEP_RESET']
>>>

So the following code:

>>> import pyb
>>> pin = pyb.Pin(14, pyb.Pin.OUT)  # Set pin 14 as an output.
>>> for i in range(10):
...    pin.value(0)     # Set pin low (or use pin.low())
...    pyb.delay(1000)  # Delay for 1000ms (1 second)
...    pin.value(1)     # Set pin high (or use pin.high())
...    pyb.delay(1000)

should be now:

>>> import machine
>>> pin = machine.Pin(14, machine.Pin.OUT)  # Set pin 14 as an output.
>>> for i in range(10):
...    pin.value(0)     # Set pin low (or use pin.low())
...    pyb.delay(1000)  # Delay for 1000ms (1 second)
...    pin.value(1)     # Set pin high (or use pin.high())
...    pyb.delay(1000)

Other interesting stuff is for example, the esp module:

>>> import esp
>>> dir(esp)
['__name__', 'osdebug', 'sleep_type', 'deepsleep', 'flash_id', 'flash_read', 'flash_write', 'flash_erase', 'flash_size', 'neopixel_write', 'apa102_write', 'dht_readinto', 'freemem', 'meminfo', 'info', 'malloc', 'free', 'esf_free_bufs', 'SLEEP_NONE', 'SLEEP_LIGHT', 'SLEEP_MODEM', 'STA_MODE', 'AP_MODE', 'STA_AP_MODE']
>>> esp.freemem()
17672
>>> esp.meminfo()
data  : 0x3ffe8000 ~ 0x3ffe8410, len: 1040
rodata: 0x3ffe8410 ~ 0x3ffe9038, len: 3112
bss   : 0x3ffe9038 ~ 0x3fff6bd0, len: 56216
heap  : 0x3fff6bd0 ~ 0x3fffc000, len: 21552

There are examples on GitHub to use the deepsleep functions:

Regarding the Wifi connectivity, by default when starting up the ESP8266 sets up a Wifi access point with the name Micropython-XXXXX where XXXX are some digits from the MAC address. Following the documentation the AP is protected with the password micropythoN, and sure enough the connection works. Still I haven’t tested it enough, for example, accessing the Python interpreter over Wifi, instead of through the serial port.

Anyway, one final test is to use Python to connect to make the ESP8266 to connect to my network. The instructions are simple, just write help(), and the micropython shows how to do it:

import network
>>> help()
Welcome to MicroPython!

For online docs please visit http://docs.micropython.org/en/latest/esp8266/ .
For diagnostic information to include in bug reports execute 'import port_diag'.

Basic WiFi configuration:

import network
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
sta_if.scan()                             # Scan for available access points
sta_if.connect("", "") # Connect to an AP

and we can see if it connected successfully:

>>> sta_if.isconnected() 
True

and what IP configuration was set:

>>> sta_if.ifconfig()
('10.42.0.173', '255.255.255.0', '10.42.0.1', '10.42.0.1')

Also I was unable to access the Python interpreter through the access point connection. Supposedly there should be a listener running on port 8266 that allows access over WIFI, but I my tests found that the port 8266 was closed. Probably I need to initialize something first…
Anyway, there is a tool webrepl that allows to use the browser through websockets to connect to the ESP8266 and access the Python prompt and also to copy files to the ESP8266, namely the main.py startup file.

To finish. during my tests I had no crashes or surprise reboots. Using Python also has the advantage, in my opinion, that is more mainstream than Lua, since we leverage desktop programming with device programming. Also the useful tool ESPLorer already supports Micropython, it means that probably it is a better alternative for quick hacks using the ESP8266 instead of Nodemcu running LUA.

Synology – Installing Python PIP package installer

A simple recipe for installing the pip utility that is needed to install Python packages/Modules:

This needs to be done with the user root so ssh to the Diskstation with that user.

1st) Install Python from the Package Installer Web interface. I have Python 2.7 installed

2nd) Connect to your Synology NAS through ssh.

3rd) Get the pip installer: wget https://bootstrap.pypa.io/get-pip.py

4th) Execute the installer: python get-pip.py. It will take a while:

 # python get-pip.py 
Collecting pip
  Downloading pip-8.0.2-py2.py3-none-any.whl (1.2MB)
    100% |████████████████████████████████| 1.2MB 42kB/s 
Collecting setuptools
  Downloading setuptools-19.6-py2.py3-none-any.whl (472kB)
    100% |████████████████████████████████| 475kB 109kB/s 
Collecting wheel
  Downloading wheel-0.26.0-py2.py3-none-any.whl (63kB)
    100% |████████████████████████████████| 65kB 291kB/s 
Installing collected packages: pip, setuptools, wheel
Successfully installed pip-8.0.2 setuptools-19.6 wheel-0.26.0

We can now execute the pip command:

# pip

Usage:   
  pip  [options]

Commands:
  install                     Install packages.
  download                    Download packages.
  uninstall                   Uninstall packages.
  freeze                      Output installed packages in requirements format.
  list                        List installed packages.
  show                        Show information about installed packages.
  search                      Search PyPI for packages.
  wheel                       Build wheels from your requirements.
  hash                        Compute hashes of package archives.
  help                        Show help for commands.
...
...

Now we can install the modules that we need.

For example, installing paho-mqtt for MQTT support:

# pip install paho-mqtt
Collecting paho-mqtt
  Downloading paho-mqtt-1.1.tar.gz (41kB)
    100% |████████████████████████████████| 45kB 589kB/s 
Building wheels for collected packages: paho-mqtt
  Running setup.py bdist_wheel for paho-mqtt ... done
  Stored in directory: /var/services/homes/root/.cache/pip/wheels/97/db/5f/1ddca8ee2f9b58f9bb68208323bd39bb0b177f32f434aa4b95
Successfully built paho-mqtt
Installing collected packages: paho-mqtt
Successfully installed paho-mqtt-1.1

And we can use now the paho.mqtt module on our python programs.

Monit and KDE Desktop notifications

I’m running a Monit based monitoring solution, but I do not have an email server (that I can use), so when something goes wrong and Monit Alerts is raised, I have no way receive a notification that something is wrong.

So I built a quick notification system, done in Python, that when a Monit detects an error, it notifies me by activating a KDE Notification on my desktop…

The solution that I use is quite simple and quick (Warning it’s an hack…):

My solution uses a python based network server running on my desktop listening for messages, and a python client deployed on the Monit server that sends messages, when called.

In short, the running server on my KDE desktop waits for a connection on a specific port, and when one connection is established it reads any text that comes on the connection payload and displays it using the KDE notification system:

MonitServer.py

## Monit Desktop Notification Network server
import socket
import sys
import knotify
import dbus

## Constants definition
HOST = ''    # Host IP Address. Empty means that we are the host
PORT = 29876    # Listening port. Choose any above 1024. The client must use the same port...
ADDR = (HOST,PORT)
BUFSIZE = 4096

## Send notification for the KDE notification system
def notify(title, text):
   knotify = dbus.SessionBus().get_object("org.kde.knotify", "/Notify")
   knotify.event("warning", "kde", [], title, text, [], [], 0, 0,
   dbus_interface="org.kde.KNotify")

print 'Server Started'
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'
# Bind socket to local host and port
try:
  s.bind((HOST, PORT))
except socket.error as msg:
    print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
    sys.exit()
print 'Socket bind complete'

#Start listening on socket
s.listen(10)
print 'Socket now listening'

#now keep talking with the client
while 1:
  #wait to accept a connection - blocking call
  conn, addr = s.accept()
  print 'Connected with ' + addr[0] + ':' + str(addr[1])
  # Read the message that comes from the monit server.
  data = conn.recv(1024)
  # Send the message to the KDE notification system
  notify("Monit Alarm!", data)

s.close()

At the Monit server, I placed the following files at the Monit bin directory: notify.py and notify.sh:

notify.py

from socket import *
import sys
from datetime import datetime

HOST = '192.168.1.200'       # My machine IP. Change to the IP where the NotifyServe.py is running
PORT = 29876       
ADDR = (HOST,PORT)

cli = socket( AF_INET,SOCK_STREAM)
cli.connect((ADDR))
print len(sys.argv)
if ( len(sys.argv) > 1 ):
  message = str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + ": " + sys.argv[1]
  cli.send( message )
else:
  cli.send("Undefined Alert!")
cli.close()

Notify.py receives a message passed as a command line parameter and sends it to the NotifyServer running on the desktop machine.

The notify.sh shell script is a wrapper for the Monit server to call the monit.py script. Keep in mind that you must use absolute paths in every single file, otherwise it won’t work.

#!/bin/bash
/usr/bin/python2      /home/monitor/monit/bin/notify.py   $1

Now on the monitrc file we can change the alert lines from (for example):

check program System_status with path "/home/monitor/ShMonitor/SystemStatus.sh"
  if status !=0 then alert

to

check program System_status with path "/home/monitor/ShMonitor/SystemStatus.sh"
  if status !=0 for 15 cycles then exec "/home/monitor/monit/bin/notify.sh SystemStatus_Problem"

I added the for 15 cycles to avoid being flooded with notifications while I solve the issue… But it really depends of the monitoring pooling cycle. Adjust as required.

So, there it is, some code snippets gather across the web, allows me to have Monit notifications right at my desktop 🙂