A First Look at the Winner Micro W600
A Second Look at the W600-PICO Development Board->

These notes are about my first encounter with the Wemos W600-Pico development board that has been available for about 3 years. As is often the case here, this will be an idiosyncratic review. I want to quickly build a Wi-Fi switch that is compatible with my home automation system running on Domoticz. Is it possible to easily mimic a Sonoff Basic based on the Micro Winner W600 module instead of an ESP8266/8285 module? I'll cut to the chase, the answer is yes, after the usual teething problems when approaching something new.

Please go to A Second Look at the W600-PICO Development Board.

Only read this post if it is necessary to use the MicroPython version supplied with the W600-PICO (version 1.10). A much more recent version of MicroPython is available. Of course, that newer version must be flashed on the board. However, learning to upload the firmware to the W600-PICO is inevitable, so there is no real value in using the original version of MicroPython.

Table of Contents

  1. The Brief
  2. The W600-Pico Development Board
  3. Flashing the MicroPython Firmware
  4. The Read-eval-print Loop (REPL)
  5. The FTP Server
  6. A Blink Example
  7. A Custom boot.py
  8. Polling a Button
  9. Wi-Fi Switch - Proof of Concept

The Brief toc

The brief, in the UK usage of the term, meaning a set of instructions that outlines the tasks to be performed, follows.

Once those elements have been dealt with, it will be possible to use the W600-Pico as a Wi-Fi switch that can be turned on and off remotely with the Domoticz home automation system. Furthermore, the relay can be controlled locally with the button and the state of the corresponding home automation virtual switch will be updated automatically.

Before getting to that final destination, it is necessary to learn a bit about the development board and MicroPython which is the obvious choice for programming the board for reasons explained below.

The W600-Pico Development Board> toc

Is it too obvious to say that the W600-Pico dev board is based on the Winner Micro W600? That microcontroller has a 32-bit ARM M3 processor running at 80 MHz. It comes with 288 KiB of RAM and up to 1 MiB of integrated flash memory. Presenting itself as a cheaper alternative to the Espressif ESP8266 or rather the ESP8285 given its integrated flash memory, it has 2.4 GHz Wi-Fi (802.11b/g/n) wireless connectivity. It also has multiple interfaces: SDIO, SPI, UART, GPIO, I²C, PWM, I²S and so on. This is just a first look at the chip so I will not investigate those capabilities at this time. Here is the pinout of the W600-Pico from Wemos.

W600-Pico pinout

The microcontroller on the board is labelled W600-B800. According to the datasheet, this identifies a revision B chip with 1 MiByte of integrated flash memory.

When boards based on the W600 appeared around 2019, they garnered some interest for a year or so and then it waned considerably. Now, contrary to the ESP8266 (or other products from Espressif), there is only a small community coalesced around this chip creating a classic chicken and egg scenario. Hobbyists are less attracted to the device because few hobbyists are using it. Consequently I have relied mostly on three rather old sources for information.

There is a two year old software development kit (SDK) from Winner Micro and an unofficial repository based on the 3.04 SDK. However aside from what appears to be an ill-fated attempt at creating an Arduino core, there is no simple way to program the chip in neither the Arduino IDE nor PlatformIO. If there is no strong desire to go through the complicated procedure to install an SDK and to learn how to use it, then using MicroPython which comes preinstalled on the W600-Pico seems to be the only avenue for the hobbyist of my ilk. The Wemos site dedicated to the W600-Pico has a link to the MicroPython firmware V1.10, but that is actually a Winner Micro fork, released on Jan 25, 2019, of version 1.10 of the MicroPython implementation. About 3 months later, wdyichen made a "pull" request to the Micro Python team asking that the Micro Winner W600 be added to the supported ports. From what I can gather, nothing came of that because of the lack of interest in the W600 at the time and the difficulty in implement TLS in the 1 MB memory.

There is a MicroPython port to the W60X by Robert Hammelrath (robert-hh) that is very active. is no release right now, so it would be necessary to install the toolchain to compile the interpreter and then flash the firmware to the dev board. While the last step is rather easy as explained in the next section, I did not want to invest the time and effort needed to compile a binary right now. Perhaps at a later date. New Information: I was wrong back in April, Robert Hammelrath (robert-hh) has released and maintains the image files of various ports of the current MicroPython version in his Shared-Stuff repository. Expect an update of this post at the start of 2023 that will use MicroPython 1.19.

Flashing the MicroPython Firmware toc

MicroPythons is something new to me and, furthermore, my knowledge of Python leaves a lot to be desired. Unfortunately, I can easily put the board into a tight loop where it seems impossible to get control back. At various times the situation seemed hopeless, and I had to resort to erasing all the flash memory and reinstalling the MicroPython firmware. Here are instructions on how to do that if you ever find yourself in that pickle. It is not necessary to install the tools to do that as explained in the rest of this section, just see this as insurance in case something goes horribly wrong.

Start by installing w600tool by Volodymyr Shymanskyy (vshymanskyy) which is the firmware flashing tool. It is a Python script with some prerequisites. As usual in such a case I prefer installing the tool in a virtual environment. Below I use my bash script from Python 3 virtual environments, but there are other ways of implementing this same idea.

  1. Create a virtual environment.
    michel@hp:~$ mkvenv w600tool creating virtual environment /home/michel/w600tool updating virtual environment /home/michel/w600tool
  2. Enable the virtual environment and install prerequisites.
    michel@hp:~$ ve w600tool (w600tool) michel@hp:~$ cd w600tool/ (w600tool) michel@hp:~/w600tool$ pip install pyserial PyPrind xmodem Collecting pyserial Using cached pyserial-3.5-py2.py3-none-any.whl (90 kB) Collecting PyPrind Downloading PyPrind-2.11.3-py2.py3-none-any.whl (8.4 kB) Collecting xmodem Downloading xmodem-0.4.6.tar.gz (32 kB) Preparing metadata (setup.py) ... done Building wheels for collected packages: xmodem Building wheel for xmodem (setup.py) ... done Created wheel for xmodem: filename=xmodem-0.4.6-py3-none-any.whl size=34564 sha256=9bf5b6a98f495bba128bc93fd6865e113b12b4aeaa78aff340b3a41692e95768 Stored in directory: /home/michel/.cache/pip/wheels/8a/46/14/833413574281b7009c9180fce7c595a7cc1b538e43fcd8b7e7 Successfully built xmodem Installing collected packages: xmodem, pyserial, PyPrind Successfully installed PyPrind-2.11.3 pyserial-3.5 xmodem-0.4.6
  3. Download the script from the GitHub repository.
    (w600tool) michel@hp:~/w600tool$ wget https://raw.githubusercontent.com/vshymanskyy/w600tool/master/w600tool.py ... 2022-04-21 17:08:44 (6,11 MB/s) - «w600tool.py» enregistré [6441/6441]
  4. Make the script executable.
    (w600tool) michel@hp:~/w600tool$ chmod +x w600tool.py (w600tool) michel@hp:~/w600tool$ ls -l w6* -rwxrwxr-x 1 michel michel 6441 avr 21 17:08 w600tool.py
  5. Check that the script can reach the W600-Pico.
    (w600tool) michel@hp:~/w600tool$ ./w600tool.py -p /dev/ttyUSB0 -b 115200 --get-mac Opening device: /dev/ttyUSB0 MAC: 286DCD2C7E89
  6. Obtain the Micropython firmware for the board.
    See Flash firmware at Wemos' Get started with MicroPython [W600 series]. It is a link to an archive on the Micro Winner site that contains the firmware in two formats.
    (w600tool) michel@hp:~/w600tool$ wget http://www.winnermicro.com/upload/1/editor/1568709203932.zip -O micropython_v1.10.zip ... 2022-04-21 17:31:46 (198 KB/s) - «micropython_v1.10.zip» enregistré [676412/676412] (w600tool) michel@hp:~/w600tool$ unzip -l micropython_v1.10.zip Archive: micropython_v1.10.zip Length Date Time Name --------- ---------- ----- ---- 0 2019-09-17 11:39 W60X_MicroPython_1.10_B1.3_IMG/ 541180 2019-09-17 11:35 W60X_MicroPython_1.10_B1.3_IMG/wm_w600.fls 334480 2019-09-17 11:35 W60X_MicroPython_1.10_B1.3_IMG/wm_w600_gz.img --------- ------- 875660 3 files
  7. Extract the .fls format image of the firmware; the W600-Pico cannot handle the compressed image.
    w600tool) michel@hp:~/w600tool$ unzip -j micropython_v1.10.zip W60X_MicroPython_1.10_B1.3_IMG/wm_w600.fls Archive: micropython_v1.10.zip inflating: wm_w600.fls

No matter if the board is in a "bricked" state or not, it should be possible to erase the flash memory and upload the original MicroPython firmware.

(w600tool) michel@hp:~/w600tool$ ./w600tool.py -p /dev/ttyUSB0 -b 115200 -e --upload-baud 115200 -u wm_w600.fls Opening device: /dev/ttyUSB0 Erasing secboot Erasing image Uploading wm_w600.fls 0% [══════════════════════════════] 100% | ETA: 23:59:59 Total time elapsed: 00:00:55 Reset board to run user code... (w600tool) michel@hp:~/w600tool$

At times one will be requested to Push reset button to enter bootloader.... And that's it, the W600-Pico will be back to the state it had when it left the factory. How did I know to upload to port /dev/ttyUSB0? That's explained at the start of the next section.

It is not really necessary to enable the virtual environment to execute the flash tool. All that is necessary to execute the script is to launch the correct Python interpreter found the virtual environment.

michel@hp:~$ /home/michel/w600tool/bin/python /home/michel/w600tool/w600tool.py -p /dev/ttyUSB0 -b 115200 --get-mac Opening device: /dev/ttyUSB0 MAC: 286DCD2C7E89

Using that fact, I wrote the following short little bash script to avoid having to type that very long command.

#!/bin/bash pushd /home/michel/w600tool > /dev/null bin/python w600tool.py -p /dev/ttyUSB0 -b 115200 -e --upload-baud 115200 -u wm_w600.fls popd > /dev/null

That script, named flashW600, made executable, and stored in the search path, provides a simple means to flash the MicroPython firmware. With that knowledge, it is now safe to embark on an exploration of board confident that we can always recover from a big mistake.

A port of MicroPython 1.19.1 is available instead of MicroPython 1.10 which is the factory installed version on the W600-Pico

The latest (2022-12-15) non-threading version can be dowloaded from the Robert Hammelrath (robert-hh) Shared-Stuff repository.

michel@hp:~/w600tool$ wget https://github.com/robert-hh/Shared-Stuff/raw/master/wm_w600_lfs.fls --2022-12-15 15:28:24-- https://github.com/robert-hh/Shared-Stuff/raw/master/wm_w600_lfs.fls Résolution de github.com (github.com)… Connexion à github.com (github.com)||:443… connecté. requête HTTP transmise, en attente de la réponse… 302 Found Emplacement : https://raw.githubusercontent.com/robert-hh/Shared-Stuff/master/wm_w600_lfs.fls [suivant] --2022-12-15 15:28:24-- https://raw.githubusercontent.com/robert-hh/Shared-Stuff/master/wm_w600_lfs.fls Résolution de raw.githubusercontent.com (raw.githubusercontent.com)…,,, ... Connexion à raw.githubusercontent.com (raw.githubusercontent.com)||:443… connecté. requête HTTP transmise, en attente de la réponse… 200 OK Taille : 608660 (594K) [application/octet-stream] Enregistre : «wm_w600_lfs.fls» wm_w600_lfs.fls 100%[===================>] 594,39K --.-KB/s ds 0,1s 2022-12-15 15:28:24 (4,42 MB/s) - «wm_w600_lfs.fls» enregistré [608660/608660]

That single download replaces steps 6 and 7. Of course, the *.fls image file name would have to be adjusted in the commands and bash scripts. More importantly, much of what follows would not work properly with the newer version of the firmware without adjustments. Hopefully an amended version of this post will be available soon in the new year to take advantage of the updated MicroPython interpreter.

The Read-eval-print Loop (REPL) toc

Start monitoring the device manager and then plug the USB (data and power) cable from the Linux system to the W600-Pico.

michel@hp:~$ udevadm monitor --udev monitor will print the received events for: UDEV - the event which udev sends out after rule processing UDEV [548140.048780] add /devices/pci0000:00/0000:00:14.0/usb3/3-9/3-9.2 (usb) UDEV [548140.052689] add /devices/pci0000:00/0000:00:14.0/usb3/3-9/3-9.2/3-9.2:1.0 (usb) UDEV [548140.056808] add /devices/pci0000:00/0000:00:14.0/usb3/3-9/3-9.2/3-9.2:1.0/ttyUSB0 (usb-serial) UDEV [548140.061882] add /devices/pci0000:00/0000:00:14.0/usb3/3-9/3-9.2/3-9.2:1.0/ttyUSB0/tty/ttyUSB0 (tty) UDEV [548140.063349] bind /devices/pci0000:00/0000:00:14.0/usb3/3-9/3-9.2/3-9.2:1.0/ttyUSB0 (usb-serial) UDEV [548140.064931] bind /devices/pci0000:00/0000:00:14.0/usb3/3-9/3-9.2/3-9.2:1.0 (usb) UDEV [548140.079270] bind /devices/pci0000:00/0000:00:14.0/usb3/3-9/3-9.2 (usb) ^C

Press the CtrlC key combination to close the monitor. Clearly, a USB device attached to ttyUSB0 was added. That clever way of finding the device assigned to the USB-serial converter on the board was suggested by Les Pounder. Alternatively, the device can be found in the usual fashion. The onboard USB to serial converter is a CH340 and these usually show up as a ttyUSBx device. Sometimes, microcontrollers show up as a ttyACMx device, so let's check both.

michel@hp:~$ ls /dev/ttyACM* /dev/ttyUSB* ls: impossible d'accéder à '/dev/ttyACM*': Aucun fichier ou dossier de ce type /dev/ttyUSB0

I used uc from the venerable uucp package as a terminal, others may prefer minicom, screen and so on. Once the serial connection is established, I was running in the MicroPyton read-eval-print loop (REPL).

michel@hp:~$ cu -l /dev/ttyUSB0 -s 115200 Connected.
__ __ \ \ /\ / /   \ \ / \ / /   \ \/ /\ \/ /   \ / \ /   / /\ / /\   / /\ \/ /\ \   / / \ / \ \   /_/ \/ \_\   WinnerMicro W600 MicroPython v1.10-282-g6a9b3cb-dirty on 2019-09-17; WinnerMicro module with W600 Type "help()" for more information.

Following the advice, I typed help().

>>> help() Welcome to MicroPython on the W600! For generic online docs please visit http://docs.micropython.org/ For access to the hardware use the 'machine' module: import machine pb26 = machine.Pin(machine.Pin.PB_26, machine.Pin.OUT, machine.Pin.PULL_DOWN) pb26.value(1) pb27 = machine.Pin(machine.Pin.PB_27, machine.Pin.IN, machine.Pin.PULL_UP) print(pb27.value()) 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("<AP_name>", "≶password>") # Connect to an AP sta_if.isconnected() # Check for successful connection Control commands: CTRL-A -- on a blank line, enter raw REPL mode CTRL-B -- on a blank line, enter normal REPL mode CTRL-C -- interrupt a running program CTRL-D -- on a blank line, do a soft reset of the board CTRL-E -- on a blank line, enter paste mode For further help on a specific object, type help(obj) For a list of available modules, type help('modules')

Based on the above, let's turn the built-in LED on.

>>> from machine import Pin >>> Led = Pin(Pin.PA_00, Pin.OUT, Pin.PULL_FLOATING) >>> Led.value(0)

As can be seen, the LED is connected to pin PA0 and is turned on by writing a 0 to the pin, which means it is grounded to 0 volts. Writing a 1, turns the LED off. Those coming from the Arduino world might be surprised by the third mandatory parameter. Even when an I/O pin is set in the output mode, it is necessary to specify if a pull up or pull down resistor or in this case if neither is engaged.

There's a list of available built in modules.

>>> help('modules') __main__ gc re ujson _boot hashlib select uos _onewire heapq socket urandom _thread io struct ure array json sys uselect binascii machine time usocket builtins math ubinascii ustruct cmath micropython ucollections utime collections network uctypes utimeq ds18x20 ntptime uerrno uzlib easyw600 onewire uhashlib w600 errno os uheapq zlib framebuf random uio Plus any modules on the filesystem

So there is a network module that, as already seen, can be used to connect to a WiFi network and a JSON module. With an MQTT module, it will be possible to create a WiFi switch.

The FTP Server toc

Looking at the list of built-in modules, it is not obvious that there is an FTP server. It is in the w600 module.

>>> help(w600) object is of type module __name__ -- w600 flash_read -- flash_write -- flash_erase -- flash_id -- flash_size -- flash_user_start -- run_ftpserver -- version --

That the board can run an FTP server is important because the REPL is a rather primitive development environment. With the FTP server it will be possible to upload other Python modules (such as an MQTT module) that can be imported into the main.py module. We can also create a main.py Python script with a preferred text editor on the desktop machine and upload it to the board.

At the risk of repeating myself, it is easy to break things in this environment, so I wrote a little script that can be copied and pasted at the REPL prompt to restore the FTP server.

import time import network wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect("<--your-ssid-->","<--your-wifi-password-->") time.sleep(2) import w600 w600.run_ftpserver(port=21,username="user",password="12345678") time.sleep(1) wlan.ifconfig(('', '', '', '')) time.sleep(0.5) print("ftp server at", wlan.ifconfig()[0], "port is 21, username is 'user', password is '12345678'")

Once the server is in place, the IP address and the FTP user name and password are displayed in the REPL. Note the use of wlan.ifconfig()to set a static IP address which makes it easier to set up tools to work with the board. If that line is omitted, and the surrounding sleep() lines which would no longer be needed, then the DHCP server on the local area network will assign an IP address to the board and it will be correctly reported in the REPL.

I find that I can just copy the WIFI/FTP connection script at the REPL prompt and it will work, except for the last line which I sometimes have to complete by hand. The better practice is to put the REPL into paste mode with the CtrlE keyboard combination and then copy and paste the script from the desktop.

>>> CTRL-E paste mode; Ctrl-C to cancel, Ctrl-D to finish === import time === === import network === === wlan = network.WLAN(network.STA_IF) === === wlan.active(True) === === wlan.connect("XXXXXXXXXXX","YYYYYYYYYYYYYY") === === time.sleep(2) === === import w600 === === w600.run_ftpserver(port=21,username="user",password="12345678") === === time.sleep(1) === === wlan.ifconfig(('', '', '', '')) === === time.sleep(0.5) === === print("ftp server at", wlan.ifconfig()[0], "port is 21, username is 'user', password is '12345678'") === === CTRL-D True ftp server at port is 21, username is 'user', password is '12345678'

Another possibility would be to set up the board as a WI-FI access point and then to start the FTP server. Connecting to the Wi-Fi network created by the W600 with the desktop it should be possible to use the FTP server. This has not been tested.

Once the server is running, an FTP client, such as ftp on most Linux machines can be used to transfer files back and forth over the Wi-Fi network. Here is a session showing how this can be done.

michel@hp:~$ ftp ftp> open Connected to 220-= welcome on W600 FTP server =- 220 Name ( user 331 Password required for user Password: 12345678 not echoed 331 Password required for user Password: 230 User logged in Remote system type is UNIX. List all files and directories in the root directory of the board ftp> pwd 257 "/" is current directory. ftp> ls 200 Port Command Successful. 150 Opening Binary mode connection for file list. drwxrwxrwx 0 root root 0 Jan 1 2018 sys drwxrwxrwx 0 root root 0 Jan 1 2018 lib drwxrwxrwx 0 root root 0 Jan 1 2018 cert -rwxrwxrwx 0 root root 540 Jan 1 2018 boot.py -rwxrwxrwx 0 root root 3997 Jan 1 2018 main.py -rwxrwxrwx 0 root root 1720 Jan 1 2018 easyw600.py -rwxrwxrwx 0 root root 39 Jan 1 2018 secrets.py -rwxrwxrwx 0 root root 1517 Jan 1 2018 button.py 226 Transfert Complete. Navigate the board's file system ftp> cd lib 250 Changed to directory "//lib" ftp> pwd 257 "//lib/.." is current directory. ftp> cd / 250 Changed to directory "/" Execute desktop commands with ! ftp> !pwd /home/michel ftp> !ls top_level* top_level.txt Copy a file from the desktop to the board ftp> put top_level.txt local: top_level.txt remote: top_level.txt 200 Port Command Successful. 150 Opening binary mode data connection for "//top_level.txt". 226 Finished. 392 bytes sent in 0.00 secs (92.4891 kB/s) ftp> ls 200 Port Command Successful. 150 Opening Binary mode connection for file list. drwxrwxrwx 0 root root 0 Jan 1 2018 sys drwxrwxrwx 0 root root 0 Jan 1 2018 lib drwxrwxrwx 0 root root 0 Jan 1 2018 cert -rwxrwxrwx 0 root root 540 Jan 1 2018 boot.py -rwxrwxrwx 0 root root 3997 Jan 1 2018 main.py -rwxrwxrwx 0 root root 1720 Jan 1 2018 easyw600.py -rwxrwxrwx 0 root root 39 Jan 1 2018 secrets.py -rwxrwxrwx 0 root root 1517 Jan 1 2018 button.py -rwxrwxrwx 0 root root 392 Jan 1 2018 top_level.txt <-- there's the copied file 226 Transfert Complete. Delete a file is the board's file system ftp> delete top_level.txt 250 Successfully deleted file "//top_level.txt". Copy a file from the board to the desktop ftp> get secrets.py local: secrets.py remote: secrets.py 200 Port Command Successful. 150 Opening binary mode data connection for "//secrets.py" (39 bytes). WARNING! 2 bare linefeeds received in ASCII mode File may not have transferred correctly. 226 Finished. 39 bytes received in 0.00 secs (8.8325 kB/s) ftp> !ls secrets.py secrets.py <-- there's the copied file on the desktp Removing that copied file on the desktop ftp> !rm secrets.py ftp> !ls secrets.py ls: impossible d'accéder à 'secrets.py': Aucun fichier ou dossier de ce type

This was a trip down memory lane, I hadn't used a command line FTP client in years, but I thought I would make a note here because it could be a way to use Geany or VSCodium as a light IDE for the W600. Let's mention here that Les Pounder suggests using ampy by Adafruit. However given his description, it looks like a command line utility not much different from ftp.

__ __   \ \ /\ / /   \ \ / \ / /   \ \/ /\ \/ /   \ / \ /   / /\ / /\   / /\ \/ /\ \   / / \ / \ \   /_/ \/ \_\   Traceback (most recent call last): File "boot.py", line 1, in NameError: name '�������������������������������������������������������������������������������������������������������������������������������������������' isn't defined Traceback (most recent call last): File "main.py", line 1, in NameError: name '����������������������������������' isn't defined MicroPython v1.10-282-g6a9b3cb-dirty on 2019-09-17; WinnerMicro module with W600 Type "help()" for more information. >>> PROBLEM IN THONNY'S BACK-END: Internal error (thonny.plugins.micropython.mp_back.ProtocolError: Could not get raw-paste confirmation). See Thonny's backend.log for more info. You may need to press "Stop/Restart" or hard-reset your MicroPython device and try again.

I have not had much luck with Thonny as can be seen above. This is unfortunate because that Python IDE does work quite well with ESP32 boards. It was a cursory examination of the IDE and further investigation is waranted if the W600 becomes a chip that is used in many projects. Luckily, Thonny does work well with recent versions of MicroPython. Some mention uPyCraft by DFRobot which does look quite similar to Thonny. While not even mentioned in the introduction, it seems that it is available for Linux assuming the prerequisites are in place (see the source code README.md file).

So far I have used FileZilla which is a well-known FTP client as described in section 5 of the MicroPython User Guide by Winner Micro. On a Linux system, it is also possible to connect to the FTP server with the Caja file manager (and probably others) so that the file system on the W600 shows up as a remote directory. To do this, go to a location as shown below.

Caja Go to Location

Enter the user name and password required by the FTP server on the W600 board.

Enter password

I shun setting passwords permanently especially for connections that are mostly temporary, but that is a personal choice, of course. As can be seen below, the root directory of the W600 file system is now available as a networked drive.

W600 file system as a networked drive

When I open a script in the remote file system into an editor (done by clicking with the right mouse button and choosing Open with...) and modify the script, the changes are uploaded to the W600 after closing the editor. Once used to this, I found this approach to be a fluid work environment. The only thing that I could not do with this method was deleting a directory in the remote (W600) file system. This could be done with FileZilla.

Don't forget, this is more than a programming environment; it is a built in over-the-air (OTA) mechanism. No need to add libraries and additional code as done with the ESP chips, although it will be necessary to modify the boot.py script to always enable the FTP server. More on that later.

A Blink Example toc

Here is the obligatory Blink script.

''' Blink for W600-Pico ''' RUNS = 5 # number of flash cycles to run CYCLES = 8 # number of quick flashes per cycle FLASH_TIME = 0.1 # 100 ms on/off time of quick flashes OFF_TIME = 1.5 # 1500 ms off time between series of flashes LED_ON = 0 # LOW turns the LED attached to GPIO 22 on VERBOSE = True # print state of LED to the serial port from os import uname from machine import Pin from time import sleep # Blue LED connected to pin labeled PA0 on the board Led = Pin(Pin.PA_00, Pin.OUT, Pin.PULL_FLOATING) print() print('W600-Pico Blink') print('---------------') print("os.uname:") print(uname()) print() sleep(0.25) for j in range(RUNS): if VERBOSE: print(j+1, '/', RUNS,' ', end='') for i in range(CYCLES): Led.value(LED_ON) if VERBOSE: print('ON ', end='') sleep(FLASH_TIME) Led.value(not Led.value()) sleep(FLASH_TIME) if VERBOSE: print('OFF') sleep(OFF_TIME) if VERBOSE: print("Done.")

I think this is pretty straight forward. We have already seen that the on-board blue LED is connected to the I/O pin labelled PA0 and that the pin must be grounded to turn on the LED. The only other thing worth mentioning is that the blinking does not go on indefinitely so as to fall through to the REPL after a while.

A Custom boot.py toc

When the W600 reboots, it executes the MicroPython firmware. After the latter has done its initial setup, it executes two Python scripts found in the root of the file system in chip's flash memory. First executed is boot.py and then if that first script had no syntax error and no programming error such as starting an infinite loop, then the main.py script is executed. The default boot.py does very little.

# boot.py -- run on boot-up # can run arbitrary Python, but best to keep it minimal print("") print(" WinnerMicro W600") print("")

Exactly why it is "best to keep [boot.py] minimal" is not clear. No matter, I decided that this was the best place to connect to the Wi-Fi network and to start the FTP server. Here is my boot.py.

from time import sleep import secrets import network WIFI_CONNECT_ATTEMPTS = 16 WIFI_SLEEP_TIME = 0.5 wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(secrets.ssid, secrets.psk) for i in range(WIFI_CONNECT_ATTEMPTS): if wlan.isconnected(): break else: sleep(WIFI_SLEEP_TIME) if wlan.isconnected(): print('Connected to', secrets.ssid) import w600 w600.run_ftpserver(port=21,username="user",password="12345678") sleep(WIFI_SLEEP_TIME) wlan.ifconfig((secrets.ip, secrets.subnet, secrets.gateway, secrets.dns)) sleep(WIFI_SLEEP_TIME) print('URL:', wlan.ifconfig()[0]+':21', 'username: "user", password:"12345678"')

Again the logic is simple. After a connection to the wireless network is attempted (wlan.connect(secrets.ssid, secrets.psk), a check is made every half second for up to 8 seconds to see if the operation succeeded. If a connection is established with the wireless network, then the FTP server is started. If it is preferable to let the DHCP server on the local network assign the IP address dynamically, then comment out or remove the wlan.ifconfig() command.

There is an accompanying secrets.py script. Here is a template for it.

# set valid values below and save as secrets.py ssid = "name_of_your_wifi_network" psk = "wifi_network_password" ip = "" subnet = "" gateway = "" dns = ""

Adjust the content of the file, specifying the correct credentials for the Wi-Fi network and for the static IP address to be assigned to the W600 board. Remember to set an unused unique IP address for each board to be connected to the network.

Both boot.py and secrets.py need to be copied to the file system of the W600 using the latter's FTP server. The content of the boot.py cannot be pasted in the REPL to get that first instance of the FTP server up because secrets.py would have to already copied to the file system. However that was already covered in 5. The FTP Server.

Polling a Button toc

This will be a first example where a third party module is used. In this case, it is MicroPython-Button by Ubi de Feo (udidefeo). Copy Button.py into the root directory of the W600 file system. The program will not work if the module is copied into the /lib directory as one would do with an ESP32 running MicroPython. I have yet to figure out the correct configuration to change this.

from Button import Button # from https://github.com/ubidefeo/MicroPython-Button from machine import Pin # builtin from time import sleep_ms # builtin released_count = 0 def button_change(button, event): global released_count if event == Button.RELEASED: released_count += 1 print('Button released. Count:', released_count) # Create a Button class instance button_one = Button(Pin.PA_01, True, button_change, internal_pullup = True) print('setup completed, start pressing button') # Run for 3 minutes for testing. for i in range(3*60*4): button_one.update() sleep_ms(250) # do other things # fall back into REPL print("Done.")

Push button circuit The Button class instance creator has three positional arguments.

  1. An I/O pin number.
  2. The I/O pin default state when the button is not pressed (True = 1.
  3. The callback function invoked when the button changes state.

Then there are two boolean positional arguments: internal_pullup and internal_pulldown. If either is set to True, then an internal resistor (to Vcc or ground respectively) is engage at the I/O pin. If both are set to true then it is the internal pull up resistor that is set up. If neither positional argument is set or if both are set to False, then nothing about the internal resistor is specified when the module sets the input pin mode. We have already seen that this causes a problem in the W600 implementation of MicroPython, so at least one of the positional arguments must be set to true. In this case, where the push button ground pin PA1 momentarily, it is internal_pullup which is set to True. I have not had any luck with just an internal pull up resistor. There were many spurious signals interpreted as key presses and releases. It remains to determine if this was because the button was connected using long 15 cm leads. In any case an external pull up resistor solved the problem and the script reliably which explains the

setup completed, start pressing button Button released. Count: 1 Button released. Count: 2 Button released. Count: 3

Wi-Fi Switch - Proof of Concept toc

There is an MQTT client module for MicroPython that works with the W600: umqtt.simple2. It is offered as an alternative to the built in umqtt module included in the MicroPython firmware since version 1.12 but which is not in the version of the firmware installed on the W600. Perhaps the original umqtt module would work also in MicroPython 1.10, but I have not tested that. Adding the MQTT client module is the only thing that needs to be done to prove that the W600-Pico could be used to create a Wi-Fi switch that works with Domoticz and probably any other home automation system that supports the MQTT protocol.

from machine import reset, Pin import time import json from umqtt.simple2 import MQTTClient from Button import Button if not wlan.isconnected(): print("WiFi not connected after", WIFI_CONNECT_ATTEMPTS*WIFI_SLEEP_TIME, "seconds") print("Restarting in 2 seconds") sleep(2) reset() # start over with hard reset #-- <params> ---------------------- # MQTT server MQTT_SERVER = "" # Domoticz parameters SUB_TOPIC = "domoticz/out" PUB_TOPIC = "domoticz/in" DOMO_IDX = 195 # I/O pin connected to LED LED_PIN = Pin.PA_00 LED_ON = 0 # I/O pin connected to relay RELAY_PIN = Pin.PA_01 RELAY_ON = 1 # I/O pin connected to n.o. push button BUTTON_PIN = Pin.PB_07 BUTTON_EXT_PULLUP = True # externally pulled high #-- </params> ------------------------ # setup I/O pins RELAY_OFF = 1-RELAY_ON Relay = Pin(RELAY_PIN, Pin.OUT, Pin.PULL_FLOATING) Relay.value(RELAY_OFF) LED_OFF = 1-LED_ON Led = Pin(LED_PIN, Pin.OUT, Pin.PULL_FLOATING) Led.value(LED_OFF) # Relay / LED control RelayOn = False # initial state def TurnRelayOn(): global RelayOn Relay.value(1) Led.value(LED_ON) RelayOn = True def TurnRelayOff(): global RelayOn Relay.value(0) Led.value(LED_OFF) RelayOn = False def ToggleRelay(): global RelayOn if RelayOn: TurnRelayOff() else: TurnRelayOn() # MQTT def publish(status): action = "On" if status else "Off" msg = '{"command": "switchlight", "idx":'+str(DOMO_IDX)+', "switchcmd": "'+action+'" }' mc.publish(PUB_TOPIC, msg) #print("MQTT publishing: [" + PUB_TOPIC + "]", msg) # MQTT callback on received messages on the subscribed topic def sub_cb(topic, msg, retain, dup): #print((topic, msg, retain, dup)) json_msg = json.loads(msg) if json_msg["idx"] == DOMO_IDX: #print(json_msg) if json_msg["nvalue"] == 0: TurnRelayOff() elif json_msg["nvalue"] == 1: TurnRelayOn() #print("Starting mqtt client & subscribing to", SUB_TOPIC); mc = MQTTClient("umqtt_client", MQTT_SERVER) mc.set_callback(sub_cb) mc.connect() mc.subscribe(b"domoticz/out") # Button def btn_change(button, event): global RelayOn if event == Button.RELEASED: ToggleRelay() publish(RelayOn) #print("button released") # W600 needs to set either the internal_pullup or internal_pulldown # resistor. It is not possible to leave it undefined as assumed in # button.py if BUTTON_EXT_PULLUP: btn = Button(BUTTON_PIN, True, btn_change, internal_pullup = True) else: btn = Button(BUTTON_PIN, False, btn_change, internal_pulldown = True) print('Setup completed.') print('Start pressing button to toogle the Domoticz device off / on.') print('Change the state of virtual device in Domoticz.') # Run endlessly #while (True): # Testing for 3 minutes for i in range(3*60*4): mc.check_msg() # non blocking mqtt message pump btn.update() # check button state sleep(0.250) mc.disconnect() # get here only if testing # fall through to REPL

Push button circuit I didn't actually connect a relay to the pin labeled PA1, only an external LED that will be turned on when the pin is set high. The test circuit is shown on the right.

Domoticz switch I was very happy to see that the external LED (i.e. the relay) could be toggled on and off with the push button switch and that the internal LED and the virtual switch in Domoticz would both correshow the state of the relay. And, of course, it worked the other way. Clicking on the bulb icon of the Domoticz virtual switch caused the relay and activity LED to change their state.

There's much more to Theo Arends' Tasmota firmware which is what is running on all the Wi-Fi switches in my home automation system. However, all communication between these IoT devices and the home automation software is done using the two above-mentioned MQTT topics. Consequently, it seems reasonable to conclude that the W600-Pico can be successfully used as the basis of a (dumb) Wi-Fi switch.

More? toc

I have done a few other things with the I²C capabilities of the W600-Pico: using a DS3231 real time clock and a 0.96" SSD1306 OLED display. These may be made available in the future. For the time being, the source code show in this post is available in a GitHub repository: w600_micropython_examples. I'll end with three other references that could be of help for others starting to use MicroPython on the W600.

A Second Look at the W600-PICO Development Board->