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 !

Leave a Reply

You must be logged in to post a comment.