Enable Raspberry Pi Camera Module 3 Wide after Arducam 64mp

My development pi has had an Arducam 64mp camera connected for my camera software development. I liked the quality of the camera but have at various times been frustrated with the software requirements to use the camera. It’s required both a custom kernel driver and a custom fork of the libcamera software packages. That’s meant that to use the 64mp camera I needed to reinstall the arducam suite after nearly every apt upgrade cycle, and definitely ones where the system kernel got updated.

I spent several days trying to get the remnants of the arducam64mp removed from my development system. I’d even built a fresh sd card image of Raspian Bullseye to make sure that the hardware was all connected and working properly.

In the end the fix was rather simple, if obscure.

First, remove or comment out the dtoverlay line from the /boot/config.txt file and make sure camera auto detect is enabled.

# dtoverlay=arducam-64mp
camera_auto_detect=1

Then run apt install with the –reinstall option for the libcamera packages and the raspberry kernel package.

sudo apt install --reinstall -y libcamera-apps libcamera-dev libcamera0 raspberrypi-kernel
sudo systemctl reboot

Then reboot. That should be then allow you to run libcamera-hello and verify that the new camera is working.

I’d found a reference How To Enable RP Cam V2 After Arducam 64MP that didn’t seem to work for getting the V3 wide camera working, perhaps because the focus hardware in the V3 camera made the 64mp think it was active.

I asked the question of how to remove the drivers on the Arducam Forum and then answered my own question.

On to playing with my new Camera Module 3! (I bought the Arducam cases from Amazon because I really liked the fit. The new wide camera protrudes from the front with enough clearance for the focus to function.)

C++ IPv6 Ping Code Example

My previous code example for IPv4 needed a bunch of modifications to work for an IPv6 address. The thing that took me the longest to figure out was that because IPv6 seems to send a lot more ICMP messages on the local network, I needed to filter the response messages to only the type I was listening for.

bool send_ping6(const std::string& ping_ip, const std::string& HostName4Output, const bool bOutput = false)
{
    bool rval = false;
    if (bOutput)
        std::cout << "[" << getTimeExcelLocal() << "] " << "send_ping6(" << ping_ip << ", " << HostName4Output << ");" << std::endl;
    struct timespec tfs;
    clock_gettime(CLOCK_MONOTONIC, &tfs);
    auto ping_sockfd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
    if (ping_sockfd < 0)
    {
        if (bOutput)
            std::cout << "[" << getTimeExcelLocal() << "] " << "Socket file descriptor not received!!" << std::endl;
    }
    else
    {
        // set socket options at ip to TTL and value to 64,
        // change to what you want by setting ttl_val
        int ttl_val = 64;
        if (setsockopt(ping_sockfd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl_val, sizeof(ttl_val)) != 0)
        {
            if (bOutput)
                std::cerr << "[" << getTimeExcelLocal() << "] " << "Setting socket options to TTL failed!" << std::endl;
        }
        else
        {
            struct icmp6_filter filt;
            ICMP6_FILTER_SETBLOCKALL(&filt);
            ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
            setsockopt(ping_sockfd, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, sizeof(filt));

            // setting timeout of recv setting
            struct timeval tv_out;
            tv_out.tv_sec = RECV_TIMEOUT;
            tv_out.tv_usec = 0;
            setsockopt(ping_sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));

            int msg_count = 0;
            int flag = 1;
            int msg_received_count = 0;
            // send icmp packet in a loop
            for (auto pingloop = 4; pingloop > 0; pingloop--)
            {
                // flag is whether packet was sent or not
                flag = 1;

                //filling packet
                struct ping_pkt pckt;
                bzero(&pckt, sizeof(pckt));
                for (auto i = 0; i < sizeof(pckt.msg) - 1; i++)
                    pckt.msg[i] = i + '0';
                pckt.msg[sizeof(pckt.msg) - 1] = 0;
                pckt.hdr.type = ICMP6_ECHO_REQUEST;
                pckt.hdr.un.echo.id = getpid();
                pckt.hdr.un.echo.sequence = msg_count++;
                pckt.hdr.checksum = checksum(&pckt, sizeof(pckt));

                usleep(PING_SLEEP_RATE);

                struct timespec time_start;
                clock_gettime(CLOCK_MONOTONIC, &time_start);

                struct sockaddr_in6 ping_addr;
                ping_addr.sin6_family = AF_INET6;
                ping_addr.sin6_port = htons(0);
                inet_pton(AF_INET6, ping_ip.c_str(), &ping_addr.sin6_addr);
                if (sendto(ping_sockfd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&ping_addr, sizeof(ping_addr)) <= 0)
                {
                    if (bOutput)
                        std::cout << "[" << getTimeExcelLocal() << "] " << "Packet Sending Failed!" << std::endl;
                    flag = 0;
                }

                //receive packet
                struct sockaddr_in6 r_addr;
                auto addr_len = sizeof(r_addr);
                if (recvfrom(ping_sockfd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&r_addr, (socklen_t*)&addr_len) <= 0 && msg_count > 1)
                {
                    if (bOutput)
                        std::cout << "[" << getTimeExcelLocal() << "] " << "Packet receive failed!" << std::endl;
                }
                else
                {
                    struct timespec time_end;
                    clock_gettime(CLOCK_MONOTONIC, &time_end);

                    double timeElapsed = ((double)(time_end.tv_nsec - time_start.tv_nsec)) / 1000000.0;
                    long double rtt_msec = (time_end.tv_sec - time_start.tv_sec) * 1000.0 + timeElapsed;

                    // if packet was not sent, don't receive
                    if (flag)
                    {
                        char szAddr[NI_MAXHOST] = { 0 };
                        inet_ntop(AF_INET6, &r_addr.sin6_addr, szAddr, sizeof(szAddr));
                        if (!(pckt.hdr.type == ICMP6_ECHO_REPLY && pckt.hdr.code == 0))
                        {
                            if (bOutput)
                                std::cout << "[" << getTimeExcelLocal() << "] " << "Error..Packet received from (" << szAddr << ") with ICMP type " << int(pckt.hdr.type) << " code " << int(pckt.hdr.code) << std::endl;
                        }
                        else
                        {
                            if (bOutput)
                                std::cout << "[" << getTimeExcelLocal() << "] " << PING_PKT_S << " bytes from (" << szAddr << ") (" << HostName4Output << ") msg_seq=" << msg_count << " ttl=" << "ttl_val" << " rtt= " << rtt_msec << " ms." << std::endl;
                            msg_received_count++;
                        }
                    }
                }
            }
            rval = msg_received_count > 0;
            struct timespec tfe;
            clock_gettime(CLOCK_MONOTONIC, &tfe);
            double timeElapsed = ((double)(tfe.tv_nsec - tfs.tv_nsec)) / 1000000.0;
            long double total_msec = (tfe.tv_sec - tfs.tv_sec) * 1000.0 + timeElapsed;
            if (bOutput)
                std::cout << "[" << getTimeExcelLocal() << "] " << "=== " << ping_ip << " ping statistics === " << msg_count << " packets sent, " << msg_received_count << " packets received, " << ((msg_count - msg_received_count) / msg_count) * 100.0 << " percent packet loss. Total time : " << total_msec << " ms." << std::endl;
        }
        close(ping_sockfd);
    }
    return(rval);
}

Because my calling routine is keeping the addresses for the hosts as strings, I’m calling each of these routines with those strings and converting them to proper addresses inside the function. I’m making a simple choice of whether it’s an IPv4 address or an IPv6 address by the fact that IPv4 addresses have “.” in them and IPv6 addresses have “:”.

bool send_ping(const std::string& ping_ip, const std::string& HostName4Output, const bool bOutput = false)
{
    bool rval = false;
    if (ping_ip.find('.') == std::string::npos)
        rval = send_ping6(ping_ip, HostName4Output, bOutput);
    else 
        rval = send_ping4(ping_ip, HostName4Output, bOutput);
    return(rval);
}

Here’s a bunch of links I found useful while creating this code:

C++ IPv4 Ping Code Example

I’ve written my own monitoring program to keep track of the availability of some of my machines. They register themselves in DNS using dynamic DNS protocols and occasionally change addresses. I realized that while recognizing when the address has changed is useful, I’d also like to know if the machine itself is reachable. Having code that would test the ICMP ping results directly in my code is useful, and this is what I ended up putting together after having found examples in variousl places on the web.

/ Define the Packet Constants
// ping packet size
#define PING_PKT_S 64
#define PING_SLEEP_RATE 1000000

// Gives the timeout delay for receiving packets in seconds
#define RECV_TIMEOUT 1

// ping packet structure
struct ping_pkt
{
    struct icmphdr hdr;
    char msg[PING_PKT_S - sizeof(struct icmphdr)];
};

// Calculating the Check Sum
unsigned short checksum(void* b, int len)
{
    unsigned short* buf = (unsigned short*) b;
    unsigned int sum = 0;

    for (sum = 0; len > 1; len -= 2)
        sum += *buf++;
    if (len == 1)
        sum += *(unsigned char*)buf;
    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    unsigned short result = ~sum;
    return result;
}

bool send_ping4(const std::string& ping_ip, const std::string& HostName4Output, const bool bOutput = false)
{
    bool rval = false;
    if (bOutput)
        std::cout << "[" << getTimeExcelLocal() << "] " << "send_ping4(" << ping_ip << ", " << HostName4Output << ");" << std::endl;
    struct timespec tfs;
    clock_gettime(CLOCK_MONOTONIC, &tfs);
    auto ping_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (ping_sockfd < 0)
    {
        if (bOutput)
            std::cout << "[" << getTimeExcelLocal() << "] " << "Socket file descriptor not received!!" << std::endl;
    }
    else
    {
        // set socket options at ip to TTL and value to 64,
        // change to what you want by setting ttl_val
        int ttl_val = 64;
        if (setsockopt(ping_sockfd, SOL_IP, IP_TTL, &ttl_val, sizeof(ttl_val)) != 0)
        {
            if (bOutput)
                std::cout << "[" << getTimeExcelLocal() << "] " << "Setting socket options to TTL failed!" << std::endl;
        }
        else
        {
            // setting timeout of recv setting
            struct timeval tv_out;
            tv_out.tv_sec = RECV_TIMEOUT;
            tv_out.tv_usec = 0;
            setsockopt(ping_sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));

            int msg_count = 0;
            int flag = 1;
            int msg_received_count = 0;
            // send icmp packet in a loop
            for (auto pingloop = 4; pingloop > 0; pingloop--)
            {
                // flag is whether packet was sent or not
                flag = 1;

                //filling packet
                struct ping_pkt pckt;
                bzero(&pckt, sizeof(pckt));
                for (auto i = 0; i < sizeof(pckt.msg) - 1; i++)
                    pckt.msg[i] = i + '0';
                pckt.msg[sizeof(pckt.msg) - 1] = 0;
                pckt.hdr.type = ICMP_ECHO;
                pckt.hdr.un.echo.id = getpid();
                pckt.hdr.un.echo.sequence = msg_count++;
                pckt.hdr.checksum = checksum(&pckt, sizeof(pckt));

                usleep(PING_SLEEP_RATE);

                struct timespec time_start;
                clock_gettime(CLOCK_MONOTONIC, &time_start);

                struct sockaddr_in ping_addr;
                ping_addr.sin_family = AF_INET;
                ping_addr.sin_port = htons(0);
                inet_pton(AF_INET, ping_ip.c_str(), &ping_addr.sin_addr.s_addr);

                if (sendto(ping_sockfd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&ping_addr, sizeof(ping_addr)) <= 0)
                {
                    if (bOutput)
                        std::cout << "[" << getTimeExcelLocal() << "] " << "Packet Sending Failed!" << std::endl;
                    flag = 0;
                }
                //receive packet
                struct sockaddr_in r_addr;
                auto addr_len = sizeof(r_addr);
                if (recvfrom(ping_sockfd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&r_addr, (socklen_t*)&addr_len) <= 0 && msg_count > 1)
                {
                    if (bOutput)
                        std::cout << "[" << getTimeExcelLocal() << "] " << "Packet receive failed!" << std::endl;
                }
                else
                {
                    struct timespec time_end;
                    clock_gettime(CLOCK_MONOTONIC, &time_end);

                    double timeElapsed = ((double)(time_end.tv_nsec - time_start.tv_nsec)) / 1000000.0;
                    long double rtt_msec = (time_end.tv_sec - time_start.tv_sec) * 1000.0 + timeElapsed;

                    // if packet was not sent, don't receive
                    if (flag)
                    {
                        if (!(pckt.hdr.type == 69 && pckt.hdr.code == 0))
                        {
                            if (bOutput)
                                std::cerr << "[" << getTimeExcelLocal() << "] " << "Error..Packet received with ICMP type " << int(pckt.hdr.type) << " code " << int(pckt.hdr.code) << std::endl;
                        }
                        else
                        {
                            char szAddr[NI_MAXHOST] = { 0 };
                            inet_ntop(AF_INET, &r_addr.sin_addr, szAddr, sizeof(szAddr));
                            if (bOutput)
                                std::cout << "[" << getTimeExcelLocal() << "] " << PING_PKT_S << " bytes from (" << szAddr << ") (" << HostName4Output << ") msg_seq=" << msg_count << " ttl=" << ttl_val << " rtt= " << rtt_msec << " ms." << std::endl;
                            msg_received_count++;
                        }
                    }
                }
            }
            rval = msg_received_count > 0;
            struct timespec tfe;
            clock_gettime(CLOCK_MONOTONIC, &tfe);
            double timeElapsed = ((double)(tfe.tv_nsec - tfs.tv_nsec)) / 1000000.0;
            long double total_msec = (tfe.tv_sec - tfs.tv_sec) * 1000.0 + timeElapsed;
            if (bOutput)
                std::cout << "[" << getTimeExcelLocal() << "] " << "=== " << ping_ip << " ping statistics === " << msg_count << " packets sent, " << msg_received_count << " packets received, " << ((msg_count - msg_received_count) / msg_count) * 100.0 << " percent packet loss. Total time : " << total_msec << " ms." << std::endl;
        }
        close(ping_sockfd);
    }
    return(rval);
}

That works great if my address is an IPv4 address, but I required doing a lot of investigation to get a successful IPv6 address.

Remote Desktop with Hotmail Account

I have been using a Microsoft Account to log into my personal workstation for the past few years because it makes working with OneDrive fairly seamless. The account I use for logging in has an @hotmail.com address. Because I have been using a Microsoft Surface as my primary machine for the past few years, I have also been taking advantage of the Windows Hello capable camera to log in with my face most of the time. I’m now in the process of migrating from a Surface 7 to a Surface 9.

There are always reasons to go back to the old machine and look at some particular setting or program. A Remote Desktop connection from my new machine to the old one is much easier than getting up and walking across the room to the other machine and allows copy and paste directly between machines. Both machines are running the Professional version of Windows so I can enable remote desktop.

I looked at who had access, it shows that my hotmail.com address already has access, as well as my local network account. When I tried to access it from the new machine, it prompted for my password, but rejected it. I repeated this process several times thinking I must be typing it incorrectly before searching for what might be happening.

I came across a solution which is very simple. I don’t understand what it’s doing, but it works. https://answers.microsoft.com/en-us/windows/forum/all/remote-desktop-not-working-with-microsoft-account/71f0c323-688a-4c97-8740-e80eb31ae11d

On the old machine I ran the command runas /u:MicrosoftAccount\MyAccount@hotmail.com winver it prompted me for my password, and then it displayed the standard windows version dialog box. I’m not sure why it worked, but I’m happy that I now can sit at my desk with the large monitor and keyboard and have access to both machines.

runas /u:MicrosoftAccount\MyAccount@hotmail.com winver

Updated: I also came across the same answer using cmd.exe instead of winver.exe as the target of runas, but for some reason it was more difficult for me to understand what was going on. I think it was simply due to the way the email address was obscured in the second one that made it more difficult for me to understand, even though it may have had more screen captures in the demonstration. https://nready.net/remote-desktop-on-windows-11-with-microsoft-account-mfa/

Thanks for the information:

Disk Usage, Raspberry Pi, and MRTG

I’ve used MRTG for many years to keep track of network and disk usage on various platoforms. I like it because it’s easy to set up and doesn’t require a database to be configured or maintained. The simple file structure and image creation means that I can easily have the images synchronized to a central web server even if they are generated on the remote device.

The weekly or monthly views of the disk usage are what I find most useful for recognizing trends of usage. I noticed that last Friday there was a huge jump in usage that I ought to spent a little time discovering what suddenly took over a large chunk of my disk.

sudo du --human-readable --max-depth=2 --one-file-system /

That command gave me a nice list of where the disk usage was allocated. Here’s a small section of the results that looked suspicious.

20G     /var
4.0K    /tmp/ssh-72XFLxsn7URe
8.0K    /tmp/systemd-private-e26b4caee9b7494e847b16a023d67460-systemd-timesyncd.service-Nuv83g
4.0K    /tmp/.Test-unix
8.0K    /tmp/systemd-private-e26b4caee9b7494e847b16a023d67460-systemd-logind.service-xl9zMh
88G     /tmp/mc-wim
4.0K    /tmp/.X11-unix
4.0K    /tmp/.font-unix
4.0K    /tmp/.ICE-unix
156K    /tmp/.qBittorrent
4.0K    /tmp/.XIM-unix
88G     /tmp

The 88G in a single directory under /tmp looks very much like the size bump I got last Friday, and I remember killing a process via a HUP signal directed at Midnight Commander which was trying to open a very large tar file. Deleting that temporary directory recovered the free space properly.

wim@WimPi4:/tmp $ df
Filesystem                             1K-blocks       Used  Available Use% Mounted on
/dev/root                              492885032  235012904  237825328  50% /
devtmpfs                                 1774272          0    1774272   0% /dev
tmpfs                                    1939136          4    1939132   1% /dev/shm
tmpfs                                     775656       2088     773568   1% /run
tmpfs                                       5120          4       5116   1% /run/lock
/dev/mmcblk0p1                            258096      50755     207341  20% /boot
tmpfs                                     387824          0     387824   0% /run/user/1000
tmpfs                                     387824          0     387824   0% /run/user/1002
tmpfs                                     387824          0     387824   0% /run/user/1001
wim@WimPi4:/tmp $ rm -r /tmp/mc-wim/
wim@WimPi4:/tmp $ df
Filesystem                             1K-blocks       Used  Available Use% Mounted on
/dev/root                              492885032  143584828  329253404  31% /
devtmpfs                                 1774272          0    1774272   0% /dev
tmpfs                                    1939136          4    1939132   1% /dev/shm
tmpfs                                     775656       2088     773568   1% /run
tmpfs                                       5120          4       5116   1% /run/lock
/dev/mmcblk0p1                            258096      50755     207341  20% /boot
tmpfs                                     387824          0     387824   0% /run/user/1000
tmpfs                                     387824          0     387824   0% /run/user/1002
tmpfs                                     387824          0     387824   0% /run/user/1001
wim@WimPi4:/tmp $

My MRTG configuration for keeping track of the disk usage on the Raspberry Pi root partition is:

######################################################################
#       Disk Monitoring
######################################################################
Options[_]: printrouter, gauge, noo, nolegend, transparent, pngdate
Factor[_]:
kMG[_]: ,k,M,G,T,P
YLegend[_]: Disk Usage
ShortLegend[_]: &nbsp;Bytes
LegendO[_]: &nbsp;Bytes
LegendI[_]: &nbsp;Bytes

Target[wimpi4_disk]: .1.3.6.1.2.1.25.2.3.1.6.31&.1.3.6.1.2.1.25.2.3.1.5.31:public@wimpi4.local * .1.3.6.1.2.1.25.2.3.1.4.31&.1.3.6.1.2.1.25.2.3.1.4.31:public@wimpi4.local
Title[wimpi4_disk]: Disk Usage -- WimPi4
MaxBytes[wimpi4_disk]: 504720539648

I got the correct size for MaxBytes by looking at the results from snmpwalk. Multiplying the cluster size (1.3.6.1.2.1.25.2.3.1.4.31) by the number of clusters (1.3.6.1.2.1.25.2.3.1.5.31) gets the total size. If I had multiple disks in the same machine that I wanted to monitor I’d be able to find the human readable name for the disk in this same output.

wim@WimPi4:/tmp $ snmpwalk -Os -c public -v 2c localhost 1.3.6.1.2.1.25.2
iso.3.6.1.2.1.25.2.2.0 = INTEGER: 3878272
iso.3.6.1.2.1.25.2.3.1.1.1 = INTEGER: 1
iso.3.6.1.2.1.25.2.3.1.1.3 = INTEGER: 3
iso.3.6.1.2.1.25.2.3.1.1.6 = INTEGER: 6
iso.3.6.1.2.1.25.2.3.1.1.7 = INTEGER: 7
iso.3.6.1.2.1.25.2.3.1.1.8 = INTEGER: 8
iso.3.6.1.2.1.25.2.3.1.1.10 = INTEGER: 10
iso.3.6.1.2.1.25.2.3.1.1.31 = INTEGER: 31
iso.3.6.1.2.1.25.2.3.1.1.37 = INTEGER: 37
iso.3.6.1.2.1.25.2.3.1.1.39 = INTEGER: 39
iso.3.6.1.2.1.25.2.3.1.1.40 = INTEGER: 40
iso.3.6.1.2.1.25.2.3.1.1.66 = INTEGER: 66
iso.3.6.1.2.1.25.2.3.1.1.67 = INTEGER: 67
iso.3.6.1.2.1.25.2.3.1.1.68 = INTEGER: 68
iso.3.6.1.2.1.25.2.3.1.1.72 = INTEGER: 72
iso.3.6.1.2.1.25.2.3.1.2.1 = OID: iso.3.6.1.2.1.25.2.1.2
iso.3.6.1.2.1.25.2.3.1.2.3 = OID: iso.3.6.1.2.1.25.2.1.3
iso.3.6.1.2.1.25.2.3.1.2.6 = OID: iso.3.6.1.2.1.25.2.1.1
iso.3.6.1.2.1.25.2.3.1.2.7 = OID: iso.3.6.1.2.1.25.2.1.1
iso.3.6.1.2.1.25.2.3.1.2.8 = OID: iso.3.6.1.2.1.25.2.1.1
iso.3.6.1.2.1.25.2.3.1.2.10 = OID: iso.3.6.1.2.1.25.2.1.3
iso.3.6.1.2.1.25.2.3.1.2.31 = OID: iso.3.6.1.2.1.25.2.1.4
iso.3.6.1.2.1.25.2.3.1.2.37 = OID: iso.3.6.1.2.1.25.2.1.4
iso.3.6.1.2.1.25.2.3.1.2.39 = OID: iso.3.6.1.2.1.25.2.1.4
iso.3.6.1.2.1.25.2.3.1.2.40 = OID: iso.3.6.1.2.1.25.2.1.4
iso.3.6.1.2.1.25.2.3.1.2.66 = OID: iso.3.6.1.2.1.25.2.1.4
iso.3.6.1.2.1.25.2.3.1.2.67 = OID: iso.3.6.1.2.1.25.2.1.4
iso.3.6.1.2.1.25.2.3.1.2.68 = OID: iso.3.6.1.2.1.25.2.1.4
iso.3.6.1.2.1.25.2.3.1.2.72 = OID: iso.3.6.1.2.1.25.2.1.4
iso.3.6.1.2.1.25.2.3.1.3.1 = STRING: "Physical memory"
iso.3.6.1.2.1.25.2.3.1.3.3 = STRING: "Virtual memory"
iso.3.6.1.2.1.25.2.3.1.3.6 = STRING: "Memory buffers"
iso.3.6.1.2.1.25.2.3.1.3.7 = STRING: "Cached memory"
iso.3.6.1.2.1.25.2.3.1.3.8 = STRING: "Shared memory"
iso.3.6.1.2.1.25.2.3.1.3.10 = STRING: "Swap space"
iso.3.6.1.2.1.25.2.3.1.3.31 = STRING: "/"
iso.3.6.1.2.1.25.2.3.1.3.37 = STRING: "/dev/shm"
iso.3.6.1.2.1.25.2.3.1.3.39 = STRING: "/run"
iso.3.6.1.2.1.25.2.3.1.3.40 = STRING: "/run/lock"
iso.3.6.1.2.1.25.2.3.1.3.66 = STRING: "/boot"
iso.3.6.1.2.1.25.2.3.1.3.67 = STRING: "/run/user/1000"
iso.3.6.1.2.1.25.2.3.1.3.68 = STRING: "/run/user/1001"
iso.3.6.1.2.1.25.2.3.1.3.72 = STRING: "/run/user/1002"
iso.3.6.1.2.1.25.2.3.1.4.1 = INTEGER: 1024
iso.3.6.1.2.1.25.2.3.1.4.3 = INTEGER: 1024
iso.3.6.1.2.1.25.2.3.1.4.6 = INTEGER: 1024
iso.3.6.1.2.1.25.2.3.1.4.7 = INTEGER: 1024
iso.3.6.1.2.1.25.2.3.1.4.8 = INTEGER: 1024
iso.3.6.1.2.1.25.2.3.1.4.10 = INTEGER: 1024
iso.3.6.1.2.1.25.2.3.1.4.31 = INTEGER: 4096
iso.3.6.1.2.1.25.2.3.1.4.37 = INTEGER: 4096
iso.3.6.1.2.1.25.2.3.1.4.39 = INTEGER: 4096
iso.3.6.1.2.1.25.2.3.1.4.40 = INTEGER: 4096
iso.3.6.1.2.1.25.2.3.1.4.66 = INTEGER: 512
iso.3.6.1.2.1.25.2.3.1.4.67 = INTEGER: 4096
iso.3.6.1.2.1.25.2.3.1.4.68 = INTEGER: 4096
iso.3.6.1.2.1.25.2.3.1.4.72 = INTEGER: 4096
iso.3.6.1.2.1.25.2.3.1.5.1 = INTEGER: 3878272
iso.3.6.1.2.1.25.2.3.1.5.3 = INTEGER: 3980668
iso.3.6.1.2.1.25.2.3.1.5.6 = INTEGER: 3878272
iso.3.6.1.2.1.25.2.3.1.5.7 = INTEGER: 2630604
iso.3.6.1.2.1.25.2.3.1.5.8 = INTEGER: 9624
iso.3.6.1.2.1.25.2.3.1.5.10 = INTEGER: 102396
iso.3.6.1.2.1.25.2.3.1.5.31 = INTEGER: 123221258
iso.3.6.1.2.1.25.2.3.1.5.37 = INTEGER: 484784
iso.3.6.1.2.1.25.2.3.1.5.39 = INTEGER: 193914
iso.3.6.1.2.1.25.2.3.1.5.40 = INTEGER: 1280
iso.3.6.1.2.1.25.2.3.1.5.66 = INTEGER: 516191
iso.3.6.1.2.1.25.2.3.1.5.67 = INTEGER: 96956
iso.3.6.1.2.1.25.2.3.1.5.68 = INTEGER: 96956
iso.3.6.1.2.1.25.2.3.1.5.72 = INTEGER: 96956
iso.3.6.1.2.1.25.2.3.1.6.1 = INTEGER: 3712960
iso.3.6.1.2.1.25.2.3.1.6.3 = INTEGER: 3815356
iso.3.6.1.2.1.25.2.3.1.6.6 = INTEGER: 15900
iso.3.6.1.2.1.25.2.3.1.6.7 = INTEGER: 2630604
iso.3.6.1.2.1.25.2.3.1.6.8 = INTEGER: 9624
iso.3.6.1.2.1.25.2.3.1.6.10 = INTEGER: 102396
iso.3.6.1.2.1.25.2.3.1.6.31 = INTEGER: 35973379
iso.3.6.1.2.1.25.2.3.1.6.37 = INTEGER: 1
iso.3.6.1.2.1.25.2.3.1.6.39 = INTEGER: 592
iso.3.6.1.2.1.25.2.3.1.6.40 = INTEGER: 1
iso.3.6.1.2.1.25.2.3.1.6.66 = INTEGER: 101509
iso.3.6.1.2.1.25.2.3.1.6.67 = INTEGER: 0
iso.3.6.1.2.1.25.2.3.1.6.68 = INTEGER: 0
iso.3.6.1.2.1.25.2.3.1.6.72 = INTEGER: 0
wim@WimPi4:/tmp $

Moving Visual Studio Cross Platform Development to a new Machine

I upgraded my workstation for the holidays and needed to get things moved over as easily as possible.

In Visual Studio, under the tools menu, there’s an option to be able to Import and Export Settings… which got me an output file that made my new installation look mostly like my old installation.

What it didn’t transfer was the details of my connections to my Raspberry Pi devices. The following image is after I managed to get a working connection. It didn’t have anything displayed under Default Host Name etc.

I’d already run ssh-keygen on my new machine, creating a default security key on the local machine. I’d already connected via ssh to each of the hosts I regularly work on and imported the public key into the authorized_keys file and verified all appeared working.

After entering all of the details in the dialog box I got a rather unhelpful set of red boxes indicating that something had gone wrong. (192.168.0.66 and 192.168.0.67 are the same host, wired and Wi-Fi, on my local network, and I may use them interchangeably for images and text here)

Searching on the web I found that there is a console program that will configure the same thing, and I assumed correctly that I might get more descriptive error messages using the console than I was getting here. https://learn.microsoft.com/en-us/cpp/linux/connectionmanager-reference?view=msvc-170

**********************************************************************
** Visual Studio 2022 Developer Command Prompt v17.4.2
** Copyright (c) 2022 Microsoft Corporation
**********************************************************************

C:\Program Files\Microsoft Visual Studio\2022\Enterprise>ConnectionManager.exe add visualstudio@192.168.0.66 --privatekey C:\Users\Wim\.ssh\id_rsa
Enter password (leave blank for no password):
Verifying connection with remote system.
Failed to add connection: Private key is invalid or is encrypted and no passphrase was given

I knew that the keys were good because I was using them to connect via SSH. I did some digging and realized that the physical file sizes on the new machine are larger than on the old machine. Looking at the length of the public keys themselves, I realized that my old keys were 2048 bits long, and the newly generated ones are 3072. I tried explicitly generating a new 2048 bit key with the command ssh-keygen -f id_rsa_2048 -b 2048 and using it, but I got similar results to what I was getting before. Finally, I copied my public and private key from the old machine and renamed them specifically for this usage on this machine.

C:\Program Files\Microsoft Visual Studio\2022\Enterprise>ConnectionManager.exe add visualstudio@192.168.0.66 --privatekey C:\Users\Wim\.ssh\id_rsa_visualstudio
Enter password (leave blank for no password):
Verifying connection with remote system.
The authenticity of host '192.168.0.66' can't be established.
ecdsa-sha2-nistp256 key fingerprint is SHA256:DGa1mVbMm3voQwVwtg06xHkANgs04zST9RP8CMfSoXY.
Are you sure you want to continue connecting (yes/no)? yes
Successfully added connection '1748855142;192.168.0.66 (username=visualstudio, port=22, authentication=PrivateKey)'.

That created the connection properly in Visual Studio. I went back into the Visual Studio interface, Tools->Options->Cross Platform->Connection Manager->Remote Headers Intellisense Manager and updated the headers and everything appears to be working now.

I don’t know why the keys I generated using the software on my new machine didn’t work, even when I specified the same number of bits.

Horizontally flipped image of Shilshole Bay Marina

I got an email this week from the Port of Seattle Shilshole Bay Marina that concluded with an image that is reversed. At first glance it looks like it was taken from the south looking north with Puget Sound to the left. Then I realized nothing was in the right place. This image is looking south with Puget Sound (west) on the left.

In photography with slides this could be a simple mistake but in modern digital photography this requires effort to flip the image. I wonder if this was intentional to see who would comment about it, or a sign of much worse things.

Gmails new interface

I’ve finally been forced to accept the new interface in Gmail.

What I hate is the vertical bar on the far left. It extends the entire height of the window, though there’s nothing on it beyond the four options displayed here, Mail, Chat, Spaces, and Meet. There’s also a hamburger menu at the top which claims to hide the main menu. It hides the Gmail labels pane instead of whatever the leftmost pane is called. There’s also a vertical bar on the far right that allows me to jump to Calendars, Keep, Tasks, or Contacts. The right bar can be minimized. It seems to me that the items on the left belong on the right along with the other apps, taking up less screen space.

Old Man Yells at Cloud

I know that this is not likely to make any difference, but sometimes it’s important to let off steam at decisions out of your control.

Questionable text messages

I’ve received a few text messages about packages needing details recently.

detcils instead of details?

Notice the spelling of the domain on that one. At first glance it seems like it could be legitimate, but then my eyes noticed the c. A whois lookup displayed nearly everything redacted for privacy. It seemed to be registered in Malaysia.

rod spa

This one didn’t even look correct to me at first glance. it’s registered via Alibaba.