Make Horus widgets 'shadow' effect an option (#5203)
[opentx.git] / radio / src / lua / api_general.cpp
blob9c1ebd6506b79a76fcf05010c189eec02600ea2e
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
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.
21 #include <ctype.h>
22 #include <stdio.h>
23 #include "opentx.h"
24 #include "stamp.h"
25 #include "lua_api.h"
26 #include "telemetry/frsky.h"
28 #if defined(PCBX12S)
29 #include "lua/lua_exports_x12s.inc" // this line must be after lua headers
30 #elif defined(PCBX10)
31 #include "lua/lua_exports_x10.inc"
32 #elif defined(PCBFLAMENCO)
33 #include "lua/lua_exports_flamenco.inc"
34 #elif defined(PCBX9E)
35 #include "lua/lua_exports_x9e.inc"
36 #elif defined(PCBX7)
37 #include "lua/lua_exports_x7.inc"
38 #elif defined(PCBTARANIS)
39 #include "lua/lua_exports_x9d.inc"
40 #endif
42 #if defined(SIMU)
43 #define RADIO_VERSION FLAVOUR "-simu"
44 #else
45 #define RADIO_VERSION FLAVOUR
46 #endif
48 #define FIND_FIELD_DESC 0x01
50 /*luadoc
51 @function getVersion()
53 Return OpenTX version
55 @retval string OpenTX version (ie "2.1.5")
57 @retval multiple (available since 2.1.7) returns 5 values:
58 * (string) OpenTX version (ie "2.1.5")
59 * (string) radio version: `x9e`, `x9d+` or `x9d`.
60 If running in simulator the "-simu" is added
61 * (number) major version (ie 2 if version 2.1.5)
62 * (number) minor version (ie 1 if version 2.1.5)
63 * (number) revision number (ie 5 if version 2.1.5)
65 @status current Introduced in 2.0.0, expanded in 2.1.7
67 ### Example
69 This example also runs in OpenTX versions where the function returned only one value:
71 ```lua
72 local function run(event)
73 local ver, radio, maj, minor, rev = getVersion()
74 print("version: "..ver)
75 if radio then print ("radio: "..radio) end
76 if maj then print ("maj: "..maj) end
77 if minor then print ("minor: "..minor) end
78 if rev then print ("rev: "..rev) end
79 return 1
80 end
82 return { run=run }
83 ```
84 Output of the above script in simulator:
85 ```
86 version: 2.1.7
87 radio: taranis-simu
88 maj: 2
89 minor: 1
90 rev: 7
91 ```
93 static int luaGetVersion(lua_State * L)
95 lua_pushstring(L, VERSION);
96 lua_pushstring(L, RADIO_VERSION);
97 lua_pushnumber(L, VERSION_MAJOR);
98 lua_pushnumber(L, VERSION_MINOR);
99 lua_pushnumber(L, VERSION_REVISION);
100 return 5;
103 /*luadoc
104 @function getTime()
106 Return the time since the radio was started in multiple of 10ms
108 @retval number Number of 10ms ticks since the radio was started Example:
109 run time: 12.54 seconds, return value: 1254
111 The timer internally uses a 32-bit counter which is enough for 30 years so
112 overflows will not happen.
114 @status current Introduced in 2.0.0
116 static int luaGetTime(lua_State * L)
118 lua_pushunsigned(L, get_tmr10ms());
119 return 1;
122 static void luaPushDateTime(lua_State * L, uint32_t year, uint32_t mon, uint32_t day,
123 uint32_t hour, uint32_t min, uint32_t sec)
125 lua_createtable(L, 0, 6);
126 lua_pushtableinteger(L, "year", year);
127 lua_pushtableinteger(L, "mon", mon);
128 lua_pushtableinteger(L, "day", day);
129 lua_pushtableinteger(L, "hour", hour);
130 lua_pushtableinteger(L, "min", min);
131 lua_pushtableinteger(L, "sec", sec);
134 /*luadoc
135 @function getDateTime()
137 Return current system date and time that is kept by the RTC unit
139 @retval table current date and time, table elements:
140 * `year` (number) year
141 * `mon` (number) month
142 * `day` (number) day of month
143 * `hour` (number) hours
144 * `min` (number) minutes
145 * `sec` (number) seconds
147 static int luaGetDateTime(lua_State * L)
149 struct gtm utm;
150 gettime(&utm);
151 luaPushDateTime(L, utm.tm_year + TM_YEAR_BASE, utm.tm_mon + 1, utm.tm_mday, utm.tm_hour, utm.tm_min, utm.tm_sec);
152 return 1;
155 static void luaPushLatLon(lua_State* L, TelemetrySensor & telemetrySensor, TelemetryItem & telemetryItem)
156 /* result is lua table containing members ["lat"] and ["lon"] as lua_Number (doubles) in decimal degrees */
158 lua_createtable(L, 0, 4);
159 lua_pushtablenumber(L, "lat", telemetryItem.gps.latitude * 0.000001); // floating point multiplication is faster than division
160 lua_pushtablenumber(L, "pilot-lat", telemetryItem.pilotLatitude * 0.000001);
161 lua_pushtablenumber(L, "lon", telemetryItem.gps.longitude * 0.000001);
162 lua_pushtablenumber(L, "pilot-lon", telemetryItem.pilotLongitude * 0.000001);
165 static void luaPushTelemetryDateTime(lua_State* L, TelemetrySensor & telemetrySensor, TelemetryItem & telemetryItem)
167 luaPushDateTime(L, telemetryItem.datetime.year, telemetryItem.datetime.month, telemetryItem.datetime.day,
168 telemetryItem.datetime.hour, telemetryItem.datetime.min, telemetryItem.datetime.sec);
171 static void luaPushCells(lua_State* L, TelemetrySensor & telemetrySensor, TelemetryItem & telemetryItem)
173 if (telemetryItem.cells.count == 0)
174 lua_pushinteger(L, (int)0); // returns zero if no cells
175 else {
176 lua_createtable(L, telemetryItem.cells.count, 0);
177 for (int i = 0; i < telemetryItem.cells.count; i++) {
178 lua_pushnumber(L, i + 1);
179 lua_pushnumber(L, telemetryItem.cells.values[i].value * 0.01f);
180 lua_settable(L, -3);
185 void luaGetValueAndPush(lua_State* L, int src)
187 getvalue_t value = getValue(src); // ignored for GPS, DATETIME, and CELLS
189 if (src >= MIXSRC_FIRST_TELEM && src <= MIXSRC_LAST_TELEM) {
190 div_t qr = div(src-MIXSRC_FIRST_TELEM, 3);
191 // telemetry values
192 if (TELEMETRY_STREAMING() && telemetryItems[qr.quot].isAvailable()) {
193 TelemetrySensor & telemetrySensor = g_model.telemetrySensors[qr.quot];
194 switch (telemetrySensor.unit) {
195 case UNIT_GPS:
196 luaPushLatLon(L, telemetrySensor, telemetryItems[qr.quot]);
197 break;
198 case UNIT_DATETIME:
199 luaPushTelemetryDateTime(L, telemetrySensor, telemetryItems[qr.quot]);
200 break;
201 case UNIT_CELLS:
202 if (qr.rem == 0) {
203 luaPushCells(L, telemetrySensor, telemetryItems[qr.quot]);
204 break;
206 // deliberate no break here to properly return `Cels-` and `Cels+`
207 default:
208 if (telemetrySensor.prec > 0)
209 lua_pushnumber(L, float(value)/telemetrySensor.getPrecDivisor());
210 else
211 lua_pushinteger(L, value);
212 break;
215 else {
216 // telemetry not working, return zero for telemetry sources
217 lua_pushinteger(L, (int)0);
220 else if (src == MIXSRC_TX_VOLTAGE) {
221 lua_pushnumber(L, float(value) * 0.1f);
223 else {
224 lua_pushinteger(L, value);
229 Return field data for a given field name
231 bool luaFindFieldByName(const char * name, LuaField & field, unsigned int flags)
233 // TODO better search method (binary lookup)
234 for (unsigned int n=0; n<DIM(luaSingleFields); ++n) {
235 if (!strcmp(name, luaSingleFields[n].name)) {
236 field.id = luaSingleFields[n].id;
237 if (flags & FIND_FIELD_DESC) {
238 strncpy(field.desc, luaSingleFields[n].desc, sizeof(field.desc)-1);
239 field.desc[sizeof(field.desc)-1] = '\0';
241 else {
242 field.desc[0] = '\0';
244 return true;
248 // search in multiples
249 unsigned int len = strlen(name);
250 for (unsigned int n=0; n<DIM(luaMultipleFields); ++n) {
251 const char * fieldName = luaMultipleFields[n].name;
252 unsigned int fieldLen = strlen(fieldName);
253 if (!strncmp(name, fieldName, fieldLen)) {
254 unsigned int index;
255 if (len == fieldLen+1 && isdigit(name[fieldLen])) {
256 index = name[fieldLen] - '1';
258 else if (len == fieldLen+2 && isdigit(name[fieldLen]) && isdigit(name[fieldLen+1])) {
259 index = 10 * (name[fieldLen] - '0') + (name[fieldLen+1] - '1');
261 else {
262 continue;
264 if (index < luaMultipleFields[n].count) {
265 field.id = luaMultipleFields[n].id + index;
266 if (flags & FIND_FIELD_DESC) {
267 snprintf(field.desc, sizeof(field.desc)-1, luaMultipleFields[n].desc, index+1);
268 field.desc[sizeof(field.desc)-1] = '\0';
270 else {
271 field.desc[0] = '\0';
273 return true;
278 // search in telemetry
279 field.desc[0] = '\0';
280 for (int i=0; i<MAX_TELEMETRY_SENSORS; i++) {
281 if (isTelemetryFieldAvailable(i)) {
282 char sensorName[TELEM_LABEL_LEN+1];
283 int len = zchar2str(sensorName, g_model.telemetrySensors[i].label, TELEM_LABEL_LEN);
284 if (!strncmp(sensorName, name, len)) {
285 if (name[len] == '\0') {
286 field.id = MIXSRC_FIRST_TELEM + 3*i;
287 field.desc[0] = '\0';
288 return true;
290 else if (name[len] == '-' && name[len+1] == '\0') {
291 field.id = MIXSRC_FIRST_TELEM + 3*i + 1;
292 field.desc[0] = '\0';
293 return true;
295 else if (name[len] == '+' && name[len+1] == '\0') {
296 field.id = MIXSRC_FIRST_TELEM + 3*i + 2;
297 field.desc[0] = '\0';
298 return true;
304 return false; // not found
307 /*luadoc
308 @function sportTelemetryPop()
310 Pops a received SPORT packet from the queue. Please note that only packets using a data ID within 0x5000 to 0x52FF
311 (frame ID == 0x10), as well as packets with a frame ID equal 0x32 (regardless of the data ID) will be passed to
312 the LUA telemetry receive queue.
314 @retval nil queue does not contain any (or enough) bytes to form a whole packet
316 @retval multiple returns 4 values:
317 * sensor ID (number)
318 * frame ID (number)
319 * data ID (number)
320 * value (number)
322 @status current Introduced in 2.2.0
324 static int luaSportTelemetryPop(lua_State * L)
326 if (!luaInputTelemetryFifo) {
327 luaInputTelemetryFifo = new Fifo<uint8_t, LUA_TELEMETRY_INPUT_FIFO_SIZE>();
328 if (!luaInputTelemetryFifo) {
329 return 0;
333 if (luaInputTelemetryFifo->size() >= sizeof(SportTelemetryPacket)) {
334 SportTelemetryPacket packet;
335 for (uint8_t i=0; i<sizeof(packet); i++) {
336 luaInputTelemetryFifo->pop(packet.raw[i]);
338 lua_pushnumber(L, packet.physicalId);
339 lua_pushnumber(L, packet.primId);
340 lua_pushnumber(L, packet.dataId);
341 lua_pushunsigned(L, packet.value);
342 return 4;
345 return 0;
348 #define BIT(x, index) (((x) >> index) & 0x01)
349 uint8_t getDataId(uint8_t physicalId)
351 uint8_t result = physicalId;
352 result += (BIT(physicalId, 0) ^ BIT(physicalId, 1) ^ BIT(physicalId, 2)) << 5;
353 result += (BIT(physicalId, 2) ^ BIT(physicalId, 3) ^ BIT(physicalId, 4)) << 6;
354 result += (BIT(physicalId, 0) ^ BIT(physicalId, 2) ^ BIT(physicalId, 4)) << 7;
355 return result;
358 /*luadoc
359 @function sportTelemetryPush()
361 This functions allows for sending SPORT telemetry data toward the receiver,
362 and more generally, to anything connected SPORT bus on the receiver or transmitter.
364 When called without parameters, it will only return the status of the output buffer without sending anything.
366 @param sensorId physical sensor ID
368 @param frameId frame ID
370 @param dataId data ID
372 @param value value
374 @retval boolean data queued in output buffer or not.
376 @status current Introduced in 2.2.0
378 static int luaSportTelemetryPush(lua_State * L)
380 if (lua_gettop(L) == 0) {
381 lua_pushboolean(L, isSportOutputBufferAvailable());
383 else if (isSportOutputBufferAvailable()) {
384 SportTelemetryPacket packet;
385 packet.physicalId = getDataId(luaL_checkunsigned(L, 1));
386 packet.primId = luaL_checkunsigned(L, 2);
387 packet.dataId = luaL_checkunsigned(L, 3);
388 packet.value = luaL_checkunsigned(L, 4);
389 sportOutputPushPacket(&packet);
390 lua_pushboolean(L, true);
392 else {
393 lua_pushboolean(L, false);
395 return 1;
398 /*luadoc
399 @function crossfireTelemetryPop()
401 Pops a received Crossfire Telemetry packet from the queue.
403 @retval nil queue does not contain any (or enough) bytes to form a whole packet
405 @retval multiple returns 2 values:
406 * command (number)
407 * packet (table) data bytes
409 @status current Introduced in 2.2.0
411 static int luaCrossfireTelemetryPop(lua_State * L)
413 if (!luaInputTelemetryFifo) {
414 luaInputTelemetryFifo = new Fifo<uint8_t, LUA_TELEMETRY_INPUT_FIFO_SIZE>();
415 if (!luaInputTelemetryFifo) {
416 return 0;
420 uint8_t length = 0, data = 0;
421 if (luaInputTelemetryFifo->probe(length) && luaInputTelemetryFifo->size() >= uint32_t(length)) {
422 // length value includes the length field
423 luaInputTelemetryFifo->pop(length);
424 luaInputTelemetryFifo->pop(data); // command
425 lua_pushnumber(L, data);
426 lua_newtable(L);
427 for (uint8_t i=1; i<length-1; i++) {
428 luaInputTelemetryFifo->pop(data);
429 lua_pushinteger(L, i);
430 lua_pushinteger(L, data);
431 lua_settable(L, -3);
433 return 2;
436 return 0;
439 /*luadoc
440 @function crossfireTelemetryPush()
442 This functions allows for sending telemetry data toward the TBS Crossfire link.
444 When called without parameters, it will only return the status of the output buffer without sending anything.
446 @param command command
448 @param data table of data bytes
450 @retval boolean data queued in output buffer or not.
452 @status current Introduced in 2.2.0
454 static int luaCrossfireTelemetryPush(lua_State * L)
456 if (lua_gettop(L) == 0) {
457 lua_pushboolean(L, isCrossfireOutputBufferAvailable());
459 else if (isCrossfireOutputBufferAvailable()) {
460 uint8_t command = luaL_checkunsigned(L, 1);
461 luaL_checktype(L, 2, LUA_TTABLE);
462 uint8_t length = luaL_len(L, 2);
463 telemetryOutputPushByte(MODULE_ADDRESS);
464 telemetryOutputPushByte(2 + length); // 1(COMMAND) + data length + 1(CRC)
465 telemetryOutputPushByte(command); // COMMAND
466 for (int i=0; i<length; i++) {
467 lua_rawgeti(L, 2, i+1);
468 telemetryOutputPushByte(luaL_checkunsigned(L, -1));
470 telemetryOutputPushByte(crc8(outputTelemetryBuffer+2, 1 + length));
471 telemetryOutputSetTrigger(command);
472 lua_pushboolean(L, true);
474 else {
475 lua_pushboolean(L, false);
477 return 1;
480 /*luadoc
481 @function getFieldInfo(name)
483 Return detailed information about field (source)
485 The list of valid sources is available:
487 | OpenTX Version | Radio |
488 |----------------|-------|
489 | 2.0 | [all](http://downloads-20.open-tx.org/firmware/lua_fields.txt) |
490 | 2.1 | [X9D and X9D+](http://downloads-21.open-tx.org/firmware/lua_fields_taranis.txt), [X9E](http://downloads-21.open-tx.org/firmware/lua_fields_taranis_x9e.txt) |
491 | 2.2 | [X9D and X9D+](http://downloads.open-tx.org/2.2/firmware/lua_fields_x9d.txt), [X9E](http://downloads.open-tx.org/2.2/firmware/lua_fields_x9e.txt), [Horus](http://downloads.open-tx.org/2.2/firmware/lua_fields_x12s.txt) |
493 @param name (string) name of the field
495 @retval table information about requested field, table elements:
496 * `id` (number) field identifier
497 * `name` (string) field name
498 * `desc` (string) field description
499 * 'unit' (number) unit identifier [Full list](../appendix/units.html)
501 @retval nil the requested field was not found
503 @status current Introduced in 2.0.8, 'unit' field added in 2.2.0
505 static int luaGetFieldInfo(lua_State * L)
507 const char * what = luaL_checkstring(L, 1);
508 LuaField field;
509 bool found = luaFindFieldByName(what, field, FIND_FIELD_DESC);
510 if (found) {
511 lua_newtable(L);
512 lua_pushtableinteger(L, "id", field.id);
513 lua_pushtablestring(L, "name", what);
514 lua_pushtablestring(L, "desc", field.desc);
515 if (field.id >= MIXSRC_FIRST_TELEM && field.id <= MIXSRC_LAST_TELEM) {
516 TelemetrySensor & telemetrySensor = g_model.telemetrySensors[(int)((field.id-MIXSRC_FIRST_TELEM)/3)];
517 lua_pushtableinteger(L, "unit", telemetrySensor.unit);
519 else {
520 lua_pushtablenil(L, "unit");
522 return 1;
524 return 0;
527 /*luadoc
528 @function getValue(source)
530 Returns the value of a source.
532 The list of fixed sources:
534 | OpenTX Version | Radio |
535 |----------------|-------|
536 | 2.0 | [all](http://downloads-20.open-tx.org/firmware/lua_fields.txt) |
537 | 2.1 | [X9D and X9D+](http://downloads-21.open-tx.org/firmware/lua_fields_taranis.txt), [X9E](http://downloads-21.open-tx.org/firmware/lua_fields_taranis_x9e.txt) |
538 | 2.2 | [X9D and X9D+](http://downloads.open-tx.org/2.2/firmware/lua_fields_x9d.txt), [X9E](http://downloads.open-tx.org/2.2/firmware/lua_fields_x9e.txt), [Horus](http://downloads.open-tx.org/2.2/firmware/lua_fields_x12s.txt) |
541 In OpenTX 2.1.x the telemetry sources no longer have a predefined name.
542 To get a telemetry value simply use it's sensor name. For example:
543 * Altitude sensor has a name "Alt"
544 * to get the current altitude use the source "Alt"
545 * to get the minimum altitude use the source "Alt-", to get the maximum use "Alt+"
547 @param source can be an identifier (number) (which was obtained by the getFieldInfo())
548 or a name (string) of the source.
550 @retval value current source value (number). Zero is returned for:
551 * non-existing sources
552 * for all telemetry source when the telemetry stream is not received
554 @retval table GPS position is returned in a table:
555 * `lat` (number) latitude, positive is North
556 * `lon` (number) longitude, positive is East
557 * `pilot-lat` (number) pilot latitude, positive is North
558 * `pilot-lon` (number) pilot longitude, positive is East
560 @retval table GPS date/time, see getDateTime()
562 @retval table Cells are returned in a table
563 (except where no cells were detected in which
564 case the returned value is 0):
565 * table has one item for each detected cell:
566 * key (number) cell number (1 to number of cells)
567 * value (number) current cell voltage
569 @status current Introduced in 2.0.0, changed in 2.1.0, `Cels+` and
570 `Cels-` added in 2.1.9
572 @notice Getting a value by its numerical identifier is faster then by its name.
573 While `Cels` sensor returns current values of all cells in a table, a `Cels+` or
574 `Cels-` will return a single value - the maximum or minimum Cels value.
576 static int luaGetValue(lua_State * L)
578 int src = 0;
579 if (lua_isnumber(L, 1)) {
580 src = luaL_checkinteger(L, 1);
582 else {
583 // convert from field name to its id
584 const char *name = luaL_checkstring(L, 1);
585 LuaField field;
586 bool found = luaFindFieldByName(name, field);
587 if (found) {
588 src = field.id;
591 luaGetValueAndPush(L, src);
592 return 1;
595 /*luadoc
596 @function getRAS()
598 Return the RAS value or nil if no valid hardware found
600 @retval number representing RAS value. Value bellow 0x33 (51 decimal) are all ok, value above 0x33 indicate a hardware antenna issue.
601 This is just a hardware pass/fail measure and does not represent the quality of the radio link
603 @notice RAS was called SWR in the past
605 @status current Introduced in 2.2.0
607 static int luaGetRAS(lua_State * L)
609 if (IS_SWR_VALUE_VALID()) {
610 lua_pushinteger(L, telemetryData.swr.value);
612 else {
613 lua_pushnil(L);
615 return 1;
619 /*luadoc
620 @function getFlightMode(mode)
622 Return flight mode data.
624 @param mode (number) flight mode number to return (0 - 8). If mode parameter
625 is not specified (or contains invalid value), then the current flight mode data is returned.
627 @retval multiple returns 2 values:
628 * (number) (current) flight mode number (0 - 8)
629 * (string) (current) flight mode name
631 @status current Introduced in 2.1.7
633 static int luaGetFlightMode(lua_State * L)
635 int mode = luaL_optinteger(L, 1, -1);
636 if (mode < 0 || mode >= MAX_FLIGHT_MODES) {
637 mode = mixerCurrentFlightMode;
639 lua_pushnumber(L, mode);
640 char name[sizeof(g_model.flightModeData[0].name)+1];
641 zchar2str(name, g_model.flightModeData[mode].name, sizeof(g_model.flightModeData[0].name));
642 lua_pushstring(L, name);
643 return 2;
646 /*luadoc
647 @function playFile(name)
649 Play a file from the SD card
651 @param path (string) full path to wav file (i.e. “/SOUNDS/en/system/tada.wav”)
652 Introduced in 2.1.0: If you use a relative path, the current language is appended
653 to the path (example: for English language: `/SOUNDS/en` is appended)
655 @status current Introduced in 2.0.0, changed in 2.1.0
657 static int luaPlayFile(lua_State * L)
659 const char * filename = luaL_checkstring(L, 1);
660 if (filename[0] != '/') {
661 // relative sound file path - use current language dir for absolute path
662 char file[AUDIO_FILENAME_MAXLEN+1];
663 char * str = getAudioPath(file);
664 strncpy(str, filename, AUDIO_FILENAME_MAXLEN - (str-file));
665 file[AUDIO_FILENAME_MAXLEN] = 0;
666 PLAY_FILE(file, 0, 0);
668 else {
669 PLAY_FILE(filename, 0, 0);
671 return 0;
674 /*luadoc
675 @function playNumber(value, unit [, attributes])
677 Play a numerical value (text to speech)
679 @param value (number) number to play. Value is interpreted as integer.
681 @param unit (number) unit identifier [Full list]((../appendix/units.html))
683 @param attributes (unsigned number) possible values:
684 * `0 or not present` plays integral part of the number (for a number 123 it plays 123)
685 * `PREC1` plays a number with one decimal place (for a number 123 it plays 12.3)
686 * `PREC2` plays a number with two decimal places (for a number 123 it plays 1.23)
688 @status current Introduced in 2.0.0
691 static int luaPlayNumber(lua_State * L)
693 int number = luaL_checkinteger(L, 1);
694 int unit = luaL_checkinteger(L, 2);
695 unsigned int att = luaL_optunsigned(L, 3, 0);
696 playNumber(number, unit, att, 0);
697 return 0;
700 /*luadoc
701 @function playDuration(duration [, hourFormat])
703 Play a time value (text to speech)
705 @param duration (number) number of seconds to play. Only integral part is used.
707 @param hourFormat (number):
708 * `0 or not present` play format: minutes and seconds.
709 * `!= 0` play format: hours, minutes and seconds.
711 @status current Introduced in 2.1.0
713 static int luaPlayDuration(lua_State * L)
715 int duration = luaL_checkinteger(L, 1);
716 bool playTime = (luaL_optinteger(L, 2, 0) != 0);
717 playDuration(duration, playTime ? PLAY_TIME : 0, 0);
718 return 0;
721 /*luadoc
722 @function playTone(frequency, duration, pause [, flags [, freqIncr]])
724 Play a tone
726 @param frequency (number) tone frequency in Hz (from 150 to 15000)
728 @param duration (number) length of the tone in milliseconds
730 @param pause (number) length of the silence after the tone in milliseconds
732 @param flags (number):
733 * `0 or not present` play with normal priority.
734 * `PLAY_BACKGROUND` play in background (built in vario function uses this context)
735 * `PLAY_NOW` play immediately
737 @param freqIncr (number) positive number increases the tone pitch (frequency with time),
738 negative number decreases it. The frequency changes every 10 milliseconds, the change is `freqIncr * 10Hz`.
739 The valid range is from -127 to 127.
741 @status current Introduced in 2.1.0
743 static int luaPlayTone(lua_State * L)
745 int frequency = luaL_checkinteger(L, 1);
746 int length = luaL_checkinteger(L, 2);
747 int pause = luaL_checkinteger(L, 3);
748 int flags = luaL_optinteger(L, 4, 0);
749 int freqIncr = luaL_optinteger(L, 5, 0);
750 audioQueue.playTone(frequency, length, pause, flags, freqIncr);
751 return 0;
754 /*luadoc
755 @function playHaptic(duration, pause [, flags])
757 Generate haptic feedback
759 @param duration (number) length of the haptic feedback in milliseconds
761 @param pause (number) length of the silence after haptic feedback in milliseconds
763 @param flags (number):
764 * `0 or not present` play with normal priority
765 * `PLAY_NOW` play immediately
767 @status current Introduced in 2.2.0
769 static int luaPlayHaptic(lua_State * L)
771 #if defined(HAPTIC)
772 int length = luaL_checkinteger(L, 1);
773 int pause = luaL_checkinteger(L, 2);
774 int flags = luaL_optinteger(L, 3, 0);
775 haptic.play(length, pause, flags);
776 #endif
777 return 0;
780 /*luadoc
781 @function killEvents(key)
783 Stops key state machine. See [Key Events](../key_events.md) for the detailed description.
785 @param key (number) key to be killed, can also include event type (only the key part is used)
787 @status current Introduced in 2.0.0
790 static int luaKillEvents(lua_State * L)
792 uint8_t key = EVT_KEY_MASK(luaL_checkinteger(L, 1));
793 // prevent killing maskable keys (only in telemetry scripts)
794 // TODO add which tpye of script is running before p_call()
795 if (IS_MASKABLE(key)) {
796 killEvents(key);
798 return 0;
801 #if LCD_DEPTH > 1 && !defined(COLORLCD)
802 /*luadoc
803 @function GREY()
805 Returns gray value which can be used in LCD functions
807 @retval (number) a value that represents amount of *greyness* (from 0 to 15)
809 @notice Only available on Taranis
811 @status current Introduced in 2.0.13
813 static int luaGrey(lua_State * L)
815 int index = luaL_checkinteger(L, 1);
816 lua_pushunsigned(L, GREY(index));
817 return 1;
819 #endif
821 /*luadoc
822 @function getGeneralSettings()
824 Returns (some of) the general radio settings
826 @retval table with elements:
827 * `battMin` (number) radio battery range - minimum value
828 * `battMax` (number) radio battery range - maximum value
829 * `imperial` (number) set to a value different from 0 if the radio is set to the
830 IMPERIAL units
831 * `language` (string) radio language (used for menus)
832 * `voice` (string) voice language (used for speech)
834 @status current Introduced in 2.0.6, `imperial` added in TODO,
835 `language` and `voice` added in 2.2.0.
838 static int luaGetGeneralSettings(lua_State * L)
840 lua_newtable(L);
841 lua_pushtablenumber(L, "battMin", (90+g_eeGeneral.vBatMin) * 0.1f);
842 lua_pushtablenumber(L, "battMax", (120+g_eeGeneral.vBatMax) * 0.1f);
843 lua_pushtableinteger(L, "imperial", g_eeGeneral.imperial);
844 lua_pushtablestring(L, "language", TRANSLATIONS);
845 lua_pushtablestring(L, "voice", currentLanguagePack->id);
846 return 1;
849 /*luadoc
850 @function popupInput(title, event, input, min, max)
852 Raises a pop-up on screen that allows uses input
854 @param title (string) text to display
856 @param event (number) the event variable that is passed in from the
857 Run function (key pressed)
859 @param input (number) value that can be adjusted by the +/­- keys
861 @param min (number) min value that input can reach (by pressing the -­ key)
863 @param max (number) max value that input can reach
865 @retval number result of the input adjustment
867 @retval "OK" user pushed ENT key
869 @retval "CANCEL" user pushed EXIT key
871 @notice Use only from stand-alone and telemetry scripts.
873 @status current Introduced in 2.0.0
875 static int luaPopupInput(lua_State * L)
877 event_t event = luaL_checkinteger(L, 2);
878 warningInputValue = luaL_checkinteger(L, 3);
879 warningInputValueMin = luaL_checkinteger(L, 4);
880 warningInputValueMax = luaL_checkinteger(L, 5);
881 warningText = luaL_checkstring(L, 1);
882 warningType = WARNING_TYPE_INPUT;
883 runPopupWarning(event);
884 if (warningResult) {
885 warningResult = 0;
886 lua_pushstring(L, "OK");
888 else if (!warningText) {
889 lua_pushstring(L, "CANCEL");
891 else {
892 lua_pushinteger(L, warningInputValue);
894 warningText = NULL;
895 return 1;
898 /*luadoc
899 @function popupWarning(title, event)
901 Raises a pop-up on screen that shows a warning
903 @param title (string) text to display
905 @param event (number) the event variable that is passed in from the
906 Run function (key pressed)
908 @retval "CANCEL" user pushed EXIT key
910 @notice Use only from stand-alone and telemetry scripts.
912 @status current Introduced in 2.2.0
914 static int luaPopupWarning(lua_State * L)
916 event_t event = luaL_checkinteger(L, 2);
917 warningText = luaL_checkstring(L, 1);
918 warningType = WARNING_TYPE_ASTERISK;
919 runPopupWarning(event);
920 if (!warningText) {
921 lua_pushstring(L, "CANCEL");
923 else {
924 warningText = NULL;
925 lua_pushnil(L);
927 return 1;
930 /*luadoc
931 @function popupConfirmation(title, event)
933 Raises a pop-up on screen that asks for confirmation
935 @param title (string) text to display
937 @param event (number) the event variable that is passed in from the
938 Run function (key pressed)
940 @retval "CANCEL" user pushed EXIT key
942 @notice Use only from stand-alone and telemetry scripts.
944 @status current Introduced in 2.2.0
946 static int luaPopupConfirmation(lua_State * L)
948 event_t event = luaL_checkinteger(L, 2);
949 warningText = luaL_checkstring(L, 1);
950 warningType = WARNING_TYPE_CONFIRM;
951 runPopupWarning(event);
952 if (!warningText) {
953 lua_pushstring(L, warningResult ? "OK" : "CANCEL");
955 else {
956 warningText = NULL;
957 lua_pushnil(L);
959 return 1;
962 /*luadoc
963 @function defaultStick(channel)
965 Get stick that is assigned to a channel. See Default Channel Order in General Settings.
967 @param channel (number) channel number (0 means CH1)
969 @retval number Stick assigned to this channel (from 0 to 3)
971 @status current Introduced in 2.0.0
973 static int luaDefaultStick(lua_State * L)
975 uint8_t channel = luaL_checkinteger(L, 1);
976 lua_pushinteger(L, channel_order(channel+1)-1);
977 return 1;
980 /*luadoc
981 @function setTelemetryValue(id, subID, instance, value [, unit [, precision [, name]]])
983 @param id Id of the sensor, valid range is from 0 to 0xFFFF
985 @param subID subID of the sensor, usually 0, valid range is from 0 to 7
987 @param instance instance of the sensor (SensorID), valid range is from 0 to 0xFF
989 @param value fed to the sensor
991 @param unit unit of the sensor [Full list](../appendix/units.html)
993 @param precision the precision of the sensor
994 * `0 or not present` no decimal precision.
995 * `!= 0` value is divided by 10^precision, e.g. value=1000, prec=2 => 10.00.
997 @param name (string) Name of the sensor if it does not yet exist (4 chars).
998 * `not present` Name defaults to the Id.
999 * `present` Sensor takes name of the argument. Argument must have name surrounded by quotes: e.g., "Name"
1001 @retval true, if the sensor was just added. In this case the value is ignored (subsequent call will set the value)
1003 @notice All three parameters `id`, `subID` and `instance` can't be zero at the same time. At least one of them
1004 must be different from zero.
1006 @status current Introduced in 2.2.0
1008 static int luaSetTelemetryValue(lua_State * L)
1010 uint16_t id = luaL_checkunsigned(L, 1);
1011 uint8_t subId = luaL_checkunsigned(L, 2) & 0x7;
1012 uint8_t instance = luaL_checkunsigned(L, 3);
1013 int32_t value = luaL_checkinteger(L, 4);
1014 uint32_t unit = luaL_optunsigned(L, 5, 0);
1015 uint32_t prec = luaL_optunsigned(L, 6, 0);
1017 char zname[4];
1018 const char* name = luaL_optstring(L, 7, NULL);
1019 if (name != NULL && strlen(name) > 0) {
1020 str2zchar(zname, name, 4);
1021 } else {
1022 zname[0] = hex2zchar((id & 0xf000) >> 12);
1023 zname[1] = hex2zchar((id & 0x0f00) >> 8);
1024 zname[2] = hex2zchar((id & 0x00f0) >> 4);
1025 zname[3] = hex2zchar((id & 0x000f) >> 0);
1027 if (id | subId | instance) {
1028 int index = setTelemetryValue(TELEM_PROTO_LUA, id, subId, instance, value, unit, prec);
1029 if (index >= 0) {
1030 TelemetrySensor &telemetrySensor = g_model.telemetrySensors[index];
1031 telemetrySensor.id = id;
1032 telemetrySensor.subId = subId;
1033 telemetrySensor.instance = instance;
1034 telemetrySensor.init(zname, unit, prec);
1035 lua_pushboolean(L, true);
1036 } else {
1037 lua_pushboolean(L, false);
1039 return 1;
1041 lua_pushboolean(L, false);
1042 return 1;
1045 /*luadoc
1046 @function defaultChannel(stick)
1048 Get channel assigned to stick. See Default Channel Order in General Settings
1050 @param stick (number) stick number (from 0 to 3)
1052 @retval number channel assigned to this stick (from 0 to 3)
1054 @retval nil stick not found
1056 @status current Introduced in 2.0.0
1058 static int luaDefaultChannel(lua_State * L)
1060 uint8_t stick = luaL_checkinteger(L, 1);
1061 for (int i=1; i<=4; i++) {
1062 int tmp = channel_order(i) - 1;
1063 if (tmp == stick) {
1064 lua_pushinteger(L, i-1);
1065 return 1;
1068 lua_pushnil(L);
1069 return 1;
1072 /*luadoc
1073 @function getRSSI()
1075 Get RSSI value as well as low and critical RSSI alarm levels (in dB)
1077 @retval rssi RSSI value (0 if no link)
1079 @retval alarm_low Configured low RSSI alarm level
1081 @retval alarm_crit Configured critical RSSI alarm level
1083 @status current Introduced in 2.2.0
1085 static int luaGetRSSI(lua_State * L)
1087 lua_pushunsigned(L, min((uint8_t)99, TELEMETRY_RSSI()));
1088 lua_pushunsigned(L, g_model.rssiAlarms.getWarningRssi());
1089 lua_pushunsigned(L, g_model.rssiAlarms.getCriticalRssi());
1090 return 3;
1093 /*luadoc
1094 @function loadScript(file [, mode], [,env])
1096 Load a Lua script file. This is similar to Lua's own [loadfile()](https://www.lua.org/manual/5.2/manual.html#pdf-loadfile)
1097 API method, but it uses OpenTx's optional pre-compilation feature to save memory and time during load.
1099 Return values are same as from Lua API loadfile() method: If the script was loaded w/out errors
1100 then the loaded script (or "chunk") is returned as a function. Otherwise, returns nil plus the error message.
1102 @param file (string) Full path and file name of script. The file extension is optional and ignored (see `mode` param to control
1103 which extension will be used). However, if an extension is specified, it should be ".lua" (or ".luac"), otherwise it is treated
1104 as part of the file name and the .lua/.luac will be appended to that.
1106 @param mode (string) (optional) Controls whether to force loading the text (.lua) or pre-compiled binary (.luac)
1107 version of the script. By default OTx will load the newest version and compile a new binary if necessary (overwriting any
1108 existing .luac version of the same script, and stripping some debug info like line numbers).
1109 You can use `mode` to control the loading behavior more specifically. Possible values are:
1110 * `b` only binary.
1111 * `t` only text.
1112 * `T` (default on simulator) prefer text but load binary if that is the only version available.
1113 * `bt` (default on radio) either binary or text, whichever is newer (binary preferred when timestamps are equal).
1114 * Add `x` to avoid automatic compilation of source file to .luac version.
1115 Eg: "tx", "bx", or "btx".
1116 * Add `c` to force compilation of source file to .luac version (even if existing version is newer than source file).
1117 Eg: "tc" or "btc" (forces "t", overrides "x").
1118 * Add `d` to keep extra debug info in the compiled binary.
1119 Eg: "td", "btd", or "tcd" (no effect with just "b" or with "x").
1121 @notice
1122 Note that you will get an error if you specify `mode` as "b" or "t" and that specific version of the file does not exist (eg. no .luac file when "b" is used).
1123 Also note that `mode` is NOT passed on to Lua's loader function, so unlike with loadfile() the actual file content is not checked (as if no mode or "bt" were passed to loadfile()).
1125 @param env (integer) See documentation for Lua function loadfile().
1127 @retval function The loaded script, or `nil` if there was an error (e.g. file not found or syntax error).
1129 @retval string Error message(s), if any. Blank if no error occurred.
1131 @status current Introduced in 2.2.0
1133 ### Example
1135 ```lua
1136 fun, err = loadScript("/SCRIPTS/FUNCTIONS/print.lua")
1137 if (fun ~= nil) then
1138 fun("Hello from loadScript()")
1139 else
1140 print(err)
1145 static int luaLoadScript(lua_State * L)
1147 // this function is replicated pretty much verbatim from luaB_loadfile() and load_aux() in lbaselib.c
1148 const char *fname = luaL_optstring(L, 1, NULL);
1149 const char *mode = luaL_optstring(L, 2, NULL);
1150 int env = (!lua_isnone(L, 3) ? 3 : 0); // 'env' index or 0 if no 'env'
1151 lua_settop(L, 0);
1152 if (fname != NULL && luaLoadScriptFileToState(L, fname , mode) == SCRIPT_OK) {
1153 if (env != 0) { // 'env' parameter?
1154 lua_pushvalue(L, env); // environment for loaded function
1155 if (!lua_setupvalue(L, -2, 1)) // set it as 1st upvalue
1156 lua_pop(L, 1); // remove 'env' if not used by previous call
1158 return 1;
1160 else {
1161 // error (message should be on top of the stack)
1162 if (!lua_isstring(L, -1)) {
1163 // probably didn't find a file or had some other error before luaL_loadfile() was run
1164 lua_pushfstring(L, "loadScript(\"%s\", \"%s\") error: File not found", (fname != NULL ? fname : "nul"), (mode != NULL ? mode : "bt"));
1166 lua_pushnil(L);
1167 lua_insert(L, -2); // move nil before error message
1168 return 2; // return nil plus error message
1172 const luaL_Reg opentxLib[] = {
1173 { "getTime", luaGetTime },
1174 { "getDateTime", luaGetDateTime },
1175 { "getVersion", luaGetVersion },
1176 { "getGeneralSettings", luaGetGeneralSettings },
1177 { "getValue", luaGetValue },
1178 { "getRAS", luaGetRAS },
1179 { "getFieldInfo", luaGetFieldInfo },
1180 { "getFlightMode", luaGetFlightMode },
1181 { "playFile", luaPlayFile },
1182 { "playNumber", luaPlayNumber },
1183 { "playDuration", luaPlayDuration },
1184 { "playTone", luaPlayTone },
1185 { "playHaptic", luaPlayHaptic },
1186 { "popupInput", luaPopupInput },
1187 { "popupWarning", luaPopupWarning },
1188 { "popupConfirmation", luaPopupConfirmation },
1189 { "defaultStick", luaDefaultStick },
1190 { "defaultChannel", luaDefaultChannel },
1191 { "getRSSI", luaGetRSSI },
1192 { "killEvents", luaKillEvents },
1193 { "loadScript", luaLoadScript },
1194 #if LCD_DEPTH > 1 && !defined(COLORLCD)
1195 { "GREY", luaGrey },
1196 #endif
1197 { "sportTelemetryPop", luaSportTelemetryPop },
1198 { "sportTelemetryPush", luaSportTelemetryPush },
1199 { "setTelemetryValue", luaSetTelemetryValue },
1200 #if defined(CROSSFIRE)
1201 { "crossfireTelemetryPop", luaCrossfireTelemetryPop },
1202 { "crossfireTelemetryPush", luaCrossfireTelemetryPush },
1203 #endif
1204 { NULL, NULL } /* sentinel */
1207 const luaR_value_entry opentxConstants[] = {
1208 { "FULLSCALE", RESX },
1209 { "XXLSIZE", XXLSIZE },
1210 { "DBLSIZE", DBLSIZE },
1211 { "MIDSIZE", MIDSIZE },
1212 { "SMLSIZE", SMLSIZE },
1213 { "INVERS", INVERS },
1214 { "BOLD", BOLD },
1215 { "BLINK", BLINK },
1216 { "RIGHT", RIGHT },
1217 { "LEFT", LEFT },
1218 { "PREC1", PREC1 },
1219 { "PREC2", PREC2 },
1220 { "VALUE", INPUT_TYPE_VALUE },
1221 { "SOURCE", INPUT_TYPE_SOURCE },
1222 { "REPLACE", MLTPX_REP },
1223 { "MIXSRC_FIRST_INPUT", MIXSRC_FIRST_INPUT },
1224 { "MIXSRC_Rud", MIXSRC_Rud },
1225 { "MIXSRC_Ele", MIXSRC_Ele },
1226 { "MIXSRC_Thr", MIXSRC_Thr },
1227 { "MIXSRC_Ail", MIXSRC_Ail },
1228 { "MIXSRC_SA", MIXSRC_SA },
1229 { "MIXSRC_SB", MIXSRC_SB },
1230 { "MIXSRC_SC", MIXSRC_SC },
1231 { "MIXSRC_SD", MIXSRC_SD },
1232 #if !defined(PCBX7)
1233 { "MIXSRC_SE", MIXSRC_SE },
1234 { "MIXSRC_SG", MIXSRC_SG },
1235 #endif
1236 { "MIXSRC_SF", MIXSRC_SF },
1237 { "MIXSRC_SH", MIXSRC_SH },
1238 { "MIXSRC_CH1", MIXSRC_CH1 },
1239 { "SWSRC_LAST", SWSRC_LAST_LOGICAL_SWITCH },
1240 #if defined(COLORLCD)
1241 { "SHADOWED", SHADOWED },
1242 { "COLOR", ZoneOption::Color },
1243 { "BOOL", ZoneOption::Bool },
1244 { "CUSTOM_COLOR", CUSTOM_COLOR },
1245 { "TEXT_COLOR", TEXT_COLOR },
1246 { "TEXT_BGCOLOR", TEXT_BGCOLOR },
1247 { "TEXT_INVERTED_COLOR", TEXT_INVERTED_COLOR },
1248 { "TEXT_INVERTED_BGCOLOR", TEXT_INVERTED_BGCOLOR },
1249 { "LINE_COLOR", LINE_COLOR },
1250 { "SCROLLBOX_COLOR", SCROLLBOX_COLOR },
1251 { "MENU_TITLE_BGCOLOR", MENU_TITLE_BGCOLOR },
1252 { "MENU_TITLE_COLOR", MENU_TITLE_COLOR },
1253 { "MENU_TITLE_DISABLE_COLOR", MENU_TITLE_DISABLE_COLOR },
1254 { "ALARM_COLOR", ALARM_COLOR },
1255 { "WARNING_COLOR", WARNING_COLOR },
1256 { "TEXT_DISABLE_COLOR", TEXT_DISABLE_COLOR },
1257 { "HEADER_COLOR", HEADER_COLOR },
1258 { "CURVE_AXIS_COLOR", CURVE_AXIS_COLOR },
1259 { "CURVE_COLOR", CURVE_COLOR },
1260 { "CURVE_CURSOR_COLOR", CURVE_CURSOR_COLOR },
1261 { "TITLE_BGCOLOR", TITLE_BGCOLOR },
1262 { "TRIM_BGCOLOR", TRIM_BGCOLOR },
1263 { "TRIM_SHADOW_COLOR", TRIM_SHADOW_COLOR },
1264 { "MAINVIEW_PANES_COLOR", MAINVIEW_PANES_COLOR },
1265 { "MAINVIEW_GRAPHICS_COLOR", MAINVIEW_GRAPHICS_COLOR },
1266 { "HEADER_BGCOLOR", HEADER_BGCOLOR },
1267 { "HEADER_ICON_BGCOLOR", HEADER_ICON_BGCOLOR },
1268 { "HEADER_CURRENT_BGCOLOR", HEADER_CURRENT_BGCOLOR },
1269 { "OVERLAY_COLOR", OVERLAY_COLOR },
1270 { "MENU_HEADER_HEIGHT", MENU_HEADER_HEIGHT },
1271 { "WHITE", (double)WHITE },
1272 { "GREY", (double)GREY },
1273 { "DARKGREY", (double)DARKGREY },
1274 { "BLACK", (double)BLACK },
1275 { "YELLOW", (double)YELLOW },
1276 { "BLUE", (double)BLUE },
1277 { "LIGHTGREY", (double)LIGHTGREY },
1278 { "RED", (double)RED },
1279 { "DARKRED", (double)DARKRED },
1280 #else
1281 { "FIXEDWIDTH", FIXEDWIDTH },
1282 #endif
1283 #if defined(PCBHORUS)
1284 { "EVT_PAGEUP_FIRST", EVT_KEY_FIRST(KEY_PGUP) },
1285 { "EVT_PAGEDN_FIRST", EVT_KEY_FIRST(KEY_PGDN) },
1286 { "EVT_TELEM_FIRST", EVT_KEY_FIRST(KEY_TELEM) },
1287 { "EVT_MODEL_FIRST", EVT_KEY_FIRST(KEY_MODEL) },
1288 { "EVT_SYS_FIRST", EVT_KEY_FIRST(KEY_RADIO) },
1289 { "EVT_RTN_FIRST", EVT_KEY_FIRST(KEY_EXIT) },
1290 #elif defined(PCBTARANIS)
1291 { "EVT_MENU_BREAK", EVT_KEY_BREAK(KEY_MENU) },
1292 { "EVT_MENU_LONG", EVT_KEY_LONG(KEY_MENU) },
1293 { "EVT_PAGE_BREAK", EVT_KEY_BREAK(KEY_PAGE) },
1294 { "EVT_PAGE_LONG", EVT_KEY_LONG(KEY_PAGE) },
1295 { "EVT_PLUS_BREAK", EVT_KEY_BREAK(KEY_PLUS) },
1296 { "EVT_MINUS_BREAK", EVT_KEY_BREAK(KEY_MINUS) },
1297 { "EVT_PLUS_FIRST", EVT_KEY_FIRST(KEY_PLUS) },
1298 { "EVT_MINUS_FIRST", EVT_KEY_FIRST(KEY_MINUS) },
1299 { "EVT_PLUS_REPT", EVT_KEY_REPT(KEY_PLUS) },
1300 { "EVT_MINUS_REPT", EVT_KEY_REPT(KEY_MINUS) },
1301 #if LCD_DEPTH > 1
1302 { "FILL_WHITE", FILL_WHITE },
1303 { "GREY_DEFAULT", GREY_DEFAULT },
1304 #endif
1305 { "FORCE", FORCE },
1306 { "ERASE", ERASE },
1307 { "ROUND", ROUND },
1308 #endif
1309 { "EVT_ENTER_BREAK", EVT_KEY_BREAK(KEY_ENTER) },
1310 { "EVT_ENTER_LONG", EVT_KEY_LONG(KEY_ENTER) },
1311 { "EVT_EXIT_BREAK", EVT_KEY_BREAK(KEY_EXIT) },
1312 #if defined(ROTARY_ENCODER_NAVIGATION)
1313 { "EVT_ROT_BREAK", EVT_KEY_BREAK(KEY_ENTER) },
1314 { "EVT_ROT_LONG", EVT_KEY_LONG(KEY_ENTER) },
1315 { "EVT_ROT_LEFT", EVT_ROTARY_LEFT },
1316 { "EVT_ROT_RIGHT", EVT_ROTARY_RIGHT },
1317 #endif
1318 { "SOLID", SOLID },
1319 { "DOTTED", DOTTED },
1320 { "LCD_W", LCD_W },
1321 { "LCD_H", LCD_H },
1322 { "PLAY_NOW", PLAY_NOW },
1323 { "PLAY_BACKGROUND", PLAY_BACKGROUND },
1324 { "TIMEHOUR", TIMEHOUR },
1326 #if defined(PCBHORUS)
1327 // Adding the unit consts for the set Telemetry function adds about 1k of flash usage
1328 {"UNIT_RAW", UNIT_RAW },
1329 {"UNIT_VOLTS", UNIT_VOLTS },
1330 {"UNIT_AMPS", UNIT_AMPS },
1331 {"UNIT_MILLIAMPS", UNIT_MILLIAMPS },
1332 {"UNIT_KTS", UNIT_KTS },
1333 {"UNIT_METERS_PER_SECOND", UNIT_METERS_PER_SECOND },
1334 {"UNIT_FEET_PER_SECOND", UNIT_FEET_PER_SECOND },
1335 {"UNIT_KMH", UNIT_KMH },
1336 {"UNIT_MPH", UNIT_MPH },
1337 {"UNIT_METERS", UNIT_METERS },
1338 {"UNIT_FEET", UNIT_FEET },
1339 {"UNIT_CELSIUS", UNIT_CELSIUS },
1340 {"UNIT_FAHRENHEIT", UNIT_FAHRENHEIT },
1341 {"UNIT_PERCENT", UNIT_PERCENT },
1342 {"UNIT_MAH", UNIT_MAH },
1343 {"UNIT_WATTS", UNIT_WATTS },
1344 {"UNIT_MILLIWATTS", UNIT_MILLIWATTS },
1345 {"UNIT_DB", UNIT_DB },
1346 {"UNIT_RPMS", UNIT_RPMS },
1347 {"UNIT_G", UNIT_G },
1348 {"UNIT_DEGREE", UNIT_DEGREE },
1349 {"UNIT_RADIANS", UNIT_RADIANS },
1350 {"UNIT_MILLILITERS", UNIT_MILLILITERS },
1351 {"UNIT_FLOZ", UNIT_FLOZ },
1352 {"UNIT_HOURS", UNIT_HOURS },
1353 {"UNIT_MINUTES", UNIT_MINUTES },
1354 {"UNIT_SECONDS", UNIT_SECONDS },
1355 {"UNIT_CELLS", UNIT_CELLS},
1356 {"UNIT_DATETIME", UNIT_DATETIME},
1357 {"UNIT_GPS", UNIT_GPS},
1358 {"UNIT_BITFIELD", UNIT_BITFIELD},
1359 {"UNIT_TEXT", UNIT_TEXT},
1360 #endif
1361 { NULL, 0 } /* sentinel */