On the ESP8266 ESP-01, CH_PD
must be pulled HIGH at boot for the chip to work. A 10K resistor can be used for the pull-up.
Some variants of the ESP-01 module already have CH_PD
in the HIGH state.
On the ESP8266 ESP-01, CH_PD
must be pulled HIGH at boot for the chip to work. A 10K resistor can be used for the pull-up.
Some variants of the ESP-01 module already have CH_PD
in the HIGH state.
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);
A VARCHAR
value that is in scientific notation can be converted to a DECIMAL
value by first converting it to a REAL
value, then to the DECIMAL
value.
For example:
DECLARE @s VARCHAR(10) = '9.0E-4';
SELECT CONVERT(DECIMAL(8, 4), CONVERT(REAL, @s));
This also works with CAST
.
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
.
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.
This post answers the question ‘How can you minimize the impact of the persistence layer on your domain models?’ posted on reddit /r/dotnet, showing how I would implement a solution in the purest OOP [sic] possible with C#.
(Note: The factory and persistence classes are simplified for demonstration purposes.)
So here is the code. It is tested with .NET 5 on Windows 10.
using System;
using System.Data;
using System.Diagnostics;
namespace Example.Domain
{
// Let's pretend this namespace is in a separate assembly so that
// the 'internal' scope modifier applies
public interface IAccount
{
string Username { get; }
bool IsEnabled { get; }
void Disable();
}
public interface ISavedAccount : IAccount
{
int Id { get; }
void Enable();
}
// Not accessible from outside assembly
internal abstract class AccountBase : IAccount
{
public string Username { get; protected set; }
public bool IsEnabled { get; protected set; }
internal AccountBase(string username)
{
Username = username;
}
public void Disable()
{
IsEnabled = false;
}
}
// Not accessible from outside assembly
internal class MyAccount : AccountBase
{
internal MyAccount(string username)
: base(username)
{
}
}
// Not accessible from outside assembly
internal class MySavedAccount : AccountBase, ISavedAccount
{
public int Id { get; private set; }
internal MySavedAccount(int id, string username, bool isEnabled)
: base(username)
{
Id = id;
if (isEnabled)
{
Enable();
}
}
public void Enable()
{
IsEnabled = true;
}
}
public static class AccountFactory
{
public static IAccount Create(string username)
{
return new MyAccount(username);
}
public static ISavedAccount Create(int id, string username, bool isEnabled)
{
return new MySavedAccount(id, username, isEnabled);
}
}
}
namespace Example.Persistence
{
using Example.Domain;
public static class AccountStorage
{
static readonly DataTable dt;
static AccountStorage()
{
dt = new DataTable();
dt.Columns.Add("id", typeof(int));
dt.Columns.Add("username", typeof(string));
dt.Columns.Add("enabled", typeof(bool));
}
public static DataRow AddAccountRow(ref IAccount account)
{
var dr = dt.NewRow();
dr["id"] = dt.Rows.Count+1;
dr["username"] = account.Username;
dr["enabled"] = account.IsEnabled;
dt.Rows.Add(dr);
// This variable does not point to a valid account after it has been saved
account = null;
return dr;
}
public static DataRow UpdateAccountRow(ISavedAccount account)
{
var dr = FindAccountRow(account.Id);
dr["username"] = account.Username;
dr["enabled"] = account.IsEnabled;
return dr;
}
public static DataRow FindAccountRow(int id)
{
for (var i=0; i<dt.Rows.Count; ++i)
{
DataRow dr = dt.Rows[i];
if (((int)dr["id"]) == id)
{
return dr;
}
}
return null;
}
}
}
namespace Example.Client
{
// Let's pretend this namespace is in a separate assembly so that it
// doesn't see the 'internal' definitions from the Example.Domain
// namespace
using Example.Domain;
using Example.Persistence;
class Program
{
static void Main(string[] args)
{
var account = AccountFactory.Create("joebloggs");
// FAIL! A new account does not have an id
// Debug.Assert(account.Id > 0);
// FAIL! A new account cannot be enabled
// account.Enable();
var newrow = AccountStorage.AddAccountRow(ref account);
Debug.Assert(account == null, "The account object is no longer valid");
Debug.Assert(((int)newrow["id"]) > 0, "The 'id' value is set");
Debug.Assert("joebloggs".Equals(newrow["username"]), "The 'username' value is set");
Debug.Assert(! (bool)newrow["enabled"], "The 'enabled' value is not set");
var savedaccount = AccountFactory.Create((int)newrow["id"], (string)newrow["username"], (bool)newrow["enabled"]);
savedaccount.Enable();
Debug.Assert(savedaccount.IsEnabled, "The account is enabled");
var unused = AccountStorage.UpdateAccountRow(savedaccount);
var loadedrow = AccountStorage.FindAccountRow(savedaccount.Id);
var loaded = AccountFactory.Create((int)loadedrow["id"], (string)loadedrow["username"], (bool)loadedrow["enabled"]);
Debug.Assert(loaded.Id > 0, "An id is assigned");
Debug.Assert("joebloggs".Equals(loaded.Username), "The username is set correctly");
Debug.Assert(loaded.IsEnabled, "The account is enabled");
}
}
}
The line below could be questionable because we pass ownership of object account
to the persistence layer, but we could also assume that there is a contract that formalises this.
// This variable does not point to a valid account after it has been saved
account = null;
If you encounter the above linking error with an ATmega328p and your code uses sprintf
, set your LIBRARY_PATH
or the linker argument -L
to /usr/lib/avr/lib/avr5
instead of /usr/lib/avr/lib
.
packadd! matchit
filetype plugin indent on
syntax on
set directory=~\vimfiles\swap undofile undodir=~\vimfiles\undo ruler visualbell incsearch ignorecase hlsearch smartcase showmatch autoindent backspace=2 wildmenu
set grepprg=rg.exe\ --vimgrep\ -i
set grepformat=%f:%l:%c:%m
nnoremap <leader>b <Esc>:ls<Cr>:b *
nnoremap <leader>e <Esc>:e *
nnoremap <leader>s <Esc>:split +b *
nnoremap <leader>E <Esc>:e %:h\*
nnoremap <leader>S <Esc>:s %:h\*
nnoremap <leader>w <Esc>:bw
nnoremap <leader>y "+y
vnoremap <leader>y "+y
nnoremap <leader>Y "+Y
nnoremap <leader>p "+p
vnoremap <leader>p "+p
nnoremap <leader>P "+P
nnoremap <BS> <Esc>:let @/=""<Cr>
nnoremap <C-s> <Esc>:up<Cr>
inoremap <C-s> <Esc>:up<Cr>
nnoremap <A-Down> <Esc>:cnext<Cr>
nnoremap <A-Up> <Esc>:cprevious<Cr>
rg.exe
is part of ripgrep.
In my previous post I showed how to restart Bluetooth on Debian Linux. Here I explain how to disable Bluetooth auto-suspend with a kernel module option.
The behaviour of kernel modules can be controlled with parameters. The command modinfo
shows information about a module and lists its parameters. In the example below, modinfo btusb
shows the parameters for the Bluetooth module.
# modinfo btusb
...
parm: disable_scofix:Disable fixup of wrong SCO buffer size (bool)
parm: force_scofix:Force fixup of wrong SCO buffers size (bool)
parm: enable_autosuspend:Enable USB autosuspend by default (bool)
parm: reset:Send HCI reset command on initialization (bool)
To control Bluetooth auto-suspend, we must use the parameter enable_autosuspend
. It can be set in a configuration file in directory /etc/modprobe.d/.
options btusb enable_autosuspend=0
Here I have added the kernel option to disable Bluetooth auto-suspend in file btusb_autosuspend-disable.conf.
On my laptop I use a Bluetooth keyboard and a Bluetooth mouse. Occasionally the Bluetooth adapter in the laptop shuts off completely, meaning that it becomes impossible to restart the Bluetooth service with sudo systemctl restart bluetooth.service
.
My troubleshooting points to powersaving as the cause of this intermittent failure, but the TLP setting that is supposed to prevent autosuspend on Bluetooth devices does not seem to prevent it. As a temporary workaround I am using the commands below to restart Bluetooth from the shell.
$ sudo rmmod btusb && sudo modprobe btusb && sudo systemctl restart bluetooth.service
This solution works on Debian testing bullseye with kernel x86_64 Linux 5.5.0-2-amd64.