A few years ago I commented on how a smaller board makes more projects possible. Then it was comparing an Arduino Uno with a Nano.
Now I'm looking at the ESP8266 boards I've been using and thinking about the practicalities of building a wearable sensor/tracker. While I can never hope to replicate the level of integration in a commercial project I am looking for something to keep it as small as possible.
I had been working on some sneaky use of the pins on an ESP-01 module in my first prototype but it's still quite chunky and the 8-pin header makes it quite tall.
Time marches ever on and now decently packaged ESP8285 modules have appeared in the usual places. These are a minor update to the old faithful ESP8266 with integrated flash memory but otherwise backwards compatible. The integrated flash makes for a slightly smaller module and offers a certain guarantee of the quality of that flash as it's coming straight from Espressif. Some cheap ESP8266 based modules come with questionable quality flash memory.
Here's a Wemos D1 Mini, ESP-01 and the new ESP-M2. It doesn't save you much over the ESP-01 but it delivers a ton more pins and can be surface mount soldered onto a board.
Now all I need to do is make a breakout board so I can do some testing with it before I have some boards made.
D1 Mini breakout
I'm a big fan of the Wemos D1 Mini and Mini Pro as the basis of microcontroller projects.
They're cheap, the ESP8266 chip is powerful and can be used with several development environments/languages. You can program them in C/C++ with the official Espressif SDK, but also the Arduino IDE where support is excellent. They'll also run more modern interpreted languages like MicroPython and Lua, again with good support if some memory limitations.
Most of the time, people consider ESP8266 boards for IoT type projects but you don't have to use the WiFi, it can be switched completely off and at heart they're a 80/160Mhz 32-bit device with enough GPIO to handle many small projects. About the only thing they suck at is low power/sleep which is poorly implemented by comparison with other modern boards.
What they do lack is such a large ecosystem of 'shields' and breakouts. Wemos make their own but they're all a bit 'Arduino sensors 101' type things.
I saw these chunky screw terminal breakout boards mentioned on Twitter and while I have no immediate use for them I picked a couple up because I know they'll be great for prototyping stuff.
They're cheap, the ESP8266 chip is powerful and can be used with several development environments/languages. You can program them in C/C++ with the official Espressif SDK, but also the Arduino IDE where support is excellent. They'll also run more modern interpreted languages like MicroPython and Lua, again with good support if some memory limitations.
Most of the time, people consider ESP8266 boards for IoT type projects but you don't have to use the WiFi, it can be switched completely off and at heart they're a 80/160Mhz 32-bit device with enough GPIO to handle many small projects. About the only thing they suck at is low power/sleep which is poorly implemented by comparison with other modern boards.
What they do lack is such a large ecosystem of 'shields' and breakouts. Wemos make their own but they're all a bit 'Arduino sensors 101' type things.
I saw these chunky screw terminal breakout boards mentioned on Twitter and while I have no immediate use for them I picked a couple up because I know they'll be great for prototyping stuff.
Made it into Hackaday
An acquaintance picked up on the Chindōgu and I made it into Hackaday, woohoo!
Things you don't normally need to worry about
Something we almost never think about nowadays with tech is 'how do I switch it on?', or vice-versa.
Decades of product design and familiarity means we 'just know' how things are supposed to turn on and off.
Except it's a big personal annoyance of mine that the Raspberry Pi has no on/off switch. I know why they've done this, it's all about saving a tiny bit on the cost and keeping the price down as low as possible.
So you're either plugging and unplugging the USB lead, switching it off at a wall socket or maybe you've bought a specific USB lead with an inline switch.
All of which is frankly rubbish. Modern computers aren't meant to be unceremoniously shut off and doing so can corrupt files and in the worst case break the file system badly enough they won't boot. It's unlikely but it does happen. So since the mid-1990s, we've expected computers to have 'smart' power switches that allow for a clean startup and shutdown process. If you shut a Raspberry Pi down, it still sits there consuming power, albeit lots less, until such time as you manually remove power.
I hate this and for anything where you expect a Pi, especially in a portable device, to be used by everyday people it needs an on/off switch that conforms to consumer norms.
I'm not the only person to think like this and there have been a few products over the years catering to this. If you want an off the shelf solution, the Pimoroni on/off shim looks good, but it doesn't fit my need to control power of differing voltages to other things.
So I've used a more generic smart power switch and made my own solution.
When you break it down, the accepted norm of a power button is now something like this.
Once this is working reliably, you need to make a systemd service pointing at this script to make sure it starts when the Pi boots up.
Create the file /lib/systemd/system/gpioshutdown.service
..which should show it as running and make sure pushing the button shuts the Pi down.
So far so good and there are a ton of examples like this out there. However it doesn't fully power the Pi off and most how-tos don't cover this.
For that you need another connection to the Pololu module and another script.
The Pololu module has an 'OFF' connection and if you drive this high it will shut off power completely. I have connected this to GPIO pin 27.
To make this go high once the Pi has finished shutting down, create the file /lib/systemd/system-shutdown/gpiopoweroff.sh
So now you have made the Pi behave like people expect a normal device to work, safely.
For extra credit it would be possible to make the shutdown script change/replace some files before shutting down if you do a 'long press' for a 'factory reset' but I haven't needed this yet.
Decades of product design and familiarity means we 'just know' how things are supposed to turn on and off.
Except it's a big personal annoyance of mine that the Raspberry Pi has no on/off switch. I know why they've done this, it's all about saving a tiny bit on the cost and keeping the price down as low as possible.
So you're either plugging and unplugging the USB lead, switching it off at a wall socket or maybe you've bought a specific USB lead with an inline switch.
All of which is frankly rubbish. Modern computers aren't meant to be unceremoniously shut off and doing so can corrupt files and in the worst case break the file system badly enough they won't boot. It's unlikely but it does happen. So since the mid-1990s, we've expected computers to have 'smart' power switches that allow for a clean startup and shutdown process. If you shut a Raspberry Pi down, it still sits there consuming power, albeit lots less, until such time as you manually remove power.
I hate this and for anything where you expect a Pi, especially in a portable device, to be used by everyday people it needs an on/off switch that conforms to consumer norms.
I'm not the only person to think like this and there have been a few products over the years catering to this. If you want an off the shelf solution, the Pimoroni on/off shim looks good, but it doesn't fit my need to control power of differing voltages to other things.
So I've used a more generic smart power switch and made my own solution.
When you break it down, the accepted norm of a power button is now something like this.
- If you push it and the device is turned off, it will turn on
- If you push it and the device is turned on, it will turn off in a prompt but orderly fashion
- (optional) If you push and hold some kind of reset/setup process may occur
The module I've gone with is the Pololu "Mini Pushbutton Power Switch with Reverse Voltage Protection, LV", which ticked my boxes. It has a very low current draw in the 'off' state and can be controlled by supplying signals on different pins. I've previously made myself little arrangements with MOSFETs for this but an off the shelf module keeps things compact.
Switching things on with the module is simple. Connect the incoming power supply to VIN & GND, feed that out to the Pi (and anything else) from VOUT & GND. Use a momentary switch to connect pin 'A' to ground and the power comes on. Pushing it again does nothing as the module latches in the on state.
Switching off is a bit more complicated as it has to be driven by the Raspberry Pi.
What I've done to achieve this is use a DPST button with the second 'pole' connecting a Pi GPIO pin to ground. So when you push the button the Pi knows you are asking it to shut down. Which needs a script. A script that needs to always run at startup.
I've opted to use a very basic systemd service for this, which is a very common way to shut down the Pi with a button, there are multiple how-tos around.
First create a file called 'gpioshutdown.sh' somewhere you put your own scripts, I put this in '/home/pi/scripts/' and used GPIO pin 17 but you can change that easily.
#!/bin/bash
gpioShutdownPin="17"
# export GPIO pin and set to input with pull-up
if [ ! -e /sys/class/gpio/gpio$gpioShutdownPin/ ]
then
echo $gpioShutdownPin > /sys/class/gpio/export
echo "in" > /sys/class/gpio/gpio$gpioShutdownPin/direction
echo "high" > /sys/class/gpio/gpio$gpioShutdownPin/direction
fi
while [ true ]
do
if [ "$(cat /sys/class/gpio/gpio$gpioShutdownPin/value)" == '0' ]
then
echo "$gpioShutdownPin low, Raspberry Pi Shutting Down!"
sleep 1
sudo /bin/systemctl poweroff
exit 0
fi
sleep 1
done
Now make the script executable.
sudo chmod +x /home/pi/scripts/gpioshutdown.shYou should test this works nicely by running it manually and checking that when you push the power button it shuts the Pi down. Don't do the next step until you've debugged this as you might find your Pi immediately shuts down every time you power it up.
Once this is working reliably, you need to make a systemd service pointing at this script to make sure it starts when the Pi boots up.
Create the file /lib/systemd/system/gpioshutdown.service
[Unit]
Description=GPIO driven shutdown service
After=multi-user.target
[Service]
Type=idle
ExecStart=/home/pi/scripts/gpioshutdown.sh
[Install]
WantedBy=multi-user.target
Then enable it with the following
sudo systemctl daemon-reload
sudo systemctl enable gpioshutdown.service
Reboot the Pi, check the status of this new service with..
sudo service gpioshutdown status
So far so good and there are a ton of examples like this out there. However it doesn't fully power the Pi off and most how-tos don't cover this.
For that you need another connection to the Pololu module and another script.
The Pololu module has an 'OFF' connection and if you drive this high it will shut off power completely. I have connected this to GPIO pin 27.
To make this go high once the Pi has finished shutting down, create the file /lib/systemd/system-shutdown/gpiopoweroff.sh
#!/bin/sh
gpioPowerOffPin="27"
if [ "$1" = "poweroff" ]; then
/bin/echo $gpioPowerOffPin > /sys/class/gpio/export
/bin/echo out > /sys/class/gpio/gpio$gpioPowerOffPin/direction
/bin/echo 1 > /sys/class/gpio/gpio$gpioPowerOffPin/value
fi
Make it executable
sudo chmod +x /lib/systemd/system-shutdown/gpiopoweroff.shAny scripts in this directory get run when the system has reached the final stage of shutting down and has remounted the file system read-only, so is safe to power off.
So now you have made the Pi behave like people expect a normal device to work, safely.
For extra credit it would be possible to make the shutdown script change/replace some files before shutting down if you do a 'long press' for a 'factory reset' but I haven't needed this yet.
Chindōgu - Raspberry Pint presentation
I did a presentation about my Raspberry Pi powered Sony Watchman project at Raspberry Pint, a London Raspberry Pi meetup.
Chindōgu - part 2
This weekend I spent a chunk of time building a second Watchman based device.
The initial plan was to add more resource by fitting a Raspberry Pi 3A+ giving it a big performance increase but after desoldering some of the headers from the board and poking around I just couldn't fit it in the case. It might be possible with extreme measures like removing the camera header and soldering the ribbon cable directly to the board but this feels like it would be a failure anyway.
I ended up doing the simple incremental change of fitting a camera and sticking with the Raspberry Pi Zero W and it can just about handle video calling with Ekiga.
As standard the Pi Zero camera comes with too short a cable for this to work at all and there's no way a full size Pi camera will fit in the space under the CRT. You can't buy Zero camera extension cables but you can convert them to a full size ribbon and back again so this is what I ended up doing. Three cables and two joints but it still works just fine. It's routed all the way round the left hand side of the case and emerges back on the right side. This is so it's possible to maintain the original slide on/off switch and volume control.
The tuner wheel has been converted to a 'rocker' type switch with a couple of tactile switches, epoxy and luck. I also made a better job of the power switch, fitting an actual slide switch rather than a momentary one and relying on a long cruddy lever to push it. This all works much better than my first prototype and I may go back and rework it in this style. I'll need to buy more camera cables/joints though.
I'm still yet to do anything about audio. I dug out a little USB microphone dongle and may see if I can slim it down and hardwire it to a USB cable plugged into the port on the Zero. I'm not keen on desoldering the USB port, just in case.
For audio output I need to test the PWM output method and build a suitable filter.
Oh and then there's all the software I need to tweak, while Ekiga works there's no realistic way to control it so I'm dialling in from a laptop to test.
I ended up doing the simple incremental change of fitting a camera and sticking with the Raspberry Pi Zero W and it can just about handle video calling with Ekiga.
The tuner wheel has been converted to a 'rocker' type switch with a couple of tactile switches, epoxy and luck. I also made a better job of the power switch, fitting an actual slide switch rather than a momentary one and relying on a long cruddy lever to push it. This all works much better than my first prototype and I may go back and rework it in this style. I'll need to buy more camera cables/joints though.
I'm still yet to do anything about audio. I dug out a little USB microphone dongle and may see if I can slim it down and hardwire it to a USB cable plugged into the port on the Zero. I'm not keen on desoldering the USB port, just in case.
For audio output I need to test the PWM output method and build a suitable filter.
Oh and then there's all the software I need to tweak, while Ekiga works there's no realistic way to control it so I'm dialling in from a laptop to test.
Obscure Arduino tips #2
This one may not be so obscure if you're a competent C++ programmer, but if you're somebody just finding their way in writing their own Arduino libraries it can be a major roadblock.
It is not uncommon for libraries that allow you to do work triggered by external events or that happen asynchronously to use callback functions. Your code will work fine but once you start turning that sketch into into a C++ class as a library, you won't be able to compile it.
The example I'll use is the ESP8266 WiFi scanning class, as this is where I encountered the problem.
While my code was a monolithic Arduino sketch I could kick off a scan with code like this.
Once you create a class, you can in principle have multiple instances of that class. Not to get into the detail of C++ classes (because I'm not a real C++ programmer) but when you need to refer to the specific instance of a class there is a 'magic' extra parameter 'this'.
A lot of the time you can ignore 'this' as a naive Arduino programmer when you make a library but you will bash up against it eventually, probably when you go to use a callback or function from another library.
You can use one of the standard C++ functional adaptors to help you, std::bind, which allows you to create a new function referring to the old, but change the arguments to the function. This allows you to pass 'this' into the callback without changing your original function at all.
So the code now looks like this.
For every argument in your original function you need a 'std::placeholder::_X' argument. So add extra parameters of 'std::placeholders::_2', 'std::placeholders::_3' etc. etc.
That's it. Unless you're actually trying to use a C library when it gets more convoluted.
It is not uncommon for libraries that allow you to do work triggered by external events or that happen asynchronously to use callback functions. Your code will work fine but once you start turning that sketch into into a C++ class as a library, you won't be able to compile it.
The example I'll use is the ESP8266 WiFi scanning class, as this is where I encountered the problem.
While my code was a monolithic Arduino sketch I could kick off a scan with code like this.
WiFi.scanNetworksAsync(myCallbackFunction,true);Where 'myCallbackFunction' is just the name of the callback function in my sketch. This is nice and easy.
Once you create a class, you can in principle have multiple instances of that class. Not to get into the detail of C++ classes (because I'm not a real C++ programmer) but when you need to refer to the specific instance of a class there is a 'magic' extra parameter 'this'.
A lot of the time you can ignore 'this' as a naive Arduino programmer when you make a library but you will bash up against it eventually, probably when you go to use a callback or function from another library.
You can use one of the standard C++ functional adaptors to help you, std::bind, which allows you to create a new function referring to the old, but change the arguments to the function. This allows you to pass 'this' into the callback without changing your original function at all.
So the code now looks like this.
WiFi.scanNetworksAsync(std::bind(&MyClass::myCallbackFunction,this,std::placeholders::_1),true);The callback function in WiFi.scanNetworksAsync is passed an integer telling it how many networks were found and to make sure that is there in the new function it needs a placeholder 'std::placeholders::_1'.
For every argument in your original function you need a 'std::placeholder::_X' argument. So add extra parameters of 'std::placeholders::_2', 'std::placeholders::_3' etc. etc.
That's it. Unless you're actually trying to use a C library when it gets more convoluted.
ESP-Now BATMAN data encapsulation
I've been working on other stuff recently but haven't totally forgotten my mesh network library.
Over the last couple of days I've been fiddling around with encapsulating data in a way that's very easy for somebody writing an Arduino sketch.
The first third of this is done in that I've written a set of overloaded functions for the common Arduino data types including both char arrays and the much maligned String type.
There's a single function 'add' that starts building a packet for you and works out how to pack the data based off the type of the single argument.
To add some data then send it (flooding the network) it's just a few lines of code.
Both functions return true or false depending on whether they are successful. You can add as many values as will fit in an ESP-Now packet and if there's no space left, 'add' returns false.
I've written a small sketch to send random amounts of random data of random types every ten seconds. It can fit about 30 values in each packet depending on exactly which types are involved.
This has been running for about eighteen hours without any hiccups so I'm happy with it.
My next task is to write a set of functions to give access to this data from a sketch when it arrives. I've written the logic to decode packets and print out the contents nicely but need to think through how to present it to a person writing their own code with the library.
It's not as easy as the 'add' function as I can't really overload it, unless I do something like make people send pointers to their variable. Which I don't think is very friendly, however efficient it might be.
There's also the matter of sending messages to specific nodes, which means a whole load more public functions to find out about the mesh and allow the retrieval of MAC addresses. Which again feels unfriendly because you're going to end up with hard coded MAC addresses unless somebody layers on their own way of mapping MAC addresses to their various nodes' identities or functions.
I might implement my idea of giving nodes names which are simple text strings. Who cares what the MAC address is after all, it's what code is running that matters.
So you could call one node 'Daylight sensor' and another 'Light switch' and the former tells the latter to switch on when it gets dark. I'm not expecting this library to be used for IoT type things like this but I think it's a good example of why having human readable names is desirable. I could just add the name to the 'status' protocol packets I already send.
It's slow discovery of requirements as I write that demonstrate why this isn't a professional piece of software engineering and also why it's taking me so long. :-)
Over the last couple of days I've been fiddling around with encapsulating data in a way that's very easy for somebody writing an Arduino sketch.
The first third of this is done in that I've written a set of overloaded functions for the common Arduino data types including both char arrays and the much maligned String type.
There's a single function 'add' that starts building a packet for you and works out how to pack the data based off the type of the single argument.
To add some data then send it (flooding the network) it's just a few lines of code.
mesh.add(value1);
mesh.add(value2);
mesh.add(value3);
mesh.add(value4);
mesh.add(value5);
mesh.add(value6);
mesh.send();
Both functions return true or false depending on whether they are successful. You can add as many values as will fit in an ESP-Now packet and if there's no space left, 'add' returns false.
I've written a small sketch to send random amounts of random data of random types every ten seconds. It can fit about 30 values in each packet depending on exactly which types are involved.
This has been running for about eighteen hours without any hiccups so I'm happy with it.
My next task is to write a set of functions to give access to this data from a sketch when it arrives. I've written the logic to decode packets and print out the contents nicely but need to think through how to present it to a person writing their own code with the library.
It's not as easy as the 'add' function as I can't really overload it, unless I do something like make people send pointers to their variable. Which I don't think is very friendly, however efficient it might be.
There's also the matter of sending messages to specific nodes, which means a whole load more public functions to find out about the mesh and allow the retrieval of MAC addresses. Which again feels unfriendly because you're going to end up with hard coded MAC addresses unless somebody layers on their own way of mapping MAC addresses to their various nodes' identities or functions.
I might implement my idea of giving nodes names which are simple text strings. Who cares what the MAC address is after all, it's what code is running that matters.
So you could call one node 'Daylight sensor' and another 'Light switch' and the former tells the latter to switch on when it gets dark. I'm not expecting this library to be used for IoT type things like this but I think it's a good example of why having human readable names is desirable. I could just add the name to the 'status' protocol packets I already send.
It's slow discovery of requirements as I write that demonstrate why this isn't a professional piece of software engineering and also why it's taking me so long. :-)
Subscribe to:
Comments (Atom)



