Flashing Z-Stack on a CC2530+CC2591 using a Wemos D1 mini

I’m messing about with Zigbee for a comms protocol to various temperature sensors, and this requires a Zigbee Coordinator. There’s a few ways of doing this, but ultimately I settled on a zigbee2mqtt bridge and a cheapie AliExpress CC2530+CC2591 module.

This module incorporates an RF amplifier, but does not have the normal debug header that the CC2530 Zigbee transceivers have and also lacks the USB-TTL adapter chip. Not a problem if you’re using a RPi as the bridge, which is what I plan on doing.

However first, you need to get Z-Stack firmware on it, so you can use it as a coordinator. This proves to be… non-trivial. Especially if you want to use a Wemos D1 Mini as the flashing device (these Wemos things are really good, incidentally).

First Steps – Getting CClib-Proxy onto the Wemos

Assuming you have a Wemos D1 mini, your first steps are to install the Arduino IDE (available from the Windows Store). Once that’s in, in Preferences, add the following URL to the Additional Boards Manager URL field;


From there, you should now be able to go to the Boards Manager, and install the esp8266 package. Once that is installed, configure your board as a “LOLIN(WEMOS) D1 R2 & Mini” and select the correct COM port.

Now it’s as simple as downloading CCLib-Proxy from this link. Open up CCLib_Proxy.ino, then change the following lines for the pinout;

 int CC_RST   = 5;
int CC_DC = 4;
int CC_DD_I = 14;
int CC_DD_O = 12;

These mappings are required. Upload to your device. You now have CClib-Proxy onto the Wemos and ready to go.

Wiring up the Wemos to the CC2530+CC2591 Module

You will need to map various pins on the Wemos to pins on the module, using the following chart;

DC (Debug Clock)P2_2D2 (GPIO4)
DD (Debug Data)P2_1D5 (GPIO14)
D6 (GPIO12)
RST (Reset)RSTD1 (GPIO5)
VCC (Supply)VCC3V3
GND (Ground)GNDG
RXD (Receive Data)P0_2RPI Pin 8 (TXD)
TXD (Transmit Data)P0_3RPI Pin 10 (RXD)
CTS (Clear To Send)P0_5RPI Pin 11 (RTS)
RTS ( Ready To Send)P0_4RPI Pin 36 (CTS)

When using a Wemos as the flashing device, it’s safe to tie the two I/O pins together (D5 and D6) and connect them to the DD pin on the CC2530. It works fine. The P0_2 through P0_5 pins are used when you’re using the finished device, not when flashing (so you don’t need to connect them up).

Pinout for CC2530+CC2591 module

The above diagram shows the pin mappings on the CC2530+CC2591 module itself. Follow those numbers and the pins above to wire it up.

Pinout of Debug Header on CC2530 (not present on combined module)

This diagram shows the pinout of the debug header (which is not present on the CC2591). However, it does show which pins on the CC2591 marry up to what purposes on the debug header (which correspond to pins on the Wemos).

After this is done, you need to use CClib to flash the firmware.

Flashing the Z-Stack Firmware

Get the firmware from this link. You will also need to install Python 2.7 for your system. Once that’s done, install pyserial with;

pip install pyserial==3.0.1

Edit the firmware .hex you downloaded, and remove the second to last line (it won’t work with the flasher you’re using). Once that is done, connect your Wemos to your computer, and then from the Python directory in your CClib download, run;

python cc_info.py -p COM9

Assuming that COM9 is your Wemos. You should see output giving you data on the CC2530. If so, fantastic. Now flash it;

python cc_write_flash.py -e -p COM9 --in=YOURFIRMWAREHERE.hex

This will take an extremely long time. Several hours. But you should see progress fairly quickly. Just hang tight. Once that’s done, you have a coordinator!

Next post will deal with testing the coordinator out.

References and Links

Necromancy and the Garmin Nuvi 1390

My father asked me to update the maps on his Garmin Nuvi 1390 on Friday evening.  This did not go so well.  Using Garmin Express, I applied the updates, unplugged it when it said, and it sat there with a black screen saying “Loading maps.” and that was all.  Nothing else.

Whoops.  Anyway, a lot of digging revealed that what was going on is that the GPS couldn’t boot, likely due to corruption in the filesystem.  So here’s how to fix that.

You first need Garmin Cure 3.  This is kicking around in various places, but you can get it at that link.  You also need the firmware image for the current firmware you have installed, which you can find here.  And you need a tool to reformat the mass storage (if this is required), which you can use RMPrepUSB for.  I’ll assume you have Garmin Connect or something installed so you have the USB drivers.

Getting access to the Mass Storage

After installing all the above, you’ll need to fire up Garmin Cure 3, point it at the firmware image you downloaded, then put it into CURE mode, and start it.  At this point the GPS should be turned off and unplugged.  Wait until it says it’s ready to load, then click the Updater button to launch the updater.

Now, you need to get the GPS into pre-boot mode.  On the Nuvi 1390, that involves holding your finger on the top left corner of the screen and turning it on.  Keeping your finger on the screen, plug in the USB cable, wait for Windows to make the “device connected” sound, and click to let the updater work.

Fixing Mass Storage Issues

Assuming you now get firmware up, what will happen is that you have the CURE firmware on.  This firmware will not load, but when you boot the GPS up and plug it into your computer, you should get the GPS appear as a mass storage device.  Let’s assume it does.  You can now copy off anything you want, try and fix it, or like in my case, reformat it.

If you reformat, you can use RMPrepUsb to do so.  Follow the instructions at this link to do so.  Notably, the Nuvi 1390 doesn’t care if the filesystem is blank when you first boot it.  Doing this will wipe anything you downloaded or anything factory loaded such as car icons, the sample Cyclops database and other things like that.

Restoring the Original Firmware and Updating

After that’s done, unplug the GPS, turn it off.  Repeat the above CURE process, but this time select ORIGINAL as the option.  This will put normal firmware on.  You should now be able to boot your device.  Run Garmin Express and update it.  Tell it to reinstall the maps, which will take a while, and you should be (mostly) back to normal.

Installing a POI Database

Restoring like this will destroy the sample Cyclops safety camera database.  That’s OK, we’ll replace it.  Using a website like PoiDB, get hold of a POI database in GPX format.  You will now need to install Garmin’s POI Loader, then point the Loader at the GPX you downloaded.  Follow the prompts and it will insert that POI database in GPI format into your GPS, and your safety camera alerts should be restored.

Notably though, and this is annoying, the safety camera alerts won’t be colorized for your speed or anything, but they’ll be there.  If you have actually paid for Cyclops you can probably just get the proper Cyclops DB installed by just using Garmin Express and don’t need to use a community-made POI database like I did.

Good luck.

CH340 Issues on Surface Pro 3?

I picked up a few LoLin NodeMCU v3 boards the other day (you can read a comparison of the various NodeMCU dev boards here).  However, I had a hiccup with it.

I couldn’t get it to work reliably on my Surface Pro 3 (running Windows 10).  Yet, when I plugged it into a colleague’s Lenovo running Ubuntu, it worked fine.

The serial UART (a CH340) was disappearing when the NodeMCU attempted to start up WiFi.  If I held down the reset button so that the ESP never initialized, the UART worked just fine.  Strange.

Then I tried plugging the NodeMCU into a dumb powered USB hub instead of directly into my Surface.  It worked just fine!

My suspicion is that the problem is caused by the power demand reported by the CH340.  It says it needs < 100mA, but I know that the power draw on these things can be upwards of 300mA.  So, I suspect that the Surface is limiting the power draw to what the USB device says is its maximum on startup.  The dumb hub doesn’t care about such things, so it powers the device regardless.

TLDR;  If you have problems with NodeMCU devices vanishing off your USB bus when you do things with them, plug them into a dumb USB hub.

ESP8266 Environment Widget – Software

Last post I mentioned how I got an ESP8266 board.  Well, I’ve put together that with a DHT11 and MQTT to create a widget that senses temperature and humidity, then posts that on a regular basis to your MQTT message broker.  The data comes out in JSON format for ease of use.  I’ll cover the hardware later, once I get the rest of my prototype boards.

Preparing your ESP8266

Following guides mentioned in my previous post, wire up your ESP8266.  Then, connect your DHT such that the output pin is going to GPIO2 on the ESP8266.

Go and install NodeMCU.  Remember to ground GPIO0 if you are going to upload firmware!  You can use either the float or integer version (I used the float version).  The DHT11 only produces integer temperature and humidity stats anyway, so it doesn’t make any difference.

Next, go to my Github repository and grab the two LUA files.  Don’t upload them to the ESP yet!  Just put them somewhere.

If you have installed NodeMCU, you can now stop grounding GPIO0.  Technically you should pull that high with a pullup, but I haven’t bothered yet.  I’ll do that in the final hardware.

Lastly, fire up LuaLoader and configure your wifi.  This is persistent, as long as you don’t reflash the NodeMCU firmware.

The init script

If you put immediately-executing code into init.lua, you run into a problem – you can’t break out into the interactive shell anymore, and therefore you have to reflash in order to get back.  Not optimal.

The way around that is to have init.lua wait a short time and then execute a different script.  You then have time to either manually abort the alarm timer, or get rid of init.lua and reboot.

Here’s a code fragment that does that;

function startup()
	print('executing script')

print("in startup, 'tmr.unregister(0)' to abort")

Pretty simple.  You get 10 seconds to abort before the main script executes.

The reader script

I’m not going to go through the reader script line-by-line, but I will mention some bits about how it works.  First up, NodeMCU is heavily callback driven.  You’re intended to use an event-based paradigm with it, leading to some strange-looking code.  Also, my Lua is pretty limited, I mostly just bumbled my way through it.

Anyway, configuration is with the variables at the top.  dht_gpio = 4 corresponds to GPIO2.  Set your mqtt details appropriately, including the client ID you want this widget to use.  Leave mqtt_ip and mqtt_client nil, they’re used internally.

The actual code execution is event driven by callbacks.  Everything is triggered off by attempting to do DNS resolution for your MQTT server.  NodeMCU includes a nice watchdog timer, and the watchdog is used extensively.  It’s armed at the beginning, and gets reset only when an MQTT message is successfully published.  If anything happens so that an MQTT message won’t be published, something bad happened and the device will eventually reset.

That should cover all possibilities of wifi going away, mqtt broker dying, no IP address etc etc.

It’s notable that NodeMCU on my ESP8266 does not have the sntp module.  This means it has no idea what the real time is, not even close.  As such, I don’t address that.  The ‘raw’ JSON that’s published is intended to be cooked by another script which will add an expiry time.  More on that later.

After unwrapping all the callbacks, our control flow looks like this;

  1. Arm the watchdog timer.  If it goes off, we reboot.
  2. DNS resolve MQTT broker.  If we got an IP…
    1. Connect to the MQTT broker.  If we connected…
      1. Start a repeating alarm every minute.  When that alarm goes off…
        1. Get the DHT sensor details.  If that worked…
          1. Publish that to MQTT and reset the watchdog
  3. Twiddle thumbs

It’s a little more complicated than that (ie, it tries to reconnect to the broker if it disconnects), but not much.

It should be fairly straightforward to wrangle that to support more complex devices such as stuff on an I2C bus.


ESP8266 First Steps

The ESP8266 is a ridiculously cheap ($2 or so from China), WiFi equipped breakout board which has a 32-bit microcontroller and Flash (1MB) onboard.  It comes in a bunch of different versions (mine is an ESP-01), and all with different Flash capacities.  The pinout of the one I have appears below;

ESP-01 Pinout (courtesy MyElectronicsLab)

The most notable limitation of the ESP8266 is the extremely limited I/O capability.  There’s only two GPIOs in addition to the UART, and even those GPIOs configure the boot mode if they’re held high/low during startup!  There is some info about how to get around this limitation at this article though.

DANGER:  The ESP8266 is a 3.3v level device.  Driving the inputs with 5v signalling without a level converter will kill it!

Anyway.  Using a simple 3.3v USB-to-UART converter and a little wiring will allow you to get something useful out of this in a few ways;

I did the last.  I threw on the floating point firmware for NodeMCU onto my ESP-01, which gives you a nice interactive serial interface, standalone capability, and a built-in WiFi connection library.  You can then upload code you’ve written in a LUA-style language to do stuff on the thing.

Now for the real magic.  You can use the GPIO2 pin to read temperature/humidity data into the ESP8266.  A very simple guide about how to do that appears in the NodeMCU documentation for the dht module.

It also turns out that NodeMCU contains a module for communicating to MQTT message brokers (it’s actually got a whole lot of really handy IOT modules already in place).

Next plan is to assemble some code for the ESP8266 so that it reads temperature/humidity data and then publishes that to a MQTT channel for aggregation by something else.

Should make for some pretty cheap WiFi-enabled temperature/humidity sensors!

ADS-B Reception on RPI3

Well, that was straightforward.  I present you with;

ADS-B Output from dump1090

Output collected using the dump1090 tool.  Setup was pretty straightforward;

apt-get install librtlsdr-dev git cmake libusb-1.0-0-dev build-essential
git clone git://github.com/MalcolmRobb/dump1090.git
cd dump1090

Once that’s done, you have a build of dump1090, which can be used with your SDR to decode ADS-B signals from nearby aircraft.  Run this to collect data;

./dump1090 --interactive --net

You should start seeing some dumped output for nearby aircraft.  If you see stuff, great!  Pop open your browser, go to http://YOURRPI:8080/ and then drag the map to near where you are.  Aircraft will appear!

I’m pretty surprised with how well I’m picking up aircraft, given how badly placed my antenna is, and how poor the antenna itself is.  Should work even better once I get a decent antenna.

Software-Defined Radio on RPI3 – First Steps!

Got myself a RTL-SDR Software-Defined Radio (also known as a cheap-as-hell USB DVB-T tuner), and hooked it up to a Raspberry Pi 3 running Raspbian.  My objective here was to just get it working, and eventually I’ll use it for spectrum analysis and ADS-B tracking.

So, I hooked it up, installed GNU Radio (by gods this is a complicated toolkit), and shoved on the default terribad antenna and put it in the shed.

The results?  Well, I got something out of it, but by oath it’s noisy.  I was expecting that, since I have an awful antenna and no ferrite chokes on anything.  But it works!

Example Waterfall plot

The above is a waterfall plot of a small subsection of the regular FM radio band.  It was created using rtl_power (a standard part of the rtl-sdr kit), and a heatmap generator (available here).  The horizontal axis is frequency in MHz, and the vertical axis is time.  Each pixel represents 1kHz of bandwidth and 1s of time.  Brightness indicates received power.

You can clearly see the thick wideband FM transmission at 103.9MHz – that’s a commercial radio station.  There’s a dull band at 103.7MHz (it sounds like noise when tuning into it), and many smaller bands all across the spectrum, which all sound like buzzes when tuning in.  That’s interference.  It’s pretty obvious the antenna is terrible.  But the concept works!

That chart was generated like this;

rtl_power -f 103.5M:104.5M:1k -p 20 -g 35 -i 1s -e 10m sample.csv
python heatmap.py sample.csv sample.jpeg

Now, you can also record arbitrary things.  Here’s a command to record audio to a playable WAV file from the radio station in the above waterfall;

rtl_fm -f 103.9e6 -M wbfm -s 200000 -r 48000 | sox -t raw -e signed -c 1 -b 16 -r 48000 - recording.wav


Now to wait for my new antenna bits to arrive…


Caller ID Name Substitution with Asterisk

There’s only a pretty short list of numbers that I care about having Caller ID name substitution enabled for on my Asterisk setup, so I elected to use Asterisk’s native database and some adjustments to my extensions to substitute in a name into the CALLERID(name) field.

First up, to insert number-to-name mappings, do this;

database put cidname 5551234 "John Smith"

For each number you want to be able to resolve numbers for.  If you then do

database show cidname

You’ll then see the entries you’ve made.  Now, you need to adjust your dialplan.  Let’s say that your incoming calls go to the Asterisk context “incoming-sip”.  Change that context to “incoming-sip-cidlookup”, and then create a new context like this;

 exten => _X!,1,GotoIf(${DB_EXISTS(cidname/${CALLERID(num)})}?:nocidname)
 exten => _X!,n,Set(CALLERID(name)=${DB(cidname/${CALLERID(num)})})
 exten => _X!,n(nocidname),Goto(incoming-sip,${EXTEN},1)

What this does is pretty easy.  If an entry exists in the cidname database for the incoming call’s callerid, then substitute the name field in that callerid for the name in the database.  Once this is done (or if no entry exists in the first place), then direct the call to the regular incoming-sip context.

For small numbers of mappings, this seems to work pretty well.  I’m looking at having an automated system that looks up incoming unknown caller IDs against a reverse phone lookup system.  More on that when I figure it out…

Simple NAT Traversal with Asterisk

As an experiment, I configured Asterisk for full NAT traversal so that my SIP server could be accessed from the Internet.  This isn’t usually what you want, but here’s how you do it…

DANGER – Doing this will expose your Asterisk SIP server directly to the Internet, and you’ll get all manner of violated by SIP spammers.  Be aware of what you’re doing and what the implications are.

I’m assuming that you have a typical home NAT setup, where you have a dynamic IP, you’re in control of the border firewall (so you can do port forwarding), and your Asterisk install is on an internal network.

Step 1- Port Forwarding

First up, on your firewall, port forward the following ports to your Asterisk box;

5060/udp         # SIP control channel
10000:11000/udp  # the ports we will use for RTP

Step 2 – RTP Port Ranges

Right.  Now, create an rtp.conf in your Asterisk config, containing the following;


This constrains the list of allowed RTP ports for SIP to use for communications.

Step 3 – Lockdown

Now, a word on security.  Put the following into the [general] section of your sip.conf;


Now in your extensions.conf, define that context and put nothing in it.  Anyone who dials in anonymously to your SIP server will be directed to that context and go nowhere.  This is what you want (presumably).  For the love of God, do not put anything outbound in that context!

Step 4 – Configure NAT Traversal

In your sip.conf, in the [general] section (it must be there), add the following;


This causes the Contact headers of outbound SIP packets to be substituted with the IP address of the DNS name you specified there, if the destination is not in the localnet field.  This is important to make NAT traversal work.

Step 5 – Configure SIP peers

For each of your peers, configure them to use NAT traversal as follows (some of these options may not be strictly required, but this is what I did and it worked);


Step 6 – Wait for the bots

Now, if this has all worked, you should now be able to connect to your SIP server from the Internet.

May God have mercy on your server.

Music-On-Hold for Asterisk

Got Music On Hold (MOH) working for Asterisk 1.8.  Turns out that it’s not that hard, but there’s some specific requirements to get it going if you don’t want to use mpg123 or something.

First, you’ll need some source music to use.  If you go to Wikipedia, you can find a whole bunch of OGG music that’s free to use.  I grabbed something by Brahms.  I’d suggest you use stuff from there, that way you aren’t license encumbered and at risk of getting sued by the RIAA hit squads.

Once you have that, you’ll need to convert it to an 8000Hz, single-channel, OGG Vorbis sound file, by using SoX (available for Cygwin, which is what I did).  The command you’ll need is;

sox input.ogg output.ogg channels 1 rate 8000

Asterisk is very fussy about the format, so it must be exactly like that.  Next up, fire up the Asterisk console (asterisk -r) and run the following;

core show file formats

Look through that and verify that ogg_vorbis is listed.  Next, we’ll verify that Asterisk can actually transcode ogg to whatever codec your VOIP is using.  Run the following;

core show translation recalc 10

And verify that for the ‘slin’ row there is a number printed in the columns that correspond to the codecs that you’re using (in my case, that’s alaw, ulaw and g729).

Drop your ogg files into /usr/share/asterisk/sounds/moh/ (in my case).  You will now need to put the following into musiconhold.conf to make it work;


Your music-on-hold channel should be set to default anyway, so that should ‘just work’.  If you want, you can create a fake extension in extensions.conf like this to test it;

exten = 444,1,Answer()
same = n,MusicOnHold()

Ringing that extension should result in hearing music.  Consult your Asterisk logs, common issues will be the OGG files not being in the exact format required.

Good luck.