Close

Let it sing!

A project log for Old Roomba, new tricks

Add mapping & MQTT-interfacing with ESP8266 and an IMU

simon-jansenSimon Jansen 08/20/2022 at 22:530 Comments

Showing progress is sometimes more important than making actual progress

There are a few things I want to accomplish before tackling the difficult maths. First off, The ability to do something that normal robot-vacuums can't. Just to make a point as to why I don't just buy the €100 more expensive model. Most of them can do scheduling and keep me posted on progress, but how many can play any tune I want?

Playing the macGyver theme song was one of the fist things I did with this roomba. 

https://hackaday.io/project/183524-controll-yer-roomba-600-series-with-esp8266/log/202547-important-high-level-stuff

Back then, I was using a previous library and this was more a proof of concept. Which also showed I had full control and was master of all robots! Now we know better off course and welcome our robot overlords.

By now I have produced a library of my own, somewhat in secrect, and want to show off my mastery of this inanimate object. What better way than to let it sing for me.

The video shows the Home Assistant MQTT-autodiscover buttons and sensors. Also the notification in the end using the event trigger when the Roomba is docked again. 

Some of the code I added:

void Roomba632::playMusic(uint8_t song[],int songNumberOfBytes){
    //set music to be played
    p_songPointer = song;
    p_songNumberOfBytes = songNumberOfBytes;
    p_playMusicFlag = true;
}

//in the roomba state handler:
case roombaIdle: //IDLE: not charging, driving, playing music or cleaning
    p_stopFlag = false; //Clear stop request flag if set
    //Keep alive
    //Check flags in appropriate order of prirority
    if(p_cleanFlag){
        SendCleanCommand();
    }
    else if(p_dockFlag){
        SendDockCommand();
    }
    else if(p_playMusicFlag){
        SendPlayMusicCommand();
    }    
    if((millis() - p_undockedTimer) > DOCKEDDEBOUNCETIME){
        if(docked){
            roombaState = roombaHome;
        }
    }
    break;

//The state machine for playing music:
case roombaMusic:
p_playMusicFlag = false;
b_songPlaying = true;
//time music or poll state
static int songPointerIndex;
static uint8_t maxIndex, i;
static int musicState;
static unsigned long commandTimerMusic, musicTimer, songTime;
switch (musicState){
    case musicInit:
        songPointerIndex = 0;
        musicState = musicWriteSongHeader;
        //Serial.write(OCPauseDatastream);
        //Serial.write(0);
        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(p_songPointer[songPointerIndex]);
                if(songPointerIndex % 2 != 0){
                    songTime += p_songPointer[songPointerIndex];
                    if (p_songPointer[songPointerIndex] != 0){
                        songTime -= 2;
                    }
                }
                songPointerIndex++;
                i++;
            }
            else{
                musicState = musicStartSong;
            }
            commandTimerMusic = millis();
        }
        break;
    case musicStartSong:
        if ((millis() - commandTimerMusic) > COMMANDTIMEOUT){
            musicTimer = millis();
            songTime = (1000 * songTime)/64;
            musicState = musicPlaySong;
            Serial.write(OCPlaySong);
            Serial.write(1);
        }
        break;
    case musicPlaySong:
        if((millis() - musicTimer) > songTime){
            if(songPointerIndex >= p_songNumberOfBytes){
                musicState = musicInit;
                //Serial.println("done singing");
                roombaState = roombaIdle;
                b_songPlaying = false;
                SendStopCommand();
                //Serial.write(OCPauseDatastream);
                //Serial.write(1);
            }
            else {
                musicState = musicWriteSongHeader; //next song
                //Serial.println("next song");
            }
        }
        //waiting while song plays
        break;
}
break;
//And seperate function to start the music commands:
void Roomba632::SendPlayMusicCommand(){
    static int commandstate;
    static unsigned long commandTimer;
    switch (commandstate){
        case startIO:
            Serial.write(OCstart);
            commandstate = setMode;
            commandTimer = millis();
            break;
        case setMode:
            if ((millis() - commandTimer) > COMMANDMODETIMEOUT){
                Serial.write(OCFullmode);
                commandstate = startIO; //reset state
                roombaState = roombaMusic; //sending music commands is handled in the music state
            }
            break;        
    }
}

A few of the problems I ran across:

For now, I consider this another feature added! The last version of this library and my sketch-files are uploaded. I want to do some more on the Home Assistant integration first. 

Discussions