Adopting the kibi

In dial-up Internet days we surmised that a 56K modem transferred data at 56 000 bits per second (or 56 kbps). Dividing this speed by 10 gave a result of 5.6 kilobytes per second (or 5.6 kBps). Thus, it was easy to calculate download times for files with the following formula, given that their sizes were expressed in kB.

download time in seconds = file size in kB / modem speed in kBps

This convention was used mostly on bulletin board systems, where users were obsessed with how fast they could download files. It was also common to distinguish between 1000 bytes (1 kB) and 1024 bytes (KB) in writing by using different cases of the letter k.

I continued to use this convention until recently when a reddit post made me aware of IEC 60027-2. This standard, set in 1999, introduced the kibi and disambiguated the units used for expressing data sizes. The blog post ‘The MB Confusion’ describes the standard better than the related Wikipedia article, and explains why its adoption is slow.

Trap with `gmtime` and other time functions

From man gmtime:

The gmtime() function converts the calendar time timep to broken-down time representation, expressed in Coordinated Universal Time (UTC). It may return NULL when the year does not fit into an integer. The return value points to a statically allocated struct which might be overwritten by subsequent calls to any of the date and time functions. The gmtime_r() function does the same, but stores the data in a user-supplied struct.

ESP8266: Always call ‘wifi_wps_enable’ before ‘wifi_set_wps_cb’

For WPS to work on the ESP8266 and using the non-OS SDK, be sure to call wifi_wps_enable() before setting the callback function with wifi_set_wps_cb().

For example:

void wps_start()
{
    wifi_wps_enable(WPS_TYPE_PBC);
    wifi_set_wps_cb(wps_callback);
    wifi_wps_start();
}

In the callback itself, call wifi_wps_disable() before wifi_station_connect().

For example:

void wps_callback(int status)
{
    wifi_wps_disable();
    wifi_station_connect();
}

ESP8266: Flash images at more than 115200 bps

If esptool is limited to 115200 bps when flashing your firmware to an ESP8266, you can try to increase the bitrate by following the steps below.

  1. Locate esptool.py for your installation. On Debian and Ubuntu, this file is located in /usr/share/esptool.
  2. Edit esptool.py and change the value on the line starting with ESP_ROM_BAUD from 115200 to the maximum of 921600 or your preferred baud rate.

ESP8266: Always call `uart_init` before `gpio_init`

I don’t remember if this information is in the documentation, but having spent a few hours to figure things out, I think it is important to record it here.

For example:

void ICACHE_FLASH_ATTR user_init(void)
{
    /*
     * Always call uart_init() before gpio_init()
     */
    uart_init(BIT_RATE_115200, BIT_RATE_115200);

    gpio_init();
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
    PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0);
    GPIO_DIS_OUTPUT(BUTTON_PIN);

    os_timer_disarm(&os_timer);
    os_timer_setfn(&os_timer, &main_on_timer, (void *)NULL);
    os_timer_arm(&os_timer, 50, 1);
}

Remember, C# does not have an exponent operator

I want to share an anecdote of confusing programming language features as a caution to my fellow programmers.

Reviewing our application logs, I noticed that the API requests were interspersed with pauses of irregular durations. The interval between each successful request is supposed to be fixed; and the interval between each failed request, exponentially growing up to a configured maximum. For example, after the first failure, the application pauses for 5 seconds; after the second, 10 seconds; after the third, 20 seconds; and so on. However, the logs showed intervals of 15 seconds, 30 seconds, 60 seconds, then 0 second—this was odd, especially the last 0 second.

This behaviour remained a mystery even after a colleague helped me look for errors in our C# code. We reviewed this line of code together but found nothing wrong; it clearly showed that on every retry the base interval was doubled.

int tmp = RetryIntervalInMs * (2 ^ retryCounter);

By coincidence, I needed the same calculation of intervals in my hobby embedded project. As I started to write the code that evening, I made an epiphanic realisation.

In our C# code, we had used the ^ operator, interpreting it as raised to the power of. This is true in some languages – BASIC, for example ­– but not in C#, where it is actually the operator for XOR. I was amazed that this mistake escaped our scrutiny..

Once we had identified the cause, it was easy to fix the error by using the correct library method Math.Pow(int, int).

int tmp = RetryIntervalInMs * (int)Math.Pow(2, retryCounter);

Avoiding time drift in virtual machines

If you use a virtual machine, you might notice that its system time starts to lag after it has been powered on for a while. To solve this problem, it is useful to run an NTP (Network Time Prococol) client within the VM to have its time synchronised regularly.

On Windows, automatic time synchronisation is enabled by default and uses the server time.windows.com.

On a Debian-based Linux distribution, you can install NTP with apt install ntp and start it with sudo systemctl start ntp.

Caveat with AddInitialRequestCultureProvider() in ASP.NET Core

AddInitialRequestCultureProvider() in ASP.NET Core localisation seems to have an undefined behaviour when it is used in both services and application builder configurations to add a custom RequestCultureProvider.

If you want to use a custom RequestCultureProvider to customise how the applicable culture is determined for an incoming HTTP request (for example, by looking up the chosen culture of a user in the database), you must configure the provider in the services collection as described in the section ‘Use a custom provider’.

You must also call IApplicationBuilder.UseRequestLocalization(), but you must not pass any argument to the method. If a RequestLocalizationOptions argument is specified here, it supersedes what is configured in the services collection, and your custom RequestCultureProvider does not work.

Typically, you configure the middleware in the application builder before the services. But in order to emphasise the significance of setting the localisation options only in the services configuration, I have reversed this order on purpose.