I accidentally searched Bing for something the other day and noticed something [mildly] interesting – Bing have added radar elevation data to their maps.
You can see the elevation data in the path of the river Croal as it passes through the south of Urban Village. Hang on, Urban where? “Urban Village”? “Innovation Core”? What are these place names and where have they come from? This is the secondly [mildly] interesting thing about Bing maps. It seems like Bing has got this from the same place as Facebook and Instagram – both clearly think that “Innovation Core” is an actual place:
That’s about it for “innovation core” – the rest seem to be academic papers by people with the surname “Bolton”. How about “urban village”? A bit harder to Google as it’s pretty generic. A press release from the council from 2019 mentions “urban village” nearby, but not as an actual place name, just the concept. A little bit further to the south and west we find “Merchant’s Quarter”:
Bit more Googling…this one is more definite – Merchant’s Quarter was defined in a supplement to the Unitary Development Plan in 2005. And scrolling through, we hit paydirt: “Figure 3 – Bolton Town Centre Character Areas”:
So Bolton Council is the source, although I wonder if whoever supplies this data has been a bit premature in using these neotoponyms?
welle.io is a software-defined DAB/DAB+ decoder. It works with RTL-SDR and SoapySDR-compatible tuners.
welle-cli is more than a mere CLI tool, as it provides a web interface for information about the DAB multiplex you’re tuned to, as well as an MP3 stream for each channel in the multiplex. This is what it looks like when I’m listening to Radio 4:
I have welle-cli running on a Raspberry Pi 4B, with an SDRPlay RSP1a connected with USB.
The official instructions for building on Raspbian cover the GUI [welle.io] which I don’t require for this application.
gives me a working web interface on port 8888, tuned to multiplex 12B. Note that if you don’t start this from its HTML directory, the web interface will not load!
CPU usage on the Pi 4B is between 85-100% of one core when serving a single stream. Performance seems to be fine, I haven’t been able to discern any signs of hitting resource limits here. CPU temp is 49°C with the loft temperature at 12°C. The web interface is a bit chatty – merely having it idle in tab requires 110kbps/22pps of network traffic, which could impact resources on smaller platforms. This is notable because it’s more than 50% of the bandwidth that listening to a single stream consumes. The polling interval for the web interface can be tweaked in index.js.
So far, I have not been able to get a stream to play for more than a few minutes. The gain figure starts at around 25 then gradually rises, then the SNR drops, then the bubbling mud commences. I have to restart welle-cli to get a usable signal again. This seems like an AGC issue.
DIP switch positions, reading left to right: Off/Off/Off/On/On.
Wired as follows:
ESP32 Audio Kit
1 – Ground
2 – 3V3
4 – SCLK
IO13 (labelled MTCK)
5 – SDIN
14 – DC
15 – Reset
16 – CS
ESP32 Audio Kit
IO15 (labelled MTDO)
gives a working display + rotary encoder.
When relocating Reset, I noticed that the display does not seem to require it.
Why is there some electrical tape on it? Because the LEDs on the board, particularly D1, are obnoxiously bright.
Amplification is provided by 2x NS1450, “a Low EMI, Filterless, 3W Mono Class D Audio Amplifier”, with the caveat being that it’s only filterless if the wire to the speakers is less than 100mm.
Todo: build an enclosure, acquire some suitable speakers, have a look at what types of batteries this thing can manage.
Bill of materials so far:
ESP32-A1S Audio Kit – £10
3.12″ SSD1322 OLED display – £16
3x JST XH 2-pin connectors – £1.8
12x DuPont jumpers – £2
1x HW040 rotary encoder – £0.8
Took quite a while to find a permutation of GPIOs that let the amp, rotary encoder and the display work simultaneously.
It seems like there is some interaction between GPIOs that isn’t what you’d expect from the actual GPIO numbers.
Thoughts on this so far
First, the negatives:
Audio quality isn’t great. Higher frequencies are a bit mushy. Fine for building a little portable streaming speaker, though.
Documentation for this board is sparse. The manufacturer’s pages are all now 404s.
Green LED D1 is needlessly bright.
The GPIO assignments are bit mysterious [although to be fair, this is the first ESP32 project I’ve done]. Changing one thing causes others to mysteriously stop working. The 5 DIP switches on the board are confusing labelled – they are all labelled IO13 or IO15, but MTDI is IO12.
The firmware is not “done” yet. I have had the occasional core dump/reboot, but I expect this to improve with time.
It’s very cheap. £10 delivered to the UK.
It’s all in one – buttons, DAC, amp and charge controller – but with the caveat that the GPIOs are what they are, and that’s that.
What are the alternatives?
Raspberry Pi Zero W [£10] + HiFiBerry Pi Zero MiniAMP [£15.5] + an SD card. Identical power output specs, probably better audio quality. Would need to do something for battery management. More than twice the price. piCorePlayer would be the obvious OS to run on this.
ESP32 WROVER board + DAC + amp. Bit more work selecting the components to use but no fussing with GPIO confusion.
Olimex ESP32-ADF – very similar specs to the ESP32 Audio Kit. Can’t confirm that it works with squeezelite-esp32. Note that Espressif’s software framework is called “ESP-ADF” and this piece of hardware from Olimex is called “ESP32-ADF”, so have fun googling that!
What else could be done with an ESP32 Audio Kit?
Wifi intercom – a pair of these could work back-to-back with some kind of SIP stack running on each. Press a button, a call is set up to the other unit and audio starts flowing between them immediately. More than two and you’d need an external server to do the audio mixing [eg, Freeswitch]. Call setup times would have to be pretty rapid for this to work.
Figures are per minute. Tested with a cheap UT-25 type USB tester.
Playing through the speakers, volume level 24: 18mWh
Through the headphone jack at volume level 30: 18mWh
Idle, or “off”: 8mWh
Display off in “off” mode: same!
Changed clock mode to hh:mm [ie, no seconds]: 6mWh
Two surprises here: the OLED being off or on seemed to make no difference to the power consumption, and neither did running through the speakers vs. headphones. Waking up regularly to process display updates seems to be relatively expensive.
Living near a busy road, I have recently taken an interest in air quality. To that end, I have acquired a PMS5003 from Pimoroni to connect to a Pi Zero W that I had been auditioning Pi Core Player on. 1
The PMS5003 is matchbox-sized and works by means of a fan that draws air over a laser which shines at an optical sensor. Airborne particles interrupt the laser beam and thus the device can count them. I am not sure how it determines the sizes of the particles. If you’re worried about noise, don’t be – the fan is quiet to the point of being inaudible. The PMS5003 is powered from the Pi Zero W so in hardware terms this is a fair simple project to put together.
Pimoroni have written a Python library for the PMS5003 so that seems like the sensible place to start. The examples/all.py script that comes with the library seemed like a good starting point, but by default it outputs values once per second which is far more frequent than I have any use for, so I added a time.sleep statement to make the script pause for 30 seconds after each output:
data = pms5003.read()
Why Zabbix? It collects time-series and textual data, presents it in different ways and carries out actions when certain thresholds are met. I have extensive experience of Zabbix through using it at work and I already have it installed at home for monitoring my small network, so it seems like a good place to start.
Approach 1 – send values with zabbix_sender
The first approach I tried with Zabbix was to use zabbix_sender to send each value to Zabbix as it arrived from all.py. This consists of ~50 lines of Perl to convert the output of all.py to the form expected by zabbix_sender, and joins the two processes together with pipes. The advantage of this method is that the raw data does not need to be written to disk, which is a worthwhile consideration on an I/O-constrained platform running off a cheap MicroSD card.2 If zabbix_sender loses contact with the server, or pms5003-all.py dies, the whole thing aborts. I use a systemd unit file to keep the script running, but I found that systemd was preventing pmsprocessor.pl from exiting when either child dies, in contrast to what happens when running pmsprocessor.pl interactively. After throwing out this question to the collective genius of the #a&a IRC channel, TC dug up the IgnoreSIGPIPE option in systemd, which defaults to true. When set to false in the unit file, it all now behaves as expected!
The second approach I tried is to write the output of all.py to a log file, via the ‘ts’ command from moreutils. The Zabbix agent then reads this log file and sends interesting values to the server. Outputting the values every 30s results in about 2MB/day of data. The advantage to this approach is that it simplifies the pipeline – our data source and sink operate independently of each other and don’t need to know what state the other is in. The disadvantage is the additional I/O as mentioned above, and now I also have a growing log file to manage somehow. It’s not enough just to truncate the file; you have to stop the process, clear it down and restart it.
Handling data, server-side
I created nine Zabbix items to handle the 12 parameters output by all.py. What about the other three? The (atmos env) versions always appear to be the same as the first three parameters, so I am not bothering to store them separately. The zabbix_sender items are “trapper” type, and they match the arbitrarily chosen item names in pmsprocessor.pl.
There is much amusement to be found in this leaflet, from the black-on-dark-green text to the ingenious new abbreviation for litre, ‘lr’. Who is it published by? Surely a link to a website, an email address or god forbid a Facebook page would make sense for some sort of campaign in this day and age? A look at the bottom [which is difficult to see because of the shit picture I took] and all is clear:
“What can you do about this change?
Nothing because you live in Bolton”
The campaign has been abandoned already and this leaflet is just to let everybody know.
Found this in Lidl this evening. £1.49/568ml, 7.3% ABV. A bit sweet for my liking, bears more than a passing resemblance to Henry Westons Vintage, although about 1% less ABV. Much nicer than their regular cider which seems to have gone off the boil (in my opinion) since a recent packaging change, although it could be my imagination.
3.5 out of 5? Doesn’t sound particularly enthusiastic, but then I’m a hard man to please. I will probably end up buying it again.