# Building an automatic plant waterer (4/?): Calibrating the sensor

A short day in the attic today.

• Part 1: resistive sensing
• Part 2: finding resistive sensing is bad and capacitive sensing is hard
• Part 3: another crack at a capacitive sensor
• Part 4: calibrating the sensor

# Day VII (weekend 6)

First, to check everything’s OK, I’m going to calibrate the sensor. I have a box of cheap ceramic capacitors in the E3 series and I’m going to go from 10pF to 2200pF, and I’m going to measure them with my old Academy PG015 capacitance meter since it’s likely to be more accurate than the capacitor rating.

Here are the measurements:

 Rating Measured capacitance (pf) count 0 0 12.99 10 10.5 18.84 22 22.6 25.80 47 48.3 40.48 100 101.7 70.90 220 221 134.03 470 453 259.21 1000 965 539.16 2200 2240 1227.2

I’m not 100% sure how to fit this. The obvious choice is a least squares straight line fit to find the slope and offset. However, the variance increases with the measurement and I didn’t record that. Also, I don’t know what the error on the capacitance meter is like.

So, I think the best choice is a fit in log space. The fixed slope of line works well with errors on both measurements and it deals with higher measurements having higher variance, to some extent. The equation to map measurements (M) to capacitances (C) is:
$C = p_1 ( M + p_2)$

So we just take the log of that and do least squares on the result. The code is really simple in Octave:

% Data
d = [
0 0 12.99
10 10.5 18.84
22 22.6 25.80
47 48.3 40.48
100 101.7 70.90
220 221 134.03
470 453 259.21
1000 965 539.16
2200 2240 1227.2
];

% Initial parameters: zero point and shift
p=[1 1];

% Least squares in log space
err = @(p) sum((log(d(2:end,2)) - (log(p(1)) + log(d(2:end,3) + p(2)))).^2);

% Find the parameters
p = fminunc(err, p);

count=115;

% Compute the capacitance for a new measurement
p(1) * (count + p(2))


Nice and easy now does it work? Well, it seems to work with a variety of capacitors I tried it with. And to get intermediate values, I tried it with this rather delightful device from a long dead radio (range 16pF to 493pF):

and it works beautifully!

So, then I tries it on the wire wound capacitive sensor. Can you guess if it worked?

Well, it did! Funny thing though is that my capacitance meter didn’t work on that. Naturally I assumed my home built device was wrong. But it seems life wanted to troll me. Here’s what my capacitance meter does when all is good:

Nice and easy. Changing the range switch alters the speed of the downwards decay curve. So far so good. But when I attached my sensor, this happened:

Well, it did! Funny thing though is that my capacitance meter didn’t work on that. Naturally I assumed my home built device was wrong. But it seems life wanted to troll me. Here’s what my capacitance meter does when all is good:

Absolutely no idea why. It is a big coil, so it might have something to do with the inductance, or maybe pickup. I expect it has a higher input impedance than my device.

TL;DR a short one today, but the sensor works well and is in excellent agreement with my dedicated capacitance meter.

# Building an automatic plant waterer (3/?): capacitive sensor try 2

Finally some progress!

• Part 1: resistive sensing
• Part 2: finding resistive sensing is bad and capacitive sensing is hard
• Part 3: another crack at a capacitive sensor
• Part 4: calibrating the sensor

# Day V (weekend 5, has it really been going that long?)

OK, so I’m not really happy about the enameled wire design. It feels like the insulation is a bit fragile and I don’t really feel I know what’s going on well enough to rely on it. So, I’ll so something much better: smear some 5 minute epoxy over some stripboard and hit it with a heat gun…

Yes, this is not at all dubious. Well turns out it is. Who knew? Nonetheless it works decently well, though the insulation only goes a few cm up and I think it’s hovering at around 1GΩ. The capacitances are:

•  Out: 13pF
• Dryish soil: 20pF
• Quite wet soil: 30pF
• Very wet soil: 44pF

Substantially less sensitive than the previous one, but proves the principle. If I can actually sense that level of capacitance using the Arduino, then I can get a nice double sided one made with the good quality thin and robust coating you get on PCBs.

# On to the Arduino!

So the Arduino environment doesn’t natively support the comparator. Fortunately its not hard. Just fiddly. As in spending 2 hours on a really silly mistake…

It’s fairly straightforward given the datasheet, specifically section 27 AC (analog comparator). The easiest way to get started is:

• Not using the multiplexer, which means the negative input is AIN1
• Using the bandgap (1.1V reference) for the positive input
• Polling by reading ACSR.ACO (analog comparator status register.analog comparator output)
• No low power stuff (it’s a high power application anyway)

It’s surprisingly easy. The Atmega328p has nice defaults for prototyping (everything starts on), there are not too many registers to swizzle up the pins and its happy to have two functions (GPIO and comparator) on one pin at the same time. The bit that I got hung up on for hours is that AIN1 is the Atmega328-P’s AIN1 pin, not the Arduino’s AIN1 pin. AIN1/PD7 (in Atmel-speak) is actually digital pin 7 in Arduino speak. N00bish mistake but really easy to make.

The basic code to control an LED looks like this:

const int led = 13;

void setup() {
pinMode(led, OUTPUT);

// Set ACME bit to 0 to disable the multiplexer
// This also sets some ADC related flags

// Set the positive input to the bandgap reference.
// This also sets disable to off, interrupts to off
// and a bunch of other stuff to off.
ACSR = bit(ACBG);
}

void loop() {

bool result = ACSR & bit(ACO);
digitalWrite(led, result);
}


It works. Yay. Only slightly dampened by the wild goose chase over pin numbers.

A wild goose, for illustrative purposes. (CC by SA https://commons.wikimedia.org/wiki/File:Greylag_goose_(Anser_anser)_head.jpg)

# Day 6 (Weekend 6)

Well, this is interesting. The epoxy based probe (above) is now reading a steady 10M even in dry soil. Looks like that isn’t a long term solution. The best wire based one is now faring a lot better. I’m resigned to either having a custom board made with proper soldermask or using conformal coating.

So back to the circuit. Now because for some reason I’m intent on absolutely minimizing cost, an important part is minimizing the pin usage. So the circuit is simply this:

All the clever bits of the circuit are provided by the microcontroller.

The capacitor charges from the positive rail through a 1M resistor.  Internally, I’ve got the comparator connected to 1.1V. The equation for the voltage is:
$V = V_0(1 - e^{-\frac{t}{RC}}).$
Rearranging gives:
$t = -\ln (1-\frac{V}{V_0})RC$
Substituting in the test capacitor (47pF), the 1M resistor, the 5V supply and the 1.1V reference gives a rather marginal 12μs. For now to make life easier, I’m going to use this circuit:

Making a 1.65V rail with resistors means that the 1.1V threshold is on a much flatter part of the charging curve, increasing the time a lot.

Making a potential divider off the 3.3V rail gives 1.65V for the supply, and a much more generous 50μs. Passives are cheap, and I can greatly extend the charging time pretty easily by dividing down the supply. But we need some code to drive it.  The code implements a basic relaxation oscillator: let the capacitor charge then when the voltage exceeds the threshold, short out the capacitor to restart the cycle, then let the capacitor charge…

void setup() {
// Set ACME bit to 0 to disable the multiplexer
// This also sets some ADC related flags

// Set the positive input to the bandgap reference.
// This also sets disable to off, interrupts to off
// and a bunch of other stuff to off.
ACSR = bit(ACBG);

// set PD7 to either hi-Z or low (depending on DDR)
PORTD &= ~bit(7);
}

void loop(){
DDRD &= ~bit(7); // Set pin 7 to hi-z

//Loop until the AC outputs 0 (i.e. when the capacitor
//exceeds 1.1V)
while(ACSR & bit(ACO)){}

DDRD |= bit(7); // Pin7 to low, emptying the cap

delayMicroseconds(50);
}


And here’s what the voltage looks like in operation:

In order to get nice graphs I either had to touch a grounded thing or switch off my fluorescent desk lamps since they seem to spew noise all over the place.

It’s not really very useful like this since all it’s doing is displaying a nice graph. The scope also disturbs the signal since it’s got a non-trivial capacitance and resistance. To do some further analysis, I wrote the following code:

void setup() {
ACSR = bit(ACBG);
Serial.begin(9600);
}

float o=0, o2=0; //IIR filter state for count and count squared
int i=0;

void loop(){
PORTD &= ~bit(7);
DDRD |= bit(7);
DDRD &= ~bit(7);

uint16_t count=0;
while(ACSR & bit(ACO)){
count++;
}
DDRD |= bit(7);

o = 0.999*o + 0.001 * count;
o2 = 0.999*o2 + 0.001 * count * count;

if(i++%256 == 0){
Serial.print(o);
Serial.print(" ");
Serial.println(sqrt(o2 - o*o));
}
}


This code counts during the charging part of the cycle, then does a moving average on both the count and count squared using a simple IIR filter and prints the running mean and standard deviation of the counts to the serial port. I happen to be a big fan of IIR filters, I think they’re fun, interesting and efficient. Even the simplest one is much better than a naïve moving average. The trick of filtering both the value and value squared is one I’ve used many times for getting a running standard deviation in addition to mean.

By default I get counts of about :

• No touch: 88.2 (σ=4.1)
• Light touch: 110 (σ=5.5)
• No touch with scope: 139

I can even spot proximity, so its really pretty sensitive, and you can see how much the scope loads it down by. It also turns out I was really pessimistic earlier. Reverting back to the simpler circuit, the numbers I get are:

• No touch: 19.9 (σ=1.3)
• Light touch: 26 (σ=1.7)

I can still spot proximity, but only with the aid of the filter: it’s about a count of 0.2 or less. So for fun, I modified the code to turn on the LED when the count exceeds 20.2, and you can see just how sensitive it is:

I think now that having faster charging helps: while there’s more quantization noise in the signal, the comparator changes state on a steeper part of the curve which means that electrical noise has less effect on the time.

## Today’s conclusions

1. The capacitive sensor is really good, cheap and easy to make. I’m going to have to use that for other things too
2. It’s definitely the way forward for this project
3. Something worked easily!

# Building an automatic plant waterer (2/?): resistive sensing

This was harder than I expected.

• Part 1: resistive sensing
• Part 2: finding resistive sensing is bad and capacitive sensing is hard
• Part 3: another crack at a capacitive sensor
• Part 4: calibrating the sensor

# Day III (weekend 3)

Not really much time this weekend. I pulled out the electrode and (it had been sitting there unpowered) and saw this:

Copper salts deposited on the electrodes. It was really hard to get my phone to reproduce the colour.

It looks like corrosion has already started. There’s not much but it’s only been there a few weeks and has probably spent a few of hours powered by now. With my current plan (maybe waking up every 30 minutes), the amount of time ‘on’ would be about 16 minutes per day (10 seconds of higher current charge in each direction). That’s only a week or two before it reaches these levels of corrosion. So, that I think precludes 10 second measurements, or at least 10 seconds of direct charging without a resistor in the way.

# Day IV (Weekend 4)

Wow really not getting as much time on this as I’d like. So, what about capacitive measurements? Having insulated electrodes should preclude any corrosion problems and I’ll bet that using soil as dielectric will increase the capacitance as the water content increases. First, an initial experiment:

The sensor is a bit of electrical tape over some strip board.

This indicates we’re into the realms of possibility, though the number of picofarads is still small enough to be pretty irritating. It’s also a pretty useless soil sensor: soil/water get through the holes behind and the result is a measurable resistance between the electrodes (about 9M or so). And the insulator is pretty thick which is going to make the capacitance low and reduce the sensitivity.

I do have some enameled wire in various grades. The finest (0.14mm) has a very thin coating. I made a couple of different sensors using the wire, mostly wrapping it around lots to get a decent surface area:

A couple of attempts at a capacitive sensor. Left, interleaved wires, right the two plates are well separated.

One slight problem: it doesn’t measure open circuit; there’s about 15M between the two sides, rising as it dries off. The flat one measures about 28pF when in the air, and about 280 in damp soil, rising to about 2nF when just watered. Unfortunately I don’t know how much the resistance is affecting this, so I’m going to have to try again. I’m going to try the next thicker grade of wire I have (0.23mm) and incidentally it has a different color coating.

Having a nice large spacing between the two plates seemed to work well in that it was easy to clean and reset back to the dry state. So, on to version 2:

Version two of the sensor with thicker wire and nicely soldered joints. 40 turns of wire in each section.

As a reminiscent aside, I remember soldering lacquered wire back in the olden days with my fixed temperature iron. I could never settle on fine sandpaper versus a flame to remove it. Those days I do not miss, now I just crank up the iron temperature.

Anyway this seems to be going better: the resistance is greater than 2G. I think I might have mentioned it before but my multimeter skipped leg day. It can measure up to 2GOhm, but only down to 20mA. Weird. On to the capacitance measurements. So they are:

• BOGUS! that’s what they are, bogus!

Well shoot. It seemed to be working great, but after a bit of use the resistance is back to being about 10M. That’s disappointing. OK, try3! I’m going to wrap the wires longitudinally so that they never even cross:

I forgot to take a picture of it! Look at the “fix” below with hot-melt to get the idea.

Anyway the wires are always separated by about 4mm. So the measurements are:

• First use: 12pF
• Finger lightly on one side: 28pF
• Fingers pressed on both sides: 100pF
• Damp soil: 185pf, 233pF, 202pF, 190pF
• Slightly compacted damp soil: 323pF,
• Same place during watering: 680pF
• Resistance: 12M

OK well, this is getting suspicious. The 20M range is maxed out. But the 2G range reads low (there’s nothing in between, that’s only a few % difference). Now the capacitance reads in the nF range as well. Hitting it with a heat gun seems to reset everything.

So putting a blob of water on in the middle doesn’t do anything. Butting a blob of water on the end where the wires are bent round quickly drops the resistance back town to 10M. I think the act of wrapping the wire breaks the insulation very slightly. Well, that’s irritating. Let’s see:

If you use hot melt and don’t have a reflow style hot air gun, you’re really missing out.

OK, so the new measurements:

• Damp soil: 250pF, 360pF
• During watering: 1nF
• After watering: 600pF and dropping
• Resistance: 𝟚𝟘𝕄

Apparently there is something hot-melt can’t fix. Observing more, the resistance is climbing very slowly, up to 26 now, now 40. Well, it might not matter. If I keep my measurement resistors well under the 20M range (say 200k), then the small error incurred due to leakage won’t matter. Still, I’d prefer to have it work properly.

So where are we? The capacitance sensor definitely works after a fashion, but we need to measure it. It bottoms out at 30pF, and is well into useful readings at about 300pF or so. I think I could get away with a 1M resistor safely. For an RC circuit, that would give a time constant of about 30us, which is small, but that’s at the 0 end of the range. It’s just about measurable on an Attiny85 with the 16MHz clock.

Additionally, the Attiny85 has a built in comparator. So, my current mental design has a relaxation oscillator in mind: charge up the capacitor through a 1M resistor, then discharge through a GPIO pin once the voltage crosses a threshold.

Sounds like a plan.

# Building an automatic plant waterer (1/?): resistive sensing

This turned into a saga. Naturally this is Part 1.

• Part 1: resistive sensing
• Part 2: finding resistive sensing is bad and capacitive sensing is hard
• Part 3: another crack at a capacitive sensor
• Part 4: calibrating the sensor

This is a blow-by-blow account rather than a neat design story, so you get to see the experiments I did to prove/disprove ideas and the dead ends that I went down. All the dead ends. So many…

# Day 1

I bought a pitcher plant. Unfortunately it turns out that I am less good at remembering to water it than I fooled myself into believing. So, instead of watering it, I’m in my lab building a device to water it for me. I’m also engaging in the entertaining game of minimizing the BoM on the electronics side as much as possible. My current thought is an old 12V supply, an attiny of some sort, a MOSFET, a soil probe and a peristaltic pump.

Through the magic of the internet I have some supplies:

A peristaltic pump (12V), some T-adapters which were supposed just be couplers but I must have ordered the wrong ones and some slightly odd sized tube because the tube I ordered did not arrive.

I ordered a series of tubes

And because I have to take everything apart, here’s the pump:

There are no gears. There are only 12 parts (motor, 2 screws, mount, case top, case bottom, 3 rollers, roller holder and tube). Compare this to the older design of cheapie peristaltic pump:

Old cheap peristaltic pump.

The old design is more complex. It also noisier and doesn’t run as smoothly, it’s harder to put the tube in and it has a real tendency to split tubes if they’re not precisely the right size. I like the new design.

## Exploration

The next bit is to figure out the moisture sensor. I’m going to measure the resistance between two conductors (on stripboard). Firstly, splitting a chunk off by bending it over in a vice is less reliable than I thought it might be…

Yuck.

Pre-scoring it heavily, then filing after was tedious but ultimately gave a much cleaner cut:

Now to try it on a victim plant. It’s a fuchsia which I’ve propagated from cuttings and I’ve been keeping indoors. I’ve not watered it in a while, so it’s very dry. I made a bunch of measurements with a spacing zero and 1 columns between the electrodes.

Interestingly it didn’t make all that much difference. Either way the resistance was between about 0.8 and 3.3MΩ. Now for the other end of the scale:

Of course I didn’t wait half an hour. I waited more like an hour. Either way the moisture looks like it’s thoroughly propagated around. Time to measure. First a spacing of one row:

Interestingly, the resistance takes ages to settle, on the order of minutes where it keeps changing. The direction depends on the measurement range, so I suspect there’s some sort of electrochemical effect going on. Brief pulsed measurements (as brief as I can get) on the 200K range give about 30K resistance, but it rapidly climbs. On the 200k range long term it gives, well,  it’s up to118K and climbing very slowly. On the 2M range long term it gives about 62K resistance.

## Now I find it’s a dodgy battery

And guess what! It’s a cell apparently (my trusty old multimeter has a 2GΩ range but nothing below 20mA. I think it skipped leg day):

Interestingly, measurements “shortly” after shorting it (hee hee) are also around 30k. Maybe less. There’s actually something interesting going on here, and it’s too fast really to see on one of these multimeters. Plus I don’t know what their characteristics are in general. So, I’ve set up a 5V supply, a 22k resistor and the moisture sensor, and I’ve put a scope across the moisture sensor. I start by shorting across the plant, then releasing the short. And this is what it looks like on both short and long timescales:

The results are… interesting. I suspect electrolysis is occurring.  I’m going to have to try feeding it with AC to see what the results look like. Everything is always more complicated than I expect! And here’s the voltage recovery after stopping shorting it:

## Trying to measure it

OK, so to the Arduino! I’m going to use one to generate AC. I use two GPIO pins as a very tiny H-bridge to generate square wave AC which is 5V pk-pk. The setup is the same before, I’ve got the sensor in series with a 22kΩ resistor. I’m measuring the voltage across the sensor. For much of the rest of this post, the measurements are going to be done in the same way, with the results shown on a scope.

For interest I’m going to always be showing the voltage in yellow and the  absolute value of the voltage in red. All things being equal, you’d expect it to be symmetric, so both halves of the trace will look the same. Hey, here’s a question: do you think it’ll be nice and simple?

So, here’s the first measurement…

Look how asymmetric the measurements are: despite the voltage reversing every cycle, all the yellow measurements are negative!

but after a while it looked like this:

Very symmetric measurements. The y axis has been doubled.

OK so what is going on here?

## Day II

You know I suspect now that I was making measurements with the same polarity every single time and I made a very crude rechargeable battery. During the AC measurements it eventually discharged which is why it went from biased to unbiased.

OK, so what about a 4 point measurement? That should eliminate effects on the driven electrodes on the other hand suddenly the complexity will have spiraled rather high, from essentially microcontroller and MOSFET to a whole analogue front end. Unless I can essentially do two 3 point measurements and subtract them. Then it’s just wires…

But first the rechargeable battery hypothesis. I’m going to apply 5V for 30 seconds at whatever current it will take across the electrodes. Then I’ll measure the open circuit voltage and the short circuit current. Also, of course take those measurements before with cleaned electrodes in an undisturbed location. Before, we get 8mv and 0.3uA. I guess the electrodes weren’t perfectly clean or the soil is not perfectly isotropic…

After it’s about 0.5V rapidly decaying to about 0.3 then 0.2, but delivering at 0.2V about 15uA rapidly decaying, slowing down at about 4uA, but continuing to decay. To investigate further, I’m going to use a square wave again, but with a more interesting pattern. I’m going to drive through the 22k resistor, then discharge through the 22k resistor, then the same but with the opposite polarity:

Charge, discharge, reverse charge, discharge. Also measured with averaging for a cleaner signal. It’s already lost a bit of symmetry.

You can see that after applying a voltage, some residual charge remains. Clearly though 100ms isn’t anything like enough to reach any kind of steady state. So, here’s a longer timescale:

10 second pulse, discharge, reverse pulse, discharge. Moderately symmetric this time.

That’s looking somewhat better. Most of them seem to have reached steady state after about 10s.

## Day III (weekend 2)

OK, so the soil is drier than it was. That means the resistance will have gone up and so I’d expect a higher voltage across the soil than the last time I did some measurements.I’m going to do very long, long and medium length measurements (100s, 10s and 1s). Mostly I picked that as my scope maxes out at 50s per division. Here’s how they look. Also wow, 50s per division takes aaaagessss.

well, they are looking oddly asymmetric (again). I didn’t leave any time between the measurements.  It looks settled after 20 seconds.

I wonder though, can I speed this up? At the moment, the battery is charging through a 22k resistor. Perhaps what I could do is put another pin in parallel with the resistor, so I can charge directly, then measure with the resistor. Time to add another pin and some more code…

The cycle is going to be charge directly, then measure using the 22k resistor, then discharge directly. Then repeat the cycle but in reverse. The first result is this:

That looks pretty promising. Those reads look pretty stable. But just to be sure, I’m going to go for some longer reads to see how they look. By the way the code for this is:


void setup() {
pinMode(4, INPUT); // This connects to the top of the moisture sensor.
pinMode(2, OUTPUT); //This connects to the top of the 22k resistor
pinMode(3, OUTPUT); // This connects to the bottom of the moisture sensor
//The bottom of the 22K resistor and the top of the moisture sensor
//are connected to form a potential divider
}

// the loop routine runs over and over again forever:
void loop() {

static const int32_t D1 = 1000;
static const int32_t D2 = 1000;

//Fast charge
pinMode(4, OUTPUT);
digitalWrite(4, HIGH);
digitalWrite(2, HIGH);
digitalWrite(3, LOW);
delay(D1);

//Slow charge/ measure
pinMode(4, INPUT);
delay(D2);

//Discharge
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
digitalWrite(2, LOW);
digitalWrite(3, LOW);
delay(D1);

//Fast charge
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
digitalWrite(2, LOW);
digitalWrite(3, HIGH);
delay(D1);

//Slow charge/ measure
pinMode(4, INPUT) ;
delay(D2);

//Discharge
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
digitalWrite(2, LOW);
digitalWrite(3, LOW);
delay(D1);


Note how I can change the fast charging versus the measuring. So using that I’m going to keep the fast charge at 1s and extend the measuring to 10s.

Uhmmmm what? I’m getting seriously confused here. It looks like the cell isn’t fully charged after the initial 1 second spike. And it looks like one direction holds more charge than the other (one flattens off, the other does not). I’m going to try bumping everything up to 10s.

If anything that seems worse than the 1s measurements.

## Conclusions so far

• Water does indeed reduce the resistance of the soil.
• Weird electrochemical effects are happening
• Longer measurements are not definitively better than shorter ones
• AC seems to be necessary to stop really unpleasant memory effects
• Shorter measurements might be better, causing less corrosion and electrolysis of the electrodes.
• It’s probably worth trying a higher resistor to capture more of the useful range.

## Moving on

So, I decided to try watering it just to see what happens. Here’s what the measurement plots look like:

I don’t remotely understand what’s going on. The cycle times are 10x apart and yet the curves look really really similar. Either way though it looks like adding water makes it vary more over time. Bleh. Or maybe it makes the charging happen faster? I’m now really quite unsure what’s going on. In fact look at this one:

It decays down as usual after the first quick charge, but then the line slopes up slightly. It’s almost like it continues to charge.

## The plan

Either way though it looks like the scheme will work.  The plan is to wake up every half an hour or so, measure the resistivity and dispense some water if it’s too high. It’s probably worth taking long measurements so they can be read on a multimeter, so the level can be calibrated easily. 10s seems decent for that.

Oh yeah! I completely forgot about doing 4 point measurements. I should totally do that. Here’s how:

4 point measurement electrode. The voltage is applied to the outer two and measured on the inner two.

This should be good. Yes:

Yeah so I have even less idea what’s going on there. Time to abandon THAT line of inquiry.

The conclusion is that the resistive sensor is probably workable, and with nice simple 2 point measurements, which is nice.

(on to Part 2)

# More constexpr in C++20

The C++ Kona meeting starts tomorrow or if you prefer on February the 18th, 2019, since you’re probably not reading this on the day I’m writing it. Apart from looking forward to C++20 in general, it matters to me because I’ve got two closely related papers under consideration.  TL;DR:

Except since they’re C++ committee papers, it’s actually more like:

Since the C++ committee doesn’t accept memes as proposals, what we actually submitted is:

1. P0533R4, a proposal to add constexpr to the elementary cmath functions. Elementary is in this case as on the same level as +,-,* and /, which we defined formally as closed over the rationals.Floating point numbers aren’t rationals of course, but arithmetic operations have the property that if you apply them to a rational number you get a rational number (unlike, say, sqrt) and importantly, there are no domain errors when fed with a finite number. Our property seemed to be the simplest one that fitted. Looking at the other functions, we found things like floor, ceil also fitted neatly.

We though this paper would be straightforward since those functions really aren’t fundamentally different from arithmetic, so  the same rules should apply.

2. P1383R0 a proposal to constexpr most of the rest of cmath. The LEWG pretty much liked P0533 and asked if we could submit a further paper to add constexpr to the non elementary functions.This paper adds most of the rest (sin, cos, exp, log, lgamma, etc). After some useful discussion, we excluded the cmath special functions, on the grounds that there are very few people in the world who know how to implement them so having them in could be a serious burden on compiler vendors.

If they get in then this post will finally be legal C++.

# Neutrogena light mask part 4: annoying resets

This post won’t make much sense without…
Part 1
Part 2
Part 3

A very small update:

Setting it to 99 lives doesn’t work that well. It reliably seems to get to 84, then when that’s done it resets to zero. It’s definitely not a glitch, it’s happened twice now. I haven’t discovered any other points where it resets yet. Perhaps it’s a little logic bomb but in there to stop people doing precisely this?

I’ve reset it to 63 this time to see where it gets to. I should probably actually run it and see, but the answer will come out eventually this way. And it’d be really annoying to run it 100 times in a row.

# Linear 3D triangulation

I came across this 3D linear triangular method in TheiaSFM:

bool TriangulateNView(const std::vector<Matrix3x4d>& poses,
const std::vector<Vector2d>& points,
Vector4d* triangulated_point)
CHECK_EQ(poses.size(), points.size());

Matrix4d design_matrix = Matrix4d::Zero();
for (int i = 0; i < points.size(); i++) {
const Vector3d norm_point = points[i].homogeneous().normalized();
const Eigen::Matrix cost_term =
poses[i].matrix() -
norm_point * norm_point.transpose() * poses[i].matrix();
design_matrix = design_matrix + cost_term.transpose() * cost_term;
}
*triangulated_point = eigen_solver.eigenvectors().col(0);
return eigen_solver.info() == Eigen::Success;
}


I was aware of the DLT (direct linear transform), but it didn't look like any formulation I've seen before. It's actually pretty neat. Let's say you're trying to find an unknown homogeneous point in 3D, $\mathbf{X} = [X, Y, Z, 1]$. What we have is $N$ poses, $P$, represented as $3\times 4$ matrices and the corresponding 2D coordinates represented as homogeneous points in $\mathbb R^3$. The 2D points are written as $\mathbf{x} = [ x, y, 1]$.

Since we're triangulating the 3D point, and we have homogeneous coordinate (i.e. $\alpha \mathbf{x} \equiv \mathbf{x}$) then for all $i$ we should have:
$\alpha_i \mathbf{x}_i \approx P_i \mathbf X$
given an scale factor $\alpha$.

Now let's pick apart the code above. Let's call design_matrix $D$ and cost_term $C$. On line 12, we have:
$\displaystyle D = \sum_{i=1}^{N} C_i^\top C_i$
And line 15 we’re finding the eigenvector corresponding to the smallest eigenvalue of D (SelfAdjointSolver produces them in a sorted order), i.e.
$\mathbf{X} \approx \displaystyle \underset{\mathbf{v}, |\mathbf{v}|=1}{\text{argmin}}\ \mathbf{v}^\top D \mathbf{v}$

We can rewrite $D = \mathbf{C}^\top\mathbf{C}$ where:
$\mathbf{C} = \left[ \begin{matrix} C_1\\ C_2\\ \vdots\\ C_N\\ \end{matrix}\right]$, which substituting in above gives:
$\mathbf{X} \approx \displaystyle \underset{\mathbf{v}, |\mathbf{v}|=1}{\text{argmin}}\ \|\mathbf{C v}\|_2^2$,
which is of course the right singular vector corresponding to the smallest singular value of $C$. Using eigen decomposition is much more efficient the size is $O(1)$, not $O(N)$, but probably at the penalty of less numerical precision.

Either way we’re trying to find the approximate nullspace of $\mathbf{C}$, which means finding something that’s roughly in the null space of all the $C_i$s. But why?

On lines 8–11, we have:
$C_i = P_i - \mathbf{\hat{x}\hat{x}^\top}P_i$,
and we’re claiming $\mathbf{X}$ is about in the null space. Let’s see what happens when we multiply by it:
$(P_i - \mathbf{\hat{x}\hat{x}^\top}P_i) \mathbf{X} = P_i \mathbf{X} -\mathbf{\hat{x}\hat{x}^\top}P_i \mathbf{X}\\$
Now, substituring in the first equation we have all the way back at the top gives:
$\approx \alpha \mathbf{x} - \alpha\mathbf{\hat{x}\hat{x}^\top x} = \alpha \mathbf{x} - \alpha\mathbf{\hat{x} |x|} = \alpha \mathbf{x} - \alpha\mathbf{x} = 0$
taa daa!

So there you go! If there is no noise, $\mathbf{X}$ is in the right null space of $C_i$ and so the null space of $\mathbf C$ and of course $D$. If there is noise, then it’s closest to being in the null space of all of $C_i$ measured in a least squares sense.

Note though that it’s just an algebraic error, not a reprojection error so it will give slightly oddly scaled results which will give more weight to some points than others. It is however a rather elegant solution.

# Boom! One day your plug goes boom!

Don’t give up in gloom…

I was pushing the plug into a socket on an extension lead and it gave a colossal flash and bang. So here’s the weird things:

1. What the hell.
2. No, seriously, what the hell?
3. The extension lead was fine and appears to still work.
4. The wiring to the wall socket was checked a couple of years ago when the electrician replaced my fuse boxes (including the wooden fusebox with asbestos lined fuse folders!) with a modern consumer unit.
5. For those of you not familiar with British foot stabbersplugs the big pin in the front with the chunk blown out of it is the Earth pin. The plug as you can see is just a simple  figure-8 adapter (Apple used to be really obnoxious and made the nonstandard, but they’ve stopped doing that now). Naturally, being a 2 pin connector, it’s not grounded so that earth pin SHOULD NOT BE CONNECTED TO ANYTHING!
6. The pin on the upper left of the main picture with the really big chunk blown out of it and the black stuff all over it is the neutral.
7. So I say again: WHAT THE HELL?

The event tripped the breaker but not the RCD (and not the 13A fuse in the extension lead either). I guess it’s a race to see which goes first…?

If you look closely, the live pin is not entirely unscathed. There’s definitely a little arc damage on it, though I don’t know if that was from the event or just regular use. You see on the live and neutral, there’s little bits of arc damage on/near the top. This is where the pins contact, so that may be from regular use.

Which made me think: the arc happened when the plug was already substantially inserted (you can see the damage is well down from the top) which means that the wipers were making good contact.

So, in conclusion:

# Neutrogena light mask part 3: about that LCD…

In Part 1 I hacked it to get 99 lives.
In Part 2 I did an excessively thorough analysis of the current limiting.
In Part 4 I found it keeps resetting on life 83.

The light mask comes with an LCD. I’ve always been curious about driving them, but never really taken the time to look. So, I figured I’d get my scope out and have a go. First off, it’s two seven segment displays. Rather handily, Neutrogena have left nice large test points for me. And if you look closely they even saved me the hassle of counting the number of them! There are 8. So, it must be a multiplexed display. Assuming it’s even vaguely symmetric, it’s got to be something like 5×3 or 4×4 (with some dead spots). So, time to break out the scope! First though, I have to solder some wires on:

Except they don’t fit under the LCD. Plus if you connect the power up backwards, then it appears to stop working. Why knew? Fortunately I have one more spare. The failure mode seems to be that one digit is no longer working (the LCD works though—I rubbed some power supply leads across the zebra strip and they all lit up.). Weird.

Um…

Yes OK, so it’s actually working fine (which is weirder) it’s just that it displays “0”, not “00” because it’s made for end users who aren’t expecting everything to be in nicely formatted hex…

So, I’m vaguely aware of some things about LCDs, in no particular order:

• They activate off DC, but that eventually destroys them so you should use AC.
• Driving simple (non multiplexed) LCDs is easy.
• Driving multiplexed ones is harder than you’d expect .
• There’s a threshold voltage below which they don’t turn on.

And here’s what the waveform for the first 4 pins looks like:

I blame my scope. No way it’s a PEBKAC error. There are like, 4 levels and they go all over the place. It’s crazy.

OK, that is indeed harder than I’d expect. Reading around was a bit boring, confusing and badly written. There’s some information which indicates it’s possible to drive them off only 2 levels. This would make sense: if you want to make a particular row/column pair light up, then you drive those two out of phase. Everything else…

Oh I see.

So, I guess the other rows have to be driven in-phase with the column, and the other columns… hmm. OK, I guess that’s why they have multiple levels. If you have a 5V supply, and you drive the row/column out of phase, the one intersecting segment sees 10v pk-pk. If you drive the other rows and columns with an idle voltage (say 2.5V DC) then the segments see either 0V if neither the row/column is driven or 5V pk-pk if a row/column is driven and the column/row is idle.

Backing up, imagine driving a matrix of light bulbs (not LEDs because I don’t want to get bogged down in Charlieplexing) from switches:

A multiplexed display matrix with one rwo switch and two column switches closed.

Switches are either closed, which on a microcontroller means driving the pin as 1 or 0 depending on which rail the switch is connected to, or open which means tristated. For current driven things, like light emitters, it’s easy: tristated means no current flows. For voltage devices, it simply means “not actively driven”, so something needs to be done with it, i.e. bias it to some neutral voltage.

I have no idea which things are rows and which things are columns on the display. However, I do know that the voltages are 0, 1.6, 3.3 and 5V. I’d hazard a guess that 3v pk-pk (i.e. a neighbouring pair) won’t drive the display but 10V pk-pk will. Not sure about 6.7. Probably?

Well, I’ve got an Arduino and some voltage regulators. For a 5V drive, I can easily get 0, 2.5V and 5V by tristating an output and pulling it to a central value:

Pulling the pins to a 2.5V rail made with a potential divider and a TS358 opamp (costs 6p!)

I used 47k because it’s high ish (won’t load the Arduino or opamp much) and I have plenty of them. Anything in the range of about 1k to several meg would probably work OK.

I could use the 3.3V output as the central value but honestly that seems to be tempting fate and I don’t know if it can sink current. Instead, I’ll use the entertainingly cheap TS358CD op-amp. So, time to cut out the useless remains of the device and wire up the Arduino!

I can still fix this!

Also, the test pads aren’t all they’re cracked up to be: the joints need to be filed down. Even now I’m not sure I’m getting perfect contact between the LCD and the board.

I had to file the tops of the joints down very carefully (totally unnecessary as it transpired).

Anyway I’ve wired up pins 1-8 on the arduino (not 0!) to 1-8 on the LCD, more for my own sanity than anything else. And with a simple program:

void setup(){}

void loop() {

pinMode(1, OUTPUT);
digitalWrite(1, HIGH);
delay(1);
digitalWrite(1, LOW);
delay(1);
pinMode(1, INPUT);
delay(1);
}


I get this scope trace:

This had me going for quite a while. The pin set to input should be getting 2.5V.  But it’s not; it’s being pulled up. I looked up: the internal pullup is 20k. The voltage is consistent with that: 2.5 * 47 / (47 + 20) + 2.5 ≈ 4 ish.

Well, that had me going for a while. I went back and forth with trying to figure out how to turn the pullup off (it’s off by default), turning it on and seeing what happened, plugging and unplugging wires and all that. Turns out that I was using pin1 which on the Arduino is the TX pin for the serial port if you want it to be. That means it has an LED attached which is doing extra pullup effectively to almost exactly the right value.

Ho ho ho.

So looks like I’ll be using pins 2-9 instead and I won’t get to keep my sanity. But at least that works.  Also I realised after a, er, little debugging that the reason the device has screws next to the LCD is so that they push the board against the zebra strip ensuring good contact. I wonder if I should use those screws…

Anyhow, now I think what remains is to so a somewhat exhaustive search over all pairs of wires driving them in opposition, to see what happens when they’re activated.

static const int A=2, B=3;
void setup(){
pinMode(B, OUTPUT);
pinMode(A, OUTPUT);
}

void loop() {
digitalWrite(A, 1);
digitalWrite(B, 0);
delay(1);
digitalWrite(A, 0);
digitalWrite(B, 1);
delay(1);
}


That only took a few minutes: it’s only actually 28 combinations. Here are the results I’ve noted down, along with the matrix that’s implied by the numbers. I’ve written the numbers as pairs indicating the two pins that have been activated:

Oh actually! It’s even better! This proves that not only is 10v pk-pk sufficient to drive the segments (I was sure of this), but 5v pk-pk isn’t, which I wasn’t so sure about. That’s nice: no extra circuitry is required. So, what we have is a 4×4 matrix. What I’m going to do is drive each of the rows in turn, while driving all 4 columns simultaneously.

The mapping is very regular though and we actually have essentially two 4×2 matrices for the two digits. The plan: each digit will be represented by a 7 bit number, one bit for each segment. Then, a 4×2 matrix will be represented as 4 2 bit numbers. The next step is a little tedious and involves designing the segment pattern for each digit and figuring out the mapping of segments to columns for each row. I’ve gone for A-Z (excluding k,m,v,w,x), picking the lowercase letter in general when there’s no other criteria and of course 0-9:

And that’s most of it. All that remains is to write a program which drives the rows in sequence and sets the correct column pattern for each row. Also, I’m going to have a function that maps ASCII to the segment patterns so I can write stuff on the display. My choice of driving is to drive each row low in turn, then repeat with each row high in turn to make it AC. I did try row 1 high then low, then row 2 high then low etc too but it didn’t make much difference. Here’s he code:

void setup(){};

//Decode uppercase ASCII and numbers into
//a list of segments, stored as a bit pattern.
uint8_t ascii_to_code(uint8_t c)
{
static const uint8_t letters[26] = {
0x77,0x7c,0x39,0x5e,
0x79,0x71,0x6f,0x74,
0x06,0x0e,0x00,0x38,
0x00,0x54,0x5c,0x73,
0x67,0x50,0x6d,0x78,
0x1c,0x00,0x00,0x00,
0x6e,0x5b
};
static const uint8_t numbers[10]={
0x3f,0x06,0x56,0x4f,
0x66,0x6d,0x79,0x07,
0x7f,0x67
};

if(c >= 65 && c = 48 && c >4 | (n&64)>>6;
}
uint8_t r3(uint8_t n){
return (n&16)>>3 | (n&4)>>2;
}
uint8_t r2(uint8_t n){
return (n&8)>>2;
}

//Set the column outputs as driven or inactive
//according to the input bit pattern. Polarity
//determins whether to drive high or low.
void set_columns(uint8_t n, bool polarity)
{
for(uint8_t i=0; i < 4; i++)
if(n&(1<<i))
{
pinMode(i+6, OUTPUT);
digitalWrite(i+6, polarity);
}
else
pinMode(i+6, INPUT);
}

void display_digit(uint8_t left, uint8_t right)
{
//Columns entries for both digits for
//the 4 rows.
uint8_t rows[4];
rows[3] = r5(left)<<2 | r5(right);
rows[2] = r4(left)<<2 | r4(right);
rows[1] = r3(left)<<2 | r3(right);
rows[0] = r2(left)<<2 | r2(right);

//Do positive columns/negative rows first
//then repeat with the polarity inverted
//Activate each row in turn and set the
//column entries for that row.
for(int p=0; p  100){
p++;
q=0;
}
}


There was a lot of fiddling around, most of which did very little. Basically, driving a multiplexed display off 3 levels is pretty marginal. I found that often some digits would ghost on, and others would be very faint. I could increase the contrast by lengthening the amount of time it took to draw the whole display, by driving a row with many cycles then moving on to the next row, but it only got really good when it was far far too slow. I did find putting a slight pause in between driving rows did help. Removing it darkened everything including ghosted on digits, lengthening it lightened everything. The value I got was about the best one I could find.

Here's what it looks like:

It's not perfect, you can see the contrast is a bit poor and so "hELLo" comes out looking a bit like "hECCo". Nonetheless I'm moderately pleased, since it does kind of work. I have to be careful with the choice of symbol though because they're not all that good.

# Neutrogena light mask part 2: down the rabbit hole

In part 1 I hacked the light mask to get 99 lives. In part 3, I play with the LCD. In part 4, I find it annoyingly doesn’t really have 99 lives.

## Final touches

Since it’s more convenient to use the built in timer (for now) than time manually, I added a programming port on the side so I can reprogram it when I need to:

The connections are CS, CK, MOSI, MISO (not required) and GND.

It’s always useful to have IPA or white spirits (effective but smelly) around to wipe off the excess epoxy on the visible bits to get nice looking results. I made the hole with a drill and needle files.

## Further analysis of the circuit

I’m now kind of curious about how accurate my guesses about the functionality were. Also, I have another controller kicking around which is now at 0 uses remaining and has no programming port. So, I pulled off the transistor. First, here’s the  hFE:

The transistor meter doesn’t get a lot of use these days

That’s comfortably in the middle of the 160-300 range expected from the datasheet.The transistor tester has obnoxiously deep holes for the legs…

So, what about the stiffness of the regulator? So, it’s driven by a 1k resistor from a 3.3v source through the base. I’ve got a bag of ST Microelectronics 7833 regulators. They’re meant for regulation not references and the tolerances are quite weak, but in practice the regulation is very good. From 4.65V to 27V, it’s bang on 3.20V output. It was a tiny bit worse at the low end (4.65 to 5.5V), giving out 3.19V when it was scorching due to being connected up backwards, but cold it’s even better. Here’s the test circuit:

With TP0 and TP1, I’ll be able to measure both the voltage across the transistor and the current through it.

### Aside

The resistor is a whacking great 10Ω beast from some old board I found in a junk pile at my old cow-orking space:

Two of the white ceramic rectangles are 10Ω, 10W resistors. The black column in the lower right is a high power 300Ω resistor. Those relays look beautiful and I want to use them for something.

I find having a junk pile of old boards good. Partly I find that despite being able to easily buy the parts I need, I often don’t think of them until I need them at which point I’d in practice have to wait until the next weekend. Having a pile of oddball parts can often yield something useful which works before the weekend expires. Also there’s something satisfying about getting use out of landfill. And then there’s the nostalgia trip: when I was a kid, many of my components were rescued off old junk boards.

There’s a bit of a knack to removing large parts, but it’s much easier now I have a half decent iron (it’s only half decent, but better than a 25W fixed temperature one!). Setting it hot (426C) helps, of course due to the thermal mass. The other trick is to add lots of extra leaded solder. This dilutes the unleaded stuff lowering the melting point and helps thermal coupling of the iron to the joint. Also, having a moderate amount of solder seems to work better with solder suckers. It’s counterintuitive to add solder when you want to remove it 🙂

The resistor is a Dale (Vishay now) 5% 10Ω wirewound ceramic cased one. Worth a couple of quid new. A nice part from a good manufacturer.

### Back to it

Hmm, no that’s not quite going to work. Even at 4V, we’ll push 400mA (the expected value) through the resistor, which may correspond to as little as 4.2V total if the transistor is well saturated. That’s below what the 7833 can regulate; unlike the one on the light mask it’s not LDO (low dropout). Looks like we’ll need two PSUs. Fortunately I have ALL the wall warts. This one (actually a brick not a wart) comes from an old IDE hard disk enclosure of 2008 vintage and gives a good 1.5A at 5 and 12V. Off comes the 5 pin mini DIN and on goes a header suitable for a breadboard or other similar socket:

I always find it’s worth doing things properly: I added a rigid strain relief and insulator using some polycaprolacetone derived plastic. It will stop shorts and annoying breakages.

The modified circuit is now this:

And I also put an ammeter in series with the base. It’s current-ly (heh heh heh) running at 2.49mA. I reckoned 2.7 before, so that’s not a bad guess! I tried a manual voltage ramp using my power supply, but it’s too slow: the transistor heats up a LOT, so the base current increases a lot too. I guess that’s part of the circuit stiffness in a sense, but I want a roughly constant temperature ramp if I can get it.

New circuit time!

I’m using an Arduino for doing the timing. It has a very short duty cycle (0.1%), so instead of dissipating somewhere around 10W, the whole assembly will dissipate 10mW or so. I guess I probably don’t need those power resistors after all. The switching is done using a N channel MOSFET (2N7000) to switch a high side power P channel MOSFET (IRLIB9343). The switch going on causes the 100μF capacitor to charge, rapidly ramping the voltage across the assembly under test, then it goes off, causing it to rapidly ramp down. Given the values, the time constant is around 1ms, so the whole test should be finished in 2-3ms or so (I’m wrong! more like 6-10ms).

The circuit’s been running a while and nothing is warm, and the base current is steady at 2.51mA, which is excellent. This is also where I love having a good 4 channel slope. I’ve set the scope to trigger off the Arduino, giving a very nice trigger. I then set it to averaging mode, to give very clean traces:

Channel 1: V1, Channel 2: Arduino, Channel 3: Vt

Plotting Vt against I=(V1 – Vt)/10 gives:

Yikes!! That’s one hell of a lot of hysteresis and the voltage goes negative??? It is however pretty decently stiff especially on the return leg. There are a few clues knocking around indicating that there’s a problem with my building of the circuit, rather than the hysteresis being due to the device under test. One is that it, well, goes negative. Then there’s a very sharp drop when the switch turns off, far faster than the discharging. And finally, the Arduino voltage takes a very long time to settle back to 0V.

Nonetheless even with this measurement we’re getting 20% regulation of current between 1 and 7V. I think the return leg is probably more accurate since it’s going slower so there are fewer transient effects. That’s more like 10% regulation.

I think the main reason is likely that I’ve been awfully lax with where I’m taking the measurements, see:

Left: the circuit, right: labeled. The power traces are in red and black. Scope probes are labeled in the trace colours (yellow, V1, magenta Vt, cyan Arduino) with ground as grey. The component under test has a white square around it.

The currents are biggish (half an amp or so) and the breadboard trace resistance as well as the contact resistance is nontrivial.You can see there’s a fair bit of stuff between the measurement ground and the transistor, especially the relatively high resistance points between the wires and pins and the breadboard. There’s quite a bit in the way of the bulk storage capacitor too, which explains the slightly wobbly traces too. The solution is to move the measurements right onto the legs of the transistor:

Left: the circuit, right: labeled. The power traces are in red and black. Yellow: V1. Magenta and green are both nominally Vt but green is soldered to the resistor leg and magenta to the transistor leg with no power flowing through the measurement wires. Cyan is the trigger as before. Grey is the scope common and is now soldered to the transistor leg too. There’s also a 1000uF capacitor placed very close to the high current circuit in the centre of the board.

Because of that there’ll be some unknown trace and contact resistance in series with the current sense resistor. I can solve that by making use of the fourth scope probe which will interestingly tell us something about the breadboard’s resistances. If it’s less than 0.5Ω then it’s within the tolerance of the resistor anyway. And while I’m at it, I’ll bump down the delay between measurements to 0.1s (100mW dissipated). That way I can actually get all 1024 measurements in the average without quitting early due to laziness. Here are the very clean looking traces:

One thing I didn’t really note before which you can see is that the decay curve doesn’t look very exponential: it’s actually pretty flat especially at the start: precisely what you”d expect from a constant current sink. The analysis gives:

Well, that’s disappointing. It’s basically the same. There’s less noise (due to more averaging) and it’s slightly smoother, but the hysteresis is still there almost unchanged. I doubt at the moment that it’s a transient effect in the device (e.g. thermal). Suspiciously, the discharge leg which is slower looks better. My current (this is a pun as you will soon see) best guess is that the base drive (see?) is taking a dip, because resistance between the emitter and the ground rail causes the emitter voltage to rise relative to ground, so the base current will drop, maybe until some capacitance charges up?

I’m not sure. The options are to measure the base current (there’s a roughly 2.5V drop across the resistor, which we can measure with the scope), and if there’s a flaw there, then I can stabilize the base current either by changing the drive circuit ground to a different place (e.g. the emitter measurement wire—it’s only a few mA) or sticking a high inductor on the base to resist transient changes in current.

The easiest thing to test is to change the width of the on pulse (and the capacitor) up and down by a factor of 10 to see what if anything changes. That should reveal a lot about the nature of the transient effect.

But first! I can check to see what the board and joint resistances are, using the extra probe that I added. I know the current from the big resistor, and it should be a simply V-vs-I plot…

## Temporal tests

Where was I? (there was around a 3 week gap here, so I’ve kind of forgotten). Oh yes, I was going to try varying the pulse length by a factor of 10 up and down. I’m currently using a 1ms pulse (the shortest available with delay() on the Arduino) for which I need a 100μF capacitor. I’m going to go for 10ms and .1ms as well. So, I have to replace that 1000μF with a 470μF one, since apparently I’ve run out of 1000μF capacitors and that’s my last one. Okey dokey then. Also, during these experiments, I tweak the power supply voltage so that the voltage across the transistor. I also had to bump the time up to 15ms for the slowest setting. By the time I got to the 10μF delay, I had to turn off interrupts and switch to delayMicroseconds(95), in order to get a 100μs delay. I also noticed some gnarly ringing, so I added gate resistors to the MOSFETS and a 100nF capacitor to the timing cap to take the edge off. Even so this is a hell of a fast circuit for a breadboard. Without the gate resistor, the gate of the 2N7000 has a rise time of about 20ns.

I also connected it up wrong, slightly smoked the DUT and burned my finger. Amazingly, it still seems to be in full working order. Ouch ouch ouch ouch. Anyway (ouch) here’s the traces:

No perfect, (ouch) but let’s see how the V/I cures look:

The legend indicates the capacitor used. That corresponds to pulse widths of 15ms, 1ms, 100μs and 10μs.

Well, that’s  lot better!! Those long pulses have awful transient effects, sine the power draw is high. As the pulse shortens that goes away, and the hysteresis cure closes up. I don’t believe that overshoot however on the quickest curve. I suspect I’m getting a different sort of transient effect there.

Also my PSU has now become unstable above 24V out. Naturally I need about 26 for this setup. Whyyyyyyyyyyy???

But that regulation is not bad (5% over quite a wide voltage range), or would be if the circuit actually hit that range. It is serving as a decent upper limit on the current, I guess, using only very cheap components and what was already required for other things (the 3.3V regulator).

## More on transient effects

I’d still like to reduce the size of the hysteresis loop. I speculated that the base current had something to do with it too. The circuit has no star ground and has pretty high currents. Due to the voltage drop along the ground rail, the regulator circuit will probably drift up and down. So, I’ve measured the voltage on either side of the base resistor. Using the scope’s maths functions (through a truly interesting interface), you can see (red channel) what the voltage fluctuation over the base resistor is.

As usual, blue is the trigger, and yellow is the capacitor voltage. Green and purple are measured either side of the base resistor and red is the difference between them. It’s a much smaller scale, so very very noisy. The temporal averaging really helps here.

That’s around a 5% change, which is certainly significant. Inductors resist change in current, so I’m going to stick a big inductor (if I have one) series with the base resistor to stabilise the current. The old PSU board has a transformer on it. I’m going to take that off and see how it looks.

Wretched device was press-fit and solered, so I almost busted it trying to take it out. The transformer has two sides: 1.6Ω/5.4Ω with inductances of  13/35mH, which is somewhat less that I expected. And that made little or no difference. That board also has a whacking great Omron LY4 relay which has a coil of a more respectable 3.5H, with 350Ω nominal resistance. Here’s the result 10th a 100μF timing capacitor:

Same as before, but looks like I got bored waiting for the averaging to finish, so the red trace is noisier.

It’s much better, but not quite as better as it looks because the current is being measured as voltage across 600Ω, not 1kΩ, so it’s more like a 33μA rather than 160μA, which is still a huge improvement. The VI curve is:

uhhhhh.

Yeah, so that’ll teach me for being lazy (no it won’t). Turns out it’s a 120V relay, not the 24V model, so the coil has a resistance of 1.7kΩ, which I would have known if I’d measured it. I have another relay with the specs of 750Ω/1.35H.

Still a 100μF timing capacitor.

Well, that’s a little bit better. As one might expect, it’s better on the outward leg, which is where the majority of the variation was. I strongly suspect I won’t be able to get much better than this without making the circuit on a board that allows substantially better design. For fun, here’s the one with the apparently optimum timescale:

10μF timing capacitor (100μs pulse width)

You know, that’s actually not bad!

Edit: I forgot to include the final circuit diagram, so here it is:

The circuit excluding the 7833 regulator and the power supply decoupling capacitors. Note the inclusion of the major board resistances (RBOARD), and the power resistors with 4 point measurements. I’ve also put the relay in in full.

# Conclusions (i.e. tl;dr)

1. I burned my finger (ouch).
2. The regulation is decidedly OK.
3. The user of the transistor was indeed a clever hack.
4. Measurement is hard.
5. This was a very long post.