It is the 1817th of March 2020 (aka the 19th of February 2025)
You are 3.138.32.171,
pleased to meet you!
mailto:blog-at-heyrick-dot-eu
ESP32 SDK craziness
After getting concerned about what, exactly, was going on with the weirdness of the Arduino SDK built into the ArduinoDroid app on my phone, I tried to install it on my little Android portable. It wouldn't work, as the compiler is native code and it required a 64 bit processor. My portable can do this, the processor identifies as a sun50iw10 which is the nerdy way of saying it's an Allwinner A133 which is a quad-core ARM-v8 Cortex-A53 clocking up to 1.6GHz. A rather impressive processor inside a freebie device, don't you think?
Unfortunately it is coupled with Android "Go edition" which is strictly 32 bit.
But that's okay, while I can't develop on this device, it did permit me to extract the apk file from the app installation, to then rename it as .zip, and to look inside. What caught my interest was a bunch of music files within the /res/raw subdirectory of the app.
MP3 files? In a code editor? Who's dumb enough to fall for that?
A bunch of MP3 files? No, that's just a silly subterfuge. They are 7zip files containing various toolchains. The "f7.mp3" is the one for ESP32.
Upon extracting that, we can see that the provided ESP32 Arduino IDE is version 1.0.3 which is ancient.
That being said, it appears as if ArduinoDroid has been abandoned, last updated nearly five years ago and, I believe, no longer compatible with Android 14. It's a shame as while it does have bugs, it works and it's pretty fast and there's nothing else that offers the same. Plus the adverts aren't intrusive like in some apps so you can actually get some work done. Better yet, it's all local so you aren't tied to needing to access some random cloud service.
The problem isn't just that the provided Arduino IDE is ancient, the problem is also that there's a lot of stuff on the internet that contradicts other stuff. So here are some random lessons learned along the way.
Using SPIFFS
This little filing system is far better for using with the internal SPI Flash than something like FAT. For starters, it is far simpler so doesn't have the overheads and wastage of the FAT structure; but perhaps the most important is that it supports wear levelling to make best use of your flash rather than overwriting the same bits as FAT would.
Note that SPIFFS is deprecated these days as it gets buggy with files over 2MB, but for small files it's fine. The alternative is "LittleFS" and I believe it's a drop-in replacement (replace "SPIFFS" with "LittleFS") but I have not tested this.
You can find tutorials of how to list files in the partition, and maybe even some telling you how to read and write strings of text to a file, but if it's a data block, well...
Using SPIFFS:
Put these at the top, in this order:
#include <FS.h>
#include <SPIFFS.h>
Mounting an SPIFFS partition:
if ( !SPIFFS.begin(true) )
{
// do whatever on mount FAILURE
}
If you pass 'true' to the begin function, it will format the SPIFFS partition for use if mounting fails. If you just do .begin() then this won't be automatically done.
Opening and reading binary data from a file:
File fp;
// Open for READING
fp = SPIFFS.open("/filename.ext", "rb");
if ( fp )
{
fp.read( (byte *)&myarray, sizeof(myarray) );
fp.close();
}
The read method takes a pointer to an array and the array size (in bytes) to load it in.
If this fails with something like "error: no matching function for call to
'fs::File::read(blah-de-blah)" then ensure that you are correctly casting the array as a sequence of bytes using (byte *) prior to the array pointer.
I did find a tutorial that mentioned readBytes, but that didn't seem to want to work for me. It's probably an alias for the read method anyway...
For writing to a file, it's exactly the same but using the write method. Note, also, that the open method is "wb" instead of "rb" this time. That's "w" for write and "b" for binary. Just like fopen() in C's standard library.
File fp;
// Open for WRITING
fp = SPIFFS.open("/filename.ext", "wb");
if ( fp )
{
fp.write( (byte *)&myarray, sizeof(myarray) );
fp.close();
}
I understand that SPIFFS is itself a flat filesystem. Paths begin with "/" because that's just expected, but don't expect to be able to support stuff in directories. It isn't FAT...
Additionally, as this is something from the Unixy world, it's annoyingly case sensitive.
Some device specific functions
You'll need to include this at the top.
#include "Esp.h"
If you aren't using Windows, it'll be case sensitive, so capital E, small s, small p.
Then, to do something like force the device to restart, simply call the appropriate function:
ESP.restart();
Yup, capital E, S, and P. It's a joke how inconsistent all this stuff is, given that case matters.
Generally, library functions/methods are named such that the first word is in lowercase and all subsequent words have an initial capital, like ESP.getCpuFrequencyMhz(). This looks really weird to me, but at least it's not that god-awful szHungarian bNotation fThat wBlighted szVisualBasic.
Getting time from NTP server
Simply use the time() function, you don't need all of the complicated add-on libraries.
If you're connected to the internet, time() will pick up on the (mostly) correct time eventually.
Until it has a time, it'll return the 1st of January 1970 (when time starts), so you may want to do something like this:
while ( year < 2025 )
{
time(&now);
localtime_r(&now, &tm);
year = tm.tm_year + 1900;
delay(25);
}
(with year=int, now = time_t, and tm = tm). This will simply loop reading the current time until it returns something where the year is at least 2025, which means it'll be a correct time. Depending on connectivity, it might take a moment to get a valid time.
It may be that my SDK here is ancient, but the values don't seem to 'stick', the next call to time() might return 1970, so best to drop this into a get-the-time function.
To get the correct time, you'll need to set up your timezone. To do this, you'll need this code somewhere at the start, prior to enquiring the time.
// assumes you did #include "time.h" at the top
configTime(0, 0, "fr.pool.ntp.org");
setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1);
// use "GMT0BST,M3.5.0/1,M10.5.0" for the UK
tzset();
This specifies the NTP server "fr.pool.ntp.org" (as I live in France; for the UK it would be "uk.pool.ntp.org").
You may read that it's possible to simply set the UTC offsets in the configTime() function. DO NOT DO THIS. While the string given to setenv() may look like gibberish, it provides the appropriate times of when to change to/from daylight savings. If you don't do this, it'll switch at the times appropriate for the United States.
You can find the appropriate string for your timezone.
Webserver URI parameters
Using the built-in WebServer.h library, you can set up handlers like:
server.on("/", handle_root);
But how do you deal with parameters given in the URI?
void handle_root()
{
String argn;
String argp;
int argc;
// How many arguments
argc = server.args();
// If there is at least one, read the first
if ( argc > 0 )
{
argn = server.argName(0);
argp = server.arg(0);
}
// rest of the code to output
// something to the client
}
If you called, say, http://192.168.1.123/?this=that then argc would be 1, argn would be "this", and argp would be "that".
The letterbox gizmo
Here I am finishing the code earlier today. It now records and can show the last sixteen events, which means the last time the letterbox was accessed (once for open, once for close). It will display 'today' in bold to make it stand out.
Koding in the Citchen.
I think the software is more or less complete now. The harder part is going to be fitting the switches inside the letterbox itself. I might, for a quick hack to see if it works, put them in place using hot-melt glue. It's a bit icky, but a lot less hassle than drilling into metal and trying to work out how/where to make brackets to hold things in the right place(s).
But it's like 3½°C and chucking it down, so... very much not today.
Putting switches in here is going to be the tricky part.
Linky weirdness
My subscribed amount of electricity is 9kVA (roughly 9kW). Here in France, how much juice you want to suck at any one time affects how much your service charge is. We used to be on 6kW, but mom learned the hard way that 6kW isn't really enough to run a little electric oven (2kW), the kettle (2kW), and flush the toilet (which may cause the 2kW water pump to cut in). Couple that with various incandescent bulbs that would have been in use at the time and, well, we'd probably have hit around 6.5kW. Click, darkness.
So we upgraded to 9kW, paid a little more, and now there's a safety margin.
So here is the Linky's screen showing that I'm subscribed to 9kW (or 9kVA as it's all Volt-Amps these days because that's a more accurate reflection of power consumption).
What I'm paying for.
And here is the meter telling me it'll trip out at 9000VA. There is apparently a margin of around 10-20% (because volts and stuff), though I haven't tested this nor do I particularly want to.
What I'm supposed to get.
And here, yesterday, was an abnormally high consumption. The car is charging, the water heater was on for a bath, and I flicked the kettle on for a well-needed cuppa.
Might not seem like much, but that would be £2 per hour.
Do you see that little icon? The sort-of-mountain one? That technically warns of over-consumption, and it appears when I go over around 6,000. Push too far, the meter will trip out (and I'll need to reset it manually).
Why? Why is this appearing at six instead of nine?
Note: I'm old fashioned. I think in kilowatts. I will mention kW in the following text, however the meter displays VA. For most intents and purposes, just think that a VA is the same as a watt. The hows-and-whys of where they differ (watts are real power, VA is apparent power) gets complicated quickly (like power factors of resistive and inductive loads), so just assume they're the same thing and handwave the technical stuff, okay? ☺
I just did a test. Turning on various appliances in the kitchen to get around about 3kW on each set of sockets (they're rated 3,680W, or 16A × 230V) gave me around 5.8kW consumption, with the icon showing. I then flicked on the water heater and the consumption hit an all-time high (for me) of just under seven and a half kilowatt. So, clearly my Linky wasn't going to trip out for going over 6kW.
Actually, I fiddled turning stuff on and off and it seems to appear over about 5.5kW. So I think maybe this icon might actually be warning of an unusually high consumption rather than "Danger, Will Robinson, Danger!".
This could be calculated by remembering and averaging consumption over the past whatever duration, or it could simply be something as basic as flagging up if one is over 65% of their rated tariff.
Keep this for posterity - probably the most I'll ever draw.
Your comments:
Please note that while I check this page every so often, I am not able to control what users write; therefore I disclaim all liability for unpleasant and/or infringing and/or defamatory material. Undesired content will be removed as soon as it is noticed. By leaving a comment, you agree not to post material that is illegal or in bad taste, and you should be aware that the time and your IP address are both recorded, should it be necessary to find out who you are. Oh, and don't bother trying to inline HTML. I'm not that stupid! ☺ As of February 2025, commenting is no longer available to UK residents, following the implementation of the vague and overly broad Online Safety Act. You must tick the box below to verify that you are not a UK resident, and you expressly agree if you are in fact a UK resident that you will indemnify me (Richard Murray), as well as the person maintaining my site (Rob O'Donnell), the hosting providers, and so on. It's a shitty law, complain to your MP. It's not that I don't want to hear from my British friends, it's because your country makes stupid laws.
You can now follow comment additions with the comment RSS feed. This is distinct from the b.log RSS feed, so you can subscribe to one or both as you wish.
This web page is licenced for your personal, private, non-commercial use only. No automated processing by advertising systems is permitted.
RIPA notice: No consent is given for interception of page transmission.