Line data Source code
1 : /*
2 : * This file is free software: you can redistribute it and/or modify it
3 : * under the terms of the GNU General Public License as published by the
4 : * Free Software Foundation, either version 3 of the License, or
5 : * (at your option) any later version.
6 : *
7 : * This file is distributed in the hope that it will be useful, but
8 : * WITHOUT ANY WARRANTY; without even the implied warranty of
9 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 : * See the GNU General Public License for more details.
11 : *
12 : * You should have received a copy of the GNU General Public License along
13 : * with this program. If not, see <http://www.gnu.org/licenses/>.
14 : *
15 : * AP_OSD partially based on betaflight and inav osd.c implemention.
16 : * clarity.mcm font is taken from inav configurator.
17 : * Many thanks to their authors.
18 : */
19 : /*
20 : parameter settings for one screen
21 : */
22 : #include "AP_OSD.h"
23 : #include "AP_OSD_Backend.h"
24 :
25 : #include <AP_HAL/AP_HAL.h>
26 : #include <AP_HAL/Util.h>
27 : #include <limits.h>
28 : #include <ctype.h>
29 : #include <AP_Vehicle/AP_Vehicle_Type.h>
30 : #include <AP_Arming/AP_Arming.h>
31 : #include <AP_Vehicle/AP_Vehicle_Type.h>
32 : #include <GCS_MAVLink/GCS.h>
33 :
34 : extern const AP_HAL::HAL& hal;
35 :
36 : #if OSD_PARAM_ENABLED
37 :
38 : const AP_Param::GroupInfo AP_OSD_ParamScreen::var_info[] = {
39 :
40 : // @Param: ENABLE
41 : // @DisplayName: Enable screen
42 : // @Description: Enable this screen
43 : // @Values: 0:Disabled,1:Enabled
44 : // @User: Standard
45 : AP_GROUPINFO_FLAGS("ENABLE", 1, AP_OSD_ParamScreen, enabled, 0, AP_PARAM_FLAG_ENABLE),
46 :
47 : // @Param: CHAN_MIN
48 : // @DisplayName: Transmitter switch screen minimum pwm
49 : // @Description: This sets the PWM lower limit for this screen
50 : // @Range: 900 2100
51 : // @User: Standard
52 : AP_GROUPINFO("CHAN_MIN", 2, AP_OSD_ParamScreen, channel_min, 900),
53 :
54 : // @Param: CHAN_MAX
55 : // @DisplayName: Transmitter switch screen maximum pwm
56 : // @Description: This sets the PWM upper limit for this screen
57 : // @Range: 900 2100
58 : // @User: Standard
59 : AP_GROUPINFO("CHAN_MAX", 3, AP_OSD_ParamScreen, channel_max, 2100),
60 :
61 : // @Group: PARAM1
62 : // @Path: AP_OSD_ParamSetting.cpp
63 : AP_SUBGROUPINFO(params[0], "PARAM1", 4, AP_OSD_ParamScreen, AP_OSD_ParamSetting),
64 :
65 : // @Group: PARAM2
66 : // @Path: AP_OSD_ParamSetting.cpp
67 : AP_SUBGROUPINFO(params[1], "PARAM2", 5, AP_OSD_ParamScreen, AP_OSD_ParamSetting),
68 :
69 : // @Group: PARAM3
70 : // @Path: AP_OSD_ParamSetting.cpp
71 : AP_SUBGROUPINFO(params[2], "PARAM3", 6, AP_OSD_ParamScreen, AP_OSD_ParamSetting),
72 :
73 : // @Group: PARAM4
74 : // @Path: AP_OSD_ParamSetting.cpp
75 : AP_SUBGROUPINFO(params[3], "PARAM4", 7, AP_OSD_ParamScreen, AP_OSD_ParamSetting),
76 :
77 : // @Group: PARAM5
78 : // @Path: AP_OSD_ParamSetting.cpp
79 : AP_SUBGROUPINFO(params[4], "PARAM5", 8, AP_OSD_ParamScreen, AP_OSD_ParamSetting),
80 :
81 : // @Group: PARAM6
82 : // @Path: AP_OSD_ParamSetting.cpp
83 : AP_SUBGROUPINFO(params[5], "PARAM6", 9, AP_OSD_ParamScreen, AP_OSD_ParamSetting),
84 :
85 : // @Group: PARAM7
86 : // @Path: AP_OSD_ParamSetting.cpp
87 : AP_SUBGROUPINFO(params[6], "PARAM7", 10, AP_OSD_ParamScreen, AP_OSD_ParamSetting),
88 :
89 : // @Group: PARAM8
90 : // @Path: AP_OSD_ParamSetting.cpp
91 : AP_SUBGROUPINFO(params[7], "PARAM8", 11, AP_OSD_ParamScreen, AP_OSD_ParamSetting),
92 :
93 : // @Group: PARAM9
94 : // @Path: AP_OSD_ParamSetting.cpp
95 : AP_SUBGROUPINFO(params[8], "PARAM9", 12, AP_OSD_ParamScreen, AP_OSD_ParamSetting),
96 :
97 : // @Param: SAVE_X
98 : // @DisplayName: SAVE_X
99 : // @Description: Horizontal position of Save button on screen
100 : // @Range: 0 25
101 : // @User: Advanced
102 : AP_GROUPINFO("SAVE_X", 13, AP_OSD_ParamScreen, save_x, 23),
103 :
104 : // @Param: SAVE_Y
105 : // @DisplayName: SAVE_Y
106 : // @Description: Vertical position of Save button on screen
107 : // @Range: 0 15
108 : // @User: Advanced
109 : AP_GROUPINFO("SAVE_Y", 14, AP_OSD_ParamScreen, save_y, 11),
110 :
111 : AP_GROUPEND
112 : };
113 :
114 : #define OSD_HOLD_BUTTON_PRESS_DELAY 100
115 : #define OSD_HOLD_BUTTON_PRESS_COUNT 18
116 :
117 : #define OSD_PARAM_DEBUG 0
118 :
119 : #if OSD_PARAM_DEBUG
120 : static const char* event_names[5] = {
121 : "NONE", "MENU_ENTER", "MENU_UP", "MENU_DOWN", "IN_MENU_EXIT"
122 : };
123 : #define debug(fmt, args ...) do { hal.console->printf("OSD: " fmt, args); } while (0)
124 : #else
125 : #define debug(fmt, args ...)
126 : #endif
127 :
128 : static const AP_OSD_ParamSetting::Initializer PARAM_DEFAULTS[AP_OSD_NUM_PARAM_SCREENS][AP_OSD_ParamScreen::NUM_PARAMS] {
129 : #if APM_BUILD_COPTER_OR_HELI
130 : {
131 : { 1, { 102, 0, 4033 }, AP_OSD_ParamSetting::Type::NONE }, // ATC_RAT_RLL_P
132 : { 2, { 102, 0, 129 }, AP_OSD_ParamSetting::Type::NONE }, // ATC_RAT_RLL_D
133 : { 3, { 102, 0, 705 }, AP_OSD_ParamSetting::Type::NONE }, // ATC_RAT_RLL_FLTD
134 : { 4, { 102, 0, 4034 }, AP_OSD_ParamSetting::Type::NONE }, // ATC_RAT_PIT_P
135 : { 5, { 102, 0, 130 }, AP_OSD_ParamSetting::Type::NONE }, // ATC_RAT_PIT_D
136 : { 6, { 102, 0, 706 }, AP_OSD_ParamSetting::Type::NONE }, // ATC_RAT_PIT_FLTD
137 : { 7, { 102, 0, 4035 }, AP_OSD_ParamSetting::Type::NONE }, // ATC_RAT_YAW_P
138 : { 8, { 102, 0, 131 }, AP_OSD_ParamSetting::Type::NONE }, // ATC_RAT_YAW_D
139 : { 9, { 102, 0, 643 }, AP_OSD_ParamSetting::Type::NONE } // ATC_RAT_YAW_FLTE
140 : },
141 : {
142 : { 1, { 3, 0, 231 }, AP_OSD_ParamSetting::Type::NONE }, // INS_LOG_BAT_OPT
143 : { 2, { 3, 0, 167 }, AP_OSD_ParamSetting::Type::NONE }, // INS_LOG_BAT_MASK
144 : { 3, { 60, 0, 0 }, AP_OSD_ParamSetting::Type::NONE }, // LOG_BITMASK
145 : { 4, { 3, 0, 18 }, AP_OSD_ParamSetting::Type::NONE }, // INS_GYRO_FILT
146 : { 5, { 102, 0, 6 }, AP_OSD_ParamSetting::Type::NONE }, // ATC_THR_MIX_MAN
147 : { 6, { 102, 0, 5 }, AP_OSD_ParamSetting::Type::NONE }, // ATC_THR_MIX_MAX
148 : { 7, { 6, 0, 25041 }, AP_OSD_ParamSetting::Type::AUX_FUNCTION }, // RC7_OPTION
149 : { 8, { 6, 0, 25105 }, AP_OSD_ParamSetting::Type::AUX_FUNCTION }, // RC8_OPTION
150 : { 9, { 36, 0, 1047 }, AP_OSD_ParamSetting::Type::FAILSAFE_ACTION_2 } // BATT_FS_LOW_ACT
151 : }
152 : #elif APM_BUILD_TYPE(APM_BUILD_ArduPlane)
153 : {
154 : { 1, { 232, 0, 265 }, AP_OSD_ParamSetting::Type::NONE }, // RLL_RATE_FF
155 : { 2, { 232, 0, 4041 }, AP_OSD_ParamSetting::Type::NONE }, // RLL_RATE_P
156 : { 3, { 232, 0, 73 }, AP_OSD_ParamSetting::Type::NONE }, // RLL_RATE_I
157 : { 4, { 233, 0, 267 }, AP_OSD_ParamSetting::Type::NONE }, // PTCH_RATE_FF
158 : { 5, { 233, 0, 4043 }, AP_OSD_ParamSetting::Type::NONE }, // PTCH_RATE_P
159 : { 6, { 233, 0, 75 }, AP_OSD_ParamSetting::Type::NONE }, // PTCH_RATE_I
160 : { 7, { 233, 0, 6 }, AP_OSD_ParamSetting::Type::NONE }, // PTCH2SRV_RLL
161 : { 8, { 199, 0, 1 }, AP_OSD_ParamSetting::Type::NONE }, // TUNE_PARAM
162 : { 9, { 199, 0, 320 }, AP_OSD_ParamSetting::Type::NONE } // TUNE_RANGE
163 : },
164 : {
165 : { 1, { 185, 0, 0 }, AP_OSD_ParamSetting::Type::NONE }, // TRIM_THROTTLE
166 : { 2, { 155, 0, 0 }, AP_OSD_ParamSetting::Type::NONE }, // AIRSPEED_CRUISE
167 : { 3, { 4, 0, 1094 }, AP_OSD_ParamSetting::Type::NONE }, // SERVO_AUTO_TRIM
168 : { 4, { 120, 0, 0 }, AP_OSD_ParamSetting::Type::NONE}, // AIRSPEED_MIN
169 : { 5, { 121, 0, 0 }, AP_OSD_ParamSetting::Type::NONE }, // AIRSPEED_MAX
170 : { 6, { 156, 0, 0 }, AP_OSD_ParamSetting::Type::NONE }, // RTL_ALTITUDE
171 : { 7, { 140, 2, 8 }, AP_OSD_ParamSetting::Type::NONE }, // AHRS_TRIM_Y
172 : { 8, { 182, 0, 0 }, AP_OSD_ParamSetting::Type::NONE }, // THR_MAX
173 : { 9, { 189, 0, 0 }, AP_OSD_ParamSetting::Type::NONE } // THR_SLEWRATE
174 : }
175 : #else
176 : {
177 : { 1 }, { 2 }, { 3 }, { 4 }, { 5 }, { 6 }, { 7 }, { 8 }, { 9 },
178 : },
179 : {
180 : { 1 }, { 2 }, { 3 }, { 4 }, { 5 }, { 6 }, { 7 }, { 8 }, { 9 }
181 : }
182 : #endif
183 : };
184 :
185 41220 : AP_OSD_ParamScreen::AP_OSD_ParamScreen(uint8_t index)
186 : {
187 41220 : for (uint8_t i = 0; i < NUM_PARAMS; i++) {
188 37098 : params[i] = PARAM_DEFAULTS[index][i];
189 : }
190 4122 : AP_Param::setup_object_defaults(this, var_info);
191 4122 : }
192 :
193 0 : AP_OSD_ParamSetting* AP_OSD_ParamScreen::get_setting(uint8_t param_idx)
194 : {
195 0 : if (param_idx >= NUM_PARAMS) {
196 0 : return nullptr;
197 : }
198 0 : params[param_idx].update(); // make sure we are fresh
199 0 : return ¶ms[param_idx];
200 : }
201 :
202 : #if OSD_ENABLED
203 0 : void AP_OSD_ParamScreen::draw_parameter(uint8_t number, uint8_t x, uint8_t y)
204 : {
205 0 : bool param_blink = false;
206 0 : bool value_blink = false;
207 0 : const bool selected = number == _selected_param;
208 :
209 0 : switch(_menu_state) {
210 0 : case MenuState::PARAM_SELECT:
211 0 : param_blink = selected;
212 0 : break;
213 0 : case MenuState::PARAM_VALUE_MODIFY:
214 0 : value_blink = selected;
215 0 : break;
216 0 : case MenuState::PARAM_PARAM_MODIFY:
217 0 : param_blink = value_blink = selected;
218 0 : break;
219 : }
220 :
221 0 : if (number >= NUM_PARAMS + 1) {
222 0 : backend->write(x, y, param_blink, "%s", _requires_save ? " SAVE" : "REBOOT");
223 0 : return;
224 : }
225 :
226 0 : AP_OSD_ParamSetting& setting = params[number-1];
227 0 : setting.update();
228 :
229 0 : ap_var_type type = setting._param_type;
230 0 : AP_Param* p = setting._param;
231 0 : if (p != nullptr) {
232 : // grab the name of the parameter
233 : char name[17];
234 0 : setting.copy_name(name, 17);
235 :
236 0 : const AP_OSD_ParamSetting::ParamMetadata* metadata = setting.get_custom_metadata();
237 :
238 0 : uint16_t value_pos = 19;
239 0 : backend->write(x, y, param_blink, "%s:", name);
240 :
241 0 : switch (type) {
242 0 : case AP_PARAM_INT8: {
243 0 : int8_t val = ((AP_Int8*)p)->get();
244 0 : if (metadata != nullptr && val >= 0 && val < metadata->values_max) {
245 0 : backend->write(value_pos, y, value_blink, "%s", metadata->values[val]);
246 : } else {
247 0 : backend->write(value_pos, y, value_blink, "%hhd", val);
248 : }
249 0 : break;
250 : }
251 0 : case AP_PARAM_INT16: {
252 0 : int16_t val = ((AP_Int16*)p)->get();
253 0 : if (metadata != nullptr && val >= 0 && val < metadata->values_max) {
254 0 : backend->write(value_pos, y, value_blink, "%s", metadata->values[val]);
255 : } else {
256 0 : backend->write(value_pos, y, value_blink, "%hd", val);
257 : }
258 0 : break;
259 : }
260 0 : case AP_PARAM_INT32: {
261 0 : int32_t val = ((AP_Int32*)p)->get();
262 0 : if (metadata != nullptr && val >= 0 && val < metadata->values_max) {
263 0 : backend->write(value_pos, y, value_blink, "%s", metadata->values[val]);
264 : } else {
265 0 : backend->write(value_pos, y, value_blink, "%d", (signed)val);
266 : }
267 0 : break;
268 : }
269 0 : case AP_PARAM_FLOAT: {
270 0 : const float val = ((AP_Float*)p)->get();
271 : // cope with really small value
272 0 : if (val < 0.01 && !is_zero(val)) {
273 0 : backend->write(value_pos, y, value_blink, "%.4f", val);
274 0 : } else if (val < 0.001 && !is_zero(val)) {
275 0 : backend->write(value_pos, y, value_blink, "%.5f", val);
276 : } else {
277 0 : backend->write(value_pos, y, value_blink, "%.3f", val);
278 : }
279 0 : break;
280 : }
281 0 : case AP_PARAM_VECTOR3F:
282 : case AP_PARAM_NONE:
283 : case AP_PARAM_GROUP:
284 0 : break;
285 : }
286 : }
287 : }
288 :
289 : // modify the selected parameter number
290 0 : void AP_OSD_ParamScreen::modify_parameter(uint8_t number, Event ev)
291 : {
292 0 : if (number > NUM_PARAMS) {
293 0 : return;
294 : }
295 :
296 0 : const AP_OSD_ParamSetting& setting = params[number-1];
297 0 : AP_Param* p = setting._param;
298 :
299 0 : if (p == nullptr || p->is_read_only()) {
300 0 : return;
301 : }
302 :
303 0 : _requires_save |= 1 << (number-1);
304 :
305 0 : const float incr = setting._param_incr * ((ev == Event::MENU_DOWN) ? -1.0f : 1.0f);
306 0 : const int32_t incr_int = int32_t(roundf(incr));
307 0 : const int32_t max_int = int32_t(roundf(setting._param_max));
308 0 : const int32_t min_int = int32_t(roundf(setting._param_min));
309 :
310 0 : switch (setting._param_type) {
311 : // there is no way to validate the ranges, so as a rough guess prevent
312 : // integer types going below -1;
313 0 : case AP_PARAM_INT8: {
314 0 : AP_Int8* param = (AP_Int8*)p;
315 0 : param->set(constrain_int16(param->get() + incr_int, min_int, max_int));
316 0 : break;
317 : }
318 0 : case AP_PARAM_INT16: {
319 0 : AP_Int16* param = (AP_Int16*)p;
320 0 : param->set(constrain_int16(param->get() + incr_int, min_int, max_int));
321 0 : break;
322 : }
323 0 : case AP_PARAM_INT32: {
324 0 : AP_Int32* param = (AP_Int32*)p;
325 0 : param->set(constrain_int32(param->get() + incr_int, min_int, max_int));
326 0 : break;
327 : }
328 0 : case AP_PARAM_FLOAT: {
329 0 : AP_Float* param = (AP_Float*)p;
330 0 : param->set(constrain_float(param->get() + incr, setting._param_min, setting._param_max));
331 0 : break;
332 : }
333 0 : case AP_PARAM_VECTOR3F:
334 : case AP_PARAM_NONE:
335 : case AP_PARAM_GROUP:
336 0 : break;
337 : }
338 :
339 : }
340 :
341 : // modify which parameter is configured for the given selection
342 0 : void AP_OSD_ParamScreen::modify_configured_parameter(uint8_t number, Event ev)
343 : {
344 0 : if (number > NUM_PARAMS) {
345 0 : return;
346 : }
347 :
348 0 : _requires_save |= 1 << (number-1);
349 :
350 0 : AP_OSD_ParamSetting& setting = params[number-1];
351 : AP_Param* param;
352 :
353 0 : if (ev == Event::MENU_DOWN) {
354 0 : param = AP_Param::next_scalar(&setting._current_token, &setting._param_type);
355 : } else {
356 : // going backwards is somewhat convoluted as the param code is geared for going forward
357 0 : ap_var_type type = AP_PARAM_NONE, prev_type = AP_PARAM_NONE, prev_prev_type = AP_PARAM_NONE;
358 0 : AP_Param::ParamToken token {}, prev_token, prev_prev_token;
359 :
360 0 : for (param = AP_Param::first(&token, &type);
361 0 : param && (setting._current_token.key != token.key
362 0 : || setting._current_token.idx != token.idx
363 0 : || setting._current_token.group_element != token.group_element);
364 0 : param = AP_Param::next_scalar(&token, &type)) {
365 0 : prev_prev_token = prev_token;
366 0 : prev_prev_type = prev_type;
367 0 : prev_token = token;
368 0 : prev_type = type;
369 : }
370 0 : if (param != nullptr) {
371 0 : param = AP_Param::next_scalar(&prev_prev_token, &prev_prev_type);
372 0 : setting._current_token = prev_prev_token;
373 0 : setting._param_type = prev_prev_type;
374 : }
375 : }
376 :
377 0 : if (param != nullptr) {
378 : // update the stored index
379 0 : setting._param_group.set(setting._current_token.group_element);
380 0 : setting._param_key.set(AP_Param::get_persistent_key(setting._current_token.key));
381 0 : setting._param_idx.set(setting._current_token.idx);
382 0 : setting._param = param;
383 0 : setting._type.set(AP_OSD_ParamSetting::Type::NONE);
384 : // force update() to refresh the token
385 0 : setting._current_token.key = 0;
386 0 : setting._current_token.idx = 0;
387 0 : setting._current_token.group_element = 0;
388 : }
389 : }
390 :
391 : // map rc input to an event
392 0 : AP_OSD_ParamScreen::Event AP_OSD_ParamScreen::map_rc_input_to_event() const
393 : {
394 0 : const RC_Channel::AuxSwitchPos throttle = rc().get_throttle_channel().get_stick_gesture_pos();
395 0 : const RC_Channel::AuxSwitchPos yaw = rc().get_yaw_channel().get_stick_gesture_pos();
396 0 : const RC_Channel::AuxSwitchPos roll = rc().get_roll_channel().get_stick_gesture_pos();
397 0 : const RC_Channel::AuxSwitchPos pitch = rc().get_pitch_channel().get_stick_gesture_pos();
398 :
399 0 : Event result = Event::NONE;
400 :
401 0 : if (yaw != RC_Channel::AuxSwitchPos::MIDDLE || throttle != RC_Channel::AuxSwitchPos::LOW) {
402 0 : return result;
403 : }
404 :
405 0 : if (pitch == RC_Channel::AuxSwitchPos::MIDDLE && roll == RC_Channel::AuxSwitchPos::LOW) {
406 0 : result = Event::MENU_EXIT;
407 0 : } else if (pitch == RC_Channel::AuxSwitchPos::MIDDLE && roll == RC_Channel::AuxSwitchPos::HIGH) {
408 0 : result = Event::MENU_ENTER;
409 0 : } else if (pitch == RC_Channel::AuxSwitchPos::LOW && roll == RC_Channel::AuxSwitchPos::MIDDLE) {
410 0 : result = Event::MENU_UP;
411 0 : } else if (pitch == RC_Channel::AuxSwitchPos::HIGH && roll == RC_Channel::AuxSwitchPos::MIDDLE) {
412 0 : result = Event::MENU_DOWN;
413 : } else {
414 : // OSD option has not changed so assume stick re-centering
415 0 : result = Event::NONE;
416 : }
417 0 : return result;
418 : }
419 :
420 : // update the state machine when disarmed
421 0 : void AP_OSD_ParamScreen::update_state_machine()
422 : {
423 0 : const uint32_t now = AP_HAL::millis();
424 0 : if ((now - _transition_start_ms) < _transition_timeout_ms) {
425 0 : return;
426 : }
427 :
428 0 : const Event ev = map_rc_input_to_event();
429 : // only take action on transitions
430 0 : if (ev == Event::NONE && ev == _last_rc_event) {
431 0 : return;
432 : }
433 :
434 : debug("update_state_machine(%s)\n", event_names[int(ev)]);
435 :
436 0 : _transition_start_ms = now;
437 0 : if (ev == _last_rc_event) {
438 0 : _transition_timeout_ms = OSD_HOLD_BUTTON_PRESS_DELAY;
439 0 : _transition_count++;
440 : } else {
441 0 : _transition_timeout_ms = osd->button_delay_ms;
442 0 : _transition_count = 0;
443 : }
444 0 : _last_rc_event = ev;
445 :
446 : // if we were armed then there is no selected parameter - so find one
447 0 : if (_selected_param == 0) {
448 0 : _selected_param = 1;
449 0 : for (uint8_t i = 0; i < NUM_PARAMS && !params[_selected_param-1].enabled; i++) {
450 0 : _selected_param++;
451 : }
452 : }
453 :
454 0 : switch (ev) {
455 0 : case Event::MENU_ENTER:
456 0 : switch(_menu_state) {
457 0 : case MenuState::PARAM_SELECT:
458 0 : if (_selected_param == SAVE_PARAM) {
459 0 : if (_transition_count >= OSD_HOLD_BUTTON_PRESS_COUNT) {
460 0 : save_parameters();
461 0 : hal.scheduler->reboot();
462 : } else {
463 0 : save_parameters();
464 : }
465 : } else {
466 0 : _menu_state = MenuState::PARAM_VALUE_MODIFY;
467 : }
468 0 : break;
469 0 : case MenuState::PARAM_VALUE_MODIFY:
470 0 : if (_transition_count >= OSD_HOLD_BUTTON_PRESS_COUNT) {
471 0 : _menu_state = MenuState::PARAM_PARAM_MODIFY;
472 : }
473 0 : break;
474 0 : case MenuState::PARAM_PARAM_MODIFY:
475 0 : break;
476 : }
477 0 : break;
478 0 : case Event::MENU_UP:
479 0 : switch (_menu_state) {
480 0 : case MenuState::PARAM_SELECT:
481 0 : _selected_param--;
482 0 : if (_selected_param < 1) {
483 0 : _selected_param = SAVE_PARAM;
484 : }
485 : // skip over parameters that are not enabled
486 0 : for (uint8_t i = 0; i < NUM_PARAMS + 1 && (_selected_param != SAVE_PARAM && !params[_selected_param-1].enabled); i++) {
487 0 : _selected_param--;
488 0 : if (_selected_param < 1) {
489 0 : _selected_param = SAVE_PARAM;
490 : }
491 : }
492 : // repeat at the standard rate
493 0 : _transition_timeout_ms = osd->button_delay_ms;
494 0 : break;
495 0 : case MenuState::PARAM_VALUE_MODIFY:
496 0 : modify_parameter(_selected_param, ev);
497 0 : break;
498 0 : case MenuState::PARAM_PARAM_MODIFY:
499 0 : modify_configured_parameter(_selected_param, ev);
500 0 : break;
501 : }
502 0 : break;
503 0 : case Event::MENU_DOWN:
504 0 : switch (_menu_state) {
505 0 : case MenuState::PARAM_SELECT:
506 0 : _selected_param++;
507 0 : if (_selected_param > SAVE_PARAM) {
508 0 : _selected_param = 1;
509 : }
510 : // skip over parameters that are not enabled
511 0 : for (uint8_t i = 0; i < NUM_PARAMS + 1 && (_selected_param != SAVE_PARAM && !params[_selected_param-1].enabled); i++) {
512 0 : _selected_param++;
513 0 : if (_selected_param > SAVE_PARAM) {
514 0 : _selected_param = 1;
515 : }
516 : }
517 : // repeat at the standard rate
518 0 : _transition_timeout_ms = osd->button_delay_ms;
519 0 : break;
520 0 : case MenuState::PARAM_VALUE_MODIFY:
521 0 : modify_parameter(_selected_param, ev);
522 0 : break;
523 0 : case MenuState::PARAM_PARAM_MODIFY:
524 0 : modify_configured_parameter(_selected_param, ev);
525 0 : break;
526 : }
527 0 : break;
528 0 : case Event::MENU_EXIT:
529 0 : switch(_menu_state) {
530 0 : case MenuState::PARAM_SELECT:
531 0 : break;
532 0 : case MenuState::PARAM_VALUE_MODIFY:
533 0 : _menu_state = MenuState::PARAM_SELECT;
534 0 : break;
535 0 : case MenuState::PARAM_PARAM_MODIFY:
536 0 : _menu_state = MenuState::PARAM_VALUE_MODIFY;
537 0 : break;
538 : }
539 0 : break;
540 0 : case Event::NONE:
541 0 : break;
542 : }
543 : }
544 :
545 : #if HAL_WITH_OSD_BITMAP || HAL_WITH_MSP_DISPLAYPORT
546 0 : void AP_OSD_ParamScreen::draw(void)
547 : {
548 0 : if (!enabled || !backend) {
549 0 : return;
550 : }
551 :
552 : // first update the state machine
553 0 : if (!AP::arming().is_armed()) {
554 0 : update_state_machine();
555 : } else {
556 0 : _selected_param = 0;
557 : }
558 :
559 0 : for (uint8_t i = 0; i < NUM_PARAMS; i++) {
560 0 : AP_OSD_ParamSetting n = params[i];
561 0 : if (n.enabled) {
562 0 : draw_parameter(n._param_number, n.xpos, n.ypos);
563 : }
564 : }
565 : // the save button
566 0 : draw_parameter(SAVE_PARAM, save_x, save_y);
567 : }
568 : #endif
569 :
570 : #endif // OSD_ENABLED
571 :
572 : // save all of the parameters
573 0 : void AP_OSD_ParamScreen::save_parameters()
574 : {
575 0 : if (!_requires_save) {
576 0 : return;
577 : }
578 :
579 0 : for (uint8_t i = 0; i < NUM_PARAMS; i++) {
580 0 : if (params[i].enabled && (_requires_save & (1 << i))) {
581 0 : AP_Param* p = params[i]._param;
582 0 : if (p != nullptr) {
583 0 : p->save();
584 : }
585 0 : params[i].save_as_new();
586 : }
587 : }
588 0 : _requires_save = 0;
589 : }
590 :
591 : // handle OSD configuration messages
592 : #if HAL_GCS_ENABLED
593 0 : void AP_OSD_ParamScreen::handle_write_msg(const mavlink_osd_param_config_t& packet, const class GCS_MAVLINK& link)
594 : {
595 : // request out of range - return an error
596 0 : if (packet.osd_index < 1 || packet.osd_index > AP_OSD_ParamScreen::NUM_PARAMS) {
597 0 : mavlink_msg_osd_param_config_reply_send(link.get_chan(), packet.request_id, OSD_PARAM_INVALID_PARAMETER_INDEX);
598 0 : return;
599 : }
600 : // set the parameter
601 0 : bool ret = params[packet.osd_index - 1].set_by_name(packet.param_id, packet.config_type, packet.min_value, packet.max_value, packet.increment);
602 0 : mavlink_msg_osd_param_config_reply_send(link.get_chan(), packet.request_id, ret ? OSD_PARAM_SUCCESS : OSD_PARAM_INVALID_PARAMETER);
603 : }
604 :
605 : // handle OSD show configuration messages
606 0 : void AP_OSD_ParamScreen::handle_read_msg(const mavlink_osd_param_show_config_t& packet, const class GCS_MAVLINK& link)
607 : {
608 : // request out of range - return an error
609 0 : if (packet.osd_index < 1 || packet.osd_index > AP_OSD_ParamScreen::NUM_PARAMS) {
610 0 : mavlink_msg_osd_param_show_config_reply_send(link.get_chan(), packet.request_id, OSD_PARAM_INVALID_PARAMETER_INDEX,
611 : nullptr, uint8_t(AP_OSD_ParamSetting::Type::NONE), 0, 0, 0);
612 0 : return;
613 : }
614 : // get the parameter and make sure it is fresh
615 0 : AP_OSD_ParamSetting& param = params[packet.osd_index - 1];
616 0 : param.update();
617 :
618 : // check for bad things
619 0 : if (param._param == nullptr) {
620 0 : mavlink_msg_osd_param_show_config_reply_send(link.get_chan(), packet.request_id, OSD_PARAM_INVALID_PARAMETER_INDEX,
621 : nullptr, uint8_t(AP_OSD_ParamSetting::Type::NONE), 0, 0, 0);
622 0 : return;
623 : }
624 : // get the name and send back the details
625 : char buf[AP_MAX_NAME_SIZE+1];
626 0 : param._param->copy_name_token(param._current_token, buf, AP_MAX_NAME_SIZE);
627 0 : buf[AP_MAX_NAME_SIZE] = 0;
628 0 : mavlink_msg_osd_param_show_config_reply_send(link.get_chan(), packet.request_id, OSD_PARAM_SUCCESS,
629 : buf, param._type, param._param_min, param._param_max, param._param_incr);
630 : }
631 : #endif
632 :
633 : #endif // OSD_PARAM_ENABLED
|