Tuesday, January 6, 2026

Enumerating Processes on Windows

Enumerating processes is a common task on a computer.  Whether you are using something like Task Manager, Process Explorer, or System Informer on Windows, or 'ps' or 'top' Linux/Unix/MacOS - sometimes you just need to know what processes are running on your system.  As a developer, sometimes you need to know programmatically what processes are running.  However, enumerating the running processes on Windows is not straightforward, or more accurately, there are multiple ways to accomplish the task.  There are no fewer than 5 different ways to enumerate running processes on Windows.  But this raises questions like which should I use?  Are there benefits to different methods?  Which method is the most efficient?  In this post we will look at the different methods, discuss the differences, as well as the pros and cons.

To start off, the 5 methods of enumerating processes programmatically on Windows are...

  1. EnumProcesses Win32 API
  2. Toolhelp library
  3. WTSEnumerateProcesses WTS API
  4. Win32_Process table in WMI
  5. NtQuerySystemInformation (an undocumented API)

 

EnumProcesses Win32 API

Let's start off by looking at the EnumProcesses() API.  I suspect this is one of the oldest methods on Windows, if not the original method.  The API is simple, you provide a buffer and the API fills it with a list of PIDs for all running processes.  It is up to you to then call additional APIs to get useful info about each PID such as the process filename.

EnumProcesses() is not a very graceful API in that if the buffer you provide is too small, the API does not tell you how large it should be.  So you may end up calling the API multiple times until the buffer was larger than the amount of data it returned.

Here is a sample function demonstrating the use of the EnumProcess API. 

std::map<uint32_t, std::wstring> EnumProcessesWin32(void)
{
    std::map<uint32_t, std::wstring> mapProcesses;

    size_t nAllocCount = 1024;
    auto pBuffer = std::make_unique<DWORD[]>(nAllocCount);

    size_t nLoopCount = 0;
    DWORD dwReturnedSize;
    EnumProcesses(pBuffer.get(), (DWORD)nAllocCount * sizeof(DWORD), &dwReturnedSize);
    while(dwReturnedSize / sizeof(DWORD) == nAllocCount && nLoopCount++ < 10)
    {
        nAllocCount += 1024;    // Increase the allocation and try again
        pBuffer = std::make_unique<DWORD[]>(nAllocCount);
        EnumProcesses(pBuffer.get(), (DWORD)nAllocCount * sizeof(DWORD), &dwReturnedSize);
    }

    if(dwReturnedSize)
    {
        size_t nCount = dwReturnedSize / sizeof(DWORD);
        for(size_t n = 0; n < nCount; ++n)
            mapProcesses.emplace(pBuffer.get()[n], GetProcessFilename(pBuffer.get()[n]));
    }

    return mapProcesses;
}


Toolhelp library

The toolhelp library is another fairly old method for enumerating processes.  This library works by first calling CreateToolhelp32Snapshot() which takes a snapshot of the processes at that moment.  You then call various APIs to analyze the snapshot one record at a time.  And when you are done you close the handle which frees the resources.  This may sound complex, but the code can be fairly simple.

std::map<uint32_t, std::wstring> EnumProcessesToolhelp(void)
{
    std::map<uint32_t, std::wstring> mapProcesses;

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(hSnapshot != INVALID_HANDLE_VALUE)
    {
        PROCESSENTRY32W pe32 = {0};
        pe32.dwSize = sizeof(pe32);

        if(Process32FirstW(hSnapshot, &pe32))
        {
            do{
                mapProcesses.emplace(pe32.th32ProcessID, pe32.szExeFile);
            }while(Process32NextW(hSnapshot, &pe32));
        }

        CloseHandle(hSnapshot);
    }

    return mapProcesses;
}


WTSEnumerateProcesses WTS API

The WTS APIs are newer, having first appeared in Windows Vista.  This API is designed for services running on multi-user systems, but the API is available on all versions of Windows.  In my opinion, the WTS API is probably the easiest and cleanest code for general use.  You call a single API which returns the results in an allocated block of memory.  After you analyze the results you call a second API to free the memory.  Here is an example function.

std::map<uint32_t, std::wstring> EnumProcessesWts(void)
{
    std::map<uint32_t, std::wstring> mapProcesses;

    PWTS_PROCESS_INFOW pwtspi;
    DWORD dwCount;
    if(WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pwtspi, &dwCount))
    {
        for(DWORD dw = 0; dw < dwCount; ++dw)
            mapProcesses.emplace(pwtspi[dw].ProcessId, pwtspi[dw].pProcessName);

        WTSFreeMemory(pwtspi);
    }

    return mapProcesses;
}


Win32_Process table in WMI

Windows Management Instrumentation is a horrible API to interact with, at least from C++.  The amount of overhead required makes this method ugly and slow.  WMI is best from scripted languages (e.g. PowerShell or VisualBasic).  WMI does have one benefit that the others lack - you can call WMI from a remote machine (assuming the firewall does not block it).  Therefore WMI is the only method to remotely enumerate processes.

The following function shows an example, but note that this example uses a helper class to handle the complexity of WMI.  So the actual code is far more complicated.

std::map<uint32_t, std::wstring> EnumProcessesWmi(void)
{
    std::map<uint32_t, std::wstring> mapProcesses;

    CWmiService wmiSvc;
    if(SUCCEEDED(wmiSvc.Open()))
    {
        CWmiClass wmiClass = wmiSvc.GetClassFormat(L"Win32_Process", L"ProcessId", L"Name");

        CWmiInstance wmiInst = wmiClass.GetFirstInstance();
        while(wmiInst.IsOpen())
        {
            uint32_t nPid = (uint32_t)wmiInst.GetAsUInteger(L"ProcessId");
            std::wstring str(wmiInst.GetAsString(L"Name"));
            mapProcesses.emplace(nPid, str);

            wmiInst = wmiClass.GetNextInstance();
        }
    }

    return mapProcesses;
}

 

NtQuerySystemInformation (an undocumented API)

The last method is the undocumented Windows API NtQuerySystemInformation.  I call the API "undocumented" but this is a little misleading.  The API is documented, Microsoft details what parameters to pass in and the structs that are returned.  But Microsoft would like to discourage the use of this API so 1) you cannot statically link to it, you must dynamically load it, 2) Microsoft says to use alternate APIs (though they don't list alternate recommended APIs), and 3) they claim the API may change at anytime in the future.  So use of this API carries some risk.  I suspect the undocumented API is the singular method to enumerate processes on Windows.  All of the other methods are wrappers around this API, providing their own set of flags and differing memory management requirements.

Because you cannot statically link to the API, the function to use this method appears more complicated than the rest.

std::map<uint32_t, std::wstring> EnumProcessesUndocumented(void)
{
    const auto SystemBasicProcessInformation = 252;
    constexpr auto STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
    using PFNNTQUERYSYSTEMINFORMATION = NTSTATUS(NTAPI*)(ULONG, PVOID, ULONG, PULONG);

    std::map<uint32_t, std::wstring> mapProcesses;

    HMODULE hNtDll = GetModuleHandleW(L"ntdll.dll");
    PFNNTQUERYSYSTEMINFORMATION pfnNtQuerySystemInformation = (PFNNTQUERYSYSTEMINFORMATION)GetProcAddress(hNtDll, "NtQuerySystemInformation");

    NTSTATUS status;
    ULONG nBufferSize = 0x80000;
    auto pProcessBuf = std::make_unique<BYTE[]>(nBufferSize);
    PSYSTEM_PROCESS_INFORMATION pspi = (PSYSTEM_PROCESS_INFORMATION)pProcessBuf.get();

    ULONG nRequired = 0;
    while((status = pfnNtQuerySystemInformation(SystemProcessInformation, pspi, nBufferSize, &nRequired)) == STATUS_INFO_LENGTH_MISMATCH && nRequired > nBufferSize)
    {    // Increase the buffer and try again
        nBufferSize = nRequired + 4096;
        pProcessBuf = std::make_unique<BYTE[]>(nBufferSize);
        pspi = (PSYSTEM_PROCESS_INFORMATION)pProcessBuf.get();
    }

    if(NT_SUCCESS(status))
    {
        while(1)
        {
            if(pspi->ImageName.Buffer)
                mapProcesses.emplace((uint32_t)pspi->UniqueProcessId, pspi->ImageName.Buffer);
            else
                mapProcesses.emplace((uint32_t)pspi->UniqueProcessId, std::wstring());    // C++ strings cannot be constructed from NULL

            if(pspi->NextEntryOffset == 0)
                break;

            pspi = (PSYSTEM_PROCESS_INFORMATION)((size_t)pspi + pspi->NextEntryOffset);
        }
    }

    return mapProcesses;
}


The undocumented method actually has a variant.  The above code uses the flag "SystemProcessInformation"  You can also call the API with the flag "SystemBasicProcessInformation" which returns less information, but does so quicker and more efficiently.


Comparing the APIs and their performance

Anytime you have multiple APIs that do the same thing, the question should be raised how do they compare performance-wise.  So I wrote test code to compare the 5 different methods.  You could call each API once using additional code to time the API call.  Or you could call each API a set number of times while timing the overall loop.  Both of these work, but I decided to go with a different method.  I created a 5 second kernel timer, and then called each API repeatedly in a loop as many times as possible.  You simply count the number of times the API was called during the 5 seconds, the more times it was able to call the API the more efficient that method is.

No surprise, WMI is the least performant of the methods at only 144 calls in 5 seconds.  That's an average of 35 milliseconds per API call.  As you'll see compared to the other methods, this is horrible.

EnumProcesses is the next worst API at 1287 calls in 5 seconds.  Almost 10x better than WMI, but still pretty bad.

The Toolhelp library did slightly better at 1664 calls in 5 seconds.

The WTS API bested them all at 2061 calls in 5 seconds.

If you are willing to use the undocumented API, then the performance increased to 3838 calls in 5 seconds.  That's almost double the performance of the WTS API.

But this is nothing compared to the undocumented API variant.  The simplified SystemBasicProcessInformation version clocked in at 102155 calls in 5 seconds.  That's 50x better than the WTS and over 700x faster than WMI.


Conclusion

I feel like the WTS method is the best method for most uses.  It's both fast and simple to use.  Both the EnumProcesses and Toolhelp are old and have been superseded.  WMI should only be considered if you need remote access.

Which leaves the undocumented API.  If you are comfortable calling an undocumented API, then it is the most performant.  With the variant being far and away the fastest method - with one big caveat.  The variant requires Windows 11 version 26100.4770 or newer.  So you would likely need to code multiple methods and fallback to supported older OSes.


You can find the full code sample on my Github page.


One final note, the code and performance numbers in this post are for demonstration purposes only.  They do not represent the maximum performance possible.  For example, the undocumented API dynamically loads the function pointer and allocates a buffer with each call.  But if you need to repeatedly call this API then it would be more efficient to perform that work outside of the loop.

 

Wednesday, December 3, 2025

Enable Bitlocker on Windows Home without a Microsoft Account

Microsoft offers 2 mainstream versions of Windows - Home and Professional.  This goes all the way back to Windows XP, and continues up to Windows 11.  For the most part the differences between the two are minor.  Most people are going to be fine with Windows Home (which costs less than Pro).

There is; however, one feature difference between the two which is critical for everyone to understand - full drive encryption.  This is a feature that encrypts your drive, thereby protesting your data even if someone steals your device.  The Pro version of Windows has "Bitlocker" which encrypts the drive.  Home has a feature called "device encryption" which is basically Bitlocker but with 1 key difference.  Bitlocker (Pro) can be enabled for both local and Microsoft Accounts, but device encryption (Home) requires a Microsoft Account.  This is so annoying, on the one hand I want encrypted drives, and on the other hand I don't want to be forced into an online account.  And I don't always have the luxury of Windows Pro (it mostly depends on what the machine shipped with, which in turn is reflected in the original cost of the machine).  Wouldn't it be great if you could have Windows Home, enable drive encryption, and still use a local account?  Well it turns out you can!!!  This guide will show you how.

The short answer is - you need to log into a Microsoft Account, but only temporarily.  Let's walk though this step by step.  If you're reading this guide then I'm going to assume you are running Windows Home.  I'm also going to assume that you have a local account (you've used one of the many methods to bypass the requirement for a Microsoft Account).  To start, open a command prompt with admin privileges (right-click and select "Run as administrator").  Enter the command "manage-bde -status"  This command will print the current status drive encryption.  In the output look for the following:

Conversion Status:    Used Space Only Encrypted
Protection Status:    Protection Off

The conversion status "used space only encrypted" means the files on the drive are encrypted (the empty space on the drive is not encrypted).  But the "protection status" is off.  How can this be?  What this means is the drive is encrypted, but the decryption keys are stored in plaintext on the local drive.  So a casual attacker could not view your files, but a skilled attacker would know how to recover the key and decrypt your files.  With Windows Pro, Microsoft offers a way to save the key to a file which removes it from the drive and ensures total security.  But with Windows Home, the only place to save the key is in the Cloud.

To solve this problem, create a new account on your computer.  Use an email address so that it is a cloud account.  And make the account into an administrator account.  Then simply log into that account once.  At this point the encryption keys are moved from the local drive to the cloud.  Go ahead and log out of the Microsoft Account and log back into your local account.

If you re-run the manage-bde command from earlier you should see: 

Conversion Status:    Used Space Only Encrypted
Protection Status:    Protection On

As you can see, your drive is now encrypted and the recovery key is no longer stored locally.  Next, open a web browser and log into your cloud account at https://account.microsoft.com/account  Under "devices" click on your computer, then view more details about the device, and lastly under Bitlocker is a link to manage recovery keys.  From here you need to copy your key info and save it in a text file.  Don't be dumb, save the file to a drive other than the encrypted drive.  Maybe an external USB drive, a NAS, a thumb drive, or a printed copy.

At this point you just need to clean everything up.  Go back into Settings on your PC and you can delete the Microsoft Account you created.  You can delete your recovery keys from the online Microsoft Account.  You could even delete your entire Microsoft Account if you so wish.  You now have Windows Home with only a local account and device encryption is fully enabled!

Saturday, June 6, 2020

Free C++ tools

I love programming in C++.  I have done some professionally for over 20 years now.  Even though C++ no longer has as much market share as it once did, now is the best time to learn C++ thanks to the availability of free and useful C++ tools.  There are so many free and useful utilities that learning and effectively coding in C++ is easier than ever.  Below is a list of my favorite free C++ utilities.

Visual Studio has long been one of the best development IDEs, but what's amazing is how many improvements Microsoft is making.  They are not content to rest on their laurels.  VS2017/2019 are truly awesome platforms for development.  And with the introduction of the Community edition, the bulk of the features are available for free to everyone.  Microsoft has offered a free version of Visual Studio going to the Express edition of the early 2000s, but that version was so crippled it was of little use.  Community is so full-featured that only the most demanding of users would need to upgrade.  Microsoft is even expanding VS beyond Windows and branching into Linux and MacOS.

CppCheck is a free static code analyzer, meaning it inspects your code files looking for common mistakes.  CppCheck is surprisingly good at what it does.  If you run it against your code for the first time, you might be surprised at the problems and suggested improvements it finds.

Clang with the Clang Power Tools extension
Although I firmly believe that Microsoft's compiler is the best option for Windows, the Clang team is making significant improvements to their compiler.  That said, Clang does have at least one cool feature - ClangTidy.  Tidy is similar to CppCheck in that it analyzes your code looking for issues.  Unfortunately, Clang has no UI so it is not easy to use.  That's where the Clang Power Tools extension comes it.  It wraps the features of Clang in an ease to use UI integrated into Visual Studio.

VerySleepy
VerySleepy is a code profiler meaning you can analyze your code looking for performance bottlenecks.  I should point out that VS2017/2019 Community comes with a built-in profiler that is easy to use.  I would recommend Microsoft's profiler included with Visual Studio.  But if for any reason you can't use the Microsoft profiler, check out VerySleepy.

I'll end this list with the latest tool I've discovered.  OpenCppCoverage is a code coverage tool, meaning when you run it the tool checks which lines of code actually executed and which did not.  This is useful when testing your code, it helps you to find code that has gone untested, which means the potential for bugs is higher.  Visual Studio does have built-in code coverage, but only for higher paying customers.  The free Community edition does not offer this feature.  Fortunately this program and corresponding Visual Studio extension do a great job.

Monday, November 18, 2019

C++ virtual destructors, a modern take

Several years ago I created a post where I looked at virtual destructors in C++.  In that post I argued that all C++ classes should have virtual destructors, unless you specifically knew what you were doing and understood the risks.  I originally wrote that before I learned the many benefits of "modem" C++ (aka C++11 and newer).  Modern C++ introduces a few new changes that really improve the language, including changes to virtual methods and class inheritance.  So I would like to revise my suggestions for class designs in C++.

First, and most importantly, it is still a bug to derive from a class that does not have a virtual destructor.  As a developer it is your job to ensure you do not derive from a class with a non-virtual destructor.  At the same time, you should not write a class without a virtual destructor that allows someone else to derive from you.  But the great news is, modern C++ has a solution to both of these problems.


Deriving from a base class without a virtual destructor:
C++ has a new keyword called "override" that tells the compiler to generate an error if the virtual function you have created does not match a virtual function in a higher class.  For example:

class CDerived : public CBase
{
public:
    CDerived();
    virtual ~CDerived() override;
};

In this example "CBase" is the base class.  If CBase has a virtual destructor then this code will compile.  But if CBase does not have a virtual destructor this code will not compile!

The keyword "override" is one of the best new features of modern C++.  Any virtual function should be marked with "override" except the virtual functions in the base class which by definition cannot derive from something else since they are in the base class.


Creating a class 
If you create a class without a virtual destructor, that's fine but someone else might derive from your class without your knowledge or permission.  This would be a bug.  If only there was a way to prevent this from happening.  Good news, in modern C++ there is a way.  Use the keyword "final" to indicate that no class may derive from you.  For example:

class CWidget final
{
public:
    CDerived();
    ~CDerived();
};

In this example I can get away without a virtual destructor because no one could ever derive from my class thanks to the "final" keyword.



Given these new keywords, I would modify my original guidelines as follows.

1.  If you derive from another class, always use "override."
2.  If you create a new class, always use a virtual destructor unless both A) your class does not derive from another class and B) your class is marked with "final."  In this situation, none of your class methods should be marked as virtual.



A few interesting side notes.  First, "override" now supersedes "virtual."  You can use both keywords as I did above, or you can just use "override" which implies "virtual."

A second, more important note, is regarding defaulted destructors.  C++ allows you to omit the destructor, or you can explicitly declare a destructor but use the "default" keyword to omit the destructors definition.  In both cases, the compiler will by default generate a non-virtual destructor.  If you want/need a virtual destructor, write the code yourself (using override) or declare as follows:

virtual ~CWidget() = default;

Related to this last point, the following code is a bug:

class CBase
{
public:
    CBase();
    virtual ~CBase();
};


class CDerived : public CBase

{
public:
    CDerived();
};

Yes the base class has a virtual destructor, and you omitted the destructor so the compiler will generate one for you.  But the generated destructor will not be virtual thus leading to bugs.  To fix this add:

    virtual ~CDerived() override = default;

Wednesday, June 6, 2018

Synology wireless router update

About 1 year ago I posted a review of the Synology RT1900AC wireless router.  To sum up, it's a good router but I have had multiple issues with it over the years.

Most of the issues were Synology working out the kinks in the router.  Anyone today who buys the RT1900AC or its bigger brother the RT2600AC would no longer experience these issues.

However, the biggest and most annoying problem I experienced was the router not connecting to the Internet.  If the connection was broken for whatever reason (like a power outage), only about 50% of the time would it reconnect.  If it failed to reconnect, the problem could only be fixed by rebooting the router, often multiple times.

Well I have good news.  I think Synology has finally fixed this issue in one of their regular router updates.  Shortly after my initial review the problem went away.  It has been almost a year now and my router has not had an issue reconnecting to the Internet.  I know 2 other people with Synology routers and they have not experienced connection issues either.  This is great news.  I can now wholeheartedly recommend either of the Synology routers to anyone looking for a better wireless router.


In other good news.  Anyone who follows technology may remember back in October of 2017 they announced KRACK, a vulnerability that affected all hardware and software that used the WPA2 protocol; including Windows, MacOS, Linux, Android, wireless routers, etc.  Synology pushed a fix for KRACK one day after the announcement.  The only company I am aware that released a fix earlier was Microsoft which had secretly pushed a fix a few weeks earlier.  But Synology got the fix out to their customers faster than most major companies include Google and Apple.  Way to go Synology.  Oh, and many Linksys and Netgear routers were never patched, so they are still vulnerable.

Monday, October 9, 2017

Upgrade to Windows 10 for free!

Good news for anyone wishing to upgrade to Windows 10 for free.  When Windows 10 was first released Microsoft offered a free upgrade for all existing Windows owners.  This upgrade was convenient in that it was offered through Windows Update, so upgrade was relatively quick and painless.  Just walk away from your computer for an hour or so and come back to find Windows 10 installed with all your data and programs still on there.

However, this free upgrade offer was a limited time only, and that offer is no longer an option.  But I have good news, anyone with a valid activated copy of Windows can still upgrade Windows 10 for free.  The key is you cannot do an in-place upgrade but instead you must perform a fresh install.  This is actually my preferred way to upgrade to a new operating system.  Here's how you do it:

1.  You need your existing Windows license key.  This license key might be printed on a sticker attached to your computer.  If not, the easiest way to get your product key is to download and run the utility ProduKey.

2.  You need to obtain the Windows 10 installation files (ISO image).  If you Google for it you can find the download and media creation links direct from Microsoft.  Be aware, you cannot upgrade to any version of Windows 10, but you need to keep it in the same edition.  So if you currently have Windows 7 Home Premium you can install Windows 10 Home.  If you have Windows 7 Professional you can install Windows 10 Professional.

3.  Make a complete backup of your current system.  Again, this is a fresh install that will erase all your data, so don't forget to back up first!

4.  Install Windows 10 onto your computer, be sure to erase the existing copy of Windows and install a fresh copy.  If you are prompted for a license key, enter your existing Windows license key.  Many newer computers the license key is saved on the motherboard itself - if so, Windows will automatically detect this license key and use it without prompting you.

5.  After installation, verify Windows 10 is activated with a "digital license."

6.  Reinstall the programs you use and restore your data from the backup.

7.  After installation, follow these suggestions to configure Windows 10 so that it's usable.


I have used this technique twice to upgrade older Windows 7 systems to Windows 10.  I have not tried Windows 8.x, but I assume it works there as well.  However, older Windows XP and Vista license keys may not work since they are technically out of service.  Also, I cannot guarantee this technique will always work.  Microsoft could stop this at anytime, so proceed with caution.

Update: I can confirm this technique still works as of June 2018.  I have also learned that it does work with "Retail" license keys but not "MSDN" license keys.  A "retail" license key is one that came with a new PC or a legit copy of Windows purchased separately.  "MSDN" license keys are keys used by developers and IT professionals.  So as long as Windows came preinstalled on your computer this technique should allow you to upgrade to Windows 10.

Friday, July 21, 2017

Synology RT1900AC wireless router review

My review of the Synology RT1900AC wireless router would best be summed up as "the wireless router I so want to recommend, but just can't because of issues."

So first a little background.  Like most people I've had several different wireless routers over the past decade.  My previous two routers (Linksys and Netgear) I replaced them not because they were broken or too slow, but because a security flaw was discovered in the router that would allow someone on the Internet to compromise my network, and the manufacturer refused to release a firmware fixing the problem.  Most router manufacturers only support their hardware for a year or two, after that they want you to buy new hardware - what a waste and what a shame.

Now Synology is a company I've used for years, they are most well known for their excellent NAS (Network Attached Storage devices).  I have had a Synology NAS for many years and what I love about them is their support.  They release regular updates for their hardware, and they support their older hardware far longer than most companies would.

So in 2015 when Synology announced they were going to release a wireless router I was very excited!  Finally a company that would support their wireless router long term.  When the Synology RT1900AC was finally released in North America in early 2016 I was a very early adopter, purchasing my unit within 1 week of release.

Unfortunately I've had a number of issues since then and had to contact their tech support on multiple different occasions.  Here's a summary of the issues I've had:

  1. I have a Raspberry Pi connected using wireless, but from time to time the connection would drop out.  The Raspberry Pi was connected using a very common "nano" wireless adapter.  After many emails with tech support on this I found a solution.  If I replaced the wireless adapter with a different one with a larger antenna, the connection issues went away.  What's frustrating about this problem, the Raspberry Pi was only about 2 feet from the RT1900AC, it should have had a strong signal.  Also, the same nano wireless adapter with my previous Netgear wireless router had no issues.  So something about the combination of this nano adapter and the RT1900AC did not work well.
  2. When connecting to my home network remotely using VPN, I could originally access machines in my network but not the RT1900AC's management interface itself.  Tech support helped me get correct firewall rules in place to allow access to the RT1900AC.
  3. When changing the firewall rules to allow access to the RT1900AC, it removed access to other machines in my network.  Neither tech support nor I was unable to find the problem, and I just gave up on VPN for about a year.  I did eventually get it working, continue reading for those details.
  4. A few months ago Synology phased our support for their old VPN server package and replaced it with a new package called "VPN Server Plus."  Since the old VPN wasn't working for me, I ditched that and installed the new one.  When I went to enable OpenVPN it gave me this weird error about installing a certificate.  Tech support had never seen that error before and had no idea what to do.  I tried a factory reset and that fixed the OpenVPN certificate error.  Now using the new VPN server I'm finally able to access both local machines in my network and the RT1900AC itself.
  5. Far and away the biggest issue I've had is not reconnecting to the Internet.  This happened to me the very first day I got the router and continues to happen to this day.  In short, if I reboot my router, there's a power outage, or my ISP drops my connection for some reason when the RT1900AC comes back up only 50% of the time will it reconnect to the Internet.  The rest of the time it won't connect, no matter how long I wait.  The router can be up for days in this unconnected state, it will never reconnect.  The only solution I've found is to reboot the router repeatedly under it does reconnent.  Sometimes I have to reboot the cable modem as well.  I've had it happen before where Internet drops and I literally reboot my router over and over for 2 hours before it finally reconnects.  Now I know this issue is Synology's, I have worked on enough networks to be able to diagnose this.  Working with tech support I rebooted my router a dozen times, and I think it reconnected 5 times and failed to connect 7 times (they analyzed logs from these attempts).  I then connected my Windows 7 computer directly to the cable modem and rebooted a dozen times.  Windows connected to the Internet all 12 times.  I've tried a factory reset, no change.  I've updated to every firmware as they release them, no change.  I purchased a new cable modem, no change.  Now I have a family member in other city with a RT1900AC and they have the exact same problem.  Synology even mailed me a replacement unit at their cost for me to try.  That unit experienced the same problem.  They have tried to diagnose this problem but cannot figure it out.  In the mean time I know of at least 2 people with this same behavior.  It's very frustrating, no one wants a wireless router that won't connect to the Internet.  What good is that?



Even though I've had issues with this router,  it's still a good router.  In fact, I would go as far to say it's better than most wireless routers.  But it's far from perfect and it definitely has not lived up to my very high expectations.  Many of the problems I've faced were fixed in software updates over the months.  But that network connection issue, if they could fix that I would wholeheartedly suggest anyone and everyone should buy this router.

I've spent most of this post talking about the problems.  But I did want to mention the good things about this router.
  • Regular software updates.  Synology publishes updates about once a month.
  • The web management UI is far and away the best web management UI.  Way better and more responsive than anything from Netgear, Linksys, etc.  I'm pretty sure they also have a phone management app, but I have not tried that.
  • The router can support multiple Internet connections (e.g. both a DSL modem and a cable modem) and can operate them in either load-balancing or failover modes.
  • Built-in support for 20 different DDNS providers, including Synology's own which works great!
  • Can connect to the Internet via mobile 3G/4G with additional hardware.
  • Very good parental controls/filtering as well as QoS and wifi priority.
  • Tons of services like VPN, SSH, FTP, SFTP, SMB, etc.
  • USB and SD card slots can function as lightweight NAS for your network.
  • VPN server and VPN client with support for common providers such as OpenVPN, SSTP, L2TP, and PPTP.
  • Additional packages such as media server to host media files for other devices on your network.

To sum up the RT1900AC router is a good piece of hardware that still has some bugs, and hopefully Synology is able to work out those bugs.  If you're a network power user and don't mind a little extra work, give it serious consideration.  If you're a regular user you might want to steer clear as you could be overwhelmed if you run into issues as I have.

Wednesday, May 31, 2017

How to make Windows 10 usable

[Updated 12/31/2018]

Recently I upgraded my computer to Windows 10.  Microsoft, for whatever reason, with every release of Windows completely changes things.  For no good reason they move stuff around and change things that weren't broken.  So here is a guide on how to make Windows 10 more like Windows 7, which makes Windows 10 usable.

  • When installing Windows 10, if you have the choice select Windows 10 Enterprise.  Enterprise is nice because it does not come pre-loaded with all the Windows Store Apps that Home and Professional do.  So out of the box it's closer to Windows 7.
  • Download and install Open-Shell (formerly known as Classic Shell).  Windows 10 does include a "start menu" which was removed in Windows 8, but it's no where near as good as the start menu in Windows 7.  Open-Shell restores that start menu in all its glory.  In fact, Open-Shell is even better than the Windows 7 start menu and works on Windows 7 too!  Spend a few minutes exploring all the settings in Open-Shell, you can customize it exactly how you want it.
  • Download and run OldNewExplorer.  This extension fixes Windows File Explorer to the look and feel more like Windows 7.  When you run OldNewExplorer, click "Install" and then select the options you want.  Here are my settings:


  • Download and install Explorer++.  Even though OldNewExplorer fixes much of the Windows File Explorer, it's still not as good as the Windows File Explorer in older versions of Windows.  That's where Explorer++ comes in.  This open-source program is a very good file manager.
  • Download and install FileLocator Lite.  This program is free for personal use and it's a very good Windows file search program.  The file search in Windows 7/10 is horrible compared to Windows XP.  FileLocator Lite restores that Windows XP style search.
  • Download and install Process Hacker.  Although Task Manager in Windows 10 isn't that bad, they did still change it from Windows 7 and I preferred the simple process view.  Process Hacker is a great open source tool that is very configurable.  It even has an option to replace the built-in Task Manager for keyboard and mouse shortcuts.  If neither the Windows 10 Task Manager nor Process Hacker work for you, give Process Explorer (also from Microsoft) a try.
  • Windows 10, more than any previous version of Windows, sends a lot of data back to Microsoft.  Data like which apps you use, how long you use them, what websites you visit.  It even downloads data from Microsoft such as ads to display in Windows.  A program called ShutUp10 allows you to turn all this off if you want.  You can turn some things off and leave other things on.  I always run this on new Windows 10 machines.  I personally don't turn off all settings, some things like Windows Update I leave enabled.  But ShutUp10 does a great job of explaining each setting and even gives their recommendations as to enabled or disabled.
  • If you want a "pure" desktop environment, remove as many of the Windows Store applications (formerly called Metro-apps) as you can.  This can easily be done using the following PowerShell script.  This script will remove all packages except A) those packages deemed by Microsoft to be required and thus cannot be uninstalled using any technique and B) the "Windows Store" package.  Strangely the Windows Store package is not marked "required" but if you remove this package you cannot install Windows Apps in the future if you want to.  Best to play it safe and leave this one package installed.  Save the following script as "RemoveApps.ps1" then run PowerShell as an administrator.  Then run the script.  If this is the first time you've run a PowerShell script on your system you might get an error.  If so, run this command "Set-ExecutionPolicy RemoteSigned" to allow the script to run.  Note, after you run this script there may still exist some Windows apps in the start menu, annoying things like "Candy Crush."  That's because these apps aren't actually installed, the Windows start menu puts them there as advertisements.  If you click on them the app is downloaded and installed.  You can right click and uninstall these annoying advertised apps.
# RemoveApps.ps1
$storepkg = (Get-AppxPackage Microsoft.WindowsStore).PackageFullName

$pkgs = (Get-AppxPackage *).PackageFullName
foreach($pkg in $pkgs)
{
    if($pkg -ne $storepkg)
    {
        Remove-AppxPackage $pkg
    }
}

$pkgs = (Get-AppxPackage -AllUsers *).PackageFullName
foreach($pkg in $pkgs)
{
    if($pkg -ne $storepkg)
    {
        Remove-AppxPackage -AllUsers $pkg
    }
}

  • Right click on the start menu and remove Task View and Windows Store.
  • Right click on the start menu and select Search | Hidden to hide the Cortana search bar.
  • Select Settings | Personalization | Colors | and enable Show Color on Title Bar.  This adds color back to the application caption bar instead of the default plain white bar.
  • Inactive windows in the background still have a white title bar.  Adding color to this helps it to stand out as a background windows.  Open regedit and browse to HKCU\SOFTWARE\Microsoft\Windows\DWM.  Create or edit the DWORD value AccentColorInactive.  Set it to the hex value for the color you want, e.g. 00c0c0c0 for 192 192 192 (light gray).
  • Add my computer and network links to the desktop by selecting Settings | Personalization | Themes | Desktop Icon Settings and enable Computer and Network.
  • Disable that annoying lock screen which forces you to "swipe" to login (which is annoying for keyboard and mouse).  Open the registry editor and ensure the following key and value exists:  Key: HKLM\SOFTWARE\Policies\Microsoft\Windows\Personalization  Value: "NoLockScreen"  Type: DWORD, set to 1.  Update:  I have noticed not all versions of Windows 10 will honor this registry key, in particular Windows 10 Home and especially newer updates.  But after much trying I got this to work, even on Windows 10 Home Creators Update.  In addition to this registry key, you need to make a Group Policy change.  The trick is, Windows 10 Home does not include the Group Policy Editor, so you need to install that.  This website has a batch file you can download and run that installs the Group Policy Editor.  Then run gpedit.msc and browse to Computer Configuration | Administrative Templates | Control Panel | Personalization.  Find "do not display the lock screen" and change it to "enabled."  If you try and that and it still doesn't work, here's one final thing to check.  Browse to (or create if missing) the following registry key: HKLM\Microsoft\Windows\CurrentVersion\SystemProtectedUserData\<SID>\AnyoneRead\LockScreen.  There should be a DWORD registry value "HideLogonBackgroundImage" and set to 1.  Note, this last registry key is a system-protected registry key, you'll need to take ownership of the registry key in order to change the value.
  • If you try and copy files to/from your Windows 10 machine using the admin shares, you might get access denied even when you enter valid credentials.  To fix this, open the registry key HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System then create a DWORD value "LocalAccountTokenFilterPolicy" and set it to 1.

These steps make Windows 10 very usable.  In fact, with these changes I think Windows 10 is better than Windows 7.  But without these changes Windows 10 is hard to use and navigate.

Thursday, August 25, 2016

C++ Programming Tips

For 2 decades now I have been a professional C++ developer.  Along the way I have learned a lot about programming in C++.  I've made a lot of mistakes and learned from them.  There are thousands of other C++ developers out there of all skill levels who could benefit from this knowledge.  So below is a list of tips I've learned that will help you to write better C++ code.  For each tip I include a brief explanation why that tip is worth remembering and using.


Consistent Coding Style
Every developer has their own coding style.  I'm not going to tell you to use one style over another.  But it is important that you are consistent in your coding style.

Reasoning:  If you are consistent in your coding style it makes things like global searches more effective.


Virtual Destructors
Always make your class destructors virtual.

Reasoning:  Without virtual destructors you can introduce bugs and memory leaks.  I wrote an entire post on this topic alone.  There is one exception.  If your class has no other virtual functions, and if your class does not derive from a base class, and if you mark your class "final" then you can safely omit the virtual destructor.


Const Function Parameters
Whenever possible, function parameters should be made "const."  If a given parameter is not modified by the function, then the parameter should be marked const.

Reasoning:  It's great self-documenting code.  Just by looking at a function declaration you can tell if a parameter will be modified.  It also can help you catch bugs in your code because the compiler may warn you if you accidentally try to violate the const variable.


Const Member Functions
Whenever possible, class member functions should be marked "const."  If a given function does not modify a class member variable or call another non-const member function, then the function should be marked const.

Reasoning:  It's great self-documenting code.  Just by looking at a function declaration you can tell if the class object will be modified.  It also can help you catch bugs in your code because the compiler may warn you if you accidentally try to violate the const function.


Static Member Functions
If a class member function does not modify a class member variable nor call a non-static member function, then that function should be marked as "static."

Reasoning:  Again, it's great self-documenting code, and it can help you catch bugs in your code because the compiler may warn you if you accidentally try to violate the static function.


No Classes without Member Variables
If you have a class that has no member variables, instead turn that class into either standalone functions or use a namespace to group the functions.

Reasoning:  A class with no member variables is not an object at all.  By using a class you are wasting a small amount of runtime memory because of things like the "this" pointer.


Properly Size Integers and Floats
When declaring a new integer or (to a lesser degree) a float, consider the range of possible values for that integer and choose accordingly.  For example, if the variable will store a person's age then all you need is an 8-bit integer, don't just type "int" and be done with it.

Reasoning:  Choosing properly sized variables will help to minimize memory usage during runtime.


Use Signed and Unsigned Integers Appropriately
Most developers seem to use signed integers when an unsigned integer is what they really should have used.  For example, a variable to hold a percentage should be an unsigned 8-bit integer since the valid range is 0 - 100.

Reasoning:  If you properly use signed and unsigned variables, the compiler will usually generate warnings if you try and improper operation.  This helps to prevent bugs in your code.


Use Pre-operators Instead of Post-Operators
Whenever possible use pre-increment (++itr) and pre-decrement (--itr) instead of post-increment (itr++) and post-decrement (itr--).

Reasoning:  It turns out that pre-operators are faster than post-operators if the object in question is a class.  For simple data types like integers there is no difference.  The reason pre is faster than post is because post must make a copy of the original object before performing the operation, but pre does not need to make a copy of the object.


Avoid Large Array Variables
Do not declare large array variables, for example:  char buffer[8192].  Instead large arrays like this should be allocated using malloc/new.

Reasoning:  All function variables, include arrays, are allocated against the stack not the heap.  It's well known the dangers of using a recursive function, you might blow the stack.  But large arrays like this could also lead to a stack overflow.  So avoid large arrays and malloc/new them instead.  It's alright to use small arrays in this way.


Use "bool" and "nullptr" instead of "BOOL" and "NULL"
If your compiler supports them, then use the newer data types bool and nullptr.

Reasoning:  By using native types instead of the older #defines the compiler can help you to catch mistakes.  For example, if you accidentally type the following the compiler should give you a warning: bool bResult = (n = 5);  You meant to type "n == 5" but make a common mistake.  Since you used "bool" the compiler should catch this.  But if you used "BOOL" the compiler sees this as valid code.


Don't Compare Against TRUE
Do not test for true like this:  if(func() == TRUE) or this:  if(func() != TRUE)  Instead test like this:  if(func() != FALSE) and this:  if(func() == FALSE)

Reasoning:  "TRUE" is defined to be 1, but a function could return something other than 1 to represent true.  The only way to guarantee a correct comparision is to only test against FALSE.  Of course the best option is to use the newer native type "bool" if it's available.


Use malloc/free Instead of new/delete for Simple Types
If you are allocating memory for native types (ints, floats, chars, enums, or structs containing only these types) then use the older C malloc instead of the newer C++ new.  If you are allocating memory for a class object, or a struct containing one or more class objects, then you must use new.

Reasoning:  Calling malloc is faster than new.  They both allocate memory on the heap, but new then checks for and calls constructors.  This means for simple data types malloc is faster.


Do Not Test for NULL After Calling new
If you allocate memory with new, do not test the result to verify the allocation was successful.  For example:

CMyClass *p = new CMyClass;
if(p)
{
     ...
}

Reasoning:  This test is a complete waste of time and code.  The new operator already tests the allocation.  If the allocation fails then new throws an exception.  So testing the result yourself is redundant and a waste.  Note this is not true of malloc which does not test the result.  So if you use malloc or other similar allocator, be sure to verify the result.


Know When to Test for NULL Before Freeing Resources
Some release/free functions test the input before attempting to deallocate them, others do not.  For example, CloseHandle on Windows does not test the handle against NULL first.  On the other hand, "delete" does test the pointer against NULL.

Reasoning:  Not testing first may result in an application crash if the release function does not check.  If the release function does check and your code also checks, that is wasted CPU cycles.  Learn which functions to check first before calling.


Size Versus Length
When naming things like functions and variables, be aware of the difference between size and length.  Size is the number of bytes an item requires whereas length is the count of or the number of items.  This is really a problem when it comes to strings (character arrays).  With "char" arrays size and length ARE the same, so many developers use "size" and "length" interchangeable.  But with newer Unicode and wide strings size and length are different.  So name functions and variables accordingly.

Reasoning:  Properly named functions and variables reduce the chance of bugs.  Since size and length are not always the same, using the wrong one would be a bug.


Do Not Put Conditional Statements on One Line of Code
Do not simplify your code by condensing things down to one line of code.  For example:

if(p) delete p;

Instead this should be:

if(p)
    delete p;

Reasoning:  When you condense code like this it makes it harder to debug.  When debugging line by line it is harder to tell if the conditional statement succeeded or failed.



Do Not Use "catch(...)"
Never use "catch(...)" in your code!  This form of exception handling catches all exceptions.

Reasoning:  Catching all exceptions prevents your code from ever crashing, which is a good thing right?  Wrong.  In the event of an exception your code continues to execute but it is now running in a "compromised" state.  Memory may be corrupted, variables or registers may be wrong.  So what ends up happening is your program will crash randomly some place else.  These random crashes are impossible to debug postmortem.  It is best to remove "catch(...)" and let the original crash occur.  Then you can debug and fix the original problem.


Do Not Use Exceptions for Flow Control
Do not use exceptions as a simple way of transferring flow to a different part of the code.  For example, if you are deep inside nested loops and it's time to exit, you could throw an exception and catch that exception before leaving that function.  Instead you should exit each loop or even use a goto.

Reasoning:  When it comes time to debug your code often times it's nice to attach the debugger and tell the debug to break on all exceptions (handled or otherwise).  But if you used exceptions for flow control then the debugger will constantly break when no problem has occurred.  Exceptions should be reserved for just that, an exceptional condition has occurred that needs special attention.  Flow control is not "exceptional."


Only Include Required Files in Your Headers
Do not #include a lot of extra things in your header files.  Only include what that file must have.

Reasoning:  First, it reduces compile time as the compiler has to open up and parse fewer files.  Secondly, if that file gets included somewhere else or in another project, it prevents you from having to pull in a lot of extra unnecessary files.


Forward Declare What You Can in Header Files Instead of Including More Headers
If your header file references a pointer to a class object only, do not #include the header for that class.  Instead forward declare the object and put the #include into the CPP file.

Reasoning:  The benefits are the same two as the previous.  First, it reduces compile time as the compiler has to open up and parse fewer files.  Secondly, if that file gets included somewhere else or in another project, it prevents you from having to pull in a lot of extra unnecessary files.


Only Include Required Files in Your CPP File
Do not pull in extra unused headers in your source code files.  Make an effort to only include required files, which means removing old headers as code changes over time.

Reasoning:  It reduces compile time as the compiler has to open up and parse fewer files.


Avoid Putting Function Bodies Into Header Files
Header files should be for class and function declarations.  All the function bodies should be located in CPP files.  There are two exceptions; inline functions and templatized functions - both of which many compilers do not allow you to split them across header and source code files.

Reasoning:  Again, it decreases the time it takes to compile your code.  It also simplifies your headers meaning you do not need to pull in as many files if the header files are used in another project.


Use "#pragma once"
In all of your header files use "#pragma once" and not the older #ifndef guards.

Reasoning:  Using pragma once is the only way to guarantee a file is included only once.  Problems can occur with guards such as copy and paste bugs (you copied from one file to another and forgot to make the guard unique).  Also, if your guard matches a guard from another file (perhaps from an SDK) then a bug might occur.


Beware of Unsafe Macros
As a general rule, do not pass a function into a macro.  For example, assume a macro MAX(a,b)  Do not do something like this:  n = MAX(1000, crypt.CalcCryptoHash());

Reasoning:  Do you see the problem with the call to MAX above?  Probably not, because the macro hid the details from you.  If I expand the macro as the compiler would, the above becomes:  n = (1000 < crypt.CalcCryptoHash() ? crypt.CalcCryptoHash() : 1000) ;  Now the problem should be obvious, the function CalcCryptoHash() is potentially called twice.  At best this is a performance hit, at worst depending on what this function does it could be a bug in your code.  So be careful when using macros.


Learn When and Where to Use References
If you are writing a function that takes a pointer (*) as an input, consider using a reference (&) instead.  In fact, probably the only time to use pointer instead of a reference is if the caller of the function might pass in NULL/nullptr.  But if the parameter can never be NULL/nullptr, then use a reference instead.  Also, if the item being passed to a function is an object (class or struct) or even a native type that is larger than the system architecture (e.g. a 64-bit integer on a 32-bit system) then pass by reference as well.

Reasoning:  References are more efficient and make for cleaner code compared to regular pointers.  When passing things by reference it becomes even more important to use "const" for variables the function will not modify.

Wednesday, August 17, 2016

Raspberry Pi Weather Station - part 4

In the last post I covered the hardware for the sensors.  In this post I want to talk about the software that ties this all together.  There are several goals with the software.  Obviously it needs to read the sensor data.  I would like it to output that data to a local web page so I can access it with a web browser.  I also want it to export the sensor data to a Personal Weather Station (PWS) account registered with WeatherUnderground.  And lastly, I would ideally like it to record or save the data to a database in case I want to analyze the data later on.

Doing searches on the Internet I quickly found WeeWX, an open-source project that does all of this.  It reads sensor data, generates a web page, and can optionally log to WeatherUnderground.  I was ready to go with this option, but then I discovered it was written in Python which gave me pause for concern (more on that later).

I next found wview which, like WeeWx, is an open-source weather station project.  It's written in C which is good, but it sounds like the project has grown rather large over the years.  It's become a huge behemoth that's capable of doing almost everything when all I want/need is a simple targeted application.

In the end I decided to write my own weather station application.  After all, isn't that was software engineers do, write software?  I wrote a small simple program that reads all the sensors and outputs the data in 3 different ways.
  1. It generates a local HTML file which can be viewed from another computer using a web browser.  My code is not a web server, it just outputs an HTML file.  So you still need a web server such as Lighttpd or nginx.
  2. It sends all sensor data off to WeatherUnderground so the results can be viewed on their web site.
  3. The sensor data is written locally to a SQLite file for later analysis.

I wanted to go back and talk about my opposition to the use of Python in WeeWX.  The main problem is I do not consider Python to be a "real" programming language.  It is a scripting language, and scripting languages have their place.  But not in final applications in my opinion.  I also do not like the extreme performance hit Python incurs.  And remember, this is running on a Raspberry Pi which is not a powerful machine to begin with, so any unnecessary overhead is exaggerated.  How much of a performance hit are we talking about?  Using the sample Python code from Adafruit, reading the temperature sensor takes approximately 5 seconds.  Most of that overhead is spent loading the Python libraries and creating the runtime environment.  Compare that to the C++ code I wrote to read the temperature sensor which is instantaneous.  To me writing this application in C++ was not only the only way to go, but it was a fun project!


I have uploaded my C++ code to GitHub for anyone to download, use, or modify.  Bear in mind this code was written specifically for my weather station.  So it assumes the sensor hardware I'm using connected to the Raspberry Pi on the GPIO pins I'm using.  But this could be a great reference for anyone doing a similar project.

And finally, the results.  Here's a link to my PWS on WeatherUnderground.

Monday, August 1, 2016

Raspberry Pi Weather Station - part 3

Next I wanted to talk about the hardware for the sensors my weather station will employ and how each sensor is wired to the Pi's GPIO pins.

BME280
As I previously said the BME280 can be access via either SPI or I2C.  I decided to use I2C.  In order to wire to the Pi you will need to connect 4 wires.  The first is the power wire which connects to the 3.3V pins on the Pi.  The second is ground which connects to one of the ground wires on the Pi.  And lastly are the two I2C data lines which get connected to the I2C pins on the Pi.  Wiring this sensor up is not hard, it just requires a little bit of soldering and time.

The rain, wind speed, and wind direction sensors were a single package and clearly not designed specifically for the Pi.  The output wires have RJ11 connectors (telephone connectors) on the end of them.  I believe they were originally designed to plug into a specific weather module that reads the sensors.  Since I am using the Raspberry Pi as my homemade weather module, I will need to modify each sensor.

Rain Gauge
The rain gauge is an interesting sensor.  It literally has two buckets side by side.  When one side fills with water it gets too heavy and tips over.  The other side then fills up and eventually tips back.  Each time this bucket tips over a magnet passes by a reed switch.  This causes a momentary close in the switch which can be detected by the Raspberry Pi.  This only requires 2 wires, power in and the signal detect line coming out.  For power in I used the same 3.3V pin as the BME280 sensor, and for the signal wire I picked any available GPIO pin.

Wind Speed
For wind speed there are 3 cups that are free to spin as the wind hits them.  Each time it spins another magnet passes by a read switch.  As with the rain, the Pi can detect these events and use that to calculate the wind speed.  Again, only 2 wires are necessary, one for power (the same 3.3V pin) and a signal detect line which is any available GPIO pin.

Wind Direction
The wind direction sensor works using magnetic reed switches, but unlike the previous sensors it contains 8 reed switches.  Each reed switch corresponds to a direction; N, NE, E, SE, S, SW, W, and NW.  As the magnet moves around according to wind direction it closes different switches which allows you to determine direction.  To complicate matters, it is possible for the magnet to close two neighboring switches at the same time.  This gives you a total of 16 possible bearings.

Reading the wind direction was by far the hardest.  The problem is the wind direction sensor is designed to be read using analog and only has 2 wires, power in and signal out.  So if you apply 5V in then the output voltage will be one of 16 different values between 0 and 5V.  The Raspberry Pi however cannot read analog values, it only reads digital signals.  One possible solution is to use an analog to digital converter which requires additional hardware.  But there is a cheaper way, hard wire the 8 reed switches directly to the Pi.  To do this I had to open the wind direction sensor and remove the circuit board.  On this circuit board are 8 small surface mount resistors which had to be desolder.  In their place I soldered 8 wires, one for each switch.  Lately I soldered a power wire to the outside ring which ties all reed switches together.  This allows me to provide power in (again, 3.3V) and read each sensor one at a time using the 8 signal wires I attached to available GPIO pins.  This required a lot soldering on a small circuit board, but the end result is a digital wind direction sensor that was originally designed to be analog.

Monday, July 25, 2016

Raspberry Pi Weather Station - part 2

Continuing this project, in this post I will talk about preparing the Raspberry Pi to become a weather station.  I won't go into the details about installing Raspbian onto the SD card or using raspi-config to set the time zone and locale info for your system.  You can read my previous Pi posts for those details.

For this project the Raspbian group has released something new - a "lite" version of Raspbian.  This is perfect, I can get a clean bare bones Pi right out of the box.  Getting wifi setup on the Pi zero is a little more complicated because there is no ethernet port.  So it's a multi-step process.  First you must use a keyboard and monitor and manually edit the /etc/wpa_supplicant/wpa_supplicant.conf file to add info for your wifi network.  Then power down the Pi, remove the keyboard and monitor, attach the wifi adapter, then power up the Pi and connect via SSH.  This is where I ran into my first problem.  The RNX-N180UBE I've used in the past and ordered for this project did not work.  I quickly discovered Rosewill is now making "version 2" of the adapter which uses a different chipset and Linux does not have drivers for this adapter.  After much searching I found this forum post with details on where to download drivers specific to the type or Raspberry Pi and version of Raspbian you are running.

For the BME280 sensor I bought, it can be accessed via SPI or I2C.  The Pi supports both, but is one better than the other?  From what I can tell, SPI is a faster connection, but I2C is a better designed interface with more control.  Since reading this sensor won't involve large amounts of data, I decided to go with I2C.  But in order for this to work you need to enable the I2C bus inside of raspi-config.

Another step was to solder in the header to the Pi Zero.  This isn't hard, and in fact it's kind of fun.  But if your soldering skills are lacking then save yourself the trouble and use a Pi 1, 2, or 3.