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.
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:
- The robot seems to need some time to process the commands. It needs a bit of time between sending the different commands. In a fully async library this can't be done with delays() off course. So we need state-machines and timers;
- How to split up a long song in chunks of 16 notes? The roomba accepts songs with a maximum of 16 notes to be defined as numbered songs 0 to 4 which can then be played. How can we split up a song with undetermined and unrestricted length in the library?
- Using uint8_t and int (int16_t) together and in calculations, but also writing the correct data over Serial;
- Because the library is set up to be quite asynchronous, there's also a lot of stuff that can go wrong in parallel. This is sometimes very hard to diagnose. Maybe I need to clean up the code some more and stick to some best-practices more. For instance: thou shall never change state of a state-machine outside of said state machine;
- I still had the enum's for the notes not set up correctly. As far as I can tell, I now use C4 as "middle note", with MIDI #60. A4 is MIDI #69, 440Hz.
enum notes{ N_G1 = 31, * * N_B2 = 47, N_C3 = 48, N_C3S = 49, N_D3 = 50, N_D3S = 51, N_E3 = 52, N_F3 = 53, N_F3S = 54, N_G3 = 55, N_G3S = 56, N_A3 = 57, N_A3S = 58, N_B3 = 59, N_C4 = 60, N_C4S = 61, N_D4 = 62, N_D4S = 63, N_E4 = 64, N_F4 = 65, N_F4S = 66, N_G4 = 67, N_G4S = 68, N_A4 = 69, N_A4S = 70, N_B4 = 71, * * N_B7 = 107 };
- It has been some time last I checked in on the library in this depth. I still comment way too little :| I had to re-interpret what I wanted to do when last I wrote it. Not cool;
- There is still an audible "gap" between songs. This is due to timing of the commands being sent and the calculated waiting time. Maybe I can get these gaps closed;
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.
- Use "availability" for the different action buttons in Home Assistant to differentiate between possible actions and divine interventions;
- Neat way to display states in Home Assistant. I think this can be done with templates. Somehow;
- How to get maps/cards??? I think I need to see results (easily) before I can try to improve accuracy;
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.