Framework Expansion Board

Illustration

One of most exciting recent developments in laptop world for me is definitely the framework laptop. A major component of that concept are its expansion cards. And, of course, you can build your own.

This repository is quite encompassing if you’re using KiCAD. However, for those who love nicer tools (ehm, DipTrace), it’s annoying to find that there is no board size specification in human readable format (and no, KiCAD XML is not). So I decided to figure it out.

To cut the long story short, here are the board outline points for the expansion card PCB:

  • (0.0, 0.0)
  • (26.0, 0.0)
  • (26.0, 26.5)
  • (25.0, 26.5)
  • (25.0, 30.0)
  • (17.7, 30.0)
  • (17.7, 28.0)
  • (16.0, 28.0)
  • (16.0, 29.0)
  • (10.0, 29.0)
  • (10.0, 28.0)
  • (8.3, 28.0)
  • (8.3, 30.0)
  • (1.0, 30.0)
  • (1.0, 26.5)
  • (0.0, 26.5)

In order to make it slightly nicer to handle, each corner is additionally rounded with a 0.3 mm radius.

And let’s not forget two holes at (1.7, 10.5) and (24.3, 10.5), both with a 2.2 mm diameter and 4.9 mm keepout region.

With that information in hand, one can create PCB board in any program they might prefer. Of course, I already did so for DipTrace and you download the files here.

And yes, PCB is just a first step in a development process. What I found the hardest is actually getting appropriate connectors for the enclosure as there’s not too much height to work with.


PS: No, I do not own framework laptop at this time. I am waiting for 15.6" model as 13.5" is simply too small for me when not used external monitor.

Web Server Certificate From a File

If one desires to run HTTPs server from C#, they might get the following warning:

Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found or is out of date. To generate a developer certificate run 'dotnet dev-certs https'. To trust the certificate (Windows and macOS only) run 'dotnet dev-certs https --trust'. For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054.

And yes, once could follow instructions and have everything running. But where’s the fun in that?

Alternative approach would be to load certificate from the file and .NET makes that really easy.

private static X509Certificate2? GetCertificate() {
  var certFilename = Path.Combine(AppContext.BaseDirectory, "my.pfx");
  if (File.Exists(certFilename)) {
    try {
      return new X509Certificate2(certFilename);
    } catch (CryptographicException ex) {
      // log error or whatever
    }
  }
  return null;
}

So, when bringing server up we can just call it using something like this:

var cert = GetCertificate();
options.Listen(IPAddress.Any, 443, listenOptions => {
  listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
  if (cert != null) {
    listenOptions.UseHttps(cert);
  } else {
    listenOptions.UseHttps();
  }
});

Zip in Git Bash

While creating build system that works across the platforms, one can find issues in the most basic things. And that’s even when shell is the same. For example, while Bash on Linux and Windows works out the same, a lot of supporting tools differ - a lot. And there’s no better example than creating a zip archive.

Under Linux you can count on zip command being available. Even if one doesn’t have it, it’s easy to install without messing with their desktop. On Windows story gets more complicated. Git Bash for example doesn’t have it even compiled and there’s no really good way to add it. Yes, you can use any application but different one is installed on every system. To create more “fun”, supporting multiple applications also means dealing with their command-line arguments. And yes, 7-Zip has completely different syntax as compared to WinRAR.

However, when it comes to making zip archive, there’s actually a solution that works for both Windows (via Git Bash) and Linux. Surprisingly, the answer is perl.

If one is careful to use Perl’s older IO::Compress::Zip library, creating an archive becomes a simple task:

perl -e '
  use strict;
  use warnings;
  use autodie;
  use IO::Compress::Zip qw(:all);
  zip [
    "src/mimetype",
    <"src/META-INF/*.*">,
    <"src/OEBPS/*.*">,
    <"src/OEBPS/chapters/*.*">
  ] => "bin/book.epub",
       FilterName => sub { s[^src/][] },
       Zip64 => 0,
  or die "Zip failed: $ZipError\n";
'

Yeah, might not be ideal when it comes to beauty but it definitely works across platforms.

Add Application to Auto-Start from PowerShell

Since PowerShell comes installed with every Windows, I find it ideal tool for initial system setup when nothing else is installed. And one of the tasks I need it for is setting up auto-start.

My standard approach for setting application auto-start used to be to just use regedit.exe and load prepared registry file. But, as I was rewriting my setup scripts, I figured it’s time to update this too.

And it’s easy enough:

New-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" `
    -Name "^^Application^^" `
    -Value "^^C:\Path\To\MyApplication.exe^^"

Network Share Login via C#

Accessing remote share programmatically from Windows is easy. Just use \\machine\share notation and you’re golden. Except if you need to access it with a specific user. While PrincipalContext can often help, for samba shares hosted by Linux, that doesn’t necessarily work.

bool Login(string path, string user, string password) {
  var nr = new NativeMethods.NETRESOURCE {
    dwType = NativeMethods.RESOURCETYPE_DISK,
    lpRemoteName = path
  };
  var result = NativeMethods.WNetUseConnection(IntPtr.Zero, nr, password, name, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
  return (result == NativeMethods.NO_ERROR);
}

And, of course, we also need our definitions:

private static class NativeMethods {
    internal const Int32 NO_ERROR = 0;
    internal const Int32 RESOURCETYPE_DISK = 0x00000001;

    [StructLayout(LayoutKind.Sequential)]
    internal class NETRESOURCE {
        public Int32 dwScope = 0;
        public Int32 dwType = 0;
        public Int32 dwDisplayType = 0;
        public Int32 dwUsage = 0;
        public IntPtr lpLocalName;
        public String lpRemoteName;
        public IntPtr lpComment;
        public IntPtr lpProvider;
    }

    [DllImport("mpr.dll", CharSet = CharSet.Ansi)]
    internal static extern Int32 WNetUseConnection(
        IntPtr hwndOwner,
        NETRESOURCE lpNetResource,
        String lpPassword,
        String lpUserId,
        Int32 dwFlags,
        IntPtr lpAccessName,
        IntPtr lpBufferSize,
        IntPtr lpResult
    );

}

And that should be enough for access.