Random number generator (Part 1)

Part 1

As I was stumbling the web looking for advanced information on thermal noise, I found some very interesting papers on RNGs, aka Random Number Generators. These devices feature hardware components which are responsible for a generating unpredictable random numbers !

This is a very interesting matter as it involves physics, electronics, mathematics, software and … ultimately cryptography. Credit is due to people smarter than I am which publications can be found at the end of this post. The idea here is to described a surprisingly simple and yet powerful method for generating random numbers of various length, actually from 1 to 32 bits.

The principle of operation relys on the amplification of the signal produced on the base of a reverse-biased transistor. The emitter is saturated with electrons and occasionally they will “tunnel” through the band gap and exit via the base. This signal is then amplified through two transistors one of them behind biased through a decoupling capacitor in order to get rid of the DC component of the signal.

Here is the electronic section of the project: All you need is a trio of general purpose NPN transistors such as the popular 2N3904 a pair of capacitor and few resistors. The schematic is rather simple.

schematics

I brought a little refinement to the original design by feeding the amplifying stage with a stable +5 V. Note that both Vin and +5 V should be decoupled by 10 µF capacitors (Watch out the operating voltage of the capacitor attached to Vin !). Under these conditions, the circuit outputs a 0 to 1  V signal which fits very well the analog to digital conversion using the 1.1  V internal reference. Please note that 5 V will not suffice to create a reverse bias condition on the base-emetter junction of T1. I managed to get a stable running state starting with Vin = 9 V.

scope

I also brought few refinements to the original code originally written by Rob Seward (v1.0 dated 4/20/2009 ). Firstly, the analog read function is replaced by a custom one which is way faster, thanks to its 8 bits resolution, low prescaler and refined code. the SetADC() function must be inserted within the setup() part of the code

/* Set adc parameters for 8 bits resolution, fast mode */
void SetADC(uint8_t adcChannel) 
{
	/* Clear ADC control registers */
	ADCSRA = 0x00;
	ADCSRB = 0x00;
	ADMUX  = 0x00;
	// ADMUX |= (1 << REFS0); /* DEFAULT 5 V*/
	ADMUX |= (1 << REFS1) | (1 << REFS0); /* INTERNAL 1.1 V */
	ADMUX |= adcChannel; /* Set channel */
	ADMUX |= (1 << ADLAR); /* Left align adc value, so as to say 8 bits resolution */
	ADCSRA |= 3; /* Set ADC prescaler */
	ADCSRA |= (1 << ADEN); /* Enable ADC */
	ReadADC(); /* Run one mock read */
}

inline uint8_t ReadADC(void) 
{
	const uint8_t ADSCmask = (1 << ADSC);
	/* Start conversion */
	ADCSRA |= ADSCmask; 
	/* Wait for conversion */
	while (ADCSRA & ADSCmask);
	/* Read digital value */
	uint8_t result = ADCH;
	/* Returned value */
	return(result);
}

Please note that in spite of its higher sampling rate capacity (about 100 kHz), we are still far from optimal sampling rate (1 Mhz or better). However, that will be enough for an experiment.

I also brought few refinements to the application specific functions:

/* Porcess the input value, so as to say the adc reading */
void ProcessInput(uint8_t adcValue)
{
	/* Set default value */
	uint8_t input = 0;
	if (adcValue > _adcThreshold) {
		input = 1;
	}
	switch(_biasRemovalType){
	case VON_NEUMANN:
		VonNeumann(input); 
		break;
	case EXC_OR:
		ExclusiveOr(input);
		break;
	case NO_BIAS_REMOVAL:
		BuildRandomNumber(input);
		break;
	}
}

/* Apply exclusive or to pairs of bits (thanks to the flip flop flag) */
void ExclusiveOr(uint8_t input)
{
	static uint8_t flipFlop = 0;
	static uint8_t previousInput = 0;
	if (flipFlop) {
		BuildRandomNumber(previousInput ^ input);
	}
	previousInput = input;
	flipFlop = !flipFlop;
}


/* Apply Von Neumann to pairs of bits (thanks to the flip flop flag) */
void VonNeumann(uint8_t input)
{
	static uint8_t flipFlop = 0;
	static uint8_t previousInput = 0;
	if (flipFlop) {
		if (input & ~previousInput){
			BuildRandomNumber(0);
		} else if (~input & previousInput){
			BuildRandomNumber(1); 
		}
	}
	previousInput = input;
	flipFlop = !flipFlop;
}


/* Append new bit to the currently built random number */
void BuildRandomNumber(uint8_t input)
{
	/* Shift previous bits */
	_outputBuffer <<= 1UL;
	/* Append input bit as LSB */
	_outputBuffer |= (input & 0x01);
	/* Update bits counter */
	_bitsCounter += 1;
	if (_bitsCounter == _outputBits) {
		/* Reset bytes counter */
		_bitsCounter = 0;
		/* Output data */
		_mainCounter += 1;
		Serial.print(_mainCounter, DEC);
		Serial.print(';');
		switch(_outputFormat){
		case BINARY:
			Serial.print(_outputBuffer, BIN); 
			break;
		case DECIMAL:
			Serial.print(_outputBuffer, DEC);
			break;
		case HEXADECIMAL:
			Serial.print(_outputBuffer, HEX);
			break;
		}
		Serial.println();
		_outputBuffer = 0x00;
	}
}

/* Record occurences of adc values in their corresponding bins */
void SortAdcValue(uint8_t byteValue)
{
	/* Update occurences counter of the current adc vlaue */
	_vSorter[byteValue] += 1; 
	/* Update counter of sorted values */
	_sortedValuesCounter += 1;
}

/* 
Compute the adc threshold which is the median of the counts of occurences for 
each adc value void 
*/
void ComputeAdcThreshold(void)
{
	uint32_t sum = 0;
	/* Compute the sum of adc occurences in all bins */
	for (uint16_t i = 0; i < _sorterSize; i++){
		sum += _vSorter[i];
	}	
	/* Compute half the sum of all observations */
	uint32_t halfSum = (sum >> 1);
	/* Reset sum */
	sum = 0;
	/* Find the adc value corresponding to half the sum of occurences */
	_adcThreshold = 0;
	while (sum < halfSum) {
		sum += _vSorter[_adcThreshold];
		_adcThreshold += 1;
	} 
}

This is how the code was looking like when I performed early tests which were not so bad in the end !

Next picture illustrates the probability mass function (aka pmf) from 65536 random numbers ranging from 0 to 65535:

pmf

As you can see, the spectrum is pretty homogeneous, showing a fine spreading of random numbers over the expected range. Next is the FFT from the raw data:

fft

Once again, the spectrum is pretty homogeneous, showing no particular recurring pattern. Additional statistics show an average value of 32711.29 which turns to a -0.17 % error. The skewness (symetry of data versus average) is 0.003. All these statistics are very encouraging. However, I might have a use the Dieharder, “A Random Number Test Suite” which looks it is the perfect tool for checking randomness of numbers.

Next steps ? I think that I will pack this material in a library (Why not PlainRNG ?) and run some long term testing…

Links:

Trick of the day

This is a funny although very serious one: When you are looking for something very special, pretty complex such as papers on thermodynamics, astronomy, advanced electronics, etc. and you fail to find some information of interest, just insert the “arduino” word to your sequence of searched words.

From my own experience, the chances of finding the information of interest jumps to 90%.

Hum, great, just great  will you say, but “what about the 10% cases when it fails ?”

Well, the information found in these “failing” cases might just strike you as it did recently. I am more and more amazed by what the people manage to get from this insignificantly slow, rough, cheap piece of glass and silicon called arduino.

arduino316

Much more than just a piece of hardware and a bunch of lines of code, this is an awesome concept and the common denominator for a whole social network of makers. Long live arduino !

The word of the day …

Thank you Michel, a senior professor in economy with which we had a pleasant time last Sunday, for talking about Jugaad during talks about news paradigms in many domains.

I like very much this concept, and I ought to spend more time with Dr. Shiv Parakash Rathnam (alias Dr. Shiv) talking about this principle that I realize now he had in mind while we were sharing experiences about R&D.

This principle is very complementary to the Occam’s razor principle (aka law of parsimony) and adds the dimension of frugality which is very common in the world of Fab Labs or even startups.

Some time ago, I heard about the Foldscope (so amazing) without understanging that this was a perfect example of the Jugaad

Figure1-v1

Did you apply Jugaad recently ? I am looking forward to reading your comments soon !

Ultrasonic scanner (Part 2)

Part 1, 2

Most of the electronic diagram shown here should look familiar to the readers of arduinoos posts. The ultrasonic sensor is added to what was described as the test bench for stepper motors.

ultrasound-scanner_1

Here is the list of the required components:

  • An Arduino UNO baord (Should I introduce it to you ?)
    • A bread board featuring a switching voltage regulator
    • A stepper motor driver (in this case a A4988)
  • A stepper motor (Cheap 5 V, 1024 steps per turn stepper motor)
  • A 3D printed base plate and a 3D printed wheel
  • A 12 V DC power supply which current rating is compatible with the stepper motor (Do  not expect to feed the stepper through the USB 5 V!)
  • A HC-SR04 ultrasonic sensor.

Running the whole assembly requires some lines of code. This code features specific commands for the HC-SR04: any working library will do the job. The rest is a mix of stepper motor driving functions and some maths.

Next is an extract from the header section of the sketch:

/* Create objects */
/* Change parameters according to hardware configuration */
PlainHCSR04 SONAR; /* Create an instance of the SONAR object */
const uint32_t _oneSecond = 1000000; /* In us */
uint16_t _frequency = 500; /* Rotational speed */
const uint8_t _pulseDelay = 1; /* In us */
const uint16_t _motorStepsPerRound = 1024;
const float _sweepRad = (TWO_PI / 1.0); 
const float _sweepRadResolution = (TWO_PI / 90.0); 
const uint16_t _motorStepsPerSweepStep = uint16_t((_motorStepsPerRound * _sweepRadResolution) / TWO_PI);
const uint8_t _ultraSoundSensorSamples = 4;
const uint16_t _stabilizationDelay = 10; /* In ms */
/* Direction port and pin */
volatile uint8_t *_dirCtrlPort = &PORTB;
const uint8_t _directionPinMask = (1 << PINB0);
/* Pulse port and pin */
volatile uint8_t *_stepCtrlPort = &PORTB;
const uint8_t _stepPinMask = ( 1 << PINB1);  
/* Enable port and pin */
volatile uint8_t *_enablePort = &PORTB;
const uint8_t _enablePinMask = ( 1 << PINB2);  

Check some of the critical variables:

  • _frequency set the rotational speed
  • _motorStepsPerRound defines the number of steps per round (1024 in my case)
  • _sweepRad defines the full rotational swing for each scan
  • _sweepRadResolution defines the rotational angle between two distance measurements
  • _ultraSoundSensorSamples defines the number of samples acquired for each distance measurements: the more samples the better the accuracy, the longer the measurement time

The set section initializes the ultrasonic sensor and the stepper motor driver:

void setup(void) 
{
	Serial.begin(115200);
	SONAR.HCSR04Initialization(&PORTD, 6, 7);
	/* Set direction ports and pins */
	*(_enablePort - 1) |= _enablePinMask; /* EnableStepper pin */
	*(_enablePort) |= _enablePinMask; /* Disable driver */
	*(_dirCtrlPort - 1) |= _directionPinMask;
	*(_dirCtrlPort) |= _directionPinMask; /* Set default direction */
	*(_stepCtrlPort - 1) |= _stepPinMask; /* Set pin direction */
	*(_stepCtrlPort) &= ~_stepPinMask; /* Set default pin state to LOW */	
	while(Serial.available()) {
		Serial.read(); /* Empty buffer */
	}
}

And here is the loop section which is executed any time any character is entered on the console. The console outputs the data acquired during a whole scan:

void loop(void) 
{
	if (Serial.available()) {
		while(Serial.available()) {
			Serial.read(); /* Empty buffer */
		}
		EnableStepper(1);
		float totalSweepRad = 0.0;
		uint16_t sweepSteps = 0;
		/* Sweep forward */
		do {
			/* Wait for stabilization before acquiring data */
			delay(_stabilizationDelay);
			/* Acquire distance */
			float distance = (SONAR.Distance(_ultraSoundSensorSamples) / 1000.0);
			/* Rotate stepper forward to next sub division */
			RotateStepper(1, _motorStepsPerSweepStep);
			/* Compute angle and cartesian coordinates */
			float x = (sin(totalSweepRad) * distance);
			float y = (cos(totalSweepRad) * distance);
			/* Upload data */
			Serial.print(sweepSteps);
			Serial.print(';');
			Serial.print(totalSweepRad, 3);
			Serial.print(';');
			Serial.print(distance, 3);
			Serial.print(';');
			Serial.print(x, 3);
			Serial.print(';');
			Serial.print(y, 3);
			Serial.println();
			/* Update variables */
			sweepSteps += 1;
			totalSweepRad = (_sweepRadResolution * sweepSteps);
		} while (totalSweepRad <= _sweepRad);
		/* Sweep back */
		RotateStepper(0, (_motorStepsPerSweepStep * sweepSteps));
		EnableStepper(0);
	}
}

The console will output fields delimited with a semi-colon. The first field contains the measurement index. Next two fields are the polar coordinates (angle and distance) while the newt two fields contain the Cartesian coordinates (so as to say x and y). In this way, it is easy to build a graph out of the acquired data such as this one, using any spreadsheet editor:

scanner_6

Enjoy !

 

 

Ultrasonic scanner (Part 1)

Part 1, 2

And now, something completely different : here is arduinoos’s ultrasonic scanner !

scanner_11

This ultrasonic scanner results from the combination of two main components: an ultrasonic sensor and a stepper motor. Both have already been covered in this blog (here and there).

scanner_7

scanner_10

Both component form the head of the scanner which is screwed on top of a basic camera tripod thanks to additional mechatronic components.

These components are very popular and available at cheap prices on the web or straight from your scrap box. An Arduino platform, a voltage regulator and a stepper-motor driver are also required. Once again these are pretty cheap components that you may already have. Both the voltage regulator and the stepper motor driver will be plugged onto a bread board.

scanner_8

Both the arduino board and the bread board sit in a custom shell which fits to the stands of the tripod thanks to a pair of clips…

scanner_9

The principle of operation of this ultrasonic scanner is rather simple. The ultrasonic sensor is mounted directly on top of the shaft of the stepper motor. On each step pulsed to the stepper motor, the ultrasonic sensor performs one (or more) measurements. So that on each completed step, the scanner exports an angular value and a distance. Applying some math results in exporting x and y coordinates too. These data can be used to reconstruct an image of the solid objects surrounding the scanner.

scanner_6

As the stepper that I choose features 1024 steps, the code will manage to set larger angular resolution that the user may change according to his needs. Same for the full swing of the scan, the user may decide to sweep any range from 0 to 360°.

Although this scanner looks like it has been designed for building maps, it can be used for  measuring the actual sonic beam of the sensor too.

Next post on same subject

 

Publication of the day

Raymond McNamara from the NUI Galway committed his final year works to “Embedded Implementation of Embedded Algorithms of Power System Monitoring”

Raymond McNamara report

Quality of AC power is definitely a contemporary problem. Many disturbances are generated by the variety of power sources including, and the list is non-exhaustive, solar power, wind power, hydroelectric power. Harmonic frequencies in the power grid are a frequent cause of power quality problems. Harmonics in power systems result in increased heating in the equipment and conductors, misfiring in variable speed drives, and torque pulsations in motors. They may even lead to massive power failures on main distribution lines. So that power distributors have a great concern for analyzing these disturbances. and thus the need for advanced studies. Raymond’s work s about analyzing these disturbances and has been kind enough to mention arduinoos publications in his References section. Thanks Raymond.

You can get Raymond’s report >here<

Stepper Motors (Part 7)

Part 12, 3, 4, 5, 6, 7

This post is about driving stepper motors. In the previous posts I described the various components involved in motorized assemblies featuring stepper motors. However, putting all these nuts and bolts together may lead to unexpected results. In my case, these defects where related to the use of stepper motors taken from my scrap box, most of them lacking clear identification or appropriate specifications. Thus the need for building a test bench for testing these motors and giving them a chance to leave a second life !

Next picture illustrates the test bench which is made of (starting from right to left):

  • An Arduino UNO baord (Should I introduce it to you ?)
    • A bread board featuring a switching voltage regulator
    • A stepper motor driver (in this case a A4988)
  • A stepper motor (Cheap 5 V, 2048 steps per turn stepper motor)
  • A 3D printed base plate and a 3D printed wheel
  • A 12 V DC power supply which current rating is compatible with the stepper motor (Do  not expect to feed the stepper through the USB 5 V!)

20160926_085802

Next picture illustrates the wiring diagram.

test_bench

After multiple attempts to run stepper motors from my scrap box, I realized how critical might be the voltage applied to the stepper through its driver. Too much voltage leads to excessive magnetic fields and unpredictable motions at certain frequencies (f = 1 / time_between_2_consecutive_pulses_applied_to_the_driver); excessive voltage also drives to motor overheating. So that this test bench features a well known, cheap and yet powerful voltage regulator. This is precisely a “buck” switching regulator, “buck” means that it lowers the input voltage (as opposed to “boost”) and switching means that a MOSFET transistor acts as a fast on-off electronic tap which leaves a known amount of current flowing from the input to the output of the module. The benefit from such design (switching regulator) is the low power dissipation across the switch at even pretty high current (up to 3 A for this module). These modules are available for a few €/$, some of them  feature a voltage display which might be convenient for those who lack measuring instrumentation.

lm2576hv

Next is an also well known module. The A4988 contains all the bits and pieces necessary for driving almost any stepper motor (please check previous posts on same topic). In this configuration, three out of the multiple configuration pins are used: Enable, Step and Direction. These pin are connected to any port and pin from an Arduino UNO board (or compatible)

a4988

The test bench is driven by a quite simple application which uses the Arduino’s IDE console as a Human Interface. The list of available commands is available at run time after typing the “?” character. In this list, the available range for the arguments as well as the actual values are printed, offering one sort of a dashboard to the user.

/*

	Stepper motor test bench, Arduino UNO compatible
	Copyright (C) 2016 Didier Longueville

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
	
*/
 
/*
	KEYWORDS: stepper motor, A4988, enable, direction, frequency, step, pulse
*/


/* Direction port and pin */
volatile uint8_t *_dirCtrlPort = &PORTB;
const uint8_t _directionPinMask = (1 << PINB0);
/* Step port and pin */
volatile uint8_t *_stepCtrlPort = &PORTB;
const uint8_t _stepPinMask = ( 1 << PINB1);  
/* Enable port and pin */
volatile uint8_t *_enablePort = &PORTB;
const uint8_t _enablePinMask = ( 1 << PINB2);  
/* Variables and constants */
const uint32_t _oneSecond = 1000000; /* In us */
uint32_t _interval = 0;
uint32_t _lastStepTime;
uint16_t _frequency;
const uint8_t _pulseDelay = 1; /* In micros */
uint32_t _pulseCounter;
uint32_t _pulses = 0;
uint16_t _argument;
uint8_t _opcode;
uint8_t _enabled;
uint8_t _direction;
uint8_t _running;

void CommandProcessor(void) 
{
	if (Serial.available()) {
		while (Serial.available()) {
			char inputChar = Serial.read();
			if (inputChar == 0x3F) {
				PrintCommands();
				_opcode = 0;
			} else if ((inputChar >= 0x41) && (inputChar <= 0x5A)) { /* Alpha character */
				/* Probably an _opcode */
				_opcode = uint8_t( inputChar);
			} else if ((inputChar >= 0x30) && (inputChar <= 0x39) && (_opcode != 0)) { /* Num character */
				_argument *= 10; /* Exponent previous value */
				_argument += uint8_t(inputChar - 48); /* Add currrent value */
			} else if ((inputChar = 0x0A) || (inputChar = 0x0D)){ /* Cr Nl */
				switch (_opcode) {
				case 'D': /* Direction */
					_direction = _argument;
					if (_direction == 0) {
						*(_dirCtrlPort) &= ~_directionPinMask;
					} else if (_direction == 1) {
						*(_dirCtrlPort) |= _directionPinMask;
					}
					break;
				case 'E': /* Enabled */
					_enabled = _argument;
					if (_enabled == 0) {
						*(_enablePort) |= _enablePinMask;
					} else if (_enabled == 1) {
						*(_enablePort) &= ~_enablePinMask;
					}
					break;
				case 'F': /* Frequency */
					_frequency = _argument;
					_interval = (_oneSecond / _frequency); /* Compute interval */
					_lastStepTime = micros();
					break;
				case 'I': /* Intervals */
					_interval = _argument;
					_frequency = (_oneSecond / _interval); /* Compute frequency */
					_lastStepTime = micros();
					break;
				case 'P': /* Pulses */
					_pulseCounter = 0;
					_pulses = _argument;
					break;
				case 'R': /* running state */
					_running = _argument; 
					break;
				}
				if (_opcode) {
					Serial.print(char(_opcode));
					Serial.print(" : ");
					Serial.print(_argument);
					Serial.println();
				}
				_opcode = 0; /* Reset opcode */
				_argument = 0; /* reset argument */
			}
		}
	}
}

void PrintCommands(void)
{
	Serial.print(F("D [0:1]: direction ("));
	Serial.print(_direction);
	Serial.println(F(")"));
	Serial.print(F("E [0:1]: enable ("));
	Serial.print(_enabled);
	Serial.println(F(")"));
	Serial.print(F("F [1:32767]: frequency in Hz ("));
	Serial.print(_frequency);
	Serial.println(F(")"));
	Serial.print(F("I [1:65535]: interval in us ("));
	Serial.print(_interval);
	Serial.println(F(")"));
	Serial.print(F("P [0:65535]: pulses and then idle, 0=infinite pulses ("));
	Serial.print(_pulses);
	Serial.println(F(")"));
	Serial.print(F("R [0:1]: running ("));
	Serial.print(_running);
	Serial.println(F(")"));
	Serial.print(F("? : help"));
	Serial.println();
}


void setup(void)
{ 
	/* Console for diag */
	Serial.begin(115200);
	/* Set direction ports and pins */
	*(_enablePort - 1) |= _enablePinMask; /* Enable pin */
	*(_enablePort) |= _enablePinMask; /* Disable driver */
	*(_dirCtrlPort - 1) |= _directionPinMask;
	*(_dirCtrlPort) |= _directionPinMask; /* Set default direction */
	*(_stepCtrlPort - 1) |= _stepPinMask; /* Set pin direction */
	*(_stepCtrlPort) &= ~_stepPinMask; /* Set default pin state to LOW */
	/* Compute default interval */
	_enabled = 0;
	_running = 0;
	_frequency = 100;
	_interval = (_oneSecond / _frequency);
	_lastStepTime = micros();
}


void loop(void)
{
	CommandProcessor();
	/* Send step pulse to the dirver */
	if ((micros() - _lastStepTime) > _interval) {
		_lastStepTime += _interval;
		if (_running) {
			/* 
			Generate pulse: A low-to-high transition on the STEP input sequences 
			the translator and advances the motor one increment 
			*/
			*(_stepCtrlPort) |= _stepPinMask;
			delayMicroseconds(_pulseDelay); 
			*(_stepCtrlPort) &= ~_stepPinMask;
			/* */
			if (_pulses != 0) {
				_pulseCounter += 1;
				if (_pulseCounter >= _pulses) {
					_pulseCounter = 0; /* Reset counter */
					_running = 0; /* Reset running state */
				}
			}
		}
	}
}

Please note that running the stepper requires that the following steps are completed: set frequency or interval time as appropriate (default frequency is 100 Hz), enable the driver (“E1”) and set running state (“R1”). Why this distinction between enabling the driver and setting a running state ? When the driver is enabled and no pulses are sent, the stepper motor coils are still fed with some current. In this way, the rotor is locked in a rest position. As it is important to check this idle current both stages (enabled and running) are individualized.

It appeared to me that setting the most appropriate current limit required that multiple operating conditions had to be checked: setting various frequencies and checking the forward and reverse motion for example. The power supply voltage controller happened to be very useful for checking unmarked steppers and setting the optimal minimal voltage in order to decrease the dissipated temperature (almost none from the driver, most from the stepper motor itself).

Are you interested in the 3D printed base ? Get the .stl file >here<

HTH

User Interface (Part 5)

Part 1234, 5

I brought several improvements since the introduction of the early design of a simplified however yet powerful human interface. Among them the possibility to display variables that the user can change and variables or constants that the user cannot change. Thus the introduction of a new set of menu types:

/* Menu types */
const uint8_t MNU_TYP_HEADER = 0x00;
const uint8_t MNU_TYP_MOD_VALUE = 0x01;
const uint8_t MNU_TYP_FXD_VALUE = 0x02;
const uint8_t MNU_TYP_FLD_BACK = 0x03;

These new data types are the MNU_TYP_MOD_VALUE type  and the MNU_TYP_FXD_VALUE type.

The way the menuItem structure has been changed too in order to ease the readability of the whole menu structure. The new structure format looks like:

struct mnuItem { 
	uint8_t menuType; /* One of MNU_TYP_x */
	int16_t minValue; /* min value */
	int16_t maxValue; /* max value */
	uint8_t nextMenuIndex; /* Next menu item */
	uint8_t lastMenuIndex; /* Must be set to 0 as default */
	const char *ptrCap;  /* Pointer to menu ptrCap */
}; 

where:

/* 
menuType: 	one of MNU_TYP_xxx,
minValue: 	min value (Base0), 
maxValue: 	max value (Base0),
nextMenuIndex: 	next sub menu index (Base0),
lastMenuIndex:	last menu index, for returning to previous level (Base0),
ptrCap: 	pointer to caption 
*/

But the most significant change applies to the storage of arrays of characters (so as to say, strings). These memory consuming data are stored in the program memory instead of a storage in the SRAM. As their content keeps constant along the firmware usage, they are eligible to program memory storage. For your records, the SRAM memory is 2k wide in the UNO boards (featuring ATMEGA 328P micro-controllers) while the program memory is 32k wide, which is x16 times larger !

Storing and reading constant data in the program memory requires that the following steps are completed:

Declare pgmspace in the header section of the main code

#include <avr/pgmspace.h> /* Progmem */

Declare the arrays of characters in the header section: these are extracted from the unreleased MicroHTR application (heating element controller)

const char CAP_P_FIRMWARE[] PROGMEM =		"**  MicroHTR  **";
const char CAP_P_VERSION[] PROGMEM =		"    rev. 1a";
/* Application related constants */
const char CAP_P_SP[] PROGMEM = 		"SET POINT";
const char CAP_P_KP[] PROGMEM = 		"KP";
const char CAP_P_KI[] PROGMEM = 		"KI";
const char CAP_P_KD[] PROGMEM = 		"KD";
const char CAP_P_INT[] PROGMEM = 		"INTERVALS";
const char CAP_P_STATISTICS[] PROGMEM = 	"STATISTICS";
const char CAP_P_MISCELLANEOUS[] PROGMEM =	"MISCELLANEOUS";
const char CAP_P_TEMPERATURE[] PROGMEM = 	"TEMPERATURE";
const char CAP_P_MAX_TEMP[] PROGMEM = 		"TEMP. MAX.";
const char CAP_P_MIN_TEMP[] PROGMEM = 		"TEMP. MIN.";
const char CAP_P_RESET[] PROGMEM = 		"RESET";
const char CAP_P_SETTINGS[] PROGMEM = 		"SETTINGS";
const char CAP_P_PRN_DATA[] PROGMEM = 		"PRINT DATA";
/* Captions, reserved words for menu driven interface (do not change) */
const char CAP_P_PARAM[] PROGMEM = 		"PARAMETERS";
const char CAP_P_EXIT[] PROGMEM = 		"EXIT";
const char CAP_P_RETURN[] PROGMEM = 		"RETURN";

 

and here is the structure for the menu:

struct mnuItem vMnuItems[] =	
{ 
/* 	{type, 				min, 		max,		next,	last, 	caption}*/
	{MNU_TYP_HEADER,	X, 			X, 			X,		X,		CAP_P_FIRMWARE}, 				
	{MNU_TYP_HEADER,	0, 			3, 			2, 		X,		CAP_P_PARAM},
	{MNU_TYP_HEADER,	0, 			5, 			6, 		X, 		CAP_P_SETTINGS}, /* Main menu */
	{MNU_TYP_HEADER,	0, 			3, 			12,		X, 		CAP_P_STATISTICS},
	{MNU_TYP_HEADER,	0, 			1, 			16,		X, 		CAP_P_MISCELLANEOUS},
	{MNU_TYP_FLD_BACK, 	X, 			X, 			0,		X, 		CAP_P_EXIT},
	{MNU_TYP_MOD_VALUE,	MIN_SP,		MAX_SP,		2,		X, 		CAP_P_SP}, /* Settings */
	{MNU_TYP_MOD_VALUE,	MIN_KP,		MAX_KP,		2,		X, 		CAP_P_KP}, 
	{MNU_TYP_MOD_VALUE,	MIN_KI,		MAX_KI,		2,		X, 		CAP_P_KI}, 
	{MNU_TYP_MOD_VALUE,	MIN_KD,		MAX_KD,		2,		X, 		CAP_P_KD}, 
	{MNU_TYP_MOD_VALUE,	MIN_INT, 	MAX_INT,	2, 		X, 		CAP_P_INT}, 
	{MNU_TYP_FLD_BACK,	X, 			X, 			1, 		X, 		CAP_P_RETURN},
	{MNU_TYP_FXD_VALUE, MAX_TEMP, 	MIN_TEMP, 	3,		X, 		CAP_P_MIN_TEMP}, /* Statistics */
	{MNU_TYP_FXD_VALUE, MAX_TEMP, 	MIN_TEMP, 	3, 		X, 		CAP_P_MAX_TEMP},
	{MNU_TYP_MOD_VALUE, NO, 		YES, 		3,		X, 		CAP_P_RESET},
	{MNU_TYP_FLD_BACK,	X, 			X, 			1, 		X, 		CAP_P_RETURN},	
	{MNU_TYP_MOD_VALUE, NO, 		YES,		4,		X, 		CAP_P_PRN_DATA}, /* Miscellaneous */	
	{MNU_TYP_FLD_BACK,	X, 			X, 			1, 		X, 		CAP_P_RETURN}	
};

Within the menu handler routine, the lines

LCD.PrintArrayOfChar(vMnuItems[menuIndex].ptrCap, 1); /* Display menu ptrCap on first line */

LCD.PrintArrayOfChar(vMnuItems[vMnuItems[menuIndex].nextMenuIndex + counts].ptrCap, 2); /* Display menu caption */

become

LCD.PrintArrayOfChar(P(vMnuItems[menuIndex].ptrCap), 1); /* Display menu ptrCap on first line */

LCD.PrintArrayOfChar(P(vMnuItems[vMnuItems[menuIndex].nextMenuIndex + counts].ptrCap), 2); /* Display menu caption */

thanks to this little function:

char* P(const char* ptr) 
{	
	strcpy_P(vCapBuffer, ptr);
	return(vCapBuffer);
}

In this way, you will be able to save hundreds of bytes from the SRAM which is good news to the Arduino’s developers.

Problem solving

/* No comments */

problem_solving

Tips and Tricks (Part 27)

Previous T&T

This T&T deals with the compression of numerical data. Using the proposed algorithm, you will be able to compress a 16 bits unsigned integer (so as to say an uint16_t or an unsigned int data type). into an 8 bits unsigned integer (so as to say an uint8_t or an unsigned char data type). How come ?

Well, this algorithm takes advantage of the principle of operation of integer to float conversion. On completion of the compression algorithm, we get an 8 bits integer from which the 4 most significant bits (msb) contain the exponent and the 4 least significant bits (lsb) contain the mantissa. This arrangement has a drawback: the loss of precision. Next figure illustrates the errors which result from of a compression/decompression cycle:

error_plot

These errors (up to 6%) are the price to pay for compressing data ranging from 0 to 65.535 down to a small compact integer ranging from 0 to 255 that uses only 2 bytes of memory…

Next diagram illustrates the way compression works

compress

and this one illustrates the way decompression works

decompress

These diagrams are inspired by the Application Note AN498 from Silicon Labs, related to the Si114X light sensor.

Next is the code containing the compress and the decompress functions:

/* Compress an unsigned 16 bits integer into an unsigned 8 bits integer */
uint8_t Compress(uint16_t data)
{
	uint8_t res;
	if (data == 0) {
		res = 0x00;
	} else if (data == 1) {
		res = 0x0F;
	} else {
		uint8_t exponent = 15;
		while((data & 0x8000) != 0x8000){
			data <<= 1; /* Shift all bits one step to the left */
			exponent -= 1;
		}
		uint8_t msb = exponent;
		/* Right align and mask the 4 fraction bits  */
		uint8_t lsb = ((data >> 11) & 0x0F);	
		/* Compute result */
		res = ((msb << 4) | lsb);
	}
	/* Returned value */
	return(res);	
}

/* Uncompress an unsigned 8 bits integer into an unsigned 16 bits integer */
uint16_t Uncompress(uint8_t data)
{
	/* Extract bits */
	uint8_t lsb = (data & 0x0F);
	uint8_t msb = ((data >> 4) & 0x0F);
	/* Compute value */
	float fraction = ((float)lsb / 16.0);
	if (msb != 0) {
		/* Applies to compressed 0s and 1s */
		fraction += 1.0;
	}
	float exponent = (1UL << msb);
	uint16_t res = round(exponent * fraction);
	/* Returned value */
	return(res);
}

Note: Please check carefully the way the compressor manages the “0” and “1” values which have special behaviors in the domain of multiplication.

Enjoy !