Bad things happen when programmer finds a soldering iron

Randomness in 8-bit Microchip PIC

Generating pseudo-random (or even fully random) numbers on desktop computers is mostly a solved problem. However, what if we need to generate random numbers on much smaller devices? For example, on Microchip’s 8-bit PIC16F1454 microcontroller?

Good news first, mersenne twister is no longer the only player in town. These days we have a whole family of both faster and more random algorithms courtesy of Guy Steele and Sebastiano Vigna. Their xoshiro / xoroshiro generators cover essentially any combination of speed and memory needs. They are probably a pinnacle of randomness quality you can fit in such a minimal space.

Since I did need a minimal footprint, I first selected xoroshiro64**.

uint32_t rotl(const uint32_t x, int k) {
    return (x << k) | (x >> (32 - k));
}

uint32_t s[2];

uint32_t next(void) {
    uint32_t s0 = s[0];
    uint32_t s1 = s[1];
    uint32_t result = rotl(s0 * 0x9E3779BB, 5) * 5;

    s1 ^= s0;
    s[0] = rotl(s0, 26) ^ s1 ^ (s1 << 9);
    s[1] = rotl(s1, 13);

    return result;
}

This code will generate random numbers that pass vast majority of DieHarder randomness tests and all that just in 58 bytes of data memory and 365 words of program memory (freeware XC8 2.31, with optimizations).

While this is probably as small as a good random number generator can reasonably get on a microcontroller, Microchip PIC is an 8-bit device at heart and dealing with 32-bit values doesn’t come naturally. Fortunately, xoroshiro algorithm family scales reasonably well and one can “borrow” the setup.

Here is my stab at making xoroshiro a bit more 8-bit friendly.

uint8_t rotl(const uint8_t x, int k) {
    return (x << k) | (x >> (8 - k));
}

uint8_t s[2] = { 0, 0xA3 };

uint8_t next(void) {
    uint8_t s0 = s[0];
    uint8_t s1 = s[1];
    uint8_t result = s0 + s1;

    s1 ^= s0;
    s[0] = rotl(s0, 6) ^ s1 ^ (s1 << 1);
    s[1] = rotl(s1, 3);

    return result;
}

Now, this is a simplified version and definitely not as random as the full implementation. Obvious change is in a state variable size (16 instead of 64) and calculation is taken from xoroshiro128+ so that multiplication can be avoided. There are no multiplication circuits in 8-bit PIC microcontrollers and thus this makes code much smaller and faster.

Lastly, the half of initial state is fixed to 0xA3. When dealing with such a small state, not all combinations of initial state are valid nor they produce equally long period and this is essentially just a workaround to keep numbers coming.

This simplified version needs 10 bytes of data memory and takes only 65 words of program memory. A great improvement (more than 5x in both data and program memory) albeit at significant randomness cost. First of all, you essentially only get 256 seeds with large enough period (64897 bytes). Secondly, the whole space can be only 16-bits to start with. While this might barely pass a few DieHarder tests (e.g. birthdays, 2dsphere, dab_dct, and rgb_permutations), it won’t come even close to the full xoroshiro64** in terms of randomness quality. And let’s not even mention higher state size algorithms.

That said, if you just need random numbers for a game or something similar on a highly constrained device, I would say that quality trade-off is worth the speed and memory usage improvements.


PS: If you initialize random number generator with static values (which is perfectly valid), you will always get the same set of random values. Sometime that is a desired feature (e.g. during debugging) but we usually want something more unpredictable. Assuming you’re using the chip with a separate low-frequency oscillator (LFINTOSC), you can rely on a drift between it and a high-frequency oscillator to get a reasonably random seed.

void init(void) {
    // setup timer if not already in use
    T1CONbits.TMR1CS = 0b11;
    T1CONbits.T1OSCEN = 1;
    T1CONbits.TMR1ON = 1;

    _delay(4096);  // just to improve randomness of timer - can be omitted

    // initialize using timer values (ideally you would wait a bit after starting timer)
    s[0] = TMR1L;
    s[1] = 0x2A;  // important to get high periods and to avoid starting from 0x0000
}

PPS: No, xoshiro algorithms are not cryptographically secure. If you need to generate keys on microcontrollers, look into specialized hardware. These algorithms are intended for general-purpose randomness.

PPPS: Code is available for download. It will use 10 bytes of data memory and should fit into 82 words of program memory with initialization from LFINTOSC.

Storing Settings on PIC16F1454

As I was playing with PIC16F1454, I came to the point where some configurability would be in order. You know how it goes with PIC microcontrollers - just write it in EEPROM and you’re good. Unless there is no EEPROM like there is none for PIC16F1454.

Never mind, I had this issue before, so I can just copy my own code (ab)using program memory for the same purpose. Guess what? There are some issue with this too.

The first of all my old code was for different microprocessor. While principle is the same, it’s not an exact match. The second reason was changes to XC8. My old code doesn’t properly compile on XC8 2.00 - they changed how location is defined. The third (and the last) reason is high-endurance flash that PIC16F1454 supports. Unlike normal flash that’s rated for 10K writes, last 128 of this PICs program memory is rated to 100K. Albeit 10K is nothing to frown about, 100K is much nicer - especially if I end up changing data a lot.

Second and third reason share the same fix. Memory definition looks like this:

#define _SETTINGS_FLASH_RAW { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
#define _SETTINGS_FLASH_LOCATION 0x1FE0

const uint8_t _SETTINGS_PROGRAM[] __at(_SETTINGS_FLASH_LOCATION) = _SETTINGS_FLASH_RAW;

This will use the last 32 bytes, starting at 0x1FE0. This address is conveniently 32 bytes before end, falling without issues within “the last 128 bytes” high-endurance category. Now, if you need more memory, just make the array bigger and move it more forward. Just remember to do so in 32-byte increments as this is the block size for flash erase operation. If you don’t reserve all that memory, you might end up erasing your code and we wouldn’t want that. I personally never had need for more than 32 bytes of memory (i.e. one flash page) but your use case might differ.

All settings can be held in structure. Here I will have two settings - Address and DisplayHeight:

typedef struct {
    uint8_t Address;
    uint8_t DisplayHeight;
} SettingsRecord;

SettingsRecord Settings;

To read these settings, we just need to copy our reserved data (seemingly in _SETTINGS_FLASH_RAW variable) into the structure:

uint8_t* settingsPtr = (uint8_t*)&Settings;
for (uint8_t i = 0; i < sizeof(Settings); i++) {
    *settingsPtr = _SETTINGS_PROGRAM[i];
    settingsPtr++;
}

Writing is a two step process. It starts by erasing the WHOLE 32-word/byte block. Following that, we get to write each byte separately:

bool hadInterruptsEnabled = (INTCONbits.GIE != 0);
INTCONbits.GIE = 0;
PMCON1bits.WREN = 1;  // enable writes

uint16_t address = _SETTINGS_FLASH_LOCATION;
uint8_t* settingsPtr = (uint8_t*)&Settings;

// erase
PMADR = address;         // set location
PMCON1bits.CFGS = 0;     // program space
PMCON1bits.FREE = 1;     // erase
PMCON2 = 0x55;           // unlock flash
PMCON2 = 0xAA;           // unlock flash
PMCON1bits.WR = 1;       // begin erase
asm("NOP"); asm("NOP");  // forced

// write
for (uint8_t i = 1; i <= sizeof(Settings); i++) {
    unsigned latched = (i == sizeof(Settings)) ? 0 : 1;
    PMADR = address;            // set location
    PMDATH = 0x3F;              // same as when erased
    PMDATL = *settingsPtr;      // load data
    PMCON1bits.CFGS = 0;        // program space
    PMCON1bits.LWLO = latched;  // load write latches
    PMCON2 = 0x55;              // unlock flash
    PMCON2 = 0xAA;              // unlock flash
    PMCON1bits.WR = 1;          // begin write
    asm("NOP"); asm("NOP");     // forced
    address++;                  // move write address
    settingsPtr++;              // move data pointer
}

PMCON1bits.WREN = 0;  // disable writes
if (hadInterruptsEnabled) { INTCONbits.GIE = 1; }

The first and last step is dealing with interrupts. During write interrupts must be disabled. Code will disable them before writing and re-enable them afterward if needed.

Erase is easy enough. Just set FREE bit in the PMCON1 register followed by magic incantation (0x55, 0xAA, WR=1) and wait for a millisecond or two. Do note that NOP instructions are mandatory due to how self-writing program memory works. It’s one of the rare instances where NOP actually serves a purpose in C code.

To write data, process is close enough. Load all the bytes you wish to write using PMADR and PMDAT registers to set address and data. All bytes except the last will have LWLO bit set and will just cause loading of data into latches. The last byte must have LWLO cleared, signaling we’re done with writing. After a millisecond or two, bytes are done.

Two things are slightly curious there. The first one is setting of PMDATH to 0x3F. This value is actually the same as for erased cell and this just means we’re not changing it’s value. Note that upper byte is not the part of high-endurance flash and only 6-bit value (words are 14-bits on this PIC). Thus we really shouldn’t use it. The second strange decision is to start loop from 1 instead of the more conventional 0. This is so that we can determine if we’re at the last byte without substracting one.

In any case, this is all you need to make your program memory work as a storage for your settings.


PS: Procedure is the same on PIC16F1454, PIC16F1455, PIC16F1459, and probably quite a few more.

PPS: Whole code is available in Git repository.

PPPS: There is quite useful application note from Microchip (AN1673A) dealing with high-endurance flash. Their code uses similar but slightly different approach. If you don’t like this code, maybe theirs will tickle your fancy.

Duplicating Non-Reentrant Functions

Illustration

As I was playing with PIC16454 using USB, I kept getting these warnings: Microchip/usb_device.c:277:: advisory: (1510) non-reentrant function "_USBDeviceInit" appears in multiple call graphs and has been duplicated by the compiler

This was due to function being called from both main function and from interrupt handler. Since function could be interrupted at any point in time, this was definitely a problem and compiler did find a valid solution. However, this was a bit suboptimal for my case.

Since I had issue with only a few functions, I decided to make use of Hybrid option is XC8 compiler stack options. With this option warnings were gone. Surprisingly, this also made my code smaller. Hybrid stack compiled into 7181 words while standard Compiled stack was 7398.

If you have reentrancy happening in just a few functions, Hybrid option might be good for you.*


* Some restrictions apply. Please contact your fellow developers if your compile lasts longer than 4 hours.

The Cost of CyberCard

After publishing text about the CyberCard project I got the question from a friend. Wasn’t it cheaper to buy Jeff Mayes’ interface driver then to build my own?

Answer is yes - at $30 that board is cheap. But that’s not all. Even the original RMCARD205 at $150 is cheaper than what I spent.

First of all, there were 4 revisions. The first revision was a bit too large. Manually filing PCB did the trick for the troubleshooting but I wanted to have revision B with the correct width. While width was now correct, I accidentally shortened it a bit. And yes, this brought me to the third revision. For that revision I also changed MCP2221A to SOIC package. It wasn’t strictly necessary but I figured having all three ICs in SOIC looked nicer than having different package styles on the same board. The last revision D was just a bit more fiddling with design without any major change. Yes, there were some other changes but this was a gist of it.

Considering each revision was around $25 in PCB cost (OSHPark) and I spent about $50 in parts for them, project was more expensive than official RMCARD205 even without accounting for my time. Since the first version was actually working, you can view all the time and money spent afterward as wasted.

But I disagree. From the moment I started working on it I knew it would end more expensive than the original part. Even for the first board I spent more money in PCB and parts than what Jeff’s adapter would cost with shipping. I found this board to be the perfect project: it would result in something useful, it was simple enough that I could work with it whenever I had some spare time, cheap enough that it wouldn’t break the bank, and an excellent chance to setup PIC16F1454 as an USB device.

I was eyeing PIC16F1454 for a few years now (I still have sample from Microchip from when it was originally announced) but I never got around to. When I first started with the board design I noticed MCP2221A USB-to-serial bridge was compatible with 16F1454’s footprint. If I was a betting man, I would have said that MCP2221A was nothing other than PIC16F1454 with the custom code. This project gave me a reason to get into this interesting PIC and do some USB programming.

I actually paid not for the final board - no matter how well it works. I paid a good money to keep me entertained and to fill my free time. And it was worth every penny.

Connecting to CyberPower OR500LCDRM1U UPS Serial Port

Illustration

To keep my file server and networking equipment running a bit longer in the case of power outage, I have them connected to CyberPower OR500LCDRM1U UPS. It’s a nice enough 1U UPS but with a major issue - no USB connection.

Well, technically there is an USB connection but it doesn’t work under anything else than Windows. If you want it working under Unix, the only option is RMCARD205, optional network module upward of $150. Essentially doubling the price of UPS.

And it’s those internal connections Jeff Mayes took advantage of for a simple serial interface. If the only thing you want is a serial interface, you might as well go with his interface driver as price is really reasonable.

However, his boards require you to either have a serial port or to have an USB-to-serial cable. What I wanted was direct USB connection. Since there was nothing out there, I decided to roll my own.

Since I had an UPS locally, it was easy enough to get physical dimensions. Unfortunately just measuring them wasn’t sufficient as they narrow as you go deeper so my first assumption of 3.1x1.7 inches was a bit off. Due to that and bottom connector that was a bit shallower then expected, the final board dimensions were more like 71x43 mm. It took a bit of probing to find the 4 signals I needed were grouped together with GND and RX on the bottom while TX and 12 V were on the top.

Connecting the appropriate serial connections to UART-to-USB converter like MCP2221A was a minimum required but I felt a bit queasy about connecting it directly to my computer. Therefore I decided to isolate the UPS interface from the computer. For this purpose I used Si8621 digital isolator offering 2,500 V isolation which was probably an overkill but allowed me to sleep better.

The last physical piece needed was a cover for card to avoid having a large opening in the back of my rack. While risk of anything getting inside is reasonably low, making a 3D printed cover was easy enough. It took a few tries to get cover design right in TinkerCAD but it avoided having a gaping hole.

If you are interested in making one for yourself, check project page for all the files.

Easy Type-C, Finally

It has been a while since USB Type-C became popular but I still avoided it for my hobby designs. It wasn’t due to its performance, oh no - performance was much better than what I needed. You see, I still use USB 2.0 for literally all my electronics projects. While not fast in the grand scheme of USB, it’s plenty fast for serial communication and that’s what I end up designing my gadgets around 90% of the time. My issues with the connector were due to soldering. Hand-soldering, to be precise.

Most of the type-C receptacle designs are of surface-mount kind. On its own that’s not an issue - I’ve hand-soldered my share of finicky narrow pitch ICs. Issue is the location of 24 pins that are needed. While 12 pins are almost reachable by a thin soldering iron, the other 12 pins are usually under the device. Not an issue if you are making them professionally or even in an oven but impossibility for soldering by hand.

And no, you cannot solder just one side of connector and be compliant. It doesn’t matter if you’re only interested in USB 2.0 as I was - you still need to connect both sides as you need to connect 5.1K resistor for device to be properly recognized. And yes, even if you fail to do that it will work with the type-A to type-C cable. It will sometimes work even with the type-C to type-C. However, things that happen only sometimes have a nasty habit of happening at the most unfortunate time.

Connectors that used a combination of through-hole design and surface-mount pads looked promising on the paper. The unreachable pads underneath were converted to pins and soldering problem got solved. Unfortunately, a lot of such connectors had a shield over the rear pads. Now rear pads became a problem. I did find a few with more open space that were easier to hand-solder but at a pricey $3.

But finally, I found the type-C connector that suits my use case perfectly - USB4085-GF-A. It’s an USB 2.0 connector that allows connection to 16 out of 24 type-C pins. The sacrificed pins mean that you won’t ever be able to use it with the high-speed devices (thus 2.0 moniker). However, the pins that remain include not only all the standard USB 2.0 power and data connections but also CC1 and CC2. These two pins are really important if you want your device to be part of the type-C universe and pull 1.5 A of current. The only thing needed in addition to the old type-A designs is two 5.1K pull-down resistors on each of those pins and you’re golden.

Mind you, the connector is not perfect. At $1.50 it’s a bit pricey but not excessively so. If you are using the standard 1.6 mm PCB, you will need to take a great care to position it flush in order for short pins to even reach to the other side. Hole spacing means you will need to use 6/6 PCB manufacturing which, while often supported, is still a bit less common than bog-standard 8/8. But these are all minor issues and hand-soldering is a breeze.

And despite all this goodness, I only used it for a few test contraptions and not in any of my proper projects. This is due to its biggest drawback - there is only one manufacturer and, outside of my initial purchase, they are pretty much on backorder the whole time. Even at the time of this writing, the quantity is 0 on DigiKey.

Once this connector becomes more available, I am looking forward to using it.


PS: For those wanting to have type-C plug directly on the board, there is a Molex 105444-0001. Just don’t forget to order 0.80 mm PCB.

PPS: For the 90° type-C connector, KUSBX-SL-CS1N14-B looks promising as it also allows connection to both CC1 and CC2 - a necessity for the proper type-C device. I haven’t tried it myself though.

Serial Number In Intel Hex - Bash Edition

When creating USB devices having an unique serial number for each is quite important. Yes, stuff will work with duplicate (or missing) serial number but you might notice Windows will get confused. With the serial devices the most noticeable effect will be a different port assignment on each plug. Not a breaking deal but a bit annoying.

The easiest way to deal with this is having a dummy serial number in code and then using post-build action to change it in the final Intel hex file. For TmpUsb project I even created a script to do exactly this. However, that script was written in PowerShell which until recently (March 2020) didn’t work on Linux and it definitely doesn’t come installed there by default. This and a few other reasons were why I decided to rewrite this in Bash for my CyberCard project.

The first step is setting up the serial number. The most crucial step here is __at macro that will ensure serial number is always at a predictable location. For my example, I selected location close to the end of addressable space (0x1F00). One could use special characters here but I would highly advise to have a valid USB serial number just in case script fails. That means at least 12 alphanumeric characters but I always go for numbers-only as it makes script a bit easier.

//Serial string descriptor
const struct{uint8_t bLength;uint8_t bDscType;uint16_t string[13];}sd003 ^^__at(0x1F00)^^={
    sizeof(sd003),USB_DESCRIPTOR_STRING, {
        '1','9','7','9','0','1','2','8','1','8','1','5','0'
    }};

The final Intel hex file location will end up multiplied by 2 and thus at offset 0x3E00 further shifted by 4 hexadecimal characters. Script then needs to replace every 4th hexadecimal byte up to the count of 13 in my case (as my serial number is 13 digits long).

The only task remaining is calling it once project is done building. Previously I would do this using project properties dialog (Project Properties, Conf, Building, Execute this line after build) but that proved to be an issue if you want to compile on both Windows and Linux. While Linux setup was quite straightforward, on Windows it would get confused if you have any Linux-based toolset installed (e.g. Git Bash) and throw an error:

"User defined post-build step: [update-serial.sh]"
nbproject/Makefile-default.mk:105: recipe for target '.build-conf' failed
make[1]: Leaving directory 'Q:/Projects/Electronics/CyberCard/Firmware/Source'
nbproject/Makefile-impl.mk:39: recipe for target '.build-impl' failed
process_begin: CreateProcess(NULL, bash Q:\Projects\Electronics\CyberCard\Firmware\Source\update-serial.sh, ...) failed.
make (e=2): The system cannot find the file specified.

One workaround was to simply wrap call to this script in .cmd (or .bat) file but that would cause issues when compiling under Linux. Only if there was a way to execute one command for Windows and another one when running on Linux… Cue the Makefile.

While you cannot differentiate OS from GUI, you can go and edit MPLAB project Makefile directly. There we can point toward .cmd on Windows and toward .sh when running on Linux.

.build-post: .build-impl
# Add your post 'build' code here...
ifeq ($(OS),Windows_NT)
        update-serial.cmd
else
        ./update-serial.sh
endif

All that is left is actually placing the script(s) in the same directory the Makefile is situated (download here) and you’re good assuming you defined serial number as I did. If you didn’t, just adjust LOCATIONS variable inside.


PS: This assumes you have Git (or some other bash-capable environment) installed in Windows.

PPS: Don’t forget to use <Tab> in Makefile for indentation. Spaces won’t do here.

ResetBox

Illustration

It all started with an itch to scratch. My modem occasionally misbehaves and needs a reboot. Unfortunately, I have it mounted way back in the 19" rack and accessing it requires removing back panel. And no, I cannot easily unplug it’s connected to UPS and wires are all tucked in the back. What I needed was a button in one of rack spacers allowing for reboot of equipment in the back.

Since I couldn’t find something like this readily available, I decided to build it.

First I enumerated all the equipment I have. One modem (12 V), one NUC (19 V), and finally two wireless access points (48 V). I was at crossroads. As voltage range is quite wide I had to choose if I want to make device work of specific voltage (cheaper) or to have a single device covering all the bases (more expensive). While going cheaper was tempting, I decided to go with wide input after all. It meant I can design thing only once and use it later for some other project without having to check voltage rating each time.

This decision also made me select DC-DC converter module. Making my own wide range converter would be possible but getting it just right would take annoyingly long time and actual cost wouldn’t be much lower. Yes, at $5 R-78HE5.0-0.3 is not cheap but it’s simplicity is worth every penny. If I ever decided to produce this box in bigger quantities, this would probably be the first thing to go in order to lower expenses and simplify design.

While not necessarily mandatory, I also opted to include EMC filter for DC-DC converter. Considering low currents it’s probably an overkill but I really didn’t want to find out later that I’m spraying too much noise around my rack. Design improvement would be to either remove filter all together (probably possible due to low current requirements) or make it much simpler and hopefully omitting SMD inductor. I hate to solder that stuff.

It took me a while to find a button as I wanted momentary switch with LED (excellent for giving status) and it had to look nice in rack. I finally found Adafruit’s S 560. This button is awesome in person.

To connect button, I selected JST S3B-XH. I needed the minimum of three wires (GND, LED, and switch input) and I already had bunch of premade cables made in the course of another project. If I had only this project to worry about, I would probably select 4-wire connector as it would make soldering a bit easier. Not it’s too hard now - just wire GND to both LED and NC input and you’re golden.

Voltage connector choice was extremely easy as literally all devices I wanted to control already used barrel connector. Since 2.1 mm barrel had became a defacto standard for network cameras, this also meant I could count on connecting it all with factory cables making the whole setup a bit nicer.

Speaking of which, I mulled long time about whether I should support only barrel setup with positive voltage on tip and negative voltage on sleeve or the inverse one too. All network devices I ever owned have positive tip. However, I decided to allow negative tip setup to. I am 95% sure this was completely unnecessary precaution but at least I don’t need to worry about it.

As far as saving money goes, supporting only positive tip barrel connectors, would allow me to remove diode bridge on input and allow me to replace mechanical relay with MOSTFET. As I’m making only 3 devices, for now I value flexibility the most. But decision to include these still bothers me a bit.

Housing hardware fell to Hammond 1593KBK. This is a reasonably sized and more important reasonably priced case with side panels. While I usually replace such panels with custom PCB with cutouts in place of connectors, this time I opted for 3D printed version. It’s definitely cheaper than PCB but it does require you to have printer. And no, it’s not as nice as PCB panel but it looks good enough - especially since device will sit in the back of the rack.

Lastly, I had to select microprocessor that would handle all logic. As most of my devices, I simply went with selecting Microchip’s part. I needed two outputs (LED and relay) and a single input (switch). In order to avoid complications, I like to keep programming lines separate too (MCLR, PGD, and PGC). Yes, you can share these lines with logic if you’re careful but having them reserved is one way to make sure. Once you include power supply, there is variety of 8-pin microcontrollers to chose from.

Finally I went with PIC12F1501-I/SN. It has 8 pins which is what we needed. SOIC package makes it easy to solder and internal oscillator minimizes extra components needed.

Code running on chip is as easy as it gets. What follows is the main loop.

void main(void) {
    io_led_on();
    while(true) {
        if (io_switch()) {
            if (debounceCounter &lt; DEBOUNCE_MAX) {
                debounceCounter++;
            } else {
                intensityCounter++;
                if ((intensityCounter % 8) == 0) { io_led_on(); } else { io_led_off(); }
            }
        } else {
            if (debounceCounter == DEBOUNCE_MAX) {
                io_relay_on();
                for (int i = 0; i &lt; RESET_COUNTER; i++) {
                    io_led_on(); wait_short();
                    io_led_off(); wait_short();
                }
                io_relay_off();
            }
            io_led_on()
            debounceCounter = 0;
        }
    }
}

If button is pressed, counter (debounceCounter variable) gets increased while button is pressed and until it reaches maximum. Side effect of handling counter like this is automatic debouncing and making sure short, accidental, swipes don’t activate reboot functionality. Once at maximum, counter stops and we dim the LED so user knows reboot will follow once button is depressed. Dimming is actually done by poor man’s PWM - just turn LED manually on 1/8 of interval (when counter is divisible by 8).

Second path executes only when the button is not pressed. If debounce counter is at maximum, a simple reset sequence starts. Here we disconnect relay (ironically by turning it on as it’s normally-connected setup) and LED blinks for 4 seconds. Then we turn relay back off so that power returns to the device.

It’s probably the simplest code I’ve written in quite a while.

If you want to check it a bit more, feel free to look up both board and code at GitHub.

Why You Should Update GPS Firmware

Illustration

Assuming you survived the great GPS rollover, you might wonder if updating your GPS firmware makes any sense at all. It’s obvious you might want new maps. But firmware? Why do you need a new one if everything works just fine.

Real answer is that you probably need not bother. Again, assuming everything works fine, there is no navigation reason for firmware upgrade. Navigation will work equally well whether you have fresh firmware or one that’s a few years old.

However, if you care about time keeping, you will definitely need to update your GPS once in a while. And it’s not because of rollover - that issue is sorted now with the new 13-bit week counter and it’s up to your grandchildren to bother with that. Nope, it’s darn leap seconds.

Leap seconds were created to account for uneven rotation of this big rock we call Earth. As such they are currently beyond our capability to calculate in advance. Once in a while astronomers look upon the skies and decide if leap second is needed. One cannot know in advance when this will happen.

AS GPS time has no concept of a leap second, firmware is what adjusts GPS time to UTC and then later to time zones we all deal with. Guess what, if you have old firmware, your GPS will adjust wrongly and thus something you believe to be a correct time will be off by a second or two (or 27 if you were really lazy).

I know it’s not a breaking deal for a vast majority of population but I simply find it rather unnerving that you would intentionally make your GPS show the wrong time.

Update darn firmware to avoid having your existence out of sync with rest of the world. :)

El-Cheapo Power Supply Noise Measurement

If you are not doing power supply measurements every day, you probably don’t have any specialized probe for it. At best you might be using two probes and math channels to get a cheap differential-like probe as the only thing needed for this is your standard probes and a pair of pass-through 50 Ω terminators. Dave went over it on EEVBlog channel better than I ever could.

While this approach might not be perfect, it has advantage of a decent result with minimum parts needed. Assuming you already have the probes, only other parts needed are pass-through 50 Ω terminators that come in handy for other stuff too. If you are lucky, your scope might even have them built-in.

Due to low cost and ease of measurement I actually prefer it to more common single-ended measurements with short ground spring. Not only I find probing with two hands easier than dealing with springy ground wire but it also alleviates the need to worry as much about noise sources. With advent of switched LED lightning single-ended measurements just got too annoying.

Disadvantage, beside the precision, is that all your measurements need scaling as your probes and termination resistor make a voltage divider. Since we already know one side of voltage divider (50 Ω terminator), we need to measure probe’s resistance. From tip to BNC connector that will be in a few hundreds ohms range. Using the voltage divider formula, we get the ratio (for example I measured about 360 Ω):

ratio = (Rprobe + 50) / 50 = (360 + 50) / 50 = 8.2

While simply multiplying by ratio resulting numbers is not too much of a hassle and you could be done here, quite a few oscilloscopes will allow you to create a custom probe. As I usually use PicoTech 2206B, I will explain how to configure custom probe in PicoScope 6 so our on-screen result is already scaled correctly.

In Tools menu the first entry leads to Custom Probes window. There clicking on New Probe will start a wizard dialog. We use the standard volts unit and a linear equation. Our precalculated scaling ratio (7.6 in my case) goes under gradient and offset stays 0. Automatic range management is fine but software frequency filter we set to 20 MHz as PicoScope 2000-series has no hardware bandwidth selection. After naming the probe we can now select it in channel’s configuration.

When dealing with the “standard” probes, be aware that most x1 probes have around 10 MHz bandwidth. As power supply noise is usually measured with 20 MHz bandwidth limit this is something to be aware of. However, the whole measurement setup is on a cheap side and this will rarely be a deal breaker. Yes, you might not see the best fidelity but you’re gonna be in the ballpark regardless.

Further more, we are not interested in the DC component. If oscilloscope supports it, use AC coupling as we’re not interested in DC values - only noise. This will allow you to use all ADC bits for signal you want to see. If oscilloscope supports only DC coupling, you won’t be able to see much as, at ±5V, your 8 bits give you only 40 mV resolution and it goes worse from there. Unless you can get your analog offset configured (unlikely if your scope is so low end that AC coupling is unavailable), you can forget about any meaningful measurement.

Those doing it on cheap can check $140 PicoScope 2204A as it does support AC coupling. For example, assuming 100 mV range, you get quite adequate 0.4 mV resolution. As you go up in model selection you will get better bandwidth and bigger sample buffer but I would think even this works well for what we can get with this testing setup.

And I am mentioning PicoScope here specifically as other cheap scopes (whether PC-based or in MP3 player case) often don’t have AC coupling and lie about bandwidth on a grand scale at a comparable price. While you can get around missing AC coupling by placing capacitor in series, it is much harder to work around bandwidth restrictions and a missing analog frontend. PicoScope is not perfect (and it cannot be at that price point) but they don’t lie about their capabilities and their PC software is the best I’ve seen.

That aside, we’re not done yet. To see our “differential” signal noise, we have one more step. Under Tools there is an option called Math Channels. Here we can select already predefined A-B function and finally we can see the noise in all its glory.

PS: To automate measurements a bit, you can use Measurements, Add Measurement. Selecting A-B as a measurement channel will allow you to add both peak-to-peak and AC RMS as the most useful values.

PPS: The lowest PicoScope model where you can do these measurement comfortably is $350 2206B with 32 MS buffer. Lower models have only either 8 kS ($140 2204A) or 16kS ($225 2205A) making any analysis a bit annoying.

PPPS: Great thing about using PicoScope for measuring power supply is that it can be easily isolated. Just disconnect laptop from power supply and your USB (and thus PicoScope) is floating. :)