5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
23 // for the MULTI protocol definition
24 // see https://github.com/pascallanger/DIY-Multiprotocol-TX-Module
25 // file Multiprotocol/multiprotocol.h
27 #define MULTI_SEND_BIND (1 << 7)
28 #define MULTI_SEND_RANGECHECK (1 << 5)
29 #define MULTI_SEND_AUTOBIND (1 << 6)
31 #define MULTI_CHANS 16
32 #define MULTI_CHAN_BITS 11
34 static void sendFrameProtocolHeader(uint8_t port
, bool failsafe
);
36 void sendChannels(uint8_t port
);
38 static void sendSetupFrame()
40 // Old multi firmware will mark config messsages as invalid frame and throw them away
43 sendByteSbus(0x80); // Module Configuration
44 sendByteSbus(1); // 1 byte data
45 uint8_t config
= 0x01 | 0x02; // inversion + multi_telemetry
46 #if !defined(PPM_PIN_SERIAL)
47 // TODO why PPM_PIN_SERIAL would change MULTI protocol?
48 config
|= 0x04; // input synchronsisation
54 static void sendFailsafeChannels(uint8_t port
)
57 uint8_t bitsavailable
= 0;
59 for (int i
= 0; i
< MULTI_CHANS
; i
++) {
60 int16_t failsafeValue
= g_model
.moduleData
[port
].failsafeChannels
[i
];
62 if (g_model
.moduleData
[port
].failsafeMode
== FAILSAFE_HOLD
)
63 failsafeValue
= FAILSAFE_CHANNEL_HOLD
;
65 if (g_model
.moduleData
[port
].failsafeMode
== FAILSAFE_NOPULSES
)
66 failsafeValue
= FAILSAFE_CHANNEL_NOPULSE
;
68 if (failsafeValue
== FAILSAFE_CHANNEL_HOLD
) {
71 else if (failsafeValue
== FAILSAFE_CHANNEL_NOPULSE
) {
75 failsafeValue
+= 2 * PPM_CH_CENTER(g_model
.moduleData
[port
].channelsStart
+ i
) - 2 * PPM_CENTER
;
76 pulseValue
= limit(1, (failsafeValue
* 800 / 1000) + 1024, 2047);
79 bits
|= pulseValue
<< bitsavailable
;
80 bitsavailable
+= MULTI_CHAN_BITS
;
81 while (bitsavailable
>= 8) {
82 sendByteSbus((uint8_t) (bits
& 0xff));
89 void setupPulsesMultimodule(uint8_t port
)
91 static int counter
= 0;
93 #if defined(PPM_PIN_SERIAL)
94 modulePulsesData
[EXTERNAL_MODULE
].dsm2
.serialByte
= 0 ;
95 modulePulsesData
[EXTERNAL_MODULE
].dsm2
.serialBitCount
= 0 ;
97 modulePulsesData
[EXTERNAL_MODULE
].dsm2
.rest
= multiSyncStatus
.getAdjustedRefreshRate();
98 modulePulsesData
[EXTERNAL_MODULE
].dsm2
.index
= 0;
101 modulePulsesData
[EXTERNAL_MODULE
].dsm2
.ptr
= modulePulsesData
[EXTERNAL_MODULE
].dsm2
.pulses
;
103 // Every 1000 cycles (=9s) send a config packet that configures the multimodule (inversion, telemetry type)
105 if (counter
% 1000== 500) {
107 } else if (counter
% 1000 == 0 && g_model
.moduleData
[port
].failsafeMode
!= FAILSAFE_NOT_SET
&& g_model
.moduleData
[port
].failsafeMode
!= FAILSAFE_RECEIVER
) {
108 sendFrameProtocolHeader(port
, true);
109 sendFailsafeChannels(port
);
112 sendFrameProtocolHeader(port
, false);
120 void sendChannels(uint8_t port
)
123 uint8_t bitsavailable
= 0;
125 // byte 4-25, channels 0..2047
126 // Range for pulses (channelsOutputs) is [-1024:+1024] for [-100%;100%]
127 // Multi uses [204;1843] as [-100%;100%]
128 for (int i
= 0; i
< MULTI_CHANS
; i
++) {
129 int channel
= g_model
.moduleData
[port
].channelsStart
+ i
;
130 int value
= channelOutputs
[channel
] + 2 * PPM_CH_CENTER(channel
) - 2 * PPM_CENTER
;
133 value
= value
* 800 / 1000 + 1024;
134 value
= limit(0, value
, 2047);
136 bits
|= value
<< bitsavailable
;
137 bitsavailable
+= MULTI_CHAN_BITS
;
138 while (bitsavailable
>= 8) {
139 sendByteSbus((uint8_t) (bits
& 0xff));
146 void sendFrameProtocolHeader(uint8_t port
, bool failsafe
)
147 {// byte 1+2, protocol information
149 // Our enumeration starts at 0
150 int type
= g_model
.moduleData
[port
].getMultiProtocol(false) + 1;
151 int subtype
= g_model
.moduleData
[port
].subType
;
152 int8_t optionValue
= g_model
.moduleData
[port
].multi
.optionValue
;
154 uint8_t protoByte
= 0;
155 if (moduleFlag
[port
] == MODULE_BIND
)
156 protoByte
|= MULTI_SEND_BIND
;
157 else if (moduleFlag
[port
] == MODULE_RANGECHECK
)
158 protoByte
|= MULTI_SEND_RANGECHECK
;
161 if (g_model
.moduleData
[port
].getMultiProtocol(true) == MM_RF_PROTO_DSM2
) {
163 // Autobinding should always be done in DSMX 11ms
164 if (g_model
.moduleData
[port
].multi
.autoBindMode
&& moduleFlag
[port
] == MODULE_BIND
)
165 subtype
= MM_RF_DSM2_SUBTYPE_AUTO
;
167 // Multi module in DSM mode wants the number of channels to be used as option value
168 optionValue
= NUM_CHANNELS(EXTERNAL_MODULE
);
172 // 15 for Multimodule is FrskyX or D16 which we map as a subprotocol of 3 (FrSky)
173 // all protos > frskyx are therefore also off by one
177 // 25 is again a FrSky protocol (FrskyV) so shift again
181 if (g_model
.moduleData
[port
].getMultiProtocol(true) == MM_RF_PROTO_FRSKY
) {
182 if (subtype
== MM_RF_FRSKY_SUBTYPE_D8
) {
186 } else if (subtype
== MM_RF_FRSKY_SUBTYPE_V8
) {
192 if (subtype
== MM_RF_FRSKY_SUBTYPE_D16_8CH
) // D16 8ch
194 else if (subtype
== MM_RF_FRSKY_SUBTYPE_D16
)
196 else if (subtype
== MM_RF_FRSKY_SUBTYPE_D16_LBT
)
199 subtype
= 3; // MM_RF_FRSKY_SUBTYPE_D16_LBT_8CH
203 // Set the highest bit of option byte in AFHDS2A protocol to instruct MULTI to passthrough telemetry bytes instead
204 // of sending Frsky D telemetry
205 if (g_model
.moduleData
[port
].getMultiProtocol(false) == MM_RF_PROTO_FS_AFHDS2A
)
206 optionValue
= optionValue
| 0x80;
208 // For custom protocol send unmodified type byte
209 if (g_model
.moduleData
[port
].getMultiProtocol(true) == MM_RF_CUSTOM_SELECTED
)
210 type
= g_model
.moduleData
[port
].getMultiProtocol(false);
213 uint8_t headerByte
= 0x54;
217 // header, byte 0, 0x55 for proto 0-31 0x54 for 32-63
219 sendByteSbus(headerByte
+1);
221 sendByteSbus(headerByte
);
225 protoByte
|= (type
& 0x1f);
226 if (g_model
.moduleData
[port
].getMultiProtocol(true) != MM_RF_PROTO_DSM2
)
227 protoByte
|= (g_model
.moduleData
[port
].multi
.autoBindMode
<< 6);
229 sendByteSbus(protoByte
);
231 // byte 2, subtype, powermode, model id
232 sendByteSbus((uint8_t) ((g_model
.header
.modelId
[port
] & 0x0f)
233 | ((subtype
& 0x7) << 4)
234 | (g_model
.moduleData
[port
].multi
.lowPowerMode
<< 7))
238 sendByteSbus((uint8_t) optionValue
);