c2ac7ab6247c9be8498bca8c5a3e8dca71d9ecbc
[pierogi] / protocols / irobotprotocol.cpp
1 #include "irobotprotocol.h"
2
3 #include "pirrx51hardware.h"
4
5 #include "pirexception.h"
6
7 // Some global communications stuff:
8 #include <QMutex>
9 extern bool commandInFlight;
10 extern QMutex commandIFMutex;
11
12 // The iRobot (Roomba) protocol seems to be extremely simple.
13 // A "zero" is encoded with a 1000 usec pulse, 3000 usec space.
14 // A "one" is encoded with a 3000 usec pulse, 1000 usec space.
15 // So, it looks a little like a shift encoded protocol, even though it
16 // is space-encoded.
17 // There is no header and no trailer.
18 // Each command may run for 100000 usec (sources differ), but it looks like
19 // commands are not repeated at all...
20 // The carrier frequency is the usual 38 kHz.
21
22 IRobotProtocol::IRobotProtocol(
23   QObject *guiObject,
24   unsigned int index)
25   : SpaceProtocol(
26       guiObject, index,
27       1000, 3000,
28       3000, 1000,
29       0, 0,
30       0,
31       100000, true)
32 {
33 }
34
35
36 void IRobotProtocol::startSendingCommand(
37   unsigned int threadableID,
38   PIRKeyName command)
39 {
40   // Exceptions here are problematic; I'll try to weed them out by putting the
41   // whole thing in a try/catch block:
42   try
43   {
44     // First, check if we are meant to be the recipient of this command:
45     if (threadableID != id) return;
46
47     clearRepeatFlag();
48
49     KeycodeCollection::const_iterator i = keycodes.find(command);
50
51     // Do we even have this key defined?
52     if (i == keycodes.end())
53     {
54       std::string s = "Tried to send a non-existent command.\n";
55       throw PIRException(s);
56     }
57
58     // construct the device:
59     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
60
61     int repeatCount = 0;
62     int commandDuration = 0;
63     while (repeatCount < MAX_REPEAT_COUNT)
64     {
65       // It looks like we only generate the command once, and remain
66       // silent for the rest of the time the button is held down.  So, no
67       // repeats.
68       if (!repeatCount)
69       {
70         commandDuration = generateCommand((*i).second, rx51device);
71
72         // Tell the device to send the command:
73         rx51device.sendCommandToDevice();
74       }
75
76       // sleep until the next repetition of command:
77       sleepUntilRepeat(commandDuration);
78
79       // Check whether we've reached the minimum required number of repetitons:
80       if (repeatCount >= minimumRepetitions)
81       {
82         // Check whether we've been asked to stop:
83         if (checkRepeatFlag())
84         {
85           QMutexLocker cifLocker(&commandIFMutex);
86           commandInFlight = false;
87           return;
88         }
89       }
90
91       ++repeatCount;
92     }
93   }
94   catch (PIRException e)
95   {
96     // inform the gui:
97     emit commandFailed(e.getError().c_str());
98   }
99
100   QMutexLocker cifLocker(&commandIFMutex);
101   commandInFlight = false;
102 }
103
104
105 int IRobotProtocol::generateCommand(
106   const PIRKeyBits &pkb,
107   PIRRX51Hardware &rx51device)
108 {
109   int duration = 0;
110
111   // The protocol seems to involve 8 command bits, a 16000 usec pause, and
112   // the same 8 bits repeated again.  So, we need to tack a 16000 usec
113   // space on at the end of the first 8 bits, and just drop the last space
114   // definition at the end of the second 8 bits:
115
116   // The first 7 bits:
117   int index = 0;
118   CommandSequence::const_iterator i = pkb.firstCode.begin();
119   while ((index < 7) && (i != pkb.firstCode.end()))
120   {
121     if (*i)
122     {
123       rx51device.addPair(onePulse, oneSpace);
124       duration += onePulse + oneSpace;
125     }
126     else
127     {
128       rx51device.addPair(zeroPulse, zeroSpace);
129       duration += zeroPulse + zeroSpace;
130     }
131
132     ++index;
133     ++i;
134   }
135
136   // Eighth bit with extra space at the end:
137   if (i != pkb.firstCode.end())
138   {
139     if (*i)
140     {
141       rx51device.addPair(onePulse, oneSpace + 16000);
142       duration += onePulse + oneSpace + 16000;
143     }
144     else
145     {
146       rx51device.addPair(zeroPulse, zeroSpace + 16000);
147       duration += zeroPulse + zeroSpace + 16000;
148     }
149   }
150
151   // The following seven bits:
152   index = 0;
153   i = pkb.firstCode.begin();
154   while ((index < 7) && (i != pkb.firstCode.end()))
155   {
156     if (*i)
157     {
158       rx51device.addPair(onePulse, oneSpace);
159       duration += onePulse + oneSpace;
160     }
161     else
162     {
163       rx51device.addPair(zeroPulse, zeroSpace);
164       duration += zeroPulse + zeroSpace;
165     }
166
167     ++index;
168     ++i;
169   }
170
171   // The last half-bit:
172   if (i != pkb.firstCode.end())
173   {
174     if (*i)
175     {
176       rx51device.addSingle(onePulse);
177       duration += onePulse;
178     }
179     else
180     {
181       rx51device.addSingle(zeroPulse);
182       duration += zeroPulse;
183     }
184   }
185
186   return duration;
187 }