Skip to content

Commit 8fe8187

Browse files
committed
Added support for several IR 3.5ch helicopters.
Including the popular SymaS107 (both R5 and R3), along with Useries and Toy-R-Us's private label of Air Hog's. Added example for emulating Hand remote control of joy sticks for various's axis's.
1 parent 7bea72d commit 8fe8187

File tree

5 files changed

+803
-10
lines changed

5 files changed

+803
-10
lines changed

IRremote.cpp

Lines changed: 207 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,82 @@ void IRsend::sendMagiQuest(uint32_t wand_id, uint16_t magnitude)
242242
space(MAGIQUEST_SPACE_ZERO);
243243
}
244244
}
245-
Serial.println();
246245
}
247-
248-
void IRsend::mark(int time) {
246+
void IRsend::sendSymaR5(uint32_t data)
247+
{
248+
enableIROut(38);
249+
mark(SYMA_HDR_MARK); // SYMA_HDR_MARK
250+
space(SYMA_HDR_SPACE); // SYMA_HDR_SPACE
251+
for (uint8_t i = 0; i < SYMA_R5_BITS+1; i++) {
252+
mark(SYMA_BIT_MARK); // SYMA_BIT_MARK
253+
if (((int32_t)data) < 0) { // if negative then MSBit is one.
254+
space(SYMA_ONE_SPACE); // SYMA_ONE_SPACE
255+
}
256+
else {
257+
space(SYMA_ZERO_SPACE); // SYMA_ZERO_SPACE
258+
}
259+
data <<= 1;
260+
}
261+
}
262+
void IRsend::sendSymaR3(uint32_t data)
263+
{
264+
enableIROut(38);
265+
mark(SYMA_HDR_MARK);
266+
space(SYMA_HDR_SPACE);
267+
data <<= 8;
268+
for (uint8_t i = 0; i < SYMA_R3_BITS+1; i++) {
269+
mark(SYMA_BIT_MARK);
270+
if (((int32_t)data) < 0) { // if negative then MSBit is one.
271+
space(SYMA_ONE_SPACE);
272+
}
273+
else {
274+
space(SYMA_ZERO_SPACE);
275+
}
276+
data <<= 1;
277+
}
278+
}
279+
void IRsend::sendUseries(uint32_t data)
280+
{
281+
enableIROut(38);
282+
mark(USERIES_HDR_MARK);
283+
for (int16_t i = 0; i < USERIES_BITS+1; i++) {
284+
space(USERIES_BIT_SPACE);
285+
if (((int32_t)data) < 0) { // if negative then MSBit is one.
286+
mark(USERIES_ZERO_MARK);
287+
}
288+
else {
289+
mark(USERIES_ONE_MARK);
290+
}
291+
data <<= 1;
292+
}
293+
space(0); // Just to be sure
294+
}
295+
void IRsend::sendFastLane(uint32_t data)
296+
{
297+
enableIROut(38);
298+
mark(FASTLANE_HDR_MARK);
299+
data <<= (32 - FASTLANE_BITS); // upshift to start point of data.
300+
for (int16_t i = 0; i < FASTLANE_BITS+1; i++) {
301+
if (i%2) {
302+
if (((int32_t)data) < 0) { // if negative then MSBit is one.
303+
mark(FASTLANE_ONE);
304+
}
305+
else {
306+
mark(FASTLANE_ZERO);
307+
}
308+
} else {
309+
if (((int32_t)data) < 0) { // if negative then MSBit is one.
310+
space(FASTLANE_ONE);
311+
}
312+
else {
313+
space(FASTLANE_ZERO);
314+
}
315+
}
316+
data <<= 1;
317+
}
318+
space(0); // Just to be sure
319+
}
320+
void IRsend::mark(int16_t time) {
249321
// Sends an IR mark for the specified number of microseconds.
250322
// The mark output is modulated at the PWM frequency.
251323
TIMER_ENABLE_PWM; // Enable pin 3 PWM output
@@ -457,6 +529,24 @@ int16_t IRrecv::decode(decode_results *results) {
457529
if (decodePanasonic(results)) {
458530
return DECODED;
459531
}
532+
#ifdef DEBUG
533+
Serial.println("Attempting Syma decode");
534+
#endif
535+
if (decodeSyma(results)) {
536+
return DECODED;
537+
}
538+
#ifdef DEBUG
539+
Serial.println("Attempting Useries decode");
540+
#endif
541+
if (decodeUseries(results)) {
542+
return DECODED;
543+
}
544+
#ifdef DEBUG
545+
Serial.println("Attempting FastLane decode");
546+
#endif
547+
if (decodeFastLane(results)) {
548+
return DECODED;
549+
}
460550
#ifdef DEBUG
461551
Serial.println("Attempting JVC decode");
462552
#endif
@@ -874,6 +964,109 @@ int32_t IRrecv::decodePanasonic(decode_results *results) {
874964
results->bits = PANASONIC_BITS;
875965
return DECODED;
876966
}
967+
int32_t IRrecv::decodeSyma(decode_results *results) {
968+
uint32_t data = 0;
969+
int16_t offset = 1;
970+
uint8_t syma_len;
971+
972+
if (irparams.rawlen == 2 * (SYMA_R5_BITS + 2)) {
973+
syma_len = SYMA_R5_BITS;
974+
}
975+
else if (irparams.rawlen == 2 * (SYMA_R3_BITS + 2)) {
976+
syma_len = SYMA_R3_BITS;
977+
}
978+
else {
979+
return ERR;
980+
}
981+
if (!MATCH_MARK(results->rawbuf[offset], SYMA_HDR_MARK)) {
982+
return ERR;
983+
}
984+
offset++;
985+
if (!MATCH_MARK(results->rawbuf[offset], SYMA_HDR_SPACE)) {
986+
return ERR;
987+
}
988+
offset++;
989+
for (uint8_t i = 0; i < syma_len; i++) {
990+
if (!MATCH_MARK(results->rawbuf[offset++], SYMA_BIT_MARK)) {
991+
return ERR;
992+
}
993+
if (MATCH_SPACE(results->rawbuf[offset], SYMA_ONE_SPACE)) {
994+
data = (data << 1) | 1;
995+
} else if (MATCH_SPACE(results->rawbuf[offset], SYMA_ZERO_SPACE)) {
996+
data <<= 1;
997+
} else {
998+
return ERR;
999+
}
1000+
offset++;
1001+
}
1002+
results->value = data;
1003+
if (syma_len == SYMA_R5_BITS) {
1004+
results->decode_type = SYMA_R5;
1005+
}
1006+
else if (syma_len == SYMA_R3_BITS) {
1007+
results->decode_type = SYMA_R3;
1008+
}
1009+
results->bits = syma_len;
1010+
results->helicopter.dword = data;
1011+
return DECODED;
1012+
}
1013+
int32_t IRrecv::decodeUseries(decode_results *results) {
1014+
uint32_t data = 0;
1015+
int16_t offset = 1;
1016+
if (irparams.rawlen < 2 * (USERIES_BITS + 2)) {
1017+
return ERR;
1018+
}
1019+
if (!MATCH_MARK(results->rawbuf[offset], USERIES_HDR_MARK)) {
1020+
return ERR;
1021+
}
1022+
offset++;
1023+
for (int16_t i = 0; i < USERIES_BITS; i++) {
1024+
if (!MATCH_SPACE(results->rawbuf[offset++], USERIES_BIT_SPACE)) {
1025+
return ERR;
1026+
}
1027+
if (MATCH_MARK(results->rawbuf[offset],USERIES_ONE_MARK)) {
1028+
data <<= 1;
1029+
} else if (MATCH_MARK(results->rawbuf[offset],USERIES_ZERO_MARK)) {
1030+
data = (data << 1) | 1;
1031+
} else {
1032+
return ERR;
1033+
}
1034+
offset++;
1035+
}
1036+
results->value = data;
1037+
results->helicopter.dword = data;
1038+
results->parity = UseriesChecksum(data);
1039+
results->decode_type = USERIES;
1040+
results->bits = USERIES_BITS;
1041+
return DECODED;
1042+
}
1043+
int32_t IRrecv::decodeFastLane(decode_results *results) {
1044+
helicopter data;
1045+
data.dword = 0;
1046+
int16_t offset = 1;
1047+
if (irparams.rawlen < (FASTLANE_BITS + 2)) {
1048+
return ERR;
1049+
}
1050+
if (!MATCH_MARK(results->rawbuf[offset], FASTLANE_HDR_MARK)) {
1051+
return ERR;
1052+
}
1053+
offset++;
1054+
for (int16_t i = 0; i < FASTLANE_BITS; i++) {
1055+
if (MATCH_MARK(results->rawbuf[offset],FASTLANE_ZERO)) {
1056+
data.dword <<= 1;
1057+
} else if (MATCH_MARK(results->rawbuf[offset],FASTLANE_ONE)) {
1058+
data.dword = (data.dword << 1) | 1;
1059+
} else {
1060+
return ERR;
1061+
}
1062+
offset++;
1063+
}
1064+
results->value = data.dword;
1065+
results->helicopter.dword = data.dword;
1066+
results->decode_type = FASTLANE;
1067+
results->bits = FASTLANE_BITS;
1068+
return DECODED;
1069+
}
8771070
int32_t IRrecv::decodeJVC(decode_results *results) {
8781071
int32_t data = 0;
8791072
int16_t offset = 1; // Skip first space
@@ -1113,3 +1306,14 @@ void IRsend::sendDISH(int32_t data, int16_t nbits)
11131306
data <<= 1;
11141307
}
11151308
}
1309+
uint8_t UseriesChecksum(uint32_t val)
1310+
{
1311+
const uint8_t map[8] = { 0, 1, 3, 2, 6, 7, 5, 4 };
1312+
uint32_t x = val & 0xFFFFFFF8;
1313+
uint32_t p = x;
1314+
while ((x >>= 7) != 0) {
1315+
p ^= x;
1316+
}
1317+
p = (p ^ (p >> 1) ^ (p >> 2) ^ (p >> 4)) & 7;
1318+
return(map[p]);
1319+
}

IRremote.h

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ union magiquest {
2929
uint64_t llword;
3030
uint8_t byte[8];
3131
// uint16_t word[4];
32-
// uint32_t lword[2];
32+
uint32_t lword[2];
3333
struct {
3434
uint16_t magnitude;
3535
uint32_t wand_id;
@@ -38,13 +38,60 @@ union magiquest {
3838
} cmd ;
3939
} ;
4040

41+
union helicopter {
42+
uint32_t dword;
43+
struct
44+
{
45+
uint8_t Throttle : 7; // 0..6 0 - 127
46+
uint8_t Channel : 1; // 7 A=0, B=1
47+
uint8_t Pitch : 7; // 8..14 0(forward) - 63(center) 127(back)
48+
uint8_t Pspacer : 1; // 15 na
49+
uint8_t Yaw : 7; // 16..22 127(left) - 63(center) - 0(right)
50+
} symaR3;
51+
struct
52+
{
53+
uint8_t Trim : 7; // 0..6 127(left) - 63(center) - 0(right)
54+
uint8_t Tspacer : 1; // 7 na
55+
uint8_t Throttle : 7; // 8..14 0 - 127
56+
uint8_t Channel : 1; // 15 A=0, B=1
57+
uint8_t Pitch : 7; // 16..22 0(forward) - 63(center) 127(back)
58+
uint8_t Pspacer : 1; // 23 na
59+
uint8_t Yaw : 7; // 24..30 127(left) - 63(center) - 0(right)
60+
} symaR5;
61+
struct
62+
{
63+
uint8_t cksum : 3; // 0..2
64+
uint8_t Rbutton : 1; // 3 0-normally off, 1-depressed
65+
uint8_t Lbutton : 1; // 4 0-normally off, 1-depressed
66+
uint8_t Turbo : 1; // 5 1-off, 0-on
67+
uint8_t Channel : 2; // 6,7 A=1, B=2, C=0
68+
uint8_t Trim : 6; // 8..13 (left)63 - 31(center) - 0(right)
69+
uint8_t Yaw : 5; // 14..18 31(left) - 15(center) - 0(right)
70+
uint8_t Pitch : 6; // 19..24 0(foward) - 31(center) - 63(back)
71+
uint8_t Throttle : 7; // 25..31 0 - 127
72+
} uSeries;
73+
struct
74+
{
75+
uint8_t Trim : 4; // 0..3 15(left) - 8(center) - 0(right)
76+
uint8_t Trim_dir : 1; // 4 0= , 1=
77+
uint8_t Yaw_dir : 1; // 5
78+
uint8_t Fire : 1; // 6
79+
uint8_t Yaw : 4; // 7..10 15(left) - 8(center) - 0(right)
80+
uint8_t Pitch : 4; // 11..14 1(back) - 8(center) 15(forward)
81+
uint8_t Throttle : 6; // 15..20 0 - 63
82+
uint8_t Channel : 2; // 21..22 ?A=0, B=1
83+
} fastlane;
84+
};
85+
4186
// Results returned from the decoder
4287
class decode_results {
4388
public:
4489
int16_t decode_type; // NEC, SONY, RC5, UNKNOWN
4590
uint16_t panasonicAddress; // This is only used for decoding Panasonic data
4691
uint16_t magiquestMagnitude; // This is only used for MagiQuest
4792
int32_t value; // Decoded value //mpf need to make unsigned.
93+
union helicopter helicopter;
94+
uint16_t parity;
4895
int16_t bits; // Number of bits in decoded value
4996
volatile uint16_t *rawbuf; // Raw intervals in .5 us ticks
5097
int16_t rawlen; // Number of records in rawbuf.
@@ -62,6 +109,10 @@ class decode_results {
62109
#define SANYO 9
63110
#define MITSUBISHI 10
64111
#define MAGIQUEST 11
112+
#define SYMA_R3 12
113+
#define SYMA_R5 13
114+
#define USERIES 14
115+
#define FASTLANE 15
65116
#define UNKNOWN -1
66117

67118
// Decoded value for NEC when a repeat code is received
@@ -86,14 +137,19 @@ class IRrecv
86137
int32_t decodeRC5(decode_results *results);
87138
int32_t decodeRC6(decode_results *results);
88139
int32_t decodePanasonic(decode_results *results);
140+
int32_t decodeSyma(decode_results *results);
141+
int32_t decodeUseries(decode_results *results);
142+
int32_t decodeFastLane(decode_results *results);
89143
int32_t decodeJVC(decode_results *results);
90144
int32_t decodeMagiQuest(decode_results *results);
91145
int32_t decodeHash(decode_results *results);
92146
int16_t compare(uint16_t oldval, uint16_t newval);
93147

94-
}
148+
}
95149
;
96150

151+
uint8_t UseriesChecksum(uint32_t val);
152+
97153
// Only used for testing; can remove virtual for shorter code
98154
#ifdef TEST
99155
#define VIRTUAL virtual
@@ -118,6 +174,10 @@ class IRsend
118174
void sendPanasonic(uint16_t address, int32_t data);
119175
void sendJVC(int32_t data, int16_t nbits, int16_t repeat); // *Note instead of sending the REPEAT constant if you want the JVC repeat signal sent, send the original code value and change the repeat argument from 0 to 1. JVC protocol repeats by skipping the header NOT by sending a separate code value like NEC does.
120176
void sendMagiQuest(uint32_t wand_id, uint16_t magitude);
177+
void sendSymaR5(uint32_t data);
178+
void sendSymaR3(uint32_t data);
179+
void sendUseries(uint32_t data);
180+
void sendFastLane(uint32_t data);
121181
// private:
122182
void enableIROut(int16_t khz);
123183
VIRTUAL void mark(int16_t usec);
@@ -127,11 +187,11 @@ class IRsend
127187

128188
// Some useful constants
129189

130-
#define USECPERTICK 50 // microseconds per clock interrupt tick
190+
#define USECPERTICK 25 // microseconds per clock interrupt tick
131191
#define RAWBUF 112 // Length of raw duration buffer
132192

133193
// Marks tend to be 100us too long, and spaces 100us too short
134194
// when received due to sensor lag.
135195
#define MARK_EXCESS 100
136-
137196
#endif
197+

0 commit comments

Comments
 (0)