online lostsh.github.io sunny | 2019-05-01 rfc 3514: evil bit not set
post 02 2019-05-01 arduino c++ | php 2 axes

Sunny: a solar tracker

Arduino C++ | PHP2018-2019

Sunny is a solar tracker built in 2018-2019 for the ISN option of a final year of French high school, by a team of three. It is a frame designed to turn a flat panel toward the sun. My share was the Arduino code, part of the construction, and the website.

This is the decision record, written from what survives: the bench sketch, the repository README, and the project site. Every entry below is a choice that was made, with the constraint behind it and the arithmetic that supports it.

What was built

ControllerArduino Uno | ATmega328P | 16 MHz
Conversion10-bit | 0-5 V | ~4.9 mV per count
Sensor head4 photoresistors + centre vane
ChannelsA1-A4 (A0 unused)
Bench frame period4 x 5 ms + 100 ms ~ 120 ms
Console9600 baud | raw counts
Actuation9 g hobby servo
Axes2, both driven
Structurecorrugated cardboard, prototype
TelemetryPHP endpoint, proof of concept
Recorded result"it works pretty good"
Five build photographs. Top row: the underside of a small cardboard plate with four axial resistors and bare tinned wire soldered across it; the finished head seen from above, a shallow cardboard box with one round photoresistor in each of four quadrants and a purple card cross standing upright in the middle; the head taped to an Arduino Uno board with orange and yellow jumper wires. Bottom row: the head labelled Sunny in felt pen, wired back to the board; and the tilting frame, a cardboard flap held by rubber bands over a cardboard box, driven by an orange plastic pulley and a servo taped to the side.
Sensor head, wiring and frame, 2018-2019

DR-01 | How to find the sun

Three methods were written up before anything was built. Only one was built.

The optical head carries no state. It needs no clock, no coordinates and no almanac, because its reference is the shadow standing in front of it: the same head works on any roof at any latitude with nothing entered and nothing configured. An equation-driven tracker has to be told where and when it is before it can point anywhere, and every one of those inputs is a thing that can be wrong. The recorded weakness follows from the same property: a head that reads a shadow needs enough light to cast one.

DR-02 | Four cells around a vane

The head is a shallow cardboard box divided into four quadrants by a card cross standing on the centre line, with one photoresistor per quadrant. The cross is the whole sensor: it is an occluder, and the four cells read the shadow it throws. The 2019 notes call this an omnidirectional sensor. What it is precisely is a four-quadrant differential head, and the difference between opposite pairs is the error signal in each of two axes.

sensor head | geometry
  from above                          from the side

  +---------+---------+                 |    vane
  |         |         |                 |
  |   (o)   |   (o)   |       --(o)-----+-----(o)--   plate
  |         |         |
  +---------+---------+       sun on the left, so the
  |         |         |       vane shadows the right-
  |   (o)   |   (o)   |       hand cell; the drive goes
  |         |         |       toward the lit pair until
  +---------+---------+       the two counts match

    (o) = photoresistor, one per quadrant
    the vane stands on both centre lines

The useful property of a differential head is that it does not need a calibrated absolute reading. Nothing has to be converted into lux; the loop only has to drive the difference between two cells toward zero. Absolute accuracy cancels out of a difference, and so does drift, as long as it is common to both cells: the two sit on the same plate, under the same glass, at the same temperature, so most of what would spoil an absolute reading spoils both of them equally and subtracts away. That is what makes four cheap cells sufficient here where one calibrated one would not be.

DR-03 | Photoresistors on a divider

The cells are photoresistors (the round type with the visible serpentine track in the photographs), each wired as one leg of a divider into an analog input. This needs no amplifier, no op-amp, no external converter: a resistor, a cell and one ADC pin per channel.

one channel of four
       +5V
        |
      [LDR]        one of four, nominally identical
        |
        +--------> A1 .. A4   ADC input
        |
      [ R ]        fixed leg
        |
       GND

Either order works and the two invert the sense of the reading: with the cell on top, more light gives a higher count; with it underneath, a lower one. The sketch prints raw counts, and for a differential head the sign convention only has to be consistent, not correct.

The fixed leg is the component that decides how much of the converter's range the sensor actually uses. A divider swings widest when that resistor sits near the geometric mean of the cell's bright and dark resistance. For a cell running 1 kohm in full sun and 100 kohm in shade:

sizing the fixed leg
  R  = sqrt(R_light x R_dark)
     = sqrt(1k x 100k) = 10 kohm

  Vout at 1 kohm   = 5 x 10k / (1k + 10k)   = 4.55 V   -> 931 counts
  Vout at 100 kohm = 5 x 10k / (100k + 10k) = 0.45 V   ->  93 counts

  usable swing ~ 838 of 1024 counts, centred in the range

Pick the resistor an order of magnitude off that mean and the swing collapses into one end of the scale, where the counts still change but the resolution per unit of light does not. The head's sensitivity is set by this one passive component, not by the code.

DR-04 | Arduino Uno, and what its converter actually gave

The Uno was chosen on what it could do. The head needs four analog inputs and the Uno brings six, all on one multiplexed 10-bit converter, so the four cells are read by the same hardware through the same reference and share whatever error it has: exactly the common-mode cancellation the differential head depends on. Four separate converters would have been worse. It also drives a servo off the same 5 V rail and talks to a host over USB without a programmer, which covers actuation and instrumentation on one board. The second reason was deliberate too: the project was framed from the start as a way into the Arduino toolchain and a language close to C.

The header comment of the bench sketch is the specification the rest of this post is quoted from: ATmega328P at 16 MHz, a 10-bit converter spanning 0 V to 5 V, so 1024 counts of about 4.9 mV each.

The first thing built was not the tracker. It was the instrument: a sketch whose only job is to walk the four channels and print them, so the head could be plotted before anything was allowed to move. That order was the right instinct.

test-V0.2-Sunny | bench sketch, 2019
/* Home-made sensor of four photocells, one per analog pin (1 to 4).
   The loop walks them and prints each value to the console, so the
   readings can be plotted afterwards.
   Converter: Arduino Uno, ATmega328P at 16 MHz, so a 10-bit ADC
   spanning 0 V to 5 V -- 1024 counts, about 4.9 mV each. */

int valReception = 0;

void setup() {
  Serial.begin(9600);
}

void loop() {
  // 5 ms between reads so as not to overload the ADC
  for (int i = 1; i <= 4; i++) {
    valReception = analogRead(i);
    Serial.print(" Sensor");
    Serial.print(i);
    Serial.print(" : ");
    Serial.print(valReception);

    delay(5);
  }
  Serial.print("\n");
  delay(100);
}

Comments and the console label are translated from the French original; the code itself is untouched. It was published on its own in 2019 and is not in the project repository, which holds the website and not the sketch. Four things in it are worth pulling out.

The budget is worth doing properly, because it settles whether any of the pacing mattered. The ATmega328P's converter needs 13 ADC clock cycles per conversion, and that clock is the system clock through a prescaler, /128 by default:

conversion budget
  ADC clock     = 16 MHz / 128            = 125 kHz
  one conversion = 13 / 125000            = 104 us      -> ~9.6 kSa/s
  sketch demand  = 4 channels / 120 ms    = 33 Sa/s     -> 0.35 % of it

  sun rate       = 360 deg / 24 h         = 15 deg/h
  per frame      = 15 x 0.120 / 3600      = 0.0005 deg

The converter runs about three hundred times faster than the sketch asks it to, and the sun moves half a thousandth of a degree between one frame and the next: three orders of magnitude below anything a cardboard flap on a rubber-band hinge can resolve. The pacing exists to make the console readable by a human, and at this ratio it costs nothing the mechanism could have used.

DR-05 | 9 g servos, and the structure that followed

The moving axis is driven by a 9 g hobby servo; the notes talk about the servos in the plural, one per axis. Nothing here was chosen against a torque budget: the servos were what could be afforded, alongside a recorded funding problem covering the solar panel, the servos themselves, and a stepper motor that was considered and never bought.

Everything mechanical about Sunny follows from that one line, and the chain is traceable. A 9 g servo of the common type holds on the order of 1.5 kg.cm at 4.8 V. Torque at the load is that figure divided by the arm, so the usable force at the edge of the panel falls off with how far out it sits:

force at the panel edge
  T      ~ 1.5 kg.cm at 4.8 V

  arm  5 cm  ->  1.5 / 5  = 300 g at the edge
  arm 10 cm  ->  1.5 / 10 = 150 g
  arm 15 cm  ->  1.5 / 15 = 100 g

  and that is the stall figure: usable holding
  torque is a fraction of it, and it has to cover
  the panel, the frame and any wind on the face

A hundred grams of budget at the edge of a flap is what selects the material. Cardboard, rubber-band hinges and a plastic pulley are not what you reach for once that number is on the page; they are the only things that fit under it. The project's own write-up lists the difficulty in exactly those terms: the mass of the components, the stiffness of the cardboard, and the power of the servos. The pulley is doing real work in that chain, and it is the one part of the drive that buys torque back rather than spending it.

DR-06 | Cardboard

Cardboard was the correct choice for a prototype: it is free, it cuts with a knife, and a design mistake costs one more piece of cardboard. Under a 100 g torque budget it is also one of the few materials stiff enough per gram to make the axis move at all. It is the reason the frame could be recut as often as it was inside a school term, and the reason the second axis could be added late without redesigning anything.

The assembled prototype outdoors on grey decking: a two-level corrugated cardboard frame carrying both drives, the sensor head with its purple vane on the upper tilting flap, a small Arduino-compatible board on a breadboard on the lower deck, coloured jumper wires running between the two levels, a servo with an orange horn on the right, and a small green breakout board at the end of a four-wire lead.
The prototype outdoors, two axes, cardboard frame

DR-07 | Two axes

The head reads two axes and the finished mechanism drives both. The first build turned on one, which is what the earliest gallery captions describe; the second stage went on later and the last version tracked in both directions cleanly. It is the arrangement in the photograph below: the lower deck carries the board and the azimuth drive, the upper flap carries the head and tilts on the second servo.

Adding the axis late worked because the two are independent. A four-quadrant head produces one error signal per axis from the same four readings, so the second loop is the same code against a different pair of cells and a different pin. Nothing in the sensing side had to change to accommodate it.

DR-08 | The web side

The plan was a site showing the tracker's state, its voltage, its position and some statistics, with a PHP page as the endpoint and a small mobile application built in App Inventor alongside it. All three were built, and in the last version the link was closed: the tracker reached the PHP endpoint and the page showed live state. It was a proof of concept rather than a product. The transport was not clean, there was no schema worth the name, and nothing about it was hardened. It ran.

What the project's own conclusion still lists under future improvements is remote control, which is the harder half: reading state out is one direction, and commanding the mechanism from the page is the one that was never attempted.

What holds up

The sensing principle is sound and cost almost nothing: an occluder and four cheap cells give a differential null that needs no calibration in physical units, no coordinates and no clock. The order of work was right, the instrument before the machine and the bench sketch before the servo, which is why there are numbers to quote at all. And the constraint chain runs cleanly from one end to the other: a torque figure sets a mass budget, the mass budget selects a material, and the material decides how the axis is hinged. Each link is checkable, which is the part that makes it a decision record rather than a build log.

The machine tracked in two axes and the telemetry reached a page. Everything still standing is on the original project site and in the repository, both still up and still in French.