Stepper Motors (Part 9)

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

Let’s go back with an easier application. Still for my friend Valerio, the puppets magician, who built this adorable model-phonograph:

It is all wooden made and powered by… a stepper motor, using the electronics which has been described in previous post and showed again here

A switch is attached to the needle’s arm

The code had to match the following expectations:

  • The turn table should slowly start when the needle leaves its rest position
  • The turn table should reach its nominal speed and stay there has long as the needle is above  the record.
  • When the needle goes back to the rest position, then the turn table should slowly slowdown, down to a minimal speed and  then stop.
  • The turn table should re-accelerate if the needle goes to the rest position and back on top of “record”.

Here is the result of our long lasting fab-lab session…

/*
Basic stepper motor driver
Used in the context of Valerio's magic phono
- At rest, the swicth is released the motor is stopped
- When the switch is activated, the motor starts and constantly gains speed up 
  to its nominal speed
- If the siwtch is released, whatever the speed of the motor, the motor is 
  slow down and stops
- If the switch is actived again while the motor has not stopped, the motor is 
  accelerated again up to its nominal speed
  
  Didier Longueville 2017
*/

/* User defined variables */
/* Pins configuration */
const uint8_t _pinDir = 2;
const uint8_t _pinStep = 4;
const uint8_t _pinSwitch = 8;
/* Timing constants */
int16_t _pulsesWidthStop = 7000;
int16_t _pulsesWidthStart = 5000;
int16_t _pulsesWidthMaxSpeed = 2000;
uint8_t _pulsesIncrements = 3;
uint8_t _pulsesDecrements = 3;

/* Application variables */
/* Timing variables */
uint32_t _now;
uint32_t _lastTime;
/* motor state */
const uint8_t STOPPED = 0;
const uint8_t UNSTABLE = 1;
const uint8_t STABLE = 2;
uint8_t _motorState = STOPPED; 
/* switch state */
const uint8_t REST = LOW;
const uint8_t PLAY = HIGH;
uint8_t _switchState; 
/* Step pulses */
int16_t _pulsesInterval;

void setup(void)
{
	/* Configure pins */
	/* Direction */
	pinMode(_pinDir, OUTPUT);
	digitalWrite(_pinDir, LOW);
	/* Step */
	pinMode(_pinStep, OUTPUT);
	digitalWrite(_pinStep, LOW);
	/* Switch */
	pinMode(_pinSwitch, INPUT_PULLUP);
}


void loop(void)
{
	do {
		/* read switch state */
		_switchState = digitalRead(_pinSwitch);
		/* record now */
		_now = micros();
	} while ((_now - _lastTime) < _pulsesInterval);
	_lastTime = _now;
	/* Interpret switch state according to motor state */
	if (_switchState == REST) {
		if (_motorState != STOPPED) {
			/* Slow down motor speed by increasing intervals between pulses */
			_pulsesInterval += _pulsesIncrements;
			/* Force motor state */
			motorState = UNSTABLE;
			if (_pulsesInterval > _pulsesWidthStop) {
				/* motor has reached its minimal speed before stopping */
				_motorState = STOPPED;
			}
		} 
	} else if (_switchState == PLAY) {
		if (_motorState == STOPPED) {
			/* Set default pulse width for motor start */
			_pulsesInterval = _pulsesWidthStart;
			/* Set motor state */
			_motorState = UNSTABLE;
		} else if (_motorState != STABLE) {
			/* Speed up motor by decreasing intervals between pulses */
			_pulsesInterval -= _pulsesDecrements;
			if (_pulsesInterval < _pulsesWidthMaxSpeed) {
				// Motor has reached its stationary speed
				_motorState = STABLE;
				_pulsesInterval = _pulsesWidthMaxSpeed;
			}
		}
	}			
	/* Send pulse */
	if (_motorState != STOPPED)	{
		/* generate positive pulse for at least 1 us */
		digitalWrite(_pinStep, HIGH);
		delayMicroseconds(2);
		digitalWrite(_pinStep, LOW);		
	}
}

For a change, it features standard pin control functions and standard timers for bit-bang control of pulses.

Are you interested in Valerio’s performances ? Here are his contact information:

Mue – Valerio Point
Association Mue Marionnettes
Mairie – 86600 Celle l’Evescault
+33 6 20 39 23 11
contact@muema.org
www.muema.org

 

To blog or not to blog ?

Blogs are wonderful tools that are very convenient for sharing all sorts of information without the burden of writing code an spending time on site maintenance (Unless you want to fine tune your style, add fancy animations or perform advanced functions). This is an almost trivial statement for those who started publishing on the net a few years ago, but believe me, in the early 90’s, running a web site was not as easy as it is now. Starting from the installation an Internet connection which would require a bunch of ten 3.5″ floppy disks. In these years I was committed to invest this emerging media by my boss and I spent some time investigating the technical means as well as the way information could be shared.

Thanks to a pretty efficient telephone networks, the French PTT (Post Telegraph and Telephon administration) developed the Minitel, an innovative network of data terminals.

minitel

In the very beginning, the Minitel was mainly used for its Yellow Pages application. Soon followed by commercial applications and inevitably by X rated applications which turned the Minitel to the “Pink Minitel”. Internet emerged approximately at the same time and I seriously wondered whether or not Internet would soon or later turn to a “Pink Internet”. Would Internet be a good place for advertising serious business ? Would web sites be a convenient tool for sharing information ? I managed to meet a local pioneer in the Internet world and we had a long and very interesting talk. In the end, Sébastien Canevet told me “Well, Internet will look like what people will feed it with. The more the bul…it, the worst the Internet’s content. The better the content, the coolest the result”.

As a result of this clear statement, I built my first web site for my company as well as my first private web site. I do not pretend that what I published was top of best, a worldwide reference for the next four generations of webmasters, nope, just plain information, honest, usable and understandable by most. I did not deviate that much from my original vision on web sites and I hope that arduinoos reflects this claim. From the statistics that are recorded from some web sites that I run, this policy seems to be fair, well accepted and even popular. That’s good news for those who would like to start their own blog or web site. Here are a few guide lines that I may recommend to follow:

  • Choose an explicit name for this site and check the availability of the corresponding domain.
  • Register your domain and get your own storage area. Free space has never be so free !
  • Create a specific eMail addresses linked to the domain.
  • Create the structure of your blog: WordPress proves to be a fine one.
  • Create a visual signature: logo, banner, font, colors.
  • Create a contact page; you may also create a “who am I” page.
  • Create the first few posts. Each of these pages shall contain an explicit title, text, some illustrations (do not forget to give credit whenever applicable); use hyperlinks to create cross-referencing and add credibility.
  • Post publications periodically: every week, twice a month, monthly it is up to you. It is a good idea to keep a few posts on the back burner to achieve this goal.
  • Always approve comments from visitors. Use an authentication tool (so as to say a captcha) in order to prevent spam.
  • Using a counter and statistics plugin will help you to understand who is paying attention to your publications and what the like to see and read. These statistics will help you in focusing on specific matters or visitors.

And now a few words about the content. Write what you like, what you are (almost) sure about, what may be of some interest to others. Keep in mind that every day, every hour someone in the world is using Internet for the first time. I am almost sure that every hour, someone is told about arduino and is currently seeking for information on the net. This person is not looking for advanced information (he will shortly though!). All (s)he needs is a starting point which is usable, credible and fun. Achieving this goal requires more communication skills than technical background. Then you may decide to go for something more specialized, based on your qualification, experience or expertise. In this way your blog may gain reputation because of its artistic content, its technical content, its innovative applications, its informative content, …. the list is not exhaustive.

I also have a strong concern for publishing or not papers on how to… build a detonator, a weapon, … , all these sorts of things that would please the villains so much. In the end, it is good to feel that most clever girls and boys are good girls and good boys. Other wise, we could not live this world.

 So, get ready for new adventures, feel free to start your own communication spot and feed the internet with constructive matter.

Undocumented commands and functions

Long time ago as I was learning about George Boole  algebra I immediately though about using a (hidden) sequence of button presses to lock and unlock safes, doors, etc.  In addition to setting arbitrary sequences of button presses, I had the idea of taking into account the time spent on pressing each button which would have greatly improved the safety of the device. As I had so little to hide away from the villains eyes, my motivation faded away and so did the project for the unlimited door lock.

Although this approach looked very trivial, it is still a very valid approach to hiding specific functions or commands from ordinary users, vulgum pecus so as to say. As I was struggling with my ol’brave HP3050 printer (HP3050A  J611 Series to be precise), I tumbled on the web and found that undocumented functions were hidden and only available through a sequence of keystrokes (actually X sign , return sign, X sign twice and you’re done), just the good’ol way !

And that’s definitely the way I would approach the setting of parameters for any embedded system. To the cost of few pins and a bunch of bytes of code.

Stepper Motors (Part 8)

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

This post is about driving multiple stepper motors continuously sweeping various ranges of steps at various speeds. Each motor features a permanent sinusoidal motion (accelerated and decelerated). The complexity of the code lies in the next requirements: Each motor has its own sweeping frequency, its own full swing range (number of steps explored on each cycle). It must be possible to introduce a phase shift between each motor, they must stay synchronized over long periods of time and ultimately you may change the settings at any time.

That was a challenge ! The most confusing part of the project lies in the need for thinking in reverse mode. In this case, we do not have fixed intervals of time so we need to define how much time must elapse between to steps. Let’s put it differently: drawing a sin curve on a spreadsheet usually requires fixed time intervals on the x axis and computed ordinates for the y axis using this sort of formula: y = sin((x * frequency  * 2 * Pi())). Computing x from y requires the use of the inverse of sin function: sin^-1 = asin (arc sine).

The code below encapsulate the Interval function which is used for computing the time after which the next step will be issued by the stepper motor driver. Note that all direction pins are wire together whatever the number of drivers, while pulse must be directed to individual drivers (as per the _vStepPins vector)

#include <math.h>

const uint8_t DIR_CW = LOW;
const uint8_t DIR_CCW = HIGH;
const float _twoPi = 6.283185307179586476925286766559;

/* User settings. See explanations below */
/* _directionPin: this is the arduino pin to which the direction pins from the stepper drivers are
attached. Numbering is as per arduino standard pin namming convention */
const uint8_t _directionPin = 8;
/* _steppers: number of stepper motors. Check next vector contents accordingly */
const uint8_t _steppers = 4;
/* _vSteps: number of steps required to explore the full swing (half the _vBnFSteps) */
const uint16_t _vSteps[_steppers] = {200, 100, 50, 25};
/* _vStepPins: these are the arduino pins to which the step pins from the stepper drivers are 
attached. Numbering is as per arduino standard pin namming convention */
const uint8_t _vStepPins[_steppers] = {9, 10, 11, 12};  
/* _vFrequency; Frequency (in Hz) of sinusoidal motion. Each step interval is computed */
const float _vFrequency[_steppers] = {0.5, 1.0, 2.0, 4.0}; 
/* _vPhaseShift: Phase shift in ms versus the absolute start time which is identical for all tepper 
motors. This value is self constrained. if f = 1 Hz, the phase shift might range from 0 to 1000 ms. 
A 500ms value corresponds to 1 x Pi (so as to say half a cycle) phase shift. */
uint16_t _vPhaseShift[_steppers] = {0, 0, 0, 0}; 
/* Do not change next vectors and variables */
uint16_t _vBnFSteps[_steppers]; /* Twice the steps */
uint16_t _vHalfSteps[_steppers];  /* Half the steps */
int16_t _vStepsCounter[_steppers]; 
uint32_t _vNextStepTime[_steppers];
uint32_t _vLoopsCounter[_steppers];
uint32_t _vStartTime[_steppers];
const uint16_t _startTimeOffset = 100; /* In milli seconds */
const uint8_t _pulseDelay = 2; /* In micro seconds */

void setup(void)
{ 
	/* Console for diag */
	Serial.begin(115200);
	Serial.println("Ready");
	/* Direction pin */
	pinMode(_directionPin, OUTPUT);
	digitalWrite(_directionPin, DIR_CW); /* Set default direction */
	/* Step pins */
	for (uint8_t i = 0; i < _steppers; i++) {
		pinMode(_vStepPins[i], OUTPUT); /* Set direction */
		digitalWrite(_vStepPins[i], LOW); /* Set default state */
	}	
	uint32_t now = millis();
	for (uint8_t i = 0; i < _steppers; i++) {
		_vBnFSteps[i] = (_vSteps[i] << 1); /* Precalculate data */
		_vHalfSteps[i] = (_vSteps[i] >> 1); /* Precalculate data */
		_vStepsCounter[i] = 0; /* Reset steps counter */
		_vPhaseShift[i] %= uint16_t(1000.0 / _vFrequency[i]); /* Constrain phase shift */
		_vStartTime[i] = (now + _vPhaseShift[i]); /* Compute start time */
		_vStartTime[i] +=_startTimeOffset;
		_vNextStepTime[i] = (_vStartTime[i] + Interval(i)); /* Set next step time */
		_vLoopsCounter[i] = 0; /* Reset loop counter */
	}
}

void loop(void)
{
	uint32_t now = millis();
	for (int8_t i = 0; i < _steppers; i++) {
		if (now > _vNextStepTime[i]) {
			/* Set direction */
			int8_t dir;
			if (_vStepsCounter[i] < _vSteps[i]) {
				dir = DIR_CW;
			} else {
				dir = DIR_CCW;
			}
			digitalWrite(_directionPin, dir);
			/* Generate pulse */
			digitalWrite(_vStepPins[i], HIGH);
			delayMicroseconds(_pulseDelay); /* Minimal pulse delay */
			digitalWrite(_vStepPins[i], LOW);
			/* Increment step and constrain computed step value */
			_vStepsCounter[i] += 1; 
			if (_vStepsCounter[i] >= _vBnFSteps[i]) { 
				_vLoopsCounter[i] += 1; /* Increment loop counter */
				_vStepsCounter[i] = 0; /* Reset steps counter */
				_vNextStepTime[i] = (_vStartTime[i] + int32_t(_vLoopsCounter[i] * (1000.0 / _vFrequency[i])));
			} else {
				_vNextStepTime[i] = (now + Interval(i));
			}
		}
	}	
}

uint16_t Interval(uint16_t stepper)
{
	/* Compute next stepper time */
	int16_t i0 = ((_vStepsCounter[stepper] % _vSteps[stepper]) - _vHalfSteps[stepper]);
	int16_t i1 = (i0 + 1);
	float y0 = ((float)i0 / _vHalfSteps[stepper]);
	float y1 = ((float)i1 / _vHalfSteps[stepper]);
	int16_t result = (int16_t)(((asin(y1) - asin(y0)) * 1000.0) / (_twoPi * _vFrequency[stepper]));
	/* Returned value */
	return(result);
}

Next post on same subject

What a shame !

I recently reactivated my Facebook account in order to express my opinions during the last campaign for the election of the French Republic President. Well, I am not naive and I know by experience (I have been myself a candidate for the house of Parliament) that a campaign may be (very) trash. However, what struck me this time is how stupidity can spam the information shared by people from this “social” network. I deactivated my account right after reading and absolutely disgusting mess of slurs poured on Cedric Villani, one of the most brilliant minds of this time. How come absolute nerds write such bullshit on someone they do not really know, probably never heard and that will surely never ever read ? How come citizen can blame a scientist for his passion for spiders just because he pretends to represent the people from his place and share his vision on developing scientific culture  ?

I would not be surprise that none of these persons have the least idea about how computer protection works and how thankful they ought to be to the mathematicians who’s science is right behind our encryption algorithms. It is likely that without their help, we would all be naked in front of hackers these days.

This episode should remind us about the nasty declaration “Whenever I hear the word ‘culture’…I release the safety on my pistol!.”. How far are we from adding the word ‘science’ to the dictionary of rude men and women ?

Simple command parser (Part 2)

Part 1, 2

In the previous version, the command parser is using one single alpha character as an opcode while the argument is computed on the fly using simple arithmetic. In this version, the opcode is extended to (almost) any number of characters and the argument is stored in a vector which is then converted into integers or floats using the iota and itof functions. Please note that the use of itof leads to the same limitations than in the previous version. Also, because the opcode has no more fixed length, a separator must be used in between the opcode and the argument. The example given below makes use of either the ‘:’ character or a space character.

Next is a new set of opcodes. They are more explicit however they require more typing efforts.

const char OPC_NONE[] = "NONE";
const char OPC_BLINKS[] = "BLK";
const char OPC_ACTIVATED[] = "ACT";
const char OPC_ON_RATIO[] = "ONR";
const char OPC_PERIOD[] = "PER";
const char OPC_LST_PARAM[] = "LST";
const char OPC_RST_PARAM[] = "RST";
const char OPC_SAV_PARAM[] = "SAV";
const char OPC_ERROR[] = "ERR";

And here is parsing code

void ParseUartData(void)
{
	if (Serial.available() > 0) {
		/* As long as data is available from the com port */
		while (Serial.available() > 0) {
			/* Set variables to their default values */
			bool delimiterDetected = false;
			char vOpcode[8];
			uint8_t opcodeChars = 0;
			vOpcode[0] = '\0';
			char vArgument[12];
			uint8_t argumentChars = 0;
			/* While cr or nl characters are not met */
			while (true) {
				/* Read next char */
				char dataIn = Serial.read();
				/* Interpret character */
				if ((dataIn == 13) || (dataIn == 10)) {
					break;
				} else  if ((dataIn == ':') || (dataIn == ' ')) {
					/* Delimiter */
					delimiterDetected = true;
				} else if ((dataIn >= 'A') && (dataIn <= 'Z')) {
					/* Parse opcode */
					if (!delimiterDetected) {
						vOpcode[opcodeChars] = dataIn;
						opcodeChars += 1;
					} else {
						vOpcode[0] = '\0';
						break;						
					}
				} else if (((dataIn >= '0') && (dataIn <= '9')) || (dataIn == '.')) {
					/* Parse argument */
					if (delimiterDetected) {
						vArgument[argumentChars] = dataIn;
						argumentChars += 1;
					} else {
						vOpcode[0] = '\0';
						break;						
					}
				} else {
					/* Unexpected character */
					vOpcode[0] = '\0';
					break;					
				}
				delay(1);
			}
			/* Append termination characters */
			vArgument[argumentChars] = '\0';
			vOpcode[opcodeChars] = '\0';
			/* Convert vector */
			/* 
			If the vector contains a decimal separator, only the integer part 
			of the floating point number is returned
			*/
			int32_t intArgument = atol(vArgument);
			/* The vector may not contain a decimal separator */
			float fltArgument = atof(vArgument);
			/* Parse opcodes */
			if (vOpcode[0] != '\0') {
				/* Uncomment for Debug */
				// Serial.print("opcode: ");
				// Serial.print(vOpcode);
				// Serial.print(", argument: ");
				// Serial.print(intArgument);
				// Serial.print(" (");
				// Serial.print(fltArgument, 6);
				// Serial.print(")");
				// Serial.println();	
				if (strcmp(OPC_ACTIVATED, vOpcode) == 0) {
					Serial.print("Turn led ");
					if (intArgument == 1) {
						*(_ledPort) |= _ledPinMask; 
						Serial.println("on");
					} else if (intArgument == 0) {
						*(_ledPort) &= ~_ledPinMask; 					
						Serial.println("off");
					}
					_blinks = 0;		
				} else if (strcmp(OPC_BLINKS, vOpcode) == 0) {
					Serial.print("Blinks = ");
					Serial.println(intArgument);
					_blinks = intArgument;
					/* Reset blink counter */
					_blinkCounter = 0;
				} else if (strcmp(OPC_ON_RATIO, vOpcode) == 0) {
					Serial.print("On/off ratio = ");
					Serial.println(fltArgument, 2);
					_onRatio = fltArgument;	
				} else if (strcmp(OPC_PERIOD, vOpcode) == 0) {
					Serial.print("Period = ");
					Serial.println(intArgument);
					_period = intArgument;
				} else if (strcmp(OPC_LST_PARAM, vOpcode) == 0) {
					PrintParameters();
				} else if (strcmp(OPC_RST_PARAM, vOpcode) == 0) {
					Serial.print("Reading parameters... ");
					GetDefaultParameters(bool(intArgument));
					Serial.println("done");
				} else if (strcmp(OPC_SAV_PARAM, vOpcode) == 0) {
					Serial.print("Saving parameters... ");
					SetDefaultParameters();
					Serial.println("done");
				} else if (strcmp(OPC_ERROR, vOpcode) == 0) {
					/* Report error */
				}
				/* Update subsidiary variables */
				_onDuration = (_period * _onRatio);
				_offDuration = (_period - _onDuration);
			}
		}
	}
}

You get the point now: variable length opcodes allow an almost unlimited choice of explicit commands which might be very useful in complex applications.

However, and once again, the aim of this series of post is to provide you with a simple yet powerful starting point with command parsing. Advanced application may require much more rugged transactions, including checksum or CRC (cyclic redundancy check) and data descriptors for example.

HTH

Simple command parser (Part 1)

Part 1, 2

Many applications require some interaction with the user. And one of the most basic manner to interact with an application is to use the serial communication port. Using Serial.print command is probably one the earliest command we all used to debug applications. The proposed command parser uses Serial.available() and Serial.read() functions and few lines of code.

As usual, I tried to make the code as simple and explicit as possible. It does not use additional libraries or tricky functions. This principle of operation is illustrated by the following code, which might be called “The ultimate blink sketch” or “Why make things simple when you can make them complex?”. The idea is to interact with the led blinking process by setting the blink period and the on/off ratio. Additional commands will blink the led, turn it steady on or off.

Instructions are passed to the application using opcodes and arguments. As it is easier to memorize a letter – typically the initial of an instruction – opcodes are single characters as proposed below:

const uint8_t OPC_NONE = 0x00;
const uint8_t OPC_BLINKS = 'B';
const uint8_t OPC_ACTIVATED = 'A';
const uint8_t OPC_ON_RATIO = 'O';
const uint8_t OPC_PERIOD = 'P';
const uint8_t OPC_LST_PARAM = 'L';
const uint8_t OPC_RST_PARAM = 'R';
const uint8_t OPC_SAV_PARAM = 'S';
const uint8_t OPC_ERROR = 'E';

The argument can be a signed integer or a floating point value. So that setting the blink duration for 1000 ms will result from the D1000 instruction. On the other hand, setting the on/off ratio to 10/90% will result from the O0.1 instruction. Note that D 1000 or O 0.1 will work too.

Parsing the instructions is performed as per the following sequence:

  • Read available characters up to the carriage return and/or line feed characters. Alphabetical characters are recognized as opcodes while numerical characters are recognized as numerical arguments.
  • Apply some basic math to format the argument properly.
  • Parse opcodes and set variables using the formatted argument values

The whole code sits in one single routine which is to be periodically executed.

void ParseUartData(void)
{
	if (Serial.available() > 0) {
		/* As long as data is available from the com port */
		while (Serial.available() > 0) {
			/* Set variables to their default values */
			uint8_t opcode = 0;
			bool opcodeDetected = false;
			int32_t intArgument = 0;
			float fltArgument = 0.0;
			bool negative = false;
			uint32_t argumentDivisor = 1;
			bool decSepDetected = false;
			/* While cr or nl charaters are not met */
			while (true) {
				/* Read next char */
				uint8_t dataIn = Serial.read();
				/* Interpret character */
				if ((dataIn == ' ')) {
					/* Spaces are accepted */
				} else if ((dataIn == 13) || (dataIn == 10)) {
					break;
				} else if ((dataIn >= 'A') && (dataIn <= 'Z')) {
					/* Parse opcode */
					if (!opcodeDetected) {
						opcode = dataIn;
						opcodeDetected = true;
					}
				} else if ((dataIn == '-')) {
					/* Parse sign */
					if (opcodeDetected) {
						if (intArgument == 0) {
							negative = true; /* Set sign */
						} else {
							/* Improper sign location */
							opcode = OPC_ERROR;
							break;
						}
					}
				} else if ((dataIn == '.')) {
					/* Parse decimals */
					if (opcodeDetected) {
						if (!decSepDetected) {
							decSepDetected = true;
						} else {
							/* Improper decimal separator location */
							opcode = OPC_ERROR;
							break;
						}
					}
				} else if ((dataIn >= '0') && (dataIn <= '9')) {
					/* Parse argument */
					if (opcodeDetected) {
						/* Shift previous value */
						intArgument *= 10;
						/* Add data to previous value */
						intArgument += (dataIn - '0'); 
						if (decSepDetected) {
							argumentDivisor *= 10;
						}
					}
				} else {
					/* Unexpected character */
					opcode = OPC_ERROR;
					break;					
				}
				delay(1);
			}
			/* Apply argument divisor and sign */
			if (negative) {
				intArgument = -intArgument;
			}
			fltArgument = (float(intArgument) / float(argumentDivisor));
			/* Parse opcodes */
			if (opcode != OPC_NONE) {
				/* Uncomment next lines for debug */
				// Serial.print("opcode: ");
				// Serial.print(char(opcode));
				// Serial.print(", argument: ");
				// Serial.print(intArgument);
				// Serial.print(" (");
				// Serial.print(fltArgument, 4);
				// Serial.print(")");
				// Serial.println();			
				switch(opcode) {
				case OPC_ACTIVATED:
					Serial.print("Turn led ");
					if (intArgument == 1) {
						*(_ledPort) |= _ledPinMask; 
						Serial.println("on");
					} else if (intArgument == 0) {
						*(_ledPort) &= ~_ledPinMask; 					
						Serial.println("off");
					}
					_blinks = 0;
					break;			
				case OPC_BLINKS:
					Serial.print("Blinks = ");
					Serial.println(intArgument);
					_blinks = intArgument;
					/* Reset blink counter */
					_blinkCounter = 0;
					break;
				case OPC_ON_RATIO:
					Serial.print("On/off ratio = ");
					Serial.println(fltArgument, 2);
					_onRatio = fltArgument;
					break;
				case OPC_PERIOD:
					Serial.print("Period = ");
					Serial.println(intArgument);
					_period = intArgument;
					break;
				case OPC_LST_PARAM: /* List parameters */
					PrintParameters();
					break;
				case OPC_RST_PARAM: /* Reset parameters */
					Serial.print("Reset parameters... ");
					GetDefaultParameters(bool(intArgument));
					Serial.println("done");
					break;
				case OPC_SAV_PARAM: /* Save parameters */
					Serial.print("Save parameters... ");
					SetDefaultParameters();
					Serial.println("done");
					break;
				case OPC_ERROR: /* Error */
					/* Report error */
					break;
				}
				/* Update subsidiary variables */
				_onDuration = (_period * _onRatio);
				_offDuration = (_period - _onDuration);
			}
		}
	}
}

Check the few tricks within the code which care about the sign, and the recognition of floats. The ‘-‘ should always lead the argument, and one  single decimal point is accepted. The .1 notation is accepted. These are basic protections and one could easily improve the ruggedness of this code. This code has few limitations such as numerical capacity, however it will work fine in 99% cases. Integers and floating point values must fit the −2147483648 to 2147483647 range and floats are limited to the same range. The precision of floating point values is affected by the loss of precision during floating point arithmetic. Spaces are not illegal, however all other characters generate an error.

Next post on same subject

 

Wire as you think

I was used to build my prototypes on strip-boards, burning my fingers, loosing my nerves and making many wiring mistakes because of the mirroring effect. And then came the illumination ! As I was visiting the R&D plant of my former division in Palo-Alto I observed engineers using breadboards to design yet pretty sophisticated devices. It did not take long before I drove to Fry’s and bought a couple of 3M branded mid-sized breadboards. Since them, I enriched my collection of breadboards with larger and smaller ones in order to fit the scale of my projects.

I very quickly developed some sort of standardization for the wire straps and later for dedicated components. At this time I used single core copper wires which were perfect for the job. My sourcing was pairs of telephone wire. The draw backs of this abundant source of wires was the lack of colors and the oxidizing of the bare copper wires. In the long term, oxide creates contact problem and leads to erratic signals.

A better option consists in getting plated copper wires in various insulating material colors. The optimal wire gauge is 0.6 mm, which translates to 22/23 AWG. This size of wire fits the bread boards as well as the headers (such as Arduino headers).

I cut these wires in standardized lengths: 1/2 inch, 1 inch and 1 1/2 inches.

These lengths are very convenient for most applications. Anyway, save some wire for specific needs. From my experience, multiple core wires are better for longer wire lengths.

And now is the time for something special. Instead of wiring passive components with bare terminals, which I find suicidal for any type of wiring…

I standardized sets of very commonly used components as shown below

The return on (time) investment is massive: wire gauges will always fit your prototyping tools, no risk of short circuits or loose contacts, and restricted risks of improper biasing. For achieving this last objective, I use black wires on the negative sides of polarized components. For diodes, I normalized the fact the tip of the diode is the cathode.

Ultimately, this sort of standardization helps a lot in tidying things up a lot as shown from the next picture. You may start with the most used series such as 1, 1.5, 2.2, 3.3, 4.7 in the x10, x100, x1k, x100k  ranges for resistors and capacitors and widen the range of values as you need them.

Well, this is it ! Once all things in place, it takes minutes to safely and quickly wire most schematics; again, and again, and again…

HTH

April fool… two days ahead

These guys at TI and have a great sense of humor !

Are they serious  ?

News and Moods

Some time ago, I suggested to append the keyword Arduino to any search for some advanced techno oriented questions. Arduino acts as an amplifying or magnifying factor as you look for the latest developments, the smartest solution in any kind of technology. It sounds like a password whispered at the entrance of a secret society building: “arduino” and the door opens on a noisy, busy, crowdy open space full of makers, hackers, designers, helpers, etc…

As I worked on DC/DC converters, for an urgent project, I decided to speed up its development by addressing directly the support team of one of top suppliers in converting chips. And I found…

LinDuino ! Amazing ! Read more about it in this publication. LT is surely not pioneering an unprecedented trend in R&D, however they address it properly.

Last not least, I had a laugh at reading this on Mouser’s web pages:

Well, you made it great Limor, and you deserve once again my great consideration. I imagine that it is quite a change compared to the early times…