I recently started working on a project commissioned by my son to measure how fast he and his sister can run… “Easy,” I thought. All I would need would be two light barriers and I could calculate speed from the time travelled over a known distance. Turned out that a simpler and cheaper solution might be just to use two ultrasonic distance sensors commonly combined with an Arduino and these would work as my “gates”. I looked up a code snippet and came across this example and many others similar to it…

Here’s what I actually setup…

The basic premise is that you have a transponder that is triggered with a short (10us) pulse, a burst of ultrasound (ping) is emitted from the transmitter and then the receiver produces a high signal whose length is equal to the time of flight (ToF) for the ping. Note that this is the round trip time ie. the time taken for the outward and return journey of the sound. This code worked fine for me however it has a fairly serious limitation in that it blocks the micro’s execution loop while waiting for the received signal here…

```duration = pulseIn(echoPin, HIGH);
distance = (duration*.0343)/2;
```

The cause of the problem is located in `pulseIn()` which is an Arduino library function that measures the time that an input is high for. This call gives us the ToF however it is blocking and stops the micro from doing anything else. This means that to have two sensors running at the same time would never work. You would have to do a measurement on one then the other continuously – not suitable for a fast-moving target. I should say at this point that the solution provided above is perfectly good for one sensor or if you are very new to microcontrollers and software etc. It does not fully harness the power of the Arduino however. What we need is to do some multitasking and microcontrollers typically use external and internal (timer) interrupts for this purpose. Doing a repeating task at a fixed period is accomplished with timer interrupts and I discovered a nice explanation of how to use the Arduino’s timer interrupts here.

To solve this problem I did the following…

#### Step 1

I used the example code to set up a 16-bit timer to give me a repeating interrupt at 41.75 Hz – the maximum possible ToF for a target at a maximum measurable range of 4m.

```void configureTimer1Interrupt(void)
{
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TCNT1  = 0; //initialize counter value to 0
// set compare match register for 41.75hz increments
OCR1A = 47903; // = 16E6 / (41.75*8) - 1 (must be <65536)
TCCR1B |= (1 << WGM12);   // turn on CTC mode
TCCR1B |= (1 << CS11);    // set prescaler to 8
TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
}
```

#### Step 2

I attached the timer interrupt to trigger the transmitter with a 10 us pulse

```ISR(TIMER1_COMPA_vect)
{   //ISR attached to timer1 that triggers the echo
digitalWrite(TRIG_PIN_IDX,HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN_IDX, LOW);
}
```

#### Step 3

I attached the receiver channel to an external interrupt that would measure the length of the ToF pulse from the receiver.

```void handleEcho(void)
{
if (risingEdge) handleEchoRisingEdge();
else handleEchoFallingEdge();
}

void handleEchoRisingEdge(void)
{
echoInitialTime = micros();
}

void handleEchoFallingEdge(void)
{
uint32_t timeOfFlightRoundTrip = micros() - echoInitialTime;
distanceCm = (0.0344 * timeOfFlightRoundTrip) / 2.0;
}

void setup()
{
...
attachInterrupt(
digitalPinToInterrupt(ECHO_PIN_IDX),
handleEcho,
CHANGE);
...
}
```

Here’s what the actual output looked like…

# A little better

I wondered if I could do better and remove the sleep from step 2 by setting up PWM (pulse width modulated) output at the specified frequency with a very low duty cycle (10us / (1/41.75Hz) = 4.175E-4). This would in fact completely remove the need for the code in Step 2; no timer interrupt would be needed. I found an enlightening post here. This does indeed work. Here’s the code snippet…

```void configureTimer1Pwm(void)
{
pinMode(TRIG_PIN_IDX, OUTPUT);
TCCR1A = _BV(COM1A1) | _BV(COM1B1) |_BV(WGM10) | _BV(WGM11);
TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS11);
OCR1A = 47903;
OCR1B = 19;
}
```

I’ve used timer1 again in “fast-PWM” mode to generate a PWM signal to drive the ultrasonic sensor. The frequency is controlled by the value in OCR1A (and prescaler) set by the clock select (CS) bits in the timer control register (TCCR1B). Effectively a counter counts up until it reaches the compare register A (OCR1A) and the output is switched on and resets. When the counter reaches compare register B, the output is turned off. You can follow the links for details of the calculations and have a look at my scrappy notes here.

I get the same output on the trigger pin as before but without the 10us sleep in `ISR(TIMER1_COMPA_vect)`. In fact, that function is no longer needed. The micro does all the signal generation that is required to drive the ultrasonic sensor. We don’t need any logic in the code anymore. All that’s required is to set up the PWM output correctly. I think that this is a preferable solution because it means that this cyclic task of driving the sensor is done at a low level and is more likely to be jitter-free and more importantly, we don’t block the micro’s execution loop.

Example code

## Taking control

Up to this point, we’ve only looked at software for the lifetester internals. For the unit to actually be of any use it needs to talk to the outside world. Obviously with Arduino, there’s the serial (UART) interface which is useful for sending and receiving strings but if we were to test many solar cells in parallel, we’d need a different bus for each device. With I2C however, we can connect many (over 100) devices to the same bus, each as a slave, and interface with all of them from a single master device. I felt it was important to make this interface light and to only use single byte commands since communication could be slower this way especially along reasonably long wires. Note that I2C was developed for communication between devices on the same board not board-to-board.

# Protocol

Bit76543210
FunctionChA/BR/WRDYERRERRCMDCMDCMD

Note that in addition to the byte-wide command register, there are longer registers for storing measurement parameters and data.

Communication with the lifetester is done through a single byte-wide register with each bit used for the functions shown in the table above.  Generally, the master should write its command into the register, poll on the RDY bit and then read from the requested register when the slave says “I’m ready” through the RDY bit. Here’s how the you would retrieve data from the lifetester channel A to illustrate:

1. Master requests a write to the command register by writing 0x40.
2. Now the master is allowed to write its command which will be 0x02 for a request to read channel A’s data.
3. Now the master will poll the slave by requesting a read of the command register – it writes 0x00. Then it reads a byte and checks the RDY bit.
4. Once the RDY bit is set to 1 by the slave, the data is ready and the slave can again request a read of the data register and read out 13 bytes (full length of the data register).

# Implementation details

So here’s how I actually did this. First thing to note is that the Arduino already has functions implemented for I2C communication as the Wire library. They expect you to register some functions (callbacks) to be registered for when data is received from or when data is requested by the master. I’ve attached the functions here in the setup function called in main…

```void setup(void)
{
...
Wire.setClock(31000L);
Wire.onRequest(Controller_RequestHandler);
...
}
```

…notice that I’ve set the clock speed deliberately slow as I’m concerned about the speed of transmission over long cables. Perhaps a better way to do this would have been to setup the I2C bus in differential mode with something like this – something for the next board revision perhaps.

## Data sent from master to slave

When the lifetester slave receives data from the master, the following function is called. The general idea, is to check the current contents of the command register first. This tells us whether (a) a new command is being written, (b) measurement parameters are being written or (c)

```STATIC DataBuffer_t transmitBuffer;
// We keep a static (module scope) copy of the command register
STATIC uint8_t      cmdReg;

{
// Tell the user that data is being transmitted with the LED
digitalWrite(COMMS_LED_PIN, HIGH);

/* Look at the current command in the command register. This tells us
what to do with this new data from the master. Is the master writ-
ing to the params register? If so, read in the new params.*/
if ((GET_COMMAND(cmdReg) == ParamsReg)
&& IS_WRITE(cmdReg))
{
if (numBytes == PARAMS_REG_SIZE)
{
// protect from another write without command
}
else
{
// chuck away bad settings - wrong size
}
}
else // expect a new command to be written from the master...
{
// Make sure old commands don't fill up buffer
/* to write a new command, the master needs to request a write to the
command register and it's only accepted if the write bit is set and
if (GET_COMMAND(newCmdReg) == CmdReg)
{
if (IS_WRITE(newCmdReg))
{
if (IS_RDY(cmdReg))
{
}
else
{
SET_ERROR(cmdReg, BusyError);
}
}
// Master requested read command reg - see request handler
else
{
}
}
/* write to command register already requested now receiving new command
we only allow a write to the command register if one has been requested
as above. */
else if (GET_COMMAND(cmdReg) == CmdReg)
{
}
else
{
// TODO: handle this. received undefined command
}
}
digitalWrite(COMMS_LED_PIN, LOW);
}
```

Once a command has been written to the slave, it’s time to update the status bits as follows. We need to do things like clear the ready bit if we’re about to load data into the data register, for example or set the error bits if an unknown command is issued…

```static void UpdateStatusBits(uint8_t newCmdReg)
{
// Commands are represented by a custom type (enum)
const ControllerCommand_t c = GET_COMMAND(newCmdReg);
if (IS_WRITE(newCmdReg))
{
switch (c)
{
case Reset:
case ParamsReg:
case CmdReg:
break;
case DataReg: // Master can't write to the data register
default:
SET_ERROR(cmdReg, UnkownCmdError);
break;
}
}
{
switch (c)
{
case Reset:
CLEAR_RDY_STATUS(cmdReg);
break;
case ParamsReg:
case DataReg:
// data requested - need to load into buffer now. Set busy
CLEAR_RDY_STATUS(cmdReg);
break;
case CmdReg:  // command not loaded - preserve reg as is for reading
break;
default:
SET_ERROR(cmdReg, UnkownCmdError);
break;
}
}
}
```

## Updating status

Every time a command is written, the lifetester has to actually do something with it by “consuming” it. You can see the update function here that is responsible for responding to incoming commands. It’s a simple switch statement that decides what to do based on the command bits of the command register (I’ve used an enum to store the commands and macros to read the out the various bits and fields).

```void Controller_ConsumeCommand(LifeTester_t *const lifeTesterChA,
LifeTester_t *const lifeTesterChB)
{
LifeTester_t *const ch =
(GET_CHANNEL(cmdReg) == LIFETESTER_CH_A) ? lifeTesterChA : lifeTesterChB;
switch (GET_COMMAND(cmdReg))
{
case Reset:
if (!IS_RDY(cmdReg))  // RW bit ignored
{
StateMachine_Reset(ch);
SET_RDY_STATUS(cmdReg);
}
break;
case ParamsReg:
if (!IS_WRITE(cmdReg))
{
WriteParamsToTransmitBuffer();
SET_RDY_STATUS(cmdReg);
}
else
{
SET_RDY_STATUS(cmdReg);
}
break;
case DataReg:
if (!IS_WRITE(cmdReg))
{
if (!IS_RDY(cmdReg))
{
// ensure data isn't loaded again
WriteDataToTransmitBuffer(ch);
SET_RDY_STATUS(cmdReg);
}
}
break;
default:
break;
}
}
```

## Data requested from slave by master

This is an easy one. By the time data is requested from the slave, data should either be in the transmit buffer to be sent, in which case transmit it, otherwise there’s an error condition and we should set the error bits.

```void Controller_RequestHandler(void)
{
digitalWrite(COMMS_LED_PIN, HIGH);
{
Wire.write(cmdReg);
}
else
{
if (!IsEmpty(&transmitBuffer))
{
TransmitData();
}
else
{
SET_ERROR(cmdReg, BusyError);
}
}
digitalWrite(COMMS_LED_PIN, LOW);
}
```

# Summary

We’ve covered how to make a two-way interface with a microcontroller through a byte-wide command register using the native Arduino I2C libraries. With it, we can issue commands, read status, and check for errors. I hope this was enough to convince you that you can do a lot with just a single byte. We haven’t covered how you read and write bits by doing bitmath. Let’s have a look at that in another post.

## A State Machine – Unit Testing

Last time I talked about how I implemented a state-machine to control the lifetester that I’ve been developing. In the process, I relied heavily on unit testing the code as I wrote it. In fact, by unit testing the code while refactoring, all the development was done on my desktop machine! This is a big departure from how I used to do things a year ago where all the development that I did was using the Arduino IDE and code was compiled and run on the target. I only needed to compile for the Arduino and plug in a reference solar cell at the very end to check that everything worked as I expected and I’m pleased to say that it did. My eyes were opened to this in my first job as an embedded software engineer at CMR. I found this was one of the most striking differences between the professional and home project software development worlds. In essence, unit testing is software designed to exercise all of the behaviour of the code (as independent units) we’re intending to write. We’re trying to check that it works as we expect, whether we give the functions good or bad inputs – there are positive and negative tests. It was put to me like this once: “Unit-test the code like a burglar rather than a postman”.

## How to write unit tests

I think it’s worth saying here that there are good and bad ways of unit testing and as with anything. I really like this guide. In short, unit-tests should be F.I.R.S.T:

• Fast – probably more important on very large projects than here but who wants to wait ages for their tests to run?
• Independent/Isolated – tests should follow the arrange, act assert format.
• Repeatable – the order that test run shouldn’t matter and the results should be the same every time they’re run. They should be responsible for setup and teardown all of their data.
• Self-validating – we don’t need to inspect anything to see if a test has passed or failed. The results should be reported automatically.
• Thorough and timely – cover every use scenario and be done drive the development of the source code not be written later.

## My attempt

Clearly, I can’t go through all the unit tests for this module as this post would be far too long but I can show you a couple of examples to give you an idea how this might work. Here goes…

```TEST(IVTestGroup, SaturatedCurrentDetectedIncrementsErrorReadingsCounter)
{
mockLifeTester->data.delayDone = true;
mockLifeTester->data.iSampleSum = MAX_CURRENT;
mockLifeTester->data.nSamples = 1U;
mockLifeTester->state = &StateMeasureThisDataPoint;
const uint32_t tInit = 34524U;
mockTime = tInit + SETTLE_TIME + SAMPLING_TIME;
ActivateThisMeasurement(mockLifeTester);
MocksForTrackingModeStep();
POINTERS_EQUAL(&StateTrackingMode, mockLifeTester->state);
CHECK_EQUAL(currentLimit, mockLifeTester->error);
mock().checkExpectations();
}
```

Above is a test the checks that if the adc readings are saturated (the current from the device goes outside the available range), the reading is counted as a bad reading and added to a counter – the lifetester should accepts a few bad readings before transitioning to the error state. In the test module, I’ve setup a `mockLifeTester` variable (instance) that I reset before every test. So the first thing to do in the test is set the lifetester to the correct mode (`MeasureThisDataPoint`), reset the error readings counter and most importantly saturate the current reading. `mockTime` is my way of returning a value from `millis()` in the source code. You can see that I’ve incremented the timer so that the sampling window and tracking delay have expired before calling the state-machine update function. Now I do the asserts and check that we’ve transitioned back to `TrackingMode` (parent state), as the sampling time is over and that the error has been counted and recorded in the `mockLifeTester` data. Of course, too many bad readings should lead to a transition to the `StateError` as follows…

```TEST(IVTestGroup, TrackingModeTooManyBadReadingsTransitionToErrorState)
{
// Setup for tracking mode.
mockLifeTester-&gt;state = &amp;StateTrackingMode;
MocksForTrackingModeStep();
MocksForErrorEntry(mockLifeTester);
POINTERS_EQUAL(&amp;StateError, mockLifeTester-&gt;state);
mock().checkExpectations();
}
```

All I need to do here is setup the the number of error readings above the allowed limit before calling the update function. This should lead to a transition to the error mode which will have happened for the test to pass in the `POINTERS_EQUAL(...)` statement.

## Mocking

The question on my mind before I began unit-testing embedded code was “How do we execute code written for an embedded platform on a PC? Won’t it try to call hardware specific functions that don’t exist?”. This is accomplished by mocking – any calls to low level i/o are replaced with our own mock functions. I’ve given an example here that I needed in the last test…

```static void MocksForErrorLedSetup(void)
{
mock().expectOneCall("Flasher::t")
.withParameter("onNew", ERROR_LED_ON_TIME)
.withParameter("offNew", ERROR_LED_OFF_TIME);
mock().expectOneCall("Flasher::keepFlashing");
}

static void MocksForSetDacToVoltage(LifeTester_t const *const lifeTester,
uint8_t v)
{
mock().expectOneCall("DacSetOutput")
.withParameter("output", v)
.withParameter("channel", lifeTester->io.dac);
}

static void MocksForErrorEntry(LifeTester_t const *const lifeTester)
{
MocksForErrorLedSetup();
MocksForSetDacToVoltage(lifeTester, 0U);
}
```

When the lifetester transitions into the error state, we expect it to set the dac and setup the flash rate of an led to indicate that the device is in its error state. So we expect our mock functions to be called. You’ll see in the tests that there’s this statement `mock().checkExpectations()` which is responsible for checking that the correct mocks are actually called number of times we expect. If we don’t say `expectOneCall("DacSetOutput")`, and a call is made to this function by the source then the test will fail. Alternatively, if we do make an expect and the function isn’t called, the test will fail too; mocking and expects are a really important tool for checking the behaviour of our code too. Asserting on the data returned is only half the picture.

## Thoughts

In this post, I’ve discussed in brief how unit testing can be used to refactor and write code that’s more robust. I hope you like it. Personally, I write unit tests for all the code that I write now even though it means you have to write twice as much code. I like the way that it helps me to think through what I’m doing and encourages me to write cleaner code where the lower layers are abstracted so they can be mocked effectively. I believe that it could be better understood and used by the ‘hacker’ community to good effect…but then would it really be hacking?

## A State-Machine

Recently, I’ve been refactoring the lifetester project. Essentially, the code that I wrote in the beginning is over a year old and it just didn’t look clean to me any more after having had a bit more experience. In particular, the core module responsible for doing current voltage scans and power point tracking needed some attention. Bear in mind that it’s responsible for the controlling the device and maintaining its state ie. it’s a state-machine. Although I didn’t realise this when I first wrote it. But why bother going to this trouble if the code already works? The short answer is that without this structure, the code is hard to read, meaning that bugs can hide, hard to change and hard to test too. This is a compelling enough case for me.

## Design

For us, a state-machine is simply a way of recording the state of a system and defining conditions necessary to transition between them; it’s a way for us to visualise the job that we’re trying to do and attach some formalism to it so we can design the behaviour as we intend. Here’s my attempt at a UML state machine diagram for the solar cell lifetester project…

You can see that the there are broadly only a few states: initialise, scanning tracking and error modes with nested sub-states within them. This is termed hierarchy in state-machine parlance. Note that this is different to a simple state-machine with no hierarchy. Because different states share some of the same behaviours, we can nest them inside ‘parent’ states that carry out these tasks for all of the ‘children’ inside them. This is the whole point. Instead of repeating yourself writing the same code for all states in tracking mode say, you can put them in a parent state that does the common tasks so that the children are only responsible for their specific duties. The other thing to note is that this whole process will be executed repeatedly in a loop (in the main sketch if you’re into Arduino) so you’ll see that each mode has an entry, step and exit function associated with it that tells the device what to do when entering the mode, in the mode and when leaving the mode respectively.  And when we’re not making a transition, we just sit in the state we’re already in and call the step function for the parent and child state.

## Defining States

States are defined by a set of actions: what to do upon entry and exit and whilst in the state itself. Actions are implemented as functions and so the state is then a collection of functions whose pointers are stored in a struct as follows…

```STATIC const LifeTesterState_t StateTrackingMode = {
{
TrackingModeEntry, // entry function (print message, led params)
TrackingModeStep,  // step function (update LED)
NULL,              // exit function
TrackingModeTran   // transition function
},                     // current state
NULL,                  // parent state pointer
"StateTrackingMode"    // label
};
```

This particular example (see above) defines the `TrackingMode` state that has no parent, as indicated by the `NULL` pointer, but has several child states. We don’t worry about the children in the definition only the parent; the parent is unique to a given state but there might be many children as in this case. `TrackingMode` has `Delay`, `MeasureThisPoint` and `MeasureNextPoint`. The reason for this will become apparent when we talk about transitions. Just for comparison, here is a child state…

```STATIC const LifeTesterState_t StateMeasureThisDataPoint = {
{
MeasureDataPointEntry,    // entry function
MeasureDataPointStep,     // step function
MeasureThisDataPointExit, // exit function
MeasureDataPointTran      // transition function
},                            // current state
&StateTrackingMode,           // parent state pointer
"StateMeasureThisDataPoint"   // label
};
```

and you can see that this state shares many of the same functions as this one…

```STATIC const LifeTesterState_t StateMeasureNextDataPoint = {
{
MeasureDataPointEntry,    // entry function
MeasureDataPointStep,     // step function
MeasureNextDataPointExit, // exit function
MeasureDataPointTran      // transition function
},                            // current state
&StateTrackingMode,           // parent state pointer
"StateMeasureNextDataPoint"   // label
};
```

## State Transitions

Now we know how to define states in this scheme, let’s talk about transitions – whenever we want to transition between an initial and target state, we need to call the exit function of the initial state and the entry function of the target state. In the case of a nested states, for example `MeasureThisPoint` to `MeasureNextPoint`, we would have to call the exit function for `MeasureThisPoint` and entry function for `MeasureNextPoint` but not for `TrackingMode` because both initial and target state are children of `TrackingMode`: we never leave or enter `TrackingMode`. However, this may vary depending on the specifics of exactly what state we leave and enter. Let’s clarify with some diagrams…

Here’s a summary (see below) of the different things that we need to do when making a state transition. Clearly, this is a simplified model for a hierarchical state-machine with only one level of nesting. Real hierarchical state machines will have many entry and exit functions to call depending on how deeply nested the initial and target states are.

CaseExit initial stateExit parent of initial stateEnter parent of target stateEnter target state
I) Common or no parentYesNoNoYes
II) Exit child stateYesNoNoNo
III) Enter child stateNoNoNoYes
IV) Different parentsYesYesYesYes

Here’s how I did this in code form:

```STATIC void StateMachineTransitionToState(LifeTester_t *const lifeTester,
LifeTesterState_t const *const targetState)
{
LifeTesterState_t const *state = lifeTester->state;

if (targetState == state)
{
}
else if (targetState == state->parent)
{
// only need to exit current state to parent - don't run parent entry
ExitCurrentChildState(lifeTester);
}
else if (targetState->parent == state)
{
EnterTargetChildState(lifeTester, targetState);
}
else if (targetState->parent == state->parent)
{
// Only need to transition out/in one level
ExitCurrentChildState(lifeTester);
EnterTargetChildState(lifeTester, targetState);
}
else
{
// Need to fully exit state and reenter target
ExitCurrentChildState(lifeTester);
ExitCurrentParentState(lifeTester);
EnterTargetParentState(lifeTester, targetState);
EnterTargetChildState(lifeTester, targetState);
}
// Finally transition is done. Copy the target state into lifetester state.
lifeTester->state = targetState;
}
```

And to avoid calling a NULL function pointer, we need to protect ourselves like this…

```static void ExitCurrentParentState(LifeTester_t *const lifeTester)
{
if (lifeTester->state->parent != NULL)
{
StateFn_t *exitFn = lifeTester->state->parent->fn.exit;
RUN_STATE_FN(exitFn, lifeTester);
}
}
```

which basically says that if the parent state is defined as `NULL` (ie. nothing), DO NOT call it. Otherwise we’ll end up with some nasty segmentation fault.

## Refactoring

So back to the point which was how measurements are now done in the state-machine scheme. Let’s look closer at `TrackingMode`. This state is responsible for maintaining the maximum power point. Unless there’s an error condition or the reset function is called, the state-machine will stay in this state indefinitely and transition between its sub-states. While in this state, the step function will be called:

```STATIC void TrackingModeStep(LifeTester_t *const lifeTester)
{
lifeTester->led.update();

const bool measurementsDone = lifeTester->data.thisDone
&& lifeTester->data.nextDone;
const bool trackDelayDone   = lifeTester->data.delayDone;
{
StateMachineTransitionOnEvent(lifeTester, ErrorEvent);
}
else if (!trackDelayDone)
{
StateMachineTransitionOnEvent(lifeTester, TrackDelayStartEvent);
}
else if (!measurementsDone)
{
StateMachineTransitionOnEvent(lifeTester, MeasurementStartEvent);
}
else // recalculate working mpp and restart measurements
{
UpdateTrackingData(lifeTester);
lifeTester->data.thisDone = false;
lifeTester->data.nextDone = false;
lifeTester->data.delayDone = false;
}
}
```

It’s responsible for raising an event that kicks off a transition to the next state. Let’s say that there’s no error and the tracking delay period has expired, then it’s time to do some measurements and the `MeasurementStateEvent` is issued and the transition function for `TrackingMode` (the current state) get’s called as follows…

```STATIC void TrackingModeTran(LifeTester_t *const lifeTester,
Event_t e)
{
if (e == MeasurementStartEvent)
{
if (!lifeTester->data.thisDone)
{
ActivateThisMeasurement(lifeTester);
StateMachineTransitionToState(lifeTester, &StateMeasureThisDataPoint);
}
else if (!lifeTester->data.nextDone)
{
ActivateNextMeasurement(lifeTester);
StateMachineTransitionToState(lifeTester, &StateMeasureNextDataPoint);
}
else
{
// nothing to measure - returns to caller
}
}
else if (e == TrackDelayStartEvent)
{
StateMachineTransitionToState(lifeTester, &StateTrackingDelay);
}
else if (e == ErrorEvent)
{
StateMachineTransitionToState(lifeTester, &StateError);
}
else
{

}
}
```

and in this case, the state-machine will transition to `MeasureThisDataPoint` because no measurement is done yet (Note the use of flags here – I couldn’t see a better way of doing this at the time). Since `MeasureThisDataPoint` is a child of `TrackingMode`, only its entry function will get called.

```STATIC void MeasureDataPointEntry(LifeTester_t *const lifeTester)
{
// Scan, This or Next is activated in the transition function
DacSetOutputToActiveVoltage(lifeTester);
if (!DacOutputSetToActiveVoltage(lifeTester))
{
lifeTester->error = DacSetFailed;
StateMachineTransitionOnEvent(lifeTester, ErrorEvent);
}
else
{
ResetForNextMeasurement(lifeTester);
}
}
```

which set’s up the lifetester so that everything is in a condition ready for measurements to begin – it set’s the dac to the correct drive voltage and raises an error if it can’t do it. Assuming, all is well, the next time the state-machine is updated, the relevant step function will be called:

```STATIC void MeasureDataPointStep(LifeTester_t *const lifeTester)
{
LifeTesterData_t *const data = &lifeTester->data;

const uint32_t tPresent = millis();
const uint16_t tSettle = Config_GetSettleTime();
const uint16_t tSample = Config_GetSampleTime();
const uint32_t tElapsed = tPresent - lifeTester->timer;
&& (tElapsed < (tSettle + tSample));
const bool     samplingExpired = (tElapsed >= (tSettle + tSample));

{
data->iSampleSum += sample;
data->nSamples++;
}
else if (samplingExpired)
{
{
*data->iActive = data->iSampleSum / data->nSamples;
*data->pActive = *data->vActive * *data->iActive;
// Readings are averaged in the transition function for now.
StateMachineTransitionOnEvent(lifeTester, MeasurementDoneEvent);
}
else
{
/*Measurement interrupted. Restart timer and try again.
Note that we'll never leave this state if adc isn't returning data.*/
lifeTester->timer = tPresent;
}
}
else
{
/* Do nothing. Just leave update. More time elapses and then
when update is called, the next state will change.*/
}
}```

This function is responsible for getting an accurate measurement of the current at the given operating point which involves waiting for the settle time to elapse and then sampling the adc over the prescribed sampling window. When it’s happy, a `MeasurementDoneEvent` is raised and the transition function for this state is called…

```STATIC void MeasureDataPointTran(LifeTester_t *const lifeTester,
Event_t e)
{
if (e == MeasurementDoneEvent)
{
// transition child->parent. Exit function will get called.
StateMachineTransitionToState(lifeTester, lifeTester->state->parent);
}
if (e == ErrorEvent)
{
StateMachineTransitionToState(lifeTester, &StateError);
}
else
{
/*Don't do anything. Transition function exits and execution returns to
calling environment (step function)*/
}
}
```

…and the state-machine will transition back to the parent state – `TrackingMode`. It’s important to note that the transition functions for each state determine the behaviour of the state-machine in a large part; they represent the arrows on the state-machine diagram. To complete the transition, the exit function for the current state will be called of course and here the status of ‘this’ measurement will be set to done.

```STATIC void MeasureThisDataPointExit(LifeTester_t *const lifeTester)
{
lifeTester->data.thisDone = true;
}
```

Now the state machine is back in the parent state `TrackingMode` however, since the flag `thisDone` is now set, the state-machine will transition to `MeasureNextDataPoint` via `TrackingModeTran` (see above). Finally, once both measurements are done, `TrackingModeTran` will be called and the drive voltage will be updated as follows…

```static void UpdateTrackingData(LifeTester_t *const lifeTester)
{
LifeTesterData_t *const data = &lifeTester->data;
/*if power is higher at the next point, we must be going uphill so move
forwards one point for next loop*/
if (data->pNext > data->pThis)
{
data->vThis += DV_MPPT;
data->vNext = data->vThis + DV_MPPT;
lifeTester->led.stopAfter(2); //two flashes
}
else // otherwise go the other way...
{
data->vThis -= DV_MPPT;
data->vNext = data->vThis + DV_MPPT;
lifeTester->led.stopAfter(1); //one flash
}
PrintNewMpp(lifeTester);
}
```

The public interface to the state-machine is made up of just a couple of functions that allow us to update the state and reset if needed. They call the step functions for the current state and its parent and invoke a transition to `InitialiseDevice` respectively.

```/*******************************************************************************
* PUBLIC API
*******************************************************************************/
void StateMachine_Reset(LifeTester_t *const lifeTester)
{
DBG_PRINTLN("Resetting device", "%s");
lifeTester->state = &StateNone;
StateMachineTransitionToState(lifeTester, &StateInitialiseDevice);
}

{
/*Call step functions in this order so that a transition from a NULL parent
state will only call one step function and one transition. Where as a tran-
sition from a child state will only call the step fucntion of its parent.
simpler to debug.*/
RunParentStepFn(lifeTester);
RunChildStepFn(lifeTester);
}
```

## Finally…

Now we have a refactored version of the previous code involving a state-machine. Hopefully it’s now clearer what each function does and we are closer to the single responsibility principle even if we have more code. The logic of this module is now clearer furthermore, by implementing a state-machine, I’ve been able to implement an API allowing me to issue a software reset command which was not possible before. The other advance here is that if a scientist were to come along at a later point with a need to change the maximum power point tracking algorithm, this would be done in one place – `UpdateTrackingData` rather than in a single monolithic function. This implementation is also testable. In fact, to write it, I had to build a test harness for it which I’d like to share with you next.

## How long do solar cells live? (maximum power point tracking)

In other posts, I’ve talked about developing the lifetester board and output from the prototypes that I’ve built. So far however, I haven’t given any detail on how maximum point tracking actually works and in this post, I want to unravel things a bit. For this first attempt, I’ve gone for a really simple hill-climbing algorithm which looks like this:

In summary, It does the following steps to update the drive voltage to maintain the MPP:

1. Scan the drive voltage and look for the maximum power point to be used as an initial guess (not shown).
2. Set the drive voltage (V) for this point, measure the current.
3. Set the drive voltage (V + dV) for the next point, measure the current.
4. If Power(next) > Power(this), set V -= dV else set V += dV.
5. Repeat step 2.

In software, the update (step) function looks like this:

```void IV_MpptUpdate(LifeTester_t *const lifeTester)
{
uint32_t tElapsed = millis() - lifeTester->timer;

if ((lifeTester->error != currentThreshold)
{
if ((tElapsed >= TRACK_DELAY_TIME)
&& tElapsed < (TRACK_DELAY_TIME + SETTLE_TIME))
{
//STAGE 1: SET INITIAL STATE OF DAC V0
DacSetOutput(lifeTester->IVData.v, lifeTester->channel.dac);
}
else if ((tElapsed >= (TRACK_DELAY_TIME + SETTLE_TIME))
&& (tElapsed < (TRACK_DELAY_TIME + SETTLE_TIME + SAMPLING_TIME)))
{
//STAGE 2: KEEP READING THE CURRENT AND SUMMING IT AFTER THE SETTLE TIME
}
else if ((tElapsed >= (TRACK_DELAY_TIME + SETTLE_TIME + SAMPLING_TIME))
&& (tElapsed < (TRACK_DELAY_TIME + 2 * SETTLE_TIME + SAMPLING_TIME)))
{
//STAGE 3: STOP SAMPLING. SET DAC TO V1
DacSetOutput((lifeTester->IVData.v + DV_MPPT), lifeTester->channel.dac);
}
else if ((tElapsed >= (TRACK_DELAY_TIME + 2 * SETTLE_TIME + SAMPLING_TIME))
&& (tElapsed < (TRACK_DELAY_TIME + 2 * SETTLE_TIME + 2 * SAMPLING_TIME)))
{
//STAGE 4: KEEP READING THE CURRENT AND SUMMING IT AFTER ANOTHER SETTLE TIME
}
//STAGE 5: MEASUREMENTS DONE. DO CALCULATIONS
else if (tElapsed >= (TRACK_DELAY_TIME + 2 * SETTLE_TIME + 2 * SAMPLING_TIME))
{
// Readings are summed together and then averaged.
lifeTester->IVData.pCurrent =
lifeTester->IVData.v * lifeTester->IVData.iCurrent;

lifeTester->IVData.pNext =
(lifeTester->IVData.v + DV_MPPT) * lifeTester->IVData.iNext;

// if power is lower here, we must be going downhill then move back one point for next loop
if (lifeTester->IVData.pNext > lifeTester->IVData.pCurrent)
{
lifeTester->IVData.v += DV_MPPT;
lifeTester->Led.stopAfter(2); //two flashes
}
else
{
lifeTester->IVData.v -= DV_MPPT;
lifeTester->Led.stopAfter(1); //one flash
}
// finished measurement now so do error detection
if (lifeTester->IVData.iCurrent < MIN_CURRENT)
{
lifeTester->error = lowCurrent;
}
else if (lifeTester->IVData.iCurrent >= MAX_CURRENT)
{
lifeTester->error = currentLimit;  //reached current limit
}
else //no error here so reset error counter and err_code to 0
{
lifeTester->error = ok;
}
PrintLifeTesterData(lifeTester);

lifeTester->IVData.iTransmit =
0.5 * (lifeTester->IVData.iCurrent + lifeTester->IVData.iNext);
lifeTester->timer = millis(); //reset timer
lifeTester->IVData.iCurrent = 0;
lifeTester->IVData.iNext = 0;
}
}
else //error condition - trigger LED
{
lifeTester->Led.t(500,500);
lifeTester->Led.keepFlashing();
}
}
```

This function operates on a custom lifetester type that contains all the relevant information regarding the state of the device under test. We pass a pointer to this data which the update function works on. It’s a psudo-object oriented approach. C is obviously not an object oriented language but by using a struct like an instance, this function is a bit like a method. This way, we can have another lifetester instance to represent another device under test or many more if we choose and they should not interact.

As illustrated in this solution, it’s important not to block the microcontroller with calls to delay(). If you call this, the device won’t be able to update another channel say or the state of leds. I wrote this code almost a year ago and although it works, it’s not clean:

• The function is too long – it’s doing more than one thing.
• There are unnecessary comments. If the code were written well, it would be self-documenting.
• There is duplication: this point and next point share almost identical code.
• Spot the magic numbers.

I’ve now refactored this code by means of a state-machine and will present it in a coming article.