Archive for One-day Builds

Adding a hide button to Facebook Marketplace with Tampermonkey

Tampermonkey, the successor to Greasemonkey, allows on-the-fly modifications to webpages. Things like adding buttons, or modifying text on a page.

 

Facebook Marketplace is fast becoming the only viable used item listing in my area. Which is annoying – It’s inferior to Craigslist in any way, but the latter is clearly dying a slow death.

The best listing site seems to be regionally dependant, but here we are.

 

Instead of just griping about the situation, I’ll fix the single lacking feature that improves my life the most. The “hide” button that CL has.

 

So here is a Tampermonkey script, written in JS/jQuery. It’s my first real dabble in jQuery, and there is a little bit of magic that certainly makes DOM manipulation much easier.


// ==UserScript==
// @name FBMP Hider
// @namespace http://tampermonkey.net/
// @version 2024-09-08
// @description Add a button to hide FB MP listings
// @author Jarrett Rainier
// @match https://www.facebook.com/marketplace/*
// @require https://code.jquery.com/jquery-3.2.1.min.js
// @require https://gist.github.com/raw/2625891/waitForKeyElements.js
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
function addHidden(id) {
console.log("Adding " + id + " to the hidden list");
var timestamp = Date.now();
GM_setValue(id.toString(),timestamp);
}
function getHidden(id) {
//console.log("Checking " + id + " on the hidden list");
var store = GM_getValue(id.toString(), null);
if (store != null) {
var timestamp = Date.now();
//Reset the clock on keeping the listing hidden
GM_setValue(id.toString(),timestamp);
}
return store
}
function getMPId(url) {
var ids = url.split('/');
//console.log(url.split('/')[3]);
return ids[3];
}
function hideMPClass(jNode, id) {
if (id != null) {
console.log("Hiding " + id);
jNode.parent().parent().parent().parent().parent().parent().parent().parent().hide()
} else {
//console.log("Didn't hide " + id);
//console.log(jNode);
}
}
function insertMPHideButton(jNode, id) {
var insertA = document.createElement('input');
//insertA.href = '#';
insertA.setAttribute('type',"button");
insertA.setAttribute('value',"hide");
insertA.setAttribute('id',"hide" + id);
insertA.innerHTML = "Hide";
insertA.addEventListener("click", function(event) { event.stopPropagation(); addHidden(id); hideMPClass(jNode, id); }, true);
jNode.parent().append(insertA);
var href = jNode.attr('href');
var newHTML = jNode.children().eq(0).html().replace("div", "a href='" + href + "' ");
var n = newHTML.lastIndexOf("div");
newHTML = newHTML.slice(0, n) + newHTML.slice(n).replace("div", "a");
jNode.attr('href', '');
newHTML = jNode.parent().html().replace("a", "div");
n = newHTML.lastIndexOf("a");
newHTML = newHTML.slice(0, n) + newHTML.slice(n).replace("a", "div");
}
function checkMPclass (jNode) {
//xlil10hfl
//console.log (jNode);
var id = getMPId(jNode.attr("href"));
if (getHidden(id) != null) {
hideMPClass(jNode, id);
} else {
insertMPHideButton(jNode, id);
}
}
(function() {
console.log ("Do you see this?");
//waitForKeyElements (".xjp7ctv > div > span > div > div > a", checkMPclass);
waitForKeyElements (".xjp7ctv > div > span > div > div > div > div > a", checkMPclass);
console.log ("Script run to completion");
})();

view raw

fbmp.js

hosted with ❤ by GitHub

Click on “view raw” and Tampermonkey should prompt for installation.

It’s already broken once due to Facebook changing their DOM around, but that’s over nearly a year of usage. If it happens repeatedly, I’m sure I can find a more resilient method.

 

 

Very Remote Control

Instead of a TV at home, I use a projector. It’s on my ceiling, with the buttons inaccessible. The remote for it also isn’t really working anymore. Problem.

There’s nothing on the remote control’s PCB except for an obscure microcontroller (TTR013), IR LED and driving transistors, and some carbon contacts for the buttons. The intermittent operation is not the contacts deteriorating, and the microcontroller is getting power, so troubleshooting opportunities are limited.

 

But, the internet is a wonderful place, and someone has just straight up recorded the exact remote control I need and posted it up on Github.

The config file is here, but they also have the raw recordings. Apparently it’s a config file for a Linux IR transmitter driver called LIRC. The config documentation is here.

 

It’s pretty straightforward, so I set out to duplicate the waveform using the RMT peripheral of the ESP32. The RMT peripheral is an arbitrary waveform generator, and one of the typical use-cases is an IR transmitter. Perfect.

My final code is here. The From my reading of the LIRC docs, the relevant config data is this:

 

header 9077 4504
one 602 1622
zero 602 511
ptrail 604
gap 108167

begin codes
ON 0x000CF20D

 

Where a binary 1 is denoted by an on-pulse of 602 microseconds and then an off-pulse of 1622 microseconds. Similarly for 0, it’s 602-on and 511-off.

The code for ON uses that sequence to write out 0x000CF20D.

 

The whole packet starts with the header sequence (9077-on, 4504-off) then 0x000CF20D, then the tail/gap sequence (604-on, 108167-off). Seems straightforward.

 

I put the ESP32 on an oscilloscope to make sure the RMT was doing what I wanted it to do.

 

It seems to be! But this is more of a logic analyser task, so I pulled that out. The waveform looked exactly as I expected, so I tested it out with an IR LED.

 

 

And it didn’t work at all. Nuts.

By connecting the remote control to the logic analyser and powering it with a nice bench power supply, I was sometimes able to get output data. Enough to grab a capture after several minutes of mashing the carbon contacts with a brass standoff.

Here’s the full waveform:

It’s worth noting that the waveform is inverted from the actual LED current, due to measuring at the LED with low-side switching. Don’t worry about it too much, just invert the logic.

Sigrok can export the waveform in a Value Change Dump format, which looked good enough for my purposes. Then I wrote a python script to convert VCD files into the RMT packet format.

It looked good, with the exception of some glitches caused by the RMT not being able to handle very long delays, relative to the fast switching. This strategy still didn’t work on my project.

Speaking of fast switching, let’s investigate that further. Abandoning the Github config files so quickly didn’t sit quite right with me. It was too perfect.

Going back to my capture of the remote, here’s a binary 1:

 

Here’s a 0:

 

And then here’s the header, along with the data portion:

 

And the header with the whole data portion:

 

It does look right. But why was this so different from the Github repo?

Well, obviously this has a carrier wave that I totally blew by.

 

Wow, okay. Back to the LIRC documentation. There is a frequency option that specifies carrier wave, and defaults to 38kHz. So, it’s not in the config, because it’s set by default, and is one line in the documentation. No wonder I missed it.

Honestly, my brutish Python script that specifies all of changing values is probably good enough, but it feels wrong to use a 1500-line lookup table instead of a fixed carrier frequency and 15 lines of actual data. The RMT peripheral made it incredibly easy to fix up.

The end result looks really good, but let’s compare with the captured data again.

Keeping in mind that the data to be sent is 0x000CF20D, I’ve annotated the capture:

 

And it looks mostly good, except… What’s that block at the end?

I can’t find anything in the original config or documentation for the config that would explain that last block. That will remain a mystery for now.

 

Anyway, it was about this time that I got suspicious of the ancient IR LEDs that were in my parts bin. If I cranked the current, they looked visibly blue, which, obviously is the wrong side of the spectrum when I’m looking for IR LEDs. I grabbed a spectrometer and measured it – Yep, that’s not right.

After combing through my parts bin, I found another IR LED and measured it: 815nm. Still not right. I was fortunately able to juuust barely be able to measure the wavelength, though.

Looks like a very broad band 815nm, if the peak stretches all the way to 750nm.

I’m pretty sure I’m looking for a 940nm LED, so some more combing and I found an IR proximity sensor. I don’t have a datasheet, but pointing a TV remote at it triggered the on-board red LED, so I knew it was simpatico.

A quick hack-job later, and I drove the LED directly from the ESP32. My projector turned on, without any further changes to my code. And it successfully turned off the projector, too.

So that’s how I controlled my home entertainment setup with an IR proximity sensor.

 

Sometime in the middle of this journey, I hooked up the LA to a little 315MHz receiver module and triggered my garage door remote. I won’t be posting the waveforms here, but minor modifications to my Python script and code worked out of the box to clone the remote. That whole process took about 15 minutes, so it was a nice and useful diversion. Because it’s attached to an ESP32, I can now trigger my garage door over the internet. From anywhere in the world! Very, very remotely.

 

This whole project is very specific to my needs, but it could be helpful to others. There is now a pipeline for converting LIRC config files into ESP32 RMT outputs. I doubt I’ll ever do this again, but, just for giggles, here’s a GPT-assisted script that will do it:


import re
import argparse
class LIRCConfigParser:
def __init__(self, file_path):
self.file_path = file_path
self.config = {}
self.header = None
self.one = None
self.zero = None
self.ptrail = None
self.gap = None
self._parse_file()
def _parse_file(self):
with open(self.file_path, 'r') as file:
content = file.read()
self._parse_timing_parameters(content)
remote_blocks = re.findall(r'begin remote(.*?)end remote', content, re.DOTALL)
for block in remote_blocks:
remote_name = re.search(r'name\s+(\S+)', block)
if remote_name:
remote_name = remote_name.group(1)
self.config[remote_name] = self._parse_remote_block(block)
def _parse_timing_parameters(self, content):
header = re.search(r'header\s+(\d+)\s+(\d+)', content)
if header:
self.header = (int(header.group(1)), int(header.group(2)))
one = re.search(r'one\s+(\d+)\s+(\d+)', content)
if one:
self.one = (int(one.group(1)), int(one.group(2)))
zero = re.search(r'zero\s+(\d+)\s+(\d+)', content)
if zero:
self.zero = (int(zero.group(1)), int(zero.group(2)))
ptrail = re.search(r'ptrail\s+(\d+)', content)
if ptrail:
self.ptrail = int(ptrail.group(1))
gap = re.search(r'gap\s+(\d+)', content)
if gap:
self.gap = int(gap.group(1))
def _parse_remote_block(self, block):
remote_config = {}
lines = block.splitlines()
key_section = False
for line in lines:
line = line.strip()
if line.startswith('begin codes'):
key_section = True
remote_config['codes'] = {}
elif line.startswith('end codes'):
key_section = False
elif key_section:
parts = line.split()
if len(parts) == 2:
key, value = parts
remote_config['codes'][key] = value
else:
if ' ' in line:
key, value = line.split(None, 1)
remote_config[key] = value
return remote_config
def get_config(self):
return self.config
def get_remote_names(self):
return list(self.config.keys())
def get_remote(self, remote_name):
return self.config.get(remote_name, None)
c_start = '''
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/rmt.h"
#include "driver/gpio.h"
#include "esp_system.h"
#include "esp_log.h"
#define RMT_IO (GPIO_NUM_18)
#define ON_OFF_CONTROL (GPIO_NUM_19)
#define PIN_INDICATOR (GPIO_NUM_5)
static const char* TAG = "ir_transmitter";
#define RMT_END {{{ 0, 1, 0, 0 }}}
'''
c_end = '''static void send_on()
{
ESP_ERROR_CHECK(rmt_write_items(RMT_CHANNEL_0, on_key, sizeof(on_key) / sizeof(on_key[0]), true));
vTaskDelay(44/portTICK_RATE_MS);
ESP_ERROR_CHECK(rmt_write_items(RMT_CHANNEL_0, tail_key, sizeof(tail_key) / sizeof(tail_key[0]), true));
}
static void send_off()
{
//Off needs to be pressed twice
ESP_ERROR_CHECK(rmt_write_items(RMT_CHANNEL_0, off_key, sizeof(off_key) / sizeof(off_key[0]), true));
vTaskDelay(2000/portTICK_RATE_MS);
ESP_ERROR_CHECK(rmt_write_items(RMT_CHANNEL_0, off_key, sizeof(off_key) / sizeof(off_key[0]), true));
}
void app_main(void)
{
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(RMT_IO, RMT_CHANNEL_0);
// set count to 1us
config.clk_div = 80;
config.tx_config.carrier_en = true;
config.tx_config.carrier_freq_hz = 38000;
config.tx_config.carrier_duty_percent = 35;
ESP_ERROR_CHECK(rmt_config(&config));
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
gpio_set_direction(ON_OFF_CONTROL, GPIO_MODE_INPUT);
gpio_set_pull_mode(ON_OFF_CONTROL, GPIO_PULLUP_ONLY);
gpio_set_direction(PIN_INDICATOR, GPIO_MODE_OUTPUT);
while (1) {
gpio_set_level(PIN_INDICATOR, 1);
if (gpio_get_level(ON_OFF_CONTROL) == 1) {
send_on();
} else {
send_off();
}
vTaskDelay(1);
gpio_set_level(PIN_INDICATOR, 0);
vTaskDelay(40/portTICK_RATE_MS);
}
}
'''
def generate_c_code(config, output_path, timings):
with open(output_path, 'w') as file:
file.write(c_start)
if timings.header and timings.one and timings.zero and timings.ptrail and timings.gap:
file.write(f'#define PACKET_HEADER {{{{ {timings.header[0]}, 1, {timings.header[1]}, 0 }}}}\n')
file.write(f'#define ONE_BIT {{{{ {timings.one[0]}, 1, {timings.one[1]}, 0 }}}}\n')
file.write(f'#define ZERO_BIT {{{{ {timings.zero[0]}, 1, {timings.zero[1]}, 0 }}}}\n')
file.write(f'#define PACKET_TAIL {{{{ {timings.ptrail}, 1, {timings.gap}, 0 }}}}\n\n')
file.write(f'#define PACKET_ZERO_NIBBLE ZERO_BIT,ZERO_BIT,ZERO_BIT,ZERO_BIT\n\n')
for remote_name, remote_config in config.items():
file.write(f"// Remote: {remote_name}\n")
if 'codes' in remote_config:
for key, value in remote_config['codes'].items():
file.write(f"//0x{value}\n")
file.write(f"static const rmt_item32_t {key.lower()}_key[] = {{\n")
file.write(f" PACKET_HEADER,\n")
for nibble in [value[i:i+4] for i in range(2, len(value), 4)]:
file.write(f" //{nibble}\n")
for bit in bin(int(nibble, 16))[2:].zfill(4):
if bit == '1':
file.write(f" ONE_BIT,\n")
else:
file.write(f" ZERO_BIT,\n")
file.write(f" PACKET_TAIL,\n")
file.write(f" RMT_END\n")
file.write(f"}};\n\n")
file.write(c_end)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Parse LIRC config files and generate a minimal C file.')
parser.add_argument('file_path', nargs='?', default='lircd.conf', help='Path to the LIRC config file')
parser.add_argument('–output', '-o', default='main.c', help='Output C file')
args = parser.parse_args()
lirc_parser = LIRCConfigParser(args.file_path)
config = lirc_parser.get_config()
generate_c_code(config, args.output, lirc_parser)
print(f"Generated C code has been written to {args.output}")

Run this with python esp32_convert_lirc_to_rmt.py config.conf --output main.c

It will generate a main.c file that should just compile and have the ESP32 happily spitting out valid data.

Key Storage

I’m on a home improvement kick. When I walking my home, there’s a long, featureless hallway with kitchen at the end of it. I was using a tray on my kitchen counter to toss keys onto when I walked in the door. This takes up valuable kitchen counterspace, and being at the end of the hallway, was not ideal.

 

I designed up a wall-mounted tray that looked kinda nice. Then I CNCed it out of a scrap chunk of pine. I did a bad job estimating the size, so it turned out way too small and not really usable.

 

Instead of just recreating it, but larger, I an additional feature to hold cards would be useful. Additionally, the CNCed wood required some hand sanding that I didn’t really want to deal with again, not to mention the CNC setup time.

None of that was particularly time consuming or difficult, but honestly, it was faster and less tedious to 3D print it and cast it in cement than to break out the CNC again.

With the larger size, I wasn’t so sure about just slotting it into a piece of plywood without supports, so I added a steel cable. It’s a nice design accent regardless.

Note the knotted up steel cable in the mould. I also used XTC-3D, a resin coating designed to smooth out 3D prints. It was expired and a little chunky, but still seemed to work well. I’d use it again. The resulting cement form was smooth and required no post-

processing.

I also designed a tensioning mechanism for the cable. There’s a captive screw that drags a nut up and down. The nut assembly holds the cable end. The whole assembly slides into a 3/8″ drill hole. At the top of the assembly, there is a hole that is sized just right for a screwdriver to come in and engage the screw, dragging the nut assembly closer, tightening the cable tension.

 

For the backing, I made a drill jig to be able to drill straight down the 3/4″ plywood without breaking through either side. The steel cable goes in, and then I attached it to the tensioning mechanism with a combination of melting the plastic to it, and some CA glue for good measure. The whole assembly then also got glued into the drill hole.

After all of that was set up, I used plumber’s putty epoxy to fill in all of the gaps between the tray and the plywood, and bolted it to the wall!

 

This project went just about as smoothly as possible, probably about 6 hours all told.

Syringe Full of Lead

I’ve used a lot of solder paste, usually from one of those big syringes.

When depositing little tiny blobs of solder on a few hundred little tiny pads, two problems emerge:

  1. That big syringe has a big plunger that really starts to hurt the heel of your hand after a lot of usage, especially when the paste is still a little cold from storage. And,
  2. when it’s so challenging, accuracy of deposition goes down.

Yes, both of these problems sound really minor, but they’re actual problems! And solvable!

There are a few different strategies for paste deposition. One of them, which I had a mild infatuation with for a while but never tried personally, uses an air pump connected to a syringe. It drives out the paste with air pressure. Apparently, though, the compression of the air leads to inconsistent and ultimately disappointing results.

Another strategy is to make it electric and attach motor, drivers, and a control system on the back of the syringe. The problem I’ve had with this is that it makes the whole assembly heavy, and therefore difficult to accurately control. The syringe-mounted button also seems like it might cause a little bit of unwanted movement when it’s being pressed.

So, like all quick projects, I let it sit for years while I pondered the issue.

After deciding on a strategy that would allow me to put the least amount of effort into this as humanly possible, I bought the following:

DC motor with gearbox and threaded rod:

 

Brass heat-set inserts:

 

1mL syringes:

 

Copper tubing:

And a pile of electronics I already had kicking around.

I 3D printed a syringe-motor adapter.

I soldered an insert onto the brass tube, and then with the motor, I had a linear actuator.

 

Pile of electronics:

 

Neat.

 

I also glued the rubber gasket/tip to the copper tube.

 

The solder paste I had available is a couple years old, and really chunky/nasty. Solder paste has a shelf life, and paying attention to that is important.

 

 

Put it all together anyway:

Little bit dribbly at first:

 

But not bad for a first test:

 

Obviously not perfect, though. The mechanism stopped being able to force the crusty paste through the needle, and broke the (admittedly flimsy) 3D printed bracket when the motor tried to exit its situation.

Using the flimsy bracket as a mechanical fuse might not even be a bad idea to keep around for future versions. The real problem was the paste, it was toast.

This will definitely be tweaked a lot. I’ll add pullback to the software to prevent the nozzle dripping, and maybe some other features. But it’s a start!

I Made An IoT

I haven’t actually made an Internet of Things, thing, before now.

This is mostly just to throw some stuff together that I already had lying around. I’ve got a DHT11 temperature/humidity sensor, a WeMos D1 Mini ESP8266 dev board, a switch-mode power supply module, and a solar panel.

I turned it into an investigation on solar charging and ESP8266 power modes, while spending As Little Time As Humanly Possible on cleanliness, quality, or polish.

Yeah, this is super simple, and equally janky.

Threw it together in an hour with liberal amounts of hot glue.

Predictably, the solar panel was never able to supply enough juice to handle the startup current of the ESP8266, and it never sent any data points.

So phase 2 was to add a maximum power point tracker (MPPT) module feeding a Nokia battery as a reservoir.

This one worked! It lasted about 18 hours until it died.

I’m sending data to Adafruit’s free(ish) MQTT platform, also for the same spend-as-little-time-possible reasons that this projectlet follows.

That spike/drop in temp/humidity is exactly the time period where I get direct sunlight in that window in May, so that’s neat.

Uptime is recorded in tens of seconds.

Now there are three problems left with this system:

There’s no way to get an idea of battery charge until it dies.

The switch-mode power supply is a buck converter only, so as soon as the battery voltage goes below 3.3v (or maybe even above that), the output voltage cuts off, or possibly sags. I haven’t read the datasheet on this, so I don’t know or particularly care in this case.

Ideally, the solar panel can keep on top of the power usage. It doesn’t currently do this, partially because it’s sending too many datapoints (temperature does not swing wildly enough to justify once every ten seconds), and because I did not use proper deepsleep in my code. It’s just a while() loop that keeps the microcontroller chugging away at full bore.

So here’s the next update.

The ESP8266 has an ADC, but it’s limited to 1V max. With a max Lithium Ion voltage of 4.2V, I used a voltage divider of 1k in series with 4.7k and 10k in parallel. And then I realised that there was a built-in 220k/100k voltage divider designed for ~3.3v max, so I swapped mine out for a series resistor get into the 4.2v ballpark. This is all very approximate and I only had 680k resistors on hand, so 7 in parallel got me to 97k.

From my Switch Mode Tale, I happen to have a SMPS kicking around that is perfectly suited to the voltage ranges of the battery, too, so that goes in.

And, finally, the code changes were trivial. Another data point (battery) was added, and naive delay loops were changed to proper deep sleeps. This also required tying pin 16 to RST.

Still curious about battery lifetime, I commented the deep sleep mode out while running another test. This is still a while loop:

It lasted three days, and you can clearly see the spikes in temperature for a few hours in the afternoon as the sun hits the panel directly. And look at that lithium battery discharge curve!

I haven’t looked into my MPPT in detail, but that would likely be one of my next targets for investigation. I know it charges the batteries because I left it to do that for a few weeks in between tests, and the batteries were fully topped up each time. But I suspect it charges them really slowly, based on the lack of noticeable bump on the graph during sunny periods.

At this point, I took a brief hiatus from this side-project to do Important Things, and then it became autumn.

Using this power usage analysis, eventually, I will chip away at some of the possible optimisations. Some of the considerations described by Erik are obvious, but some of the others are quite clever. A mental shift to paying attention to the order in peripheral bring-up in a WiFi device is fun, and not something I’ve had to do much before. I will come back to this, for sure. Likely when I finish Rev 2 of my Sugar Glider board. As it stands, this side-project is in danger of going over my allotted five hours of actual build-time.

 

 

PCBs of Unusual Style

Shallow

 

As a test, I designed a nautilus-themed PCB in PCBmodE.

 

PCBmodE is not your standard ECAD package. It’s a collection of JSON files that get converted into SVG or gerber files.

There are some limited tools to convert SVG files back into JSON, too. It can be thought of as forward- and back-annotation.

There’s no schematic editor. There’s no traditional PCB editor. The only interface is Inkscape itself. (Inkscape is an open-source vector software, like Adobe Illustrator)

The end takeaway is that these circuits are drawn, not engineered.

 

The whole repo is here. All of the JSON files are the source files, which can then be compiled into SVG (for viewing and some minor edits), or to gerber (for manufacturing).

 

 

The OSHPark gerber viewer says it’ll look like this:

 

 

And here is the final board:

 

It’s surprisingly difficult to photograph purple LEDs. It was suggested to me that they may have strong UV components that are overexposing that part of the image, so they usually show up as white or very light blue.

 

Rest assured, they are way more pleasing to the eye in person.

 

 

It’s interesting to note that the battery holder footprint was a Boldport component (it looks like a ladybug!). It’s designed for one of those cheap stamped metal CR2032 battery holders, but I don’t have any. It was easier/faster for me to grab some sheet copper and cut out a holder with tin snips.

 

The workflow of PCBmodE is a little bit jarring for someone expecting a standard PCB tool. It’s all laid out in the official docs, but I didn’t believe it until I tried it. Editing JSON files is almost the only way to interact with the software. You edit, compile, and view it in Inkscape. Some very small amount of things – Component position and traces, mainly – can be extracted back into JSON, but that’s it.

Even for outlines, the best method is to draw them in Inkscape, then copy the SVG paths and then paste them into the appropriate JSON:

For moving components around, there are a lot of SVG layers present for each component, but the extract command only seems to care about the origin. That little dot in the middle:

 

Three last things:

There’s also no “generate new board” command. Official recommended procedure is to fork one of the existing boards and modify. The docs are outdated, the sample is for an old version of PCBmodE that won’t work. I like BINCO for something really simple, or The Lady for something that uses every trick in the book.

Don’t use the official repo. This fork(as of June 2018) is the almost-official dev branch that has a lot of improvements (including Python 3!), and presumably will be merged back into mainline at some point. This command will install the proper one:

pip install git+https://github.com/threebytesfull/pcbmode.git@improve-test-coverage

 

And, finally, there is one issue that will prevent manufacturable gerbers. I described it here.

 

The next step

So, obviously this software requires some fussing, initially. It’s pretty nice once you have it set up, though. No issues, as long as you know what they are ahead of time.

What’s still a pain for daily use, however, is building usable circuits. Lacking a schematic and having to hand-draw traces as vector paths is painful.

So I quickly grabbed the first complete PCB available in Upverter, my LightBeam board. And then I wrote a conversion tool.

Here it is in Upverter:

 

And then in PCBmodE:

Workflow is now: Make a PCB in Upverter (or import it from something else!), Export as OpenJSON, convert it with my tool, then edit the PCBmodE JSON files as required to prettify them up, and transform them into something amazing.

The tool is here, pull requests / issues welcome. Only a small amount of available things to convert are completed, there’s lots more work that could be done.

Clipping the Leads

Here’s a quick one.

 

I’m tired of having to solder programming headers onto projects that don’t need them after initial development.

 

So I bought a bag of clothespins for a couple bucks from China and went wild with some epoxy and Pogo pins.

Soldering made the epoxy fail faster than I expected, so I had to reglue it afterwards.

It works great!

The clothespins are ubiquitous on AliExpress. From the pictures, I expected them to be larger, and plastic. All good, though, these tiny wood ones worked fine.

It’s pretty sturdy on the programming headers. Clips on quickly, and doesn’t need soldering! Success!

 

To make this more repeatable and less like an epoxy-globbed-on mess, I took critical dimension measurements and mocked up something in Fusion 360 for future, more polished versions. They should be possible to 3D print, for all occasions.

 

Here’s the original duplicate half-clothespin, to be modified for any new debug header encountered. A full clothespin consists of two of these, plus a torsion spring. Download here.

 

Here’s a 5-pin holder, designed for the PIC header format, or any other 5 pin design. Download here.

Next I’ll probably do a 4-pin version for SWD that STM32 (and many other ARM) microcontrollers use. Nothing I have right now regularly requires larger JTAG systems yet, but I’m sure it’ll come. This system should work fine with those, too.

FFChkMate

I’ve posted here before about my troubles with flexible flat connectors. Well, not directly, but that always seems to be a tangential obstacle in the already perilous minefield of hardware hacking.
Basically, I hate them and I want them to go away.

The answer is, obviously, a breakout board, but I was having trouble finding exactly what I wanted. The system that I was having trouble with is 0.14mm pitch, and an odd number of pins. That means that the connector’s pins are staggered instead of directly across from one another. Slightly unusual, and I couldn’t find an inexpensive breakout board for it.

Over an evening, I started designing a board that should help. The idea is to keep a few of these boards on hand, and if you’re working with an FFC cable with a pitch of 0.14mm, toss an extra cable and a couple connectors onto your next Digikey order. Should run you a couple bucks.

In choosing what kind of connectors I wanted the board to support, I had some decisions to make. Odd or even pins? How many?

I solved those by parsing Digikey’s catalogue:

FFC Chart

 

A general theory is that the more connectors of a certain pin count in the catalogue, the more common it is in the wild. As shown, it’s definitely important to support more than 30 pins. There’s another big spike at each common count, ending at about 51. That settled that.

There was another clever idea involved: the centre of the board is a flat cable connector footprint, with a little bit of a tweak. The middle row of pads correspond to pin 1, and every second pin after that. Depending on whether your connector has an even or odd pin count, you use the outer row of pads that are in-line or staggered, respectively. Those all break out to the pin headers as shown on the silkscreen.
There’s your breakout. If you need your cables connected to the device under test, use two boards, solder female header pins on one, male on the other, and sandwich them together, both connectors on either the inside or the outside as shown.

Obviously this may cause issues with length-matched traces, but you know. Cross your fingers. Worth a shot.

Future goals: Actually draw the traces myself, because the autorouter is terrible. I just wanted to spend as little time on this as possible.

 

FFChkMate

 

It seems to work fine, though. I’m not trying to get GHz signal through it or anything.

 

You can get one through dirtypcbs here, or I’ll release the files when I eventually dig them out and make the traces pretty.

I like candy. Do you like candy?

At a place that I hang out on very rare occasions, there’s a big red button.

button1

When somebody holds it, it starts up an air raid siren that can get really loud. Unfortunately, it spins up very slowly, so people let off as soon as they figure out what the button does. That results in one tiny little blip of the siren, barely enough to bother anyone. That’s no good at all!

So for this build, I didn’t want to spend very long at it, and attempted to do everything very roughly and as quickly as possible. I found all of the appropriate components kicking around, and designed around those.

Here’s what I came up with:

button2

I found a transformer with outputs that measure at about 12VAC. 10:1 winding ratio, I guess. After rectification and smoothing, it’s a little over 18VDC. On the right side, I used an ALA2F12, a 12V relay. The transistor is a 2N3904, just a very generic NPN BJT because this application doesn’t require anything special.

The original button was just the AC line voltage to the fuse, then through the switch to the load, very very basic.

Okay. Starting from the left:

  • Transformer outputs at 12 volts or so,
  • through the half-wave rectifier diode (1N4007 I think) – results in 12 * root 2, about 18V,
  • big filtering capacitor (200v, 820uF),
  • 1MΩ bleeder resistor so the system doesn’t hold charge indefinitely,
  • original switch (connected to the big red button),
  • 100Ω resistor(to prevent sparks)
  • into timing cap (160v, 220uF) – charges to full very quickly,
  • another bleeder resistor,
  • Rb controlling current going into the transistor’s base (more on this later),
  • BJT  base.
  • At the top: resistor controlling going into the relay coil,
  • relay coil,
  • BJT collector going to ground.

Because I’m abusing a 12V relay by driving it with 18V, I had to compensate for that a little. According to the data sheet, the coil is nominally 272Ω, taking 43mA of current. Ignoring the transistor’s collector-emitter voltage (probably ~0.2V): 18v / 43mA – 272Ω = 146Ω. So I tossed a 150Ω resistor in series with the coil, and it seems to work.

For the base of the transistor, this resistor (along with the capacitor) is what controls the active time of the system. It also controls the maximum current that can conduct through the collector-emitter junction of the 3604 Typically the gain of those are in the 70-100 range, so current going into the base should be Ib = 43mA/100 = 0.43mA. Base-emitter junction is around 0.7v, so the base resistor can be figured out by 18v – 0.7v / Ib when the base cap is fully charged. Overdrive Ib to 1mA to ensure max-on, so I used 18kΩ.

I tested everything to check timing issues, overheating, etc., and it works well. The relay gets latched for about 7 seconds, which is perfect. If I cared about being more precise with that, or wanted to change the timing values, it’s pretty simple to treat that portion as an RC circuit and tweak the resistor or capacitor values.

So I built it

Perfect! Start to finish, took about seven hours. Not including the abortive first attempt last week.

So before, when newcomers pressed the button, there was only a very short blip of the air raid siren. Now, the thing latched for a good seven seconds. And it gets very loud in that time.

Stop-gap PCB creation (has stopped)

Preface:

This is a year-old post that I never published. I guess I was waiting to be able to snap some pictures, but that never happened. Most of these components have been e-wasted by now.

I’ve talked about various avenues of rapid prototyping circuit boards before, and not really come up with any definitive solutions.

My current favourite possibility is using a dye-sublimation printer, but they’re fairly difficult to get ahold of. I’m not willing to throw money at the solution just yet.

In the meantime, one of the more reliable methods is toner transfer, using a clothing iron. There are two easily-controlled variables that affect the transfer quality. There are a lot more than two, but those are the ones that involve a human element.

As a solution to that, I started work on my own laminator. I took the fuser from a laser printer and mounted it to a board.

Mounting and getting it working independently had a fairly involved process. First I needed to drive the motor.

It required a lot of torque and I have no access to any simple motor that can handle that. The only thing I have that comes close is one of my brushless DC motors, but man, I don’t want to use complicated driving circuitry for that. What I ended up doing is mating a simple 12v DC motor to the gearing for one of those motors. I laser cut a bracket that has mounting holes for both sides, ground down a shaft, and it seems to work. A very consistent and slow speed, and huge amounts of torque.

The next step was the heating element. I ran it off one of the 30v supplies we have kicking around, and it got to the “reasonably warm” level while drawing just over 1 amp. Fortunately (or unfortunately, depending on your perspective), a local e-cycle company was closing down and had an impromptu fire-sale. I picked up a 60v power supply that was labelled “broken” for free. Replaced a PTC (that literally crumbled away in my hands), and it was good to go again. The new supply got it to “properly hot” in a minute or less.

The next step was measuring the temperature. The fuser had a thermister output that starting at around 33kohms. As it gets hotter, the resistance drops. I measured around 19kohms when it was “slightly hot”. That’s the totally objective temperature description I’m going with here.

I used a voltage divider with a 100k potentiometer to tune it to 0xFF at room temperature. The reasoning being that I didn’t actually need to know the “proper” lamination temperature, just the relative values that apply to this system.

So I had the whole system, the drive motor, the heater element, the temperature measurement. It turns out that while my system is 0xFF at room temperature, everything melts at 0xA0. That’s not very hot at all! I think. I have no idea what that translates to in real measurements.

There is another printer kicking around that I should be able to pull the fuser out of. The rollers didn’t really feel that hot overall when they melted, so it’s possible that it was just because I wasn’t running the motor at the same time.

Fortunately, there’s no shortage of old laser printers destined for the scrapheap.