Light sensor (Part 2)

Part 1, 2

In the previous part of this publication, I covered the subject of sensitivity. As for many sensor, the range is very broad and a simple configuration cannot help covering the whole range of light intensities.

As in the dated controllers, we might want to use a hand operated sensitivity selector. Although this option works, it is uncomfortable and we would prefer an auto-ranging device which will automatically select the best matching resistor, depending on the actual brightness.
The schematics below illustrate a simple and cheap design which will help covering light intensity from few Lux to hundreds of thousands Lux in four ranges.

The current Ipce flowing through the photo-transistor (U2) passes through the shunt resistor R5. The voltage generated across the resistor is buffered and amplified by U1 configured in non-inverting mode . The voltage divider is made of R4 and one of R1, R2 or R3 resistors which can be individually switched to ground thanks to the CD4066 quad analog switch. Each switch is controlled by a separate line which is wired to the pins from an arduino board. If none from the switches is closed, the amplifier is in unitary gain, so that this design features a 4 stages gain amplifier.
R5 is choosen so that the sensor will not saturate under strong lighting conditions. In my case, a resistor of 100 Ohm was appropriate for the TEPT5600 phototransistor. R1, R2 and R3 where choosen in order to obtain progressive gains of about x10, x100 and x1000. C1 filter the power supply from Arduino: 10 to 100 µF will do the job.

Next is an example of the code which drives this auto-ranging circuit. Starting from the highest sensitivity, the code reads the analog input. If the signal exceeds a predefined level, the code decrements the range counter, switches the next resistor and performs a new reading. The loop breaks when the analog reading lies in the allowed range or when the lowest sensitivity range is reached.

const uint16_t _autoGainTriggerLevel = 1008;
const uint8_t _stages = 4;
/* all switch driver pins must belong to the same port */
volatile uint8_t *_port = &PORTD;
const uint8_t _vStageCtrlMasks[] = {0, (1 << PIND2), (1 << PIND3), (1 << PIND4)};
const float _vGain[] = {1.0, 11.0, 101.0, 1001.0};
const float _responseFactor = 3.0;
const uint16_t _shuntResistor = 100;


void initSwitches(void)
{
    /* set swicthes driving pins */
	for (uint8_t i = 0; i < _stages; i++)
    {
        *(_port - 1) |= _vStageCtrlMasks[i]; 
    }
}


void setSwitch(uint8_t stage)
{
    /* clear swicthes */
 	for (uint8_t i = 0; i < _stages; i++)
    {
        *_port &= ~_vStageCtrlMasks[i]; 
    }
    /* set switch */
    *_port |= _vStageCtrlMasks[stage]; 
    delay(10);
}


void setup(void)
{
	/* initialize serial comm port */
	Serial.begin(38400); 
	/* initialize resistor switches */
    initSwitches();
}


void loop(void) 
{
    uint16_t adcCounts;
    /* find the best range starting for the most sensitive */
    uint8_t stage = _stages;
    do
    {
        stage -= 1;
        setSwitch(stage);
        adcCounts = analogRead(0);
        Serial.print(stage);
        Serial.print(" ; ");
        Serial.print(adcCounts);
        Serial.print(" ; ");
        Serial.print(_vGain[stage]);
        Serial.print(" ; ");
        Serial.print(adcCounts / _vGain[stage]);
        Serial.print(" ; ");
        float lightIntensity = _responseFactor * (float(adcCounts * 10e07) / float(_vGain[stage] * _shuntResistor * 1024 * 7));
        Serial.print(lightIntensity);
        Serial.println();      
    }
    while ((adcCounts > _autoGainTriggerLevel) && (stage > 0));
    Serial.println();      
    /* repeat after delay */
    delay(2000);
}

First we have a couple of declarations: global application related variables and hardware related variables. The response factor relates to the bin of the photo-transistor (as explained before). The _vGain vector contains the gains computed after R1, R2, R3 and R4 ( Gain=(1+(R4/Rx)) ). One function configures the pins wired to the switches and an other sets individual pins (after resetting all switches). The auto-ranging process lies in the loop function. It is almost self-explanatory. Data is printed so that it can be reconstructed as a scatter of x, y data points. Next is a plot of the brightness measured at my desk (on the side which is next to the window): today which is actually a cloudy day.

Light sensor (Part 1)

Part 1, 2

Sensing light is probably one of the most common sensing and probably one of the simplest measurement to start with.

Light sensors are used to detect light or brightness in a manner similar to the human eye. They are most commonly found in industrial lighting, consumer electronics, and automotive systems, where they allow settings to be adjusted automatically in response to changing ambient light conditions. By turning on, turning off, or adjusting features, ambient light sensors can conserve battery power and provide extra safety while eliminating the need for manual adjustments. (as per Vishay’s documentation)

Here is a list of various lighting conditions

ILLUMINANCE EXAMPLE
10-5 lux Light from Sirius star, the brightest star
10-4 lux Total starlight, overcast sky
0.002 lux Moonless clear night sky with airflow
0.01 lux Quarter moon
0.27 lux Full moon on a clear night
1 lux Full moon overhead at tropical latitudes
3.4 lux Dark limit of civil twilight under a clear sky
50 lux Family living room
80 lux Hallway / toilet
100 lux Very dark overcast day
320 to 500 lux Office lighting
400 lux Sunrise or sunset on a clear day
1000 luxOvercast day, typical TV studio lighting
10 000 to 25 000 lux Full daylight (not direct sun)
32000 to 130000 lux Direct sunlight

Sensing light is safe, simple and cheap. Many application notes describe the use of the dated LDR (Light Detecting Resistor), so that I will skip this sensor.

Some time ago I used the TEPT5600 ambient light sensor which is a photo-transistor. This versatile sensor can be used in various ways to address various applications exposed to variable brightness (Ev, in lux).

Photo-transistors are equivalent to photo-diodes used in conjunction with bipolar transistor amplifiers as shown below:

Typically, the current amplification, B factor, is between 100 and 1000. The active area of photo-transistor is usually about 0.5 x 0.5 mm2. The data of spectral responsiveness are equivalent to those of photo-diodes, but must be multiplied by the factor current amplification, B.
Next picture illustrates the linear luminescence to photo-current relationship:

Next is a cut view of the TEPT5600 sensor as per Vishay’s documentation

This image has an empty alt attribute; its file name is image-4.png

More about physicis and technolgy involved in phototransistors > here <

BINNING
…For a given irradiance, phototransistors may show lot-to-lot variability of the output current …. The lot-to-lot variability of photodiodes is significantly lower because it is caused only by the variability of the photosensitivity. Vishay offers its ambient light sensors with phototransistor output in binned groups. These groups cannot be ordered separately but each reel is marked with a label A, B, or C that will allow the user to select the appropriate load resistor to compensate for these wide tolerances. (as per Vishay’s documentation)

Next is a table showing the opto current flowing though the collector and the emitter (Ipce, in µA) biased with 5 V while exposed to a Ev of 20 lux.

Binned groupMin.Max.
TEPT5600 A2550.4
TEPT5600 B41.784
TEPT5600 C69.4140
TEPT5600 D113.4226.8

Next are a few exemples of use of the TEPT5600 sensor. For more information, read this publication.

Basic switch

The output goes high at Ev > 25 lux, Ipce: 10 μA, Vout: 2.0 V, Input Leakage Current: < 1 μA

Improved Switch featuring a level converter

Output Low at Ev > 10 lux, Ipce: 4 μA, Gate Threshold: 2.0 V, Input Leakage Current: < 1 μA

Basic Light Meter
As the photo-transistor acts as a current source directly proportional to the luminescence, a simple resistor in series with the sensor suffices.

EV: 10 lux to 1000 lux, Ipce: 4 μA to 400 μA, Vout: 16 mV to 1.6 V

Low Illuminance Light Meter

Ev: 0.1 to 10 lux, Ipce: 40 nA to 4 μA, Vout: 16 mV to 1.6 V . The output signal is amplified and buffered by an operational amplifier before the A/D converter

Next post on same topic

Power meter (Part 3)

Part 1, 2, 3

Once the hardware is ready, let’s have a look at the data and the software used to read these data.

The customer information can operate in two different modes:

  • historic: for compatibility reasons with older meters, this is the default mode.
  • standard: this new mode, available from the latest Linky meters. It is faster than the historic mode, and has more information, with a specific format. Just ask your provider for setting remotely this mode on your meter.

We will start with the historic mode which specifications are available from the technical documentation. The baud rate in this mode is 1200.
Each byte contains 1 start bit, 7 data bits, 1 parity bit and 1 stop bit.

Data is transferred as frames which are delimited by a STX character (0x02) and and a ETX character (0x03).

Each frame contains a set of data; each set of data is delimited by a LF character (0x10) and a CR character (0x0D). Each set of data contains one keyword (which length is variable and always less or equal 8 capital characters), one space, data (variable length of alpha numeric characters), one space and one checksum character as shown in the picture below

The checksum is computed with the keyword, a space and the data it self. It consists in adding all characters, applying a modulo 64 and adding the value 0x20. In this way, the checksum is printed in human readable character (from ‘space’ (0x20) to ‘_’ (0x5F)). Next is an example of code used to compute this checksum: (updated)

inline char computeChecksum(const char *aKeyword, const char *aData) 
{
	char sum = 0;
	while(*aKeyword) 
	{
		sum += *aKeyword;
		aKeyword += 1;
	}
	sum += 0x20;
	while(*aData) 
	{
		sum += *aData;
		aData += 1;
	}
	sum = ((sum & 0x3F) + 0x20);
	return (sum);
}

While talking about general purpose functions, let’s deal a bit with the parity check. The Linky power meter uses 7 bits data and a parity bit. This bit is set so that the ultimate parity of the data and the parity bits are even (even number of bits set to ‘1’). Although, faster algorithms exist, as we have kind of plenty of time (1200 baud) the classic method shall be used, as shown below

inline bool getParity(uint8_t byteValue) 
{ 
	bool parity = false; 
	while (byteValue) 
	{ 
		parity = !parity; 
		byteValue &= (byteValue - 1); 
	}      
	return (parity); 
} 

Reading a frame consists in listening to the incoming STX character, collecting consecutive bytes, checking their parity, extracting the 7 data bits, storing the byte in a vector and do that until the ETX occurs. An extra feature prevents the function from running for ever in case of lack of data for a certain amount of time (timeout).

bool readFrame
(
char *aFrame,
const uint16_t frameSize, 
const uint16_t timeOut,
const bool checkParity
)
{
	/* reset the content of the array */
	*aFrame = '\0';	
	bool waitForEtx = true;
	bool waitForStx = true;
	uint16_t bytesCount = 0;
	uint32_t startTime = millis();
	while (waitForStx) 
	{
		if (((millis() - startTime) > timeOut) || (bytesCount == frameSize))
		/* timeout and overflow protect */
		{
			return false;
		}
		/* if data is available in the incoming buffer */
		if (PwrMtrSerial.available())
		{			
			/* read the incoming data */
			char dataIn = PwrMtrSerial.read();
			if (checkParity && getParity(dataIn))
			/* check partity: true if odd, even (false) is expected */
			{
				return false;
			}
			/* mask the parity bit */
			dataIn &= 0x7F;
			/* parse incoming data */
			if (waitForEtx)
			{
				if (dataIn == 0x02)
				/* start of text */
				{
					/* change state */
					waitForEtx = false;
				}

			}
			else
			{
				if (dataIn == 0x03)
				/* end of text */
				{
					/* append termination character */
					*(aFrame + bytesCount) = '\0';	
					/* change state of readiness */
					waitForStx = false;
				}
				else 
				{
					/* record incoming character and increment pointer */
					*(aFrame + bytesCount) = dataIn;
					bytesCount += 1;
				}			
			}
		}
		else
		{
			delay(1);
		}
	}
	return (true);
}

Power meter (Part 2)

Part 1, 2, 3

I have been tumbling through the net, looking for interfaces to the TIC. So far, I found two types of reference designs:

  • The quick, dirty, safe but questionable design
  • The academic design

Most of the interfaces described on the web are very basic. They feature a single optocoupler and a biasing resistor.
The other option is much sophisticated and features a signal shaping stage. The design is far better, but requires TTL components and their power supply.

Credit

Once again, I decided to design one of mine, with my own constants: state of the art, compact, cheap and safe. This one features features a few cheap analog components arranged in these sections: power supply, demodulator and opto-switch.

Power supply: here we use a simple half wave rectifier (D1 and D3) biased through R2. The rectified signal is dampened by C1 and optionally regulated by D5. The output is a stable DC voltage.

Demodulator: once again we use a simple half wave rectifier (D4 and D2) biased through R1. The rectified signal is moderately dampened by C2 and applied through R3 to the base of Q1 acting as a switch. R3 discharges C2 in order to prevent tailing pulses.

Opto-switch: this is the easy part of the schematics ! The internal LED is biased through R5 and switched by Q1. Tx is switched to ground when a wave hits the input of the demodulator.

Warning: As you can see on the schematics, the interface has a separate ground from the Arduino, which is probably connected to your PC and thus to the mains ground. At all times the interface GND (labelled GNDA) must be floating versus GROUND.

Note: During the development satge, is used a reference signal generated with my RIGOL DG1022U DDS function generator, using the burst mode applied to a 50 kHz, 6 Vpp, 0V offset signal.

And the plots are screen shots from my good’ol RIGOL DS1052E tweeked to 100 Mhz

Both are electronic’s enthusiasts best friends ! Affordable, compact, reliable (except the encoders from the scope), accurate and pretty powerful.

Power meter (Part 1)

Part 1, 2, 3

Yesterday’s electric meters were mostly electromechanical meters. They were measuring consumption, they required a technician to read the past power consumption, diagnose them and change their parameters such as power ratings

Today’s electric meters are communicating meters, which means that they can receive and send data without the need for the physical presence of a technician. Installed in end-consumer’s properties and linked to a supervision centre, they are in constant interaction with the network. This is what makes them “intelligent” (this is what they say). Part of the possible interactions is the power shut down if you do not pay your bills, or in case of major network failure in order to prevent the dominos effects…

We heard a lot of objections about these new meters: some had their computers destroyed, some blame the accuracy of the power readings, etc. To be honest, I have no major objections about the induced consequences of the installation of such smart meters. However, I a strong concern about the use of the data collected from the use that we make of electricity: how, when, how much, which equipment (easy is you use the amps signature). I mostly agree with what is written in Canard PC . I will address this matter later.

Tadaaa, last week week, we had a smart meter installed. A flashing green Linky meter made in the local Itron plant. And obviously, it did not take long before I had a look at this new toy and tried to get as much as it is supposed to provide to customers.

Firstly, I gathered a couple of technical documents which anyone should read before doing anything serious and safe. Although the user interface as been carefully designed in order to avoid any reciprocal disturbances between the power line and the customer link, let’s remember that we deal with real power.

Getting access to the “TIC” (Télé Information Client) is easy: just remove the green cover and locate the button which controls the push buttons of the header

Warning: Although the technical documents specify that the TIC can handle high voltage, do not mix the C1 and C2 ports with the I1, I2 and A ports from the TIC.

Connecting individual wires is not so easy as the push button drives the three ports simultaneously…

This is what Itron agreed for the specifications of the meter:

From this ugly picture, you are supposed to understand that the TIC signal is an amplitude modulated signal (AM) using a carrier frequency of 50 kHz (not 50 Hz !). If the amplitude lies between Vevh0 and Vevl0, the corresponding data bit equals 0; if the amplitude lies between 0 and Vevl0, the corresponding data bit equals 1.

Next is an illustration from the real world showing a bit “1” followed by a bit “0”:

The signal is almost 6 V pp and the frequency is 50 kHz (pretty noisy, featuring plenty of harmonics).

Last not least here are a few words about the “A” port which is barely described. The Linky meter specifies that the voltage between “i1” and “A” (unloaded) is 13 V rms maximum, while under a typical resistive load ranging from 225 and 335 ohms, the voltage between “i1” and “A” (loaded) is 6 Vrms ± 10% @ 50 kHz resulting in a minimum of 130 mW available to feed some remote electronics. The signal between “i1” and “A” (unloaded) looks like the one illustrated below: a noisy square signal @ 50 kHz:

I will cover in the next post the way we can use this “i1”, “i2” and “A” lines

Logiciels Libres (Freeware)

Le socle interministériel de logiciels libres (SILL) est un ensemble de logiciels libres préconisés par l’État français en 2016, dans le cadre de la modernisation globale de ses systèmes d’informations (SI). Sa gouvernance est réalisée par le Premier ministre sous trois grands principes :

  • L’approche de l’État privilégie l’efficacité globale, en dehors de tout dogmatisme, pour lui permettre de choisir entre les différentes solutions, libres, éditeurs ou mixtes.;
  • Pour chaque fonctionnalité, un logiciel est préconisé en précisant la version ;
  • La préconisation est évolutive dans le temps et elle fait l’objet d’une validation annuelle

En suivant ce lien, vous découvrirez la liste des logiciels libres de droit préconisés par l’état français. Ce sont essentiellement des logiciels orientés bureautique, business. Les makers ajouterons très probablement FreeCad (modélisation 3D) et KiCad (schéma, routage, bom, etc.) à cette liste !

La stratégie gouvernementale n’a pas toujours connu cette orientation. Bien que sous la même bannière, les militaires et les gendarmes avaient divergé dans leurs choix… Plus choquante fut la décision du ministère de l’éducation de s’allier à Micro$oft, une aubaine extraordinaire pour le géant informatique.

Je suis globalement en accord avec les propositions. J’y ajouterais volontiers Irfanview en tant qu’afficheur (et +!) d’images et FreeMind pour la gestion heuristique de projets.

TASCAM Sound Recorder (Part 5)

Part 1, 2, 3, 4, 5

As a professional equipment, the TASCAM DR40-X recorder features XLR inputs.

These are in fact combo inputs which are compatible with standard XLR connectors and 6.35 mm jack connectors . More important than the hardware, the electronics behind the connectors are very important. Most audio devices rely on unbalanced connections: the audio signal travels through one single wire. To prevent external interference, this wire is insulated and wrapped in braided wires which act as a shield. Although this method is satisfactory for short wires and standard use of audio devices, this shielding method is insufficient. The solution lies in the use of two wires for transmitting the audio signal: both wires conduct the same signal except that one is the negate of the other. This method is called the balanced mode.

Let’s see how it works with the example below:

First we start from a pure sine wave. The signal is split in two parts with same amplitude but one is negated versus the other. As they travel through the (twisted) wires, they are exposed to an unexpected burst. When the signal reaches the end of the cable, both signals are distorted in the same way. When the signal is ultimately reconstructed by adding one signal and the negated other, the burst is canceled.

Quality has a price: this method requires one extra wire, special 3 pins connectors and additional electronics. Splitting the signal and negating one is plain easy. However, this requires that some electronics is embedded in the source device along with its power supply. To prevent the use of batteries in small equipment (e.g. microphones), audio engineers had a bright idea: use the signal cable to transport energy to the source device. This energy must be a stable DC current which create a steady signal offset that we can null by means of capacitors. This power supply is called the “phantom power” as it exists but is invisible to the final listener.

Next picture illustrates the principle of the phantom power:

From right to left, a DC regulator feeds both signal lines through Re0 and Re1. At the receiver stage, Rr0 and Rr1 collect the voltage and use it to power the pre-amplifier.

The phantom power supplies comply with the CEI 268-15A rules. P48 used 48 V biasing through 6.8 k Ohm resistors, P24 used 24 V biasing through 1.2 k Ohm resistors and P12 uses 12 V biasing through 680 Ohm resistors

Then comes the problem of connecting a standard microphone to the recorder. Most microphones are electret microphones that require some biasing to operate. This biasing must be supplied by the phantom power thanks to an adapter. I chose the Rode VXLR+ adapter which is affordable (~25€), well made and looks like it can do the job.

The VXLR+ contains a small PCB which obviously care about decoupling the audio signal (thanks to the large 50 V capacitors) and some components for biasing the microphone.

This device worked perfectly well with commercial products as well as with my custom microphones.

Please note that the cheaper Rode VXLR does not contain any electronics and acts as a simple hardware adapter as shown on the picture below:

Next is a set pictures illustrating the various pin-outs involved in audio connectors:

Pinout of XLR connectors, view from the protuberant pins (male connector):

Pinout of jack connectors (3.5 mm) are specified by their number of rings, using the initial “T” for tip, “R” for ring(s) and “S” for sleeve. Next connector is a TRRS type

Various combinations exist which use jack connectors:

  • OMTP standard: T=audio left, R1=audio right, R2=microphone, S=ground
  • CTIA standard (apple): T=audio left, R1=audio right, R2=ground, S=
    microphone
  • Mono microphone: T=microphone, S=ground
  • Stereo microphones: T=microphone left, R=microphone right, S=ground

HTH

3D prints: Stevenson screen

Building a basic weather station today is simple and cheap thanks to specialized integrated circuits featuring temperature, humidity and pressure sensing. Up to now my favorite is the Bosh bme280; you may easily find it ready for use on small PCBs, easy to connect on any prototyping platform. Adafruit industries is selling a very convenient shield PID: 2652

Running this nice shield is really easy. However, turning it to a real weather station is another story. No need to be a professional meteorologist to guess that leaving this outdoor module “as is” will lead to inaccurate readings and rapid degradation. We just need a proper shelter, a so-called Stevenson screen. This enclosure must leave enough space to place the sensor and break the sun’s rays as well as the wind flow. Here is a proposal for a compact Stevenson screen printed in several stackable parts.

The lower stage features a stand for the shield and two screws, a hole and a grove for the signal cable and two hexagonal carvings for a pair of nuts to be used with mounting screws.

Next stages feature a Z shaped cross section

Then come a top sealed stage and the final cover stage. The air space between these two last stages acts as a thermal insulator against direct solar radiation.

 Download the STL files > here <

Enjoy!

Let’s celebrate Apollo XI !

I remember very well the 20th of July 1969… I was in a holiday camp and the director had the bright idea to get a portable TV set, to power it with the battery of his car and he managed to have as many kids as possible watching this event. At this time, I had no idea at all about what was happening, which were the roots of this project and which would be the consequences.

The image on the the TV screen was no better than my illustration, but god, that was almost incredible !

Years after, I discovered programming science through the use of hp 25 pocket calculators. These calculators were coming with the fabulous (at this time) lunar module landing game ! (More about this program). And a few years later, I joined hp and I got a calculator for free !

So I had the idea to clone this program on arduino just for fun !

I made it plain simple so that anyone can have some fun with it. When the game starts, you are 1000 m above the lunar surface, the LEM (Lunar Excursion Module) speed is 10 m/s, the fuel tank contains 150 arbitrary units while the moon gravity is 1.62 (1/6 compared to the earth gravity as you may know). Because the LEM is inexorably attracted by the moon, you need to fire the engines in order to decrease your downward speed and land at a speed equal or lower than 1 m/s …

To run the game, once the code has been uploaded, open the console and either repeatedly use the “Enter” key from your keyboard or use the button that you will attach between pin 2 and ground (or pin 2 and 3 like I did).
Next is the code. Again, plain simple:

/* 
Lunar module landing game
Didier Longueville July 2019
*/

/* LEM variables */
const float _gPerFuelIUnit = 2.0;
const float _maxLandingSpeed = 3.4; 	/* eq to a freefall of 12 feet */
const float _initialSpeed = 10; 		/* m/s */
const int16_t _initialDistance = 1000;	/* m */
const uint8_t _initialFuelLevel = 150;	/* liters */
/* application variables */
float _speed;
int16_t _distance;
volatile uint8_t _fuelLevel;
uint8_t _lastFuelLevel;
/* timing variables */
const uint32_t _intervalMs = 500; 	/* in ms */
const float _intervalSec = (_intervalMs / 1000.0); /* in s */
uint32_t _lastTime; 				/* in ms */
/* constants */
float _lunarGravity = 1.62; 		/* lunar gravity m/(s*s)*/
/* hardware */
const uint8_t _interruptPin = 2;
const uint8_t _groundedPin = 3;
volatile bool _preset = false; /* this variable MUST be volatile because of its insertion with the ISR */


void interruptService(void)
{
	/* read button state and set state (GND true) */
	uint8_t state = (digitalRead(_interruptPin) == LOW);
	/* apply some debouncing using kind of flip-flop */
	if (state && !_preset)
	{
		_preset = 1;
	}
	else if (!state && _preset)
	{
		_preset = 0;
		if (_fuelLevel > 0) 
		{
			_fuelLevel -= 1;
		}
	}
}


void initGame(void)
{
	_speed = _initialSpeed;
	_distance = _initialDistance;
	_fuelLevel = _initialFuelLevel;	
	_lastFuelLevel = _fuelLevel;
}


void setup(void)
{
	Serial.begin(38400);
	Serial.println();
	initGame();
	/* init the "fuel trottle" pin */
	pinMode(_groundedPin, OUTPUT);	
	digitalWrite(_groundedPin, LOW);
	pinMode(_interruptPin, INPUT_PULLUP);	
	attachInterrupt(digitalPinToInterrupt(_interruptPin), interruptService, CHANGE);
	Serial.println("New landing...");
}

void loop(void)
{
	uint32_t now = millis();
	uint32_t elapsedTime = (now - _lastTime);
	if (elapsedTime >= _intervalMs) 
	{
		/* compute */
		uint8_t fuelConsumption = (_lastFuelLevel - _fuelLevel);
		float gamma = (_lunarGravity - (fuelConsumption * _gPerFuelIUnit));
		_distance -= (((0.5 * gamma) * (_intervalSec * _intervalSec)) + (_speed * _intervalSec));
		if (_distance < 0)
		{
			_distance = 0;
		}
		_speed += (gamma * _intervalSec);
		_lastFuelLevel = _fuelLevel;
		/* display variables */
		Serial.print("s: ");
		Serial.print(_speed, 1);
		Serial.print("   d: ");
		Serial.print(_distance);
		Serial.print("   f: ");
		Serial.print(_fuelLevel);
		Serial.println();
		if (_distance == 0)
		{
			if (_speed > _maxLandingSpeed)
			{			
				Serial.println(">>>>> CRASH ! <<<<<");
				while(true);
			}
			else
			{
				Serial.println("--- You made it ! ---");
				while(true);
				
			}
		}
		/* timing stuff: record last event time */
		_lastTime += _intervalMs;
	}
	if (Serial.available()) 
	{
		while (Serial.available()) 
		{
			Serial.read();
			if (_fuelLevel > 0) 
			{
				_fuelLevel -= 1;
			}
		}
	}
}

All in all, this code contains a few ideas for debouching without a timer (and thus not suspending operation to the button release) and timing the computing function.

Enjoy !

Date and time serial: how to bypass the lack of precision of floats

Data type is probably the first concern for any new programmer in c/c++. These language require in minimal knowledge about integers and floats (Check this thread). Although these data types look familiar to all educated people, the principle of significant figures, which should be the universal way of formatting data, is, most of the time, ambiguous, foggy, unsure, etc.. Pocket calculators are mainly responsible for this lack of understanding; at the time of slide rules, who would bother about the nth precision digit and carry these insignificant figures over a bunch of operations ? Nowadays, most students are happy to copy the full content of their display as the result of operations which lead to express physical measurements with quasi unit-less figures.

Back to to the world of 8 bits controllers, the range and precision of floats must be taken into account and results shall be carefully interpreted before releasing a code. As I was recently working on RTC drivers and applications, I faced the need for exporting time-stamped logged data to a PC based application.

The best idea I could find to express absolute date and time consists in using the “Microsoft” – nobody’s perfect – date and time serial. This float is made of an integer part which represents the number of days since the 1 Jan 1900 at 0:0:0 (date serial 1 because of the Lotus-1-2-3 bug which was agreed by Excel for compatibility reasons !); the fractional part is a fraction of 1 day, so as to say 1 s = (1 / 24 * 60 * 60). Using these rules, it is right now 43604.685417 and that’s a lot more precision than the float data type can handle. However, sticking to serial date and time makes a lot of sens as converting the serial in MS EXCEL or compatible spreadsheets such as LibreOffice Calc, is as easy as: year(A1) & “-” & month(A1) & “-” & day(A1) & ” ” & hour(A1) & “:” & minute(A1) & “:” & second(A1), with the serial data and time in cell A1.

The trick I used is rather simple: firstly, I recorded the date serial as a 32 bits integer (16 bits suffice if your are short in memory) and the time serial in a 32 bits float. Then I reconstruct the full data and time information for printing and exporting to an application which can handle doubles. Next is the simple function which concatenates both serials:

/*
concatenate a 32 bits integer with a fractional 32 bits float to print a date 
and time serial (iiiii.ffffff)
*/
void printDateAndTimeSerial(uint32_t date, float time)
{
	/* create char array buffer */
	char *ptrArray = (char*)malloc(11 * sizeof(char));
	/* convert the integer part in a char array */
	ptrArray = utoa(date, ptrArray, 10);
	/* create char array buffer for the float conversion */
	char *ptrFloat = (char*)malloc(8 * sizeof(char));
	/* convert the float in an char array */
	ptrFloat = dtostrf(time, 8, 6, ptrFloat);
	/* copy the fractional part of the float, including the decimal separator */
	memcpy(ptrArray + 5, ptrFloat + 1, 7);
	/* print result */
	Serial.print(ptrArray);
	/* free memory space */
	free(ptrArray);
	free(ptrFloat);
}

HTH