Close

Unlimited memory!*

A project log for Old Roomba, new tricks

Add mapping and MQTT-interfacing with ESP8266 and an IMU

simon-jansenSimon Jansen 05/04/2025 at 14:260 Comments

Songs and driving scripts are now stored in and read from PROGMEM. This means we can use large scripts (for calibration or as consistent cleaning routes) without running out of memory.

//Starwars' Imperial March
const uint8_t imperialMarch[] PROGMEM = {
  N_A4,  32,  N_A4,  32,  N_A4,  32,  N_F4,  23,  N_C5,  10,  N_A4,  32,  N_F4,  23,  N_C5,  10,  N_A4,  42,  0   ,  32,  N_E5,  32,  N_E5,  32,  N_E5,  32,  N_F5,  23, N_C5,  10,  N_G4S, 32,
  N_F4,  23,  N_C5,  10,  N_A4,  42,  0   ,  32,  N_A5,  32,  N_A4,  19,  N_A4,  10,  N_A5,  32,  N_G5S, 21,  N_G5,  11,  N_F5S,  8,  N_F5,   8,  N_F5S, 16, 0    , 21,  N_A4S, 16,  N_D5S, 32,
  N_D5,  21,  N_C5S, 11,  N_C5,   8,  N_B4,   8,  N_C5,  16,  0    , 23,  N_F4,  16,  N_G4S, 32,  N_F4,  23,  N_A4,   8,  N_C5,  32,  N_A4,  24,  N_C5,   8,  N_E5,  42, 0    , 32,  N_A5,  32,
  N_A4,  19,  N_A4,  10,  N_A5,  32,  N_G5S, 21,  N_G5,  11,  N_F4S,  8,  N_F5,   8,  N_F4S, 16,  0    , 21,  N_A4S, 16,  N_D5S, 32,  N_D5,  21,  N_C5S, 11,  N_C5,   8, N_B4,   8,  N_C5,  16,  
  0   ,  23,  N_F4,  16,  N_G4S, 32,  N_F4,  24,  N_C5,   8,  N_A4,  32,  N_F4,  24,  N_C5,   8,  N_A4,  42,  0     ,42
};
//macGyver theme song!
const uint8_t macGyver[] PROGMEM = {
  N_B3, 16, N_E4, 16, N_A4, 16, N_B4, 16, N_A4, 16, N_B3, 16, N_E4, 16, N_B3, 16, 0   , 16, N_E4, 16,  N_A4, 16, N_B4, 16,  N_A4, 16,  N_E4, 16,  N_B3, 16,  N_E4, 16,
  0   , 16, N_E4, 16, N_A4, 16, N_B4, 16, N_A4, 16, N_B3, 16, N_E4, 16, N_B3, 32, 0   , 16, N_A4, 16,  N_D5, 16, N_C5, 16,  N_D5, 16,  N_C5, 16,  N_B4, 16,  N_A4, 16,
  N_B4, 48, N_A4, 76, 0   , 4,  N_A4, 48, N_G4, 76, 0   , 4,  N_B4, 14, 0   , 2,  N_B4, 48, N_A4, 76,  0   , 4,  N_A4, 48,  N_G4, 32,  N_A4, 72,  0   , 8,   N_C5, 12,
  0   , 2,  N_C5, 12, 0   , 2,  N_C5, 12, 0   , 2,  N_C5, 12, 0   , 2,  N_C5, 12, 0   , 2,  N_C5, 12,  0   , 2,  N_B4, 64,  N_F4S, 16, N_A4, 32,  N_G4, 80,  N_C5, 14,
  0, 2,     N_C5, 32, N_B4, 32, N_C5, 16, N_B4, 16, N_A4, 16, N_G4, 16, N_E5, 32, N_A4, 64, N_C5, 14,  0   , 2,  N_C5, 80,  N_F4S, 16, N_A4, 32,  N_G4, 76,  N_C5, 14,
     0,  2, N_C5, 32, N_B4, 32, N_C5, 16, N_B4, 16, N_G4, 16, N_E5, 32, N_A4, 64, N_B4, 64, N_C5, 16,  N_B4, 16, N_A4, 16,  N_C5, 32,  N_B4, 16,  N_A4, 16,  N_D5, 32,
  N_C5, 16, N_B4, 16, N_D5, 32, N_C5, 16, N_B4, 16, N_E5, 32, N_D5, 16, N_E5, 16, N_F5S,32, N_B4, 32,  N_G5S, 48,N_F5S, 32, N_F5, 32,  N_B4, 32,  N_G5, 16,  N_E5, 16,
  N_B4, 16, N_F5S, 16,N_D5, 16, N_A4, 16, N_E5, 16, N_C5, 16, N_G4, 16, N_D5, 16, N_B4, 16, N_G4, 16,  N_C5, 16, N_E4, 16,  N_B4, 16,  N_D4, 16,  N_C5, 16,  N_B4, 16,
  N_A4, 16, N_G4, 16, N_A4S, 32,N_A4, 32, N_G5, 16, N_G4, 16, N_D5, 16, N_G4, 16, N_D5S, 16,N_D4S, 16, N_A4S, 16,N_A4, 16,  N_G4, 16,  N_G3, 16,  N_D4, 16,  N_G3, 16,
  N_D4S, 16,N_G3, 16, N_A3S, 16,N_A3, 16, N_G3, 14, 0    , 2, N_G3, 14, 0   , 2,  N_G3, 14,0     , 2,  N_G3, 14, 0   , 2,   N_G3, 14,  0   , 2,   N_G3, 14,  0   , 2,
  N_G3, 14, 0   , 2
};
const uint8_t Square[] PROGMEM = {
  RMDriveF, 20, //50cm
  RMTurnL, 64,  //quarter turn L
  RMDriveF, 20, //50cm
  RMTurnL, 64,  //quarter turn L
  RMDriveF, 20, //50cm
  RMTurnL, 64,  //quarter turn L
  RMDriveF, 20, //50cm
  RMTurnL, 64,  //quarter turn L
  RMDriveF, 20, //50cm
  RMTurnR, 64,  //quarter turn R
  RMDriveF, 20, //50cm
  RMTurnR, 64,  //quarter turn R
  RMDriveF, 20, //50cm
  RMTurnR, 64,  //quarter turn R
  RMDriveF, 20, //50cm
  RMTurnR, 64,  //quarter turn R
  RMDriveF, 20, //50cm
  RMHalt        //Stop
};

const uint8_t Empty[] PROGMEM = {
  //RMDriveRawBL, 50, 50, 32, //Velocity: reverse 150, Arc: Left 500mm, Time 32*128ms=4sec
  RMDriveR, 40,
  RMTurnL, 64,
  RMDriveF, 16,
  RMTurnL, 128,
  RMHalt
};

const uint8_t CallibrationRun[] PROGMEM = {
  //RMDriveRawBL, 50, 50, 32, //Velocity: reverse 150, Arc: Left 500mm, Time 32*128ms=4sec
  RMDriveR, 40,
  RMTurnL, 128,
  RMTurnL, 128,
  RMTurnR, 128,
  RMTurnR, 128,
  RMDriveF, 16,
  RMHalt
};

With snippets..

const uint8_t *p_songPointer;
int p_songNumberOfBytes;
const uint8_t *p_scriptPointer;
unsigned long p_undockedTimer;

void playScript(const uint8_t *script);
void playMusic(const uint8_t *song, size_t songNumberOfBytes);

void Roomba632::playScript(const uint8_t *script){
	//memcpy(script,script,sizeof(script));
	p_scriptPointer = script;
	p_startScriptFlag = true;
}

void Roomba632::playMusic(const uint8_t *song, size_t songNumberOfBytes){
	//set music to be played
	p_songPointer = song;
	p_songNumberOfBytes = songNumberOfBytes;
	p_playMusicFlag = true;
}
			static int songPointerIndex;
			static uint8_t maxIndex, i;
			static int musicState = musicInit;
			static unsigned long commandTimerMusic, musicTimer, songTime;
			switch (musicState){
				case musicInit:
					songPointerIndex = 0;
					musicState = musicWriteSongHeader;
					commandTimerMusic = millis();
        			break;
				case musicWriteSongHeader:
					if ((millis() - commandTimerMusic) > COMMANDTIMEOUT){
						Serial.write(OCSong);
						Serial.write(1);
						songTime = 0;
						if ((p_songNumberOfBytes-songPointerIndex)<MAXSONGLENGTH){
							maxIndex = (p_songNumberOfBytes-songPointerIndex);
						}
						else{
							maxIndex = MAXSONGLENGTH;
						}
						Serial.write(maxIndex/2); //number of notes
						musicState = musicWriteNotes;
						i = 0;
						commandTimerMusic = millis();
					}
					break;
				case musicWriteNotes:
					if ((millis() - commandTimerMusic) > COMMANDTIMEOUT){
						if(i < maxIndex){
							Serial.write(pgm_read_byte(p_songPointer + songPointerIndex));
							if(songPointerIndex % 2 != 0){
								songTime += pgm_read_byte(p_songPointer + songPointerIndex);
								if (pgm_read_byte(p_songPointer + songPointerIndex) != 0){
									songTime -= 2;
								}
							}
							songPointerIndex++;
							i++;
						}
						else{
							musicState = musicStartSong;
						}
						commandTimerMusic = millis();
					}
					break;

..

int Roomba632::ScriptHandler(){
	int16_t velocity, radius;
	uint8_t scriptDistance, scriptAngle, scriptTime;
	static uint8_t scriptRepeat;
	static int scriptState = RMIdle, p_programCounter; //program counter for scripts;
	static unsigned long scriptTimer, waitingTime;
	switch (scriptState){
		case RMIdle:	//0
			if (p_startScriptFlag){
				p_startScriptFlag = false;
				scriptState = RMStart;
			}
			break;
		case RMStart:	//1
			if(OImode == Off){
				Serial.write(OCstart);
				OImode = Passive;
				scriptState = RMStarting;
			}
			else{
				p_programCounter= 0;	//Reset script
				scriptState = pgm_read_byte(p_scriptPointer + p_programCounter);
			}
			scriptTimer = millis();
			break;
		case RMStarting://2
			if ((millis() - scriptTimer) > COMMANDSTARTTIMEOUT){
				if (OImode < Safe){
					Serial.write(OCSafemode);
					OImode = Safe;
				}
				p_programCounter= 0;	//Reset script
				scriptState = pgm_read_byte(p_scriptPointer + p_programCounter);
				scriptTimer = millis();
			}
			break;
		case RMHalt:	//3 scriptable
			if ((millis() - scriptTimer) > COMMANDMODETIMEOUT){
				Serial.write(OCStop);
				OImode = Off;
				scriptTimer = millis();
				scriptState = RMHalting;
			}
			break;
		case RMHalting:	//4
			if((millis() - scriptTimer) > COMMANDSTARTTIMEOUT){
				p_programCounter = 0;
				scriptState = RMIdle;
			}
			break;
		case RMDriveF:	//5 scriptable verb + noun
			if ((millis() - scriptTimer) > COMMANDMODETIMEOUT){
				scriptDistance = pgm_read_byte(p_scriptPointer + p_programCounter +1); //Distance in steps of 25mm (gives maximum of 6.375m)
				waitingTime = scriptDistance * DISTANCESCALAR; 
				velocity = 250; //mm/s
				radius = 32767; //Drive straight
				SendDriveCommand(velocity, radius);
				scriptTimer = millis();
				scriptState = RMDrivingF;
			}
			break;
		case RMDrivingF://6
			if((millis() - scriptTimer) > waitingTime){
				p_programCounter = p_programCounter + 2;
				scriptState = pgm_read_byte(p_scriptPointer + p_programCounter);
				scriptTimer = millis();
			}
			break;

*less limited

Discussions