Fix doc path
[opentx.git] / radio / src / lua / api_general.cpp
blob0e1581e9a3ddf936cff155bea4d162953d13c862
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(PCBX9E)
33 #include "lua/lua_exports_x9e.inc"
34 #elif defined(PCBX7)
35 #include "lua/lua_exports_x7.inc"
36 #elif defined(PCBXLITE)
37 #include "lua/lua_exports_xlite.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 type: `x12s`, `x10`, `x9e`, `x9d+`, `x9d` or `x7`.
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, radio type strings changed in 2.2.0
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 uint32_t hour12 = hour;
127 if (hour == 0) {
128 hour12 = 12;
130 else if (hour > 12) {
131 hour12 = hour - 12;
133 lua_createtable(L, 0, 8);
134 lua_pushtableinteger(L, "year", year);
135 lua_pushtableinteger(L, "mon", mon);
136 lua_pushtableinteger(L, "day", day);
137 lua_pushtableinteger(L, "hour", hour);
138 lua_pushtableinteger(L, "min", min);
139 lua_pushtableinteger(L, "sec", sec);
140 lua_pushtableinteger(L, "hour12", hour12);
141 if (hour < 12) {
142 lua_pushtablestring(L, "suffix", "am");
144 else {
145 lua_pushtablestring(L, "suffix", "pm");
149 /*luadoc
150 @function getDateTime()
152 Return current system date and time that is kept by the RTC unit
154 @retval table current date and time, table elements:
155 * `year` (number) year
156 * `mon` (number) month
157 * `day` (number) day of month
158 * `hour` (number) hours
159 * `hour12` (number) hours in US format
160 * `min` (number) minutes
161 * `sec` (number) seconds
162 * `suffix` (text) am or pm
164 static int luaGetDateTime(lua_State * L)
166 struct gtm utm;
167 gettime(&utm);
168 luaPushDateTime(L, utm.tm_year + TM_YEAR_BASE, utm.tm_mon + 1, utm.tm_mday, utm.tm_hour, utm.tm_min, utm.tm_sec);
169 return 1;
172 /*luadoc
173 @function getRtcTime()
175 Return current RTC system date as unix timstamp (in seconds since 1. Jan 1970)
177 Please note the RTC timestamp is kept internally as a 32bit integer, which will overflow
178 in 2038.
180 @retval number Number of seconds elapsed since 1. Jan 1970
183 #if defined(RTCLOCK)
184 static int luaGetRtcTime(lua_State * L)
186 lua_pushunsigned(L, g_rtcTime);
187 return 1;
189 #endif
191 static void luaPushLatLon(lua_State* L, TelemetrySensor & telemetrySensor, TelemetryItem & telemetryItem)
192 /* result is lua table containing members ["lat"] and ["lon"] as lua_Number (doubles) in decimal degrees */
194 lua_createtable(L, 0, 4);
195 lua_pushtablenumber(L, "lat", telemetryItem.gps.latitude * 0.000001); // floating point multiplication is faster than division
196 lua_pushtablenumber(L, "pilot-lat", telemetryItem.pilotLatitude * 0.000001);
197 lua_pushtablenumber(L, "lon", telemetryItem.gps.longitude * 0.000001);
198 lua_pushtablenumber(L, "pilot-lon", telemetryItem.pilotLongitude * 0.000001);
201 static void luaPushTelemetryDateTime(lua_State* L, TelemetrySensor & telemetrySensor, TelemetryItem & telemetryItem)
203 luaPushDateTime(L, telemetryItem.datetime.year, telemetryItem.datetime.month, telemetryItem.datetime.day,
204 telemetryItem.datetime.hour, telemetryItem.datetime.min, telemetryItem.datetime.sec);
207 static void luaPushCells(lua_State* L, TelemetrySensor & telemetrySensor, TelemetryItem & telemetryItem)
209 if (telemetryItem.cells.count == 0)
210 lua_pushinteger(L, (int)0); // returns zero if no cells
211 else {
212 lua_createtable(L, telemetryItem.cells.count, 0);
213 for (int i = 0; i < telemetryItem.cells.count; i++) {
214 lua_pushnumber(L, i + 1);
215 lua_pushnumber(L, telemetryItem.cells.values[i].value * 0.01f);
216 lua_settable(L, -3);
221 void luaGetValueAndPush(lua_State* L, int src)
223 getvalue_t value = getValue(src); // ignored for GPS, DATETIME, and CELLS
225 if (src >= MIXSRC_FIRST_TELEM && src <= MIXSRC_LAST_TELEM) {
226 div_t qr = div(src-MIXSRC_FIRST_TELEM, 3);
227 // telemetry values
228 if (TELEMETRY_STREAMING() && telemetryItems[qr.quot].isAvailable()) {
229 TelemetrySensor & telemetrySensor = g_model.telemetrySensors[qr.quot];
230 switch (telemetrySensor.unit) {
231 case UNIT_GPS:
232 luaPushLatLon(L, telemetrySensor, telemetryItems[qr.quot]);
233 break;
234 case UNIT_DATETIME:
235 luaPushTelemetryDateTime(L, telemetrySensor, telemetryItems[qr.quot]);
236 break;
237 case UNIT_TEXT:
238 lua_pushstring(L, telemetryItems[qr.quot].text);
239 break;
240 case UNIT_CELLS:
241 if (qr.rem == 0) {
242 luaPushCells(L, telemetrySensor, telemetryItems[qr.quot]);
243 break;
245 // deliberate no break here to properly return `Cels-` and `Cels+`
246 default:
247 if (telemetrySensor.prec > 0)
248 lua_pushnumber(L, float(value)/telemetrySensor.getPrecDivisor());
249 else
250 lua_pushinteger(L, value);
251 break;
254 else {
255 // telemetry not working, return zero for telemetry sources
256 lua_pushinteger(L, (int)0);
259 else if (src == MIXSRC_TX_VOLTAGE) {
260 lua_pushnumber(L, float(value) * 0.1f);
262 else {
263 lua_pushinteger(L, value);
268 Return field data for a given field name
270 bool luaFindFieldByName(const char * name, LuaField & field, unsigned int flags)
272 // TODO better search method (binary lookup)
273 for (unsigned int n=0; n<DIM(luaSingleFields); ++n) {
274 if (!strcmp(name, luaSingleFields[n].name)) {
275 field.id = luaSingleFields[n].id;
276 if (flags & FIND_FIELD_DESC) {
277 strncpy(field.desc, luaSingleFields[n].desc, sizeof(field.desc)-1);
278 field.desc[sizeof(field.desc)-1] = '\0';
280 else {
281 field.desc[0] = '\0';
283 return true;
287 // search in multiples
288 unsigned int len = strlen(name);
289 for (unsigned int n=0; n<DIM(luaMultipleFields); ++n) {
290 const char * fieldName = luaMultipleFields[n].name;
291 unsigned int fieldLen = strlen(fieldName);
292 if (!strncmp(name, fieldName, fieldLen)) {
293 unsigned int index;
294 if (len == fieldLen+1 && isdigit(name[fieldLen])) {
295 index = name[fieldLen] - '1';
297 else if (len == fieldLen+2 && isdigit(name[fieldLen]) && isdigit(name[fieldLen+1])) {
298 index = 10 * (name[fieldLen] - '0') + (name[fieldLen+1] - '1');
300 else {
301 continue;
303 if (index < luaMultipleFields[n].count) {
304 if(luaMultipleFields[n].id == MIXSRC_FIRST_TELEM)
305 field.id = luaMultipleFields[n].id + index*3;
306 else
307 field.id = luaMultipleFields[n].id + index;
308 if (flags & FIND_FIELD_DESC) {
309 snprintf(field.desc, sizeof(field.desc)-1, luaMultipleFields[n].desc, index+1);
310 field.desc[sizeof(field.desc)-1] = '\0';
312 else {
313 field.desc[0] = '\0';
315 return true;
320 // search in telemetry
321 field.desc[0] = '\0';
322 for (int i=0; i<MAX_TELEMETRY_SENSORS; i++) {
323 if (isTelemetryFieldAvailable(i)) {
324 char sensorName[TELEM_LABEL_LEN+1];
325 int len = zchar2str(sensorName, g_model.telemetrySensors[i].label, TELEM_LABEL_LEN);
326 if (!strncmp(sensorName, name, len)) {
327 if (name[len] == '\0') {
328 field.id = MIXSRC_FIRST_TELEM + 3*i;
329 field.desc[0] = '\0';
330 return true;
332 else if (name[len] == '-' && name[len+1] == '\0') {
333 field.id = MIXSRC_FIRST_TELEM + 3*i + 1;
334 field.desc[0] = '\0';
335 return true;
337 else if (name[len] == '+' && name[len+1] == '\0') {
338 field.id = MIXSRC_FIRST_TELEM + 3*i + 2;
339 field.desc[0] = '\0';
340 return true;
346 return false; // not found
349 /*luadoc
350 @function sportTelemetryPop()
352 Pops a received SPORT packet from the queue. Please note that only packets using a data ID within 0x5000 to 0x52FF
353 (frame ID == 0x10), as well as packets with a frame ID equal 0x32 (regardless of the data ID) will be passed to
354 the LUA telemetry receive queue.
356 @retval nil queue does not contain any (or enough) bytes to form a whole packet
358 @retval multiple returns 4 values:
359 * sensor ID (number)
360 * frame ID (number)
361 * data ID (number)
362 * value (number)
364 @status current Introduced in 2.2.0
366 static int luaSportTelemetryPop(lua_State * L)
368 if (!luaInputTelemetryFifo) {
369 luaInputTelemetryFifo = new Fifo<uint8_t, LUA_TELEMETRY_INPUT_FIFO_SIZE>();
370 if (!luaInputTelemetryFifo) {
371 return 0;
375 if (luaInputTelemetryFifo->size() >= sizeof(SportTelemetryPacket)) {
376 SportTelemetryPacket packet;
377 for (uint8_t i=0; i<sizeof(packet); i++) {
378 luaInputTelemetryFifo->pop(packet.raw[i]);
380 lua_pushnumber(L, packet.physicalId);
381 lua_pushnumber(L, packet.primId);
382 lua_pushnumber(L, packet.dataId);
383 lua_pushunsigned(L, packet.value);
384 return 4;
387 return 0;
390 #define BIT(x, index) (((x) >> index) & 0x01)
391 uint8_t getDataId(uint8_t physicalId)
393 uint8_t result = physicalId;
394 result += (BIT(physicalId, 0) ^ BIT(physicalId, 1) ^ BIT(physicalId, 2)) << 5;
395 result += (BIT(physicalId, 2) ^ BIT(physicalId, 3) ^ BIT(physicalId, 4)) << 6;
396 result += (BIT(physicalId, 0) ^ BIT(physicalId, 2) ^ BIT(physicalId, 4)) << 7;
397 return result;
400 /*luadoc
401 @function sportTelemetryPush()
403 This functions allows for sending SPORT telemetry data toward the receiver,
404 and more generally, to anything connected SPORT bus on the receiver or transmitter.
406 When called without parameters, it will only return the status of the output buffer without sending anything.
408 @param sensorId physical sensor ID
410 @param frameId frame ID
412 @param dataId data ID
414 @param value value
416 @retval boolean data queued in output buffer or not.
418 @status current Introduced in 2.2.0
420 static int luaSportTelemetryPush(lua_State * L)
422 if (lua_gettop(L) == 0) {
423 lua_pushboolean(L, isSportOutputBufferAvailable());
425 else if (isSportOutputBufferAvailable()) {
426 SportTelemetryPacket packet;
427 packet.physicalId = getDataId(luaL_checkunsigned(L, 1));
428 packet.primId = luaL_checkunsigned(L, 2);
429 packet.dataId = luaL_checkunsigned(L, 3);
430 packet.value = luaL_checkunsigned(L, 4);
431 sportOutputPushPacket(&packet);
432 lua_pushboolean(L, true);
434 else {
435 lua_pushboolean(L, false);
437 return 1;
440 /*luadoc
441 @function crossfireTelemetryPop()
443 Pops a received Crossfire Telemetry packet from the queue.
445 @retval nil queue does not contain any (or enough) bytes to form a whole packet
447 @retval multiple returns 2 values:
448 * command (number)
449 * packet (table) data bytes
451 @status current Introduced in 2.2.0
453 static int luaCrossfireTelemetryPop(lua_State * L)
455 if (!luaInputTelemetryFifo) {
456 luaInputTelemetryFifo = new Fifo<uint8_t, LUA_TELEMETRY_INPUT_FIFO_SIZE>();
457 if (!luaInputTelemetryFifo) {
458 return 0;
462 uint8_t length = 0, data = 0;
463 if (luaInputTelemetryFifo->probe(length) && luaInputTelemetryFifo->size() >= uint32_t(length)) {
464 // length value includes the length field
465 luaInputTelemetryFifo->pop(length);
466 luaInputTelemetryFifo->pop(data); // command
467 lua_pushnumber(L, data);
468 lua_newtable(L);
469 for (uint8_t i=1; i<length-1; i++) {
470 luaInputTelemetryFifo->pop(data);
471 lua_pushinteger(L, i);
472 lua_pushinteger(L, data);
473 lua_settable(L, -3);
475 return 2;
478 return 0;
481 /*luadoc
482 @function crossfireTelemetryPush()
484 This functions allows for sending telemetry data toward the TBS Crossfire link.
486 When called without parameters, it will only return the status of the output buffer without sending anything.
488 @param command command
490 @param data table of data bytes
492 @retval boolean data queued in output buffer or not.
494 @status current Introduced in 2.2.0
496 static int luaCrossfireTelemetryPush(lua_State * L)
498 if (lua_gettop(L) == 0) {
499 lua_pushboolean(L, isCrossfireOutputBufferAvailable());
501 else if (isCrossfireOutputBufferAvailable()) {
502 uint8_t command = luaL_checkunsigned(L, 1);
503 luaL_checktype(L, 2, LUA_TTABLE);
504 uint8_t length = luaL_len(L, 2);
505 telemetryOutputPushByte(MODULE_ADDRESS);
506 telemetryOutputPushByte(2 + length); // 1(COMMAND) + data length + 1(CRC)
507 telemetryOutputPushByte(command); // COMMAND
508 for (int i=0; i<length; i++) {
509 lua_rawgeti(L, 2, i+1);
510 telemetryOutputPushByte(luaL_checkunsigned(L, -1));
512 telemetryOutputPushByte(crc8(outputTelemetryBuffer+2, 1 + length));
513 telemetryOutputSetTrigger(command);
514 lua_pushboolean(L, true);
516 else {
517 lua_pushboolean(L, false);
519 return 1;
522 /*luadoc
523 @function getFieldInfo(name)
525 Return detailed information about field (source)
527 The list of valid sources is available:
529 | OpenTX Version | Radio |
530 |----------------|-------|
531 | 2.0 | [all](http://downloads-20.open-tx.org/firmware/lua_fields.txt) |
532 | 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) |
533 | 2.2 | [X9D and X9D+](http://downloads.open-tx.org/2.2/release/firmware/lua_fields_x9d.txt), [X9E](http://downloads.open-tx.org/2.2/release/firmware/lua_fields_x9e.txt), [Horus](http://downloads.open-tx.org/2.2/release/firmware/lua_fields_x12s.txt) |
535 @param name (string) name of the field
537 @retval table information about requested field, table elements:
538 * `id` (number) field identifier
539 * `name` (string) field name
540 * `desc` (string) field description
541 * 'unit' (number) unit identifier [Full list](../appendix/units.html)
543 @retval nil the requested field was not found
545 @status current Introduced in 2.0.8, 'unit' field added in 2.2.0
547 static int luaGetFieldInfo(lua_State * L)
549 const char * what = luaL_checkstring(L, 1);
550 LuaField field;
551 bool found = luaFindFieldByName(what, field, FIND_FIELD_DESC);
552 if (found) {
553 lua_newtable(L);
554 lua_pushtableinteger(L, "id", field.id);
555 lua_pushtablestring(L, "name", what);
556 lua_pushtablestring(L, "desc", field.desc);
557 if (field.id >= MIXSRC_FIRST_TELEM && field.id <= MIXSRC_LAST_TELEM) {
558 TelemetrySensor & telemetrySensor = g_model.telemetrySensors[(int)((field.id-MIXSRC_FIRST_TELEM)/3)];
559 lua_pushtableinteger(L, "unit", telemetrySensor.unit);
561 else {
562 lua_pushtablenil(L, "unit");
564 return 1;
566 return 0;
569 /*luadoc
570 @function getValue(source)
572 Returns the value of a source.
574 The list of fixed sources:
576 | OpenTX Version | Radio |
577 |----------------|-------|
578 | 2.0 | [all](http://downloads-20.open-tx.org/firmware/lua_fields.txt) |
579 | 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) |
580 | 2.2 | [X9D and X9D+](http://downloads.open-tx.org/2.2/release/firmware/lua_fields_x9d.txt), [X9E](http://downloads.open-tx.org/2.2/release/firmware/lua_fields_x9e.txt), [Horus](http://downloads.open-tx.org/2.2/release/firmware/lua_fields_x12s.txt) |
583 In OpenTX 2.1.x the telemetry sources no longer have a predefined name.
584 To get a telemetry value simply use it's sensor name. For example:
585 * Altitude sensor has a name "Alt"
586 * to get the current altitude use the source "Alt"
587 * to get the minimum altitude use the source "Alt-", to get the maximum use "Alt+"
589 @param source can be an identifier (number) (which was obtained by the getFieldInfo())
590 or a name (string) of the source.
592 @retval value current source value (number). Zero is returned for:
593 * non-existing sources
594 * for all telemetry source when the telemetry stream is not received
595 * far all non allowed sensors while FAI MODE is active
597 @retval table GPS position is returned in a table:
598 * `lat` (number) latitude, positive is North
599 * `lon` (number) longitude, positive is East
600 * `pilot-lat` (number) pilot latitude, positive is North
601 * `pilot-lon` (number) pilot longitude, positive is East
603 @retval table GPS date/time, see getDateTime()
605 @retval table Cells are returned in a table
606 (except where no cells were detected in which
607 case the returned value is 0):
608 * table has one item for each detected cell:
609 * key (number) cell number (1 to number of cells)
610 * value (number) current cell voltage
612 @status current Introduced in 2.0.0, changed in 2.1.0, `Cels+` and
613 `Cels-` added in 2.1.9
615 @notice Getting a value by its numerical identifier is faster then by its name.
616 While `Cels` sensor returns current values of all cells in a table, a `Cels+` or
617 `Cels-` will return a single value - the maximum or minimum Cels value.
619 static int luaGetValue(lua_State * L)
621 int src = 0;
622 if (lua_isnumber(L, 1)) {
623 src = luaL_checkinteger(L, 1);
625 else {
626 // convert from field name to its id
627 const char *name = luaL_checkstring(L, 1);
628 LuaField field;
629 bool found = luaFindFieldByName(name, field);
630 if (found) {
631 src = field.id;
634 luaGetValueAndPush(L, src);
635 return 1;
638 /*luadoc
639 @function getRAS()
641 Return the RAS value or nil if no valid hardware found
643 @retval number representing RAS value. Value bellow 0x33 (51 decimal) are all ok, value above 0x33 indicate a hardware antenna issue.
644 This is just a hardware pass/fail measure and does not represent the quality of the radio link
646 @notice RAS was called SWR in the past
648 @status current Introduced in 2.2.0
650 static int luaGetRAS(lua_State * L)
652 if (IS_RAS_VALUE_VALID()) {
653 lua_pushinteger(L, telemetryData.swr.value);
655 else {
656 lua_pushnil(L);
658 return 1;
661 /*luadoc
662 @function getTxGPS()
664 Return the internal GPS position or nil if no valid hardware found
666 @retval table representing the current radio position
667 * `lat` (number) internal GPS latitude, positive is North
668 * `lon` (number) internal GPS longitude, positive is East
669 * 'numsat' (number) current number of sats locked in by the GPS sensor
670 * 'fix' (boolean) fix status
671 * 'alt' (number) internal GPS altitude in 0.1m
672 * 'speed' (number) internal GPSspeed in 0.1m/s
673 * 'heading' (number) internal GPS ground course estimation in degrees * 10
675 @status current Introduced in 2.2.2
677 static int luaGetTxGPS(lua_State * L)
679 #if defined(INTERNAL_GPS)
680 lua_createtable(L, 0, 7);
681 lua_pushtablenumber(L, "lat", gpsData.latitude * 0.000001);
682 lua_pushtablenumber(L, "lon", gpsData.longitude * 0.000001);
683 lua_pushtableinteger(L, "numsat", gpsData.numSat);
684 lua_pushtableinteger(L, "alt", gpsData.altitude);
685 lua_pushtableinteger(L, "speed", gpsData.speed);
686 lua_pushtableinteger(L, "heading", gpsData.groundCourse);
687 if (gpsData.fix)
688 lua_pushtableboolean(L, "fix", true);
689 else
690 lua_pushtableboolean(L, "fix", false);
691 #else
692 lua_pushnil(L);
693 #endif
694 return 1;
698 /*luadoc
699 @function getFlightMode(mode)
701 Return flight mode data.
703 @param mode (number) flight mode number to return (0 - 8). If mode parameter
704 is not specified (or contains invalid value), then the current flight mode data is returned.
706 @retval multiple returns 2 values:
707 * (number) (current) flight mode number (0 - 8)
708 * (string) (current) flight mode name
710 @status current Introduced in 2.1.7
712 static int luaGetFlightMode(lua_State * L)
714 int mode = luaL_optinteger(L, 1, -1);
715 if (mode < 0 || mode >= MAX_FLIGHT_MODES) {
716 mode = mixerCurrentFlightMode;
718 lua_pushnumber(L, mode);
719 char name[sizeof(g_model.flightModeData[0].name)+1];
720 zchar2str(name, g_model.flightModeData[mode].name, sizeof(g_model.flightModeData[0].name));
721 lua_pushstring(L, name);
722 return 2;
725 /*luadoc
726 @function playFile(name)
728 Play a file from the SD card
730 @param path (string) full path to wav file (i.e. “/SOUNDS/en/system/tada.wav”)
731 Introduced in 2.1.0: If you use a relative path, the current language is appended
732 to the path (example: for English language: `/SOUNDS/en` is appended)
734 @status current Introduced in 2.0.0, changed in 2.1.0
736 static int luaPlayFile(lua_State * L)
738 const char * filename = luaL_checkstring(L, 1);
739 if (filename[0] != '/') {
740 // relative sound file path - use current language dir for absolute path
741 char file[AUDIO_FILENAME_MAXLEN+1];
742 char * str = getAudioPath(file);
743 strncpy(str, filename, AUDIO_FILENAME_MAXLEN - (str-file));
744 file[AUDIO_FILENAME_MAXLEN] = 0;
745 PLAY_FILE(file, 0, 0);
747 else {
748 PLAY_FILE(filename, 0, 0);
750 return 0;
753 /*luadoc
754 @function playNumber(value, unit [, attributes])
756 Play a numerical value (text to speech)
758 @param value (number) number to play. Value is interpreted as integer.
760 @param unit (number) unit identifier [Full list]((../appendix/units.html))
762 @param attributes (unsigned number) possible values:
763 * `0 or not present` plays integral part of the number (for a number 123 it plays 123)
764 * `PREC1` plays a number with one decimal place (for a number 123 it plays 12.3)
765 * `PREC2` plays a number with two decimal places (for a number 123 it plays 1.23)
767 @status current Introduced in 2.0.0
770 static int luaPlayNumber(lua_State * L)
772 int number = luaL_checkinteger(L, 1);
773 int unit = luaL_checkinteger(L, 2);
774 unsigned int att = luaL_optunsigned(L, 3, 0);
775 playNumber(number, unit, att, 0);
776 return 0;
779 /*luadoc
780 @function playDuration(duration [, hourFormat])
782 Play a time value (text to speech)
784 @param duration (number) number of seconds to play. Only integral part is used.
786 @param hourFormat (number):
787 * `0 or not present` play format: minutes and seconds.
788 * `!= 0` play format: hours, minutes and seconds.
790 @status current Introduced in 2.1.0
792 static int luaPlayDuration(lua_State * L)
794 int duration = luaL_checkinteger(L, 1);
795 bool playTime = (luaL_optinteger(L, 2, 0) != 0);
796 playDuration(duration, playTime ? PLAY_TIME : 0, 0);
797 return 0;
800 /*luadoc
801 @function playTone(frequency, duration, pause [, flags [, freqIncr]])
803 Play a tone
805 @param frequency (number) tone frequency in Hz (from 150 to 15000)
807 @param duration (number) length of the tone in milliseconds
809 @param pause (number) length of the silence after the tone in milliseconds
811 @param flags (number):
812 * `0 or not present` play with normal priority.
813 * `PLAY_BACKGROUND` play in background (built in vario function uses this context)
814 * `PLAY_NOW` play immediately
816 @param freqIncr (number) positive number increases the tone pitch (frequency with time),
817 negative number decreases it. The frequency changes every 10 milliseconds, the change is `freqIncr * 10Hz`.
818 The valid range is from -127 to 127.
820 @status current Introduced in 2.1.0
822 static int luaPlayTone(lua_State * L)
824 int frequency = luaL_checkinteger(L, 1);
825 int length = luaL_checkinteger(L, 2);
826 int pause = luaL_checkinteger(L, 3);
827 int flags = luaL_optinteger(L, 4, 0);
828 int freqIncr = luaL_optinteger(L, 5, 0);
829 audioQueue.playTone(frequency, length, pause, flags, freqIncr);
830 return 0;
833 /*luadoc
834 @function playHaptic(duration, pause [, flags])
836 Generate haptic feedback
838 @param duration (number) length of the haptic feedback in milliseconds
840 @param pause (number) length of the silence after haptic feedback in milliseconds
842 @param flags (number):
843 * `0 or not present` play with normal priority
844 * `PLAY_NOW` play immediately
846 @status current Introduced in 2.2.0
848 static int luaPlayHaptic(lua_State * L)
850 #if defined(HAPTIC)
851 int length = luaL_checkinteger(L, 1);
852 int pause = luaL_checkinteger(L, 2);
853 int flags = luaL_optinteger(L, 3, 0);
854 haptic.play(length, pause, flags);
855 #else
856 UNUSED(L);
857 #endif
858 return 0;
861 /*luadoc
862 @function killEvents(key)
864 Stops key state machine. See [Key Events](../key_events.md) for the detailed description.
866 @param key (number) key to be killed, can also include event type (only the key part is used)
868 @status current Introduced in 2.0.0
871 static int luaKillEvents(lua_State * L)
873 uint8_t key = EVT_KEY_MASK(luaL_checkinteger(L, 1));
874 // prevent killing maskable keys (only in telemetry scripts)
875 // TODO add which tpye of script is running before p_call()
876 if (IS_MASKABLE(key)) {
877 killEvents(key);
879 return 0;
882 #if LCD_DEPTH > 1 && !defined(COLORLCD)
883 /*luadoc
884 @function GREY()
886 Returns gray value which can be used in LCD functions
888 @retval (number) a value that represents amount of *greyness* (from 0 to 15)
890 @notice Only available on Taranis
892 @status current Introduced in 2.0.13
894 static int luaGrey(lua_State * L)
896 int index = luaL_checkinteger(L, 1);
897 lua_pushunsigned(L, GREY(index));
898 return 1;
900 #endif
902 /*luadoc
903 @function getGeneralSettings()
905 Returns (some of) the general radio settings
907 @retval table with elements:
908 * `battWarn` (number) radio battery range - warning value
909 * `battMin` (number) radio battery range - minimum value
910 * `battMax` (number) radio battery range - maximum value
911 * `imperial` (number) set to a value different from 0 if the radio is set to the
912 IMPERIAL units
913 * `language` (string) radio language (used for menus)
914 * `voice` (string) voice language (used for speech)
915 * `gtimer` (number) radio global timer in seconds (does not include current session)
917 @status current Introduced in 2.0.6, `imperial` added in TODO,
918 `language` and `voice` added in 2.2.0, gtimer added in 2.2.2.
921 static int luaGetGeneralSettings(lua_State * L)
923 lua_newtable(L);
924 lua_pushtablenumber(L, "battWarn", (g_eeGeneral.vBatWarn) * 0.1f);
925 lua_pushtablenumber(L, "battMin", (90+g_eeGeneral.vBatMin) * 0.1f);
926 lua_pushtablenumber(L, "battMax", (120+g_eeGeneral.vBatMax) * 0.1f);
927 lua_pushtableinteger(L, "imperial", g_eeGeneral.imperial);
928 lua_pushtablestring(L, "language", TRANSLATIONS);
929 lua_pushtablestring(L, "voice", currentLanguagePack->id);
930 lua_pushtableinteger(L, "gtimer", g_eeGeneral.globalTimer);
931 return 1;
934 /*luadoc
935 @function popupInput(title, event, input, min, max)
937 Raises a pop-up on screen that allows uses input
939 @param title (string) text to display
941 @param event (number) the event variable that is passed in from the
942 Run function (key pressed)
944 @param input (number) value that can be adjusted by the +/­- keys
946 @param min (number) min value that input can reach (by pressing the -­ key)
948 @param max (number) max value that input can reach
950 @retval number result of the input adjustment
952 @retval "OK" user pushed ENT key
954 @retval "CANCEL" user pushed EXIT key
956 @notice Use only from stand-alone and telemetry scripts.
958 @status current Introduced in 2.0.0
960 static int luaPopupInput(lua_State * L)
962 event_t event = luaL_checkinteger(L, 2);
963 warningInputValue = luaL_checkinteger(L, 3);
964 warningInputValueMin = luaL_checkinteger(L, 4);
965 warningInputValueMax = luaL_checkinteger(L, 5);
966 warningText = luaL_checkstring(L, 1);
967 warningType = WARNING_TYPE_INPUT;
968 runPopupWarning(event);
969 if (warningResult) {
970 warningResult = 0;
971 lua_pushstring(L, "OK");
973 else if (!warningText) {
974 lua_pushstring(L, "CANCEL");
976 else {
977 lua_pushinteger(L, warningInputValue);
979 warningText = NULL;
980 return 1;
983 /*luadoc
984 @function popupWarning(title, event)
986 Raises a pop-up on screen that shows a warning
988 @param title (string) text to display
990 @param event (number) the event variable that is passed in from the
991 Run function (key pressed)
993 @retval "CANCEL" user pushed EXIT key
995 @notice Use only from stand-alone and telemetry scripts.
997 @status current Introduced in 2.2.0
999 static int luaPopupWarning(lua_State * L)
1001 event_t event = luaL_checkinteger(L, 2);
1002 warningText = luaL_checkstring(L, 1);
1003 warningType = WARNING_TYPE_ASTERISK;
1004 runPopupWarning(event);
1005 if (!warningText) {
1006 lua_pushstring(L, "CANCEL");
1008 else {
1009 warningText = NULL;
1010 lua_pushnil(L);
1012 return 1;
1015 /*luadoc
1016 @function popupConfirmation(title, event)
1018 Raises a pop-up on screen that asks for confirmation
1020 @param title (string) text to display
1022 @param event (number) the event variable that is passed in from the
1023 Run function (key pressed)
1025 @retval "CANCEL" user pushed EXIT key
1027 @notice Use only from stand-alone and telemetry scripts.
1029 @status current Introduced in 2.2.0
1031 static int luaPopupConfirmation(lua_State * L)
1033 event_t event = luaL_checkinteger(L, 2);
1034 warningText = luaL_checkstring(L, 1);
1035 warningType = WARNING_TYPE_CONFIRM;
1036 runPopupWarning(event);
1037 if (!warningText) {
1038 lua_pushstring(L, warningResult ? "OK" : "CANCEL");
1040 else {
1041 warningText = NULL;
1042 lua_pushnil(L);
1044 return 1;
1047 /*luadoc
1048 @function defaultStick(channel)
1050 Get stick that is assigned to a channel. See Default Channel Order in General Settings.
1052 @param channel (number) channel number (0 means CH1)
1054 @retval number Stick assigned to this channel (from 0 to 3)
1056 @status current Introduced in 2.0.0
1058 static int luaDefaultStick(lua_State * L)
1060 uint8_t channel = luaL_checkinteger(L, 1);
1061 lua_pushinteger(L, channel_order(channel+1)-1);
1062 return 1;
1065 /*luadoc
1066 @function setTelemetryValue(id, subID, instance, value [, unit [, precision [, name]]])
1068 @param id Id of the sensor, valid range is from 0 to 0xFFFF
1070 @param subID subID of the sensor, usually 0, valid range is from 0 to 7
1072 @param instance instance of the sensor (SensorID), valid range is from 0 to 0xFF
1074 @param value fed to the sensor
1076 @param unit unit of the sensor [Full list](../appendix/units.html)
1078 @param precision the precision of the sensor
1079 * `0 or not present` no decimal precision.
1080 * `!= 0` value is divided by 10^precision, e.g. value=1000, prec=2 => 10.00.
1082 @param name (string) Name of the sensor if it does not yet exist (4 chars).
1083 * `not present` Name defaults to the Id.
1084 * `present` Sensor takes name of the argument. Argument must have name surrounded by quotes: e.g., "Name"
1086 @retval true, if the sensor was just added. In this case the value is ignored (subsequent call will set the value)
1088 @notice All three parameters `id`, `subID` and `instance` can't be zero at the same time. At least one of them
1089 must be different from zero.
1091 @status current Introduced in 2.2.0
1093 static int luaSetTelemetryValue(lua_State * L)
1095 uint16_t id = luaL_checkunsigned(L, 1);
1096 uint8_t subId = luaL_checkunsigned(L, 2) & 0x7;
1097 uint8_t instance = luaL_checkunsigned(L, 3);
1098 int32_t value = luaL_checkinteger(L, 4);
1099 uint32_t unit = luaL_optunsigned(L, 5, 0);
1100 uint32_t prec = luaL_optunsigned(L, 6, 0);
1102 char zname[4];
1103 const char* name = luaL_optstring(L, 7, NULL);
1104 if (name != NULL && strlen(name) > 0) {
1105 str2zchar(zname, name, 4);
1106 } else {
1107 zname[0] = hex2zchar((id & 0xf000) >> 12);
1108 zname[1] = hex2zchar((id & 0x0f00) >> 8);
1109 zname[2] = hex2zchar((id & 0x00f0) >> 4);
1110 zname[3] = hex2zchar((id & 0x000f) >> 0);
1112 if (id | subId | instance) {
1113 int index = setTelemetryValue(TELEM_PROTO_LUA, id, subId, instance, value, unit, prec);
1114 if (index >= 0) {
1115 TelemetrySensor &telemetrySensor = g_model.telemetrySensors[index];
1116 telemetrySensor.id = id;
1117 telemetrySensor.subId = subId;
1118 telemetrySensor.instance = instance;
1119 telemetrySensor.init(zname, unit, prec);
1120 lua_pushboolean(L, true);
1121 } else {
1122 lua_pushboolean(L, false);
1124 return 1;
1126 lua_pushboolean(L, false);
1127 return 1;
1130 /*luadoc
1131 @function defaultChannel(stick)
1133 Get channel assigned to stick. See Default Channel Order in General Settings
1135 @param stick (number) stick number (from 0 to 3)
1137 @retval number channel assigned to this stick (from 0 to 3)
1139 @retval nil stick not found
1141 @status current Introduced in 2.0.0
1143 static int luaDefaultChannel(lua_State * L)
1145 uint8_t stick = luaL_checkinteger(L, 1);
1146 for (int i=1; i<=4; i++) {
1147 int tmp = channel_order(i) - 1;
1148 if (tmp == stick) {
1149 lua_pushinteger(L, i-1);
1150 return 1;
1153 lua_pushnil(L);
1154 return 1;
1157 /*luadoc
1158 @function getRSSI()
1160 Get RSSI value as well as low and critical RSSI alarm levels (in dB)
1162 @retval rssi RSSI value (0 if no link)
1164 @retval alarm_low Configured low RSSI alarm level
1166 @retval alarm_crit Configured critical RSSI alarm level
1168 @status current Introduced in 2.2.0
1170 static int luaGetRSSI(lua_State * L)
1172 lua_pushunsigned(L, min((uint8_t)99, TELEMETRY_RSSI()));
1173 lua_pushunsigned(L, g_model.rssiAlarms.getWarningRssi());
1174 lua_pushunsigned(L, g_model.rssiAlarms.getCriticalRssi());
1175 return 3;
1178 /*luadoc
1179 @function loadScript(file [, mode], [,env])
1181 Load a Lua script file. This is similar to Lua's own [loadfile()](https://www.lua.org/manual/5.2/manual.html#pdf-loadfile)
1182 API method, but it uses OpenTx's optional pre-compilation feature to save memory and time during load.
1184 Return values are same as from Lua API loadfile() method: If the script was loaded w/out errors
1185 then the loaded script (or "chunk") is returned as a function. Otherwise, returns nil plus the error message.
1187 @param file (string) Full path and file name of script. The file extension is optional and ignored (see `mode` param to control
1188 which extension will be used). However, if an extension is specified, it should be ".lua" (or ".luac"), otherwise it is treated
1189 as part of the file name and the .lua/.luac will be appended to that.
1191 @param mode (string) (optional) Controls whether to force loading the text (.lua) or pre-compiled binary (.luac)
1192 version of the script. By default OTx will load the newest version and compile a new binary if necessary (overwriting any
1193 existing .luac version of the same script, and stripping some debug info like line numbers).
1194 You can use `mode` to control the loading behavior more specifically. Possible values are:
1195 * `b` only binary.
1196 * `t` only text.
1197 * `T` (default on simulator) prefer text but load binary if that is the only version available.
1198 * `bt` (default on radio) either binary or text, whichever is newer (binary preferred when timestamps are equal).
1199 * Add `x` to avoid automatic compilation of source file to .luac version.
1200 Eg: "tx", "bx", or "btx".
1201 * Add `c` to force compilation of source file to .luac version (even if existing version is newer than source file).
1202 Eg: "tc" or "btc" (forces "t", overrides "x").
1203 * Add `d` to keep extra debug info in the compiled binary.
1204 Eg: "td", "btd", or "tcd" (no effect with just "b" or with "x").
1206 @notice
1207 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).
1208 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()).
1210 @param env (integer) See documentation for Lua function loadfile().
1212 @retval function The loaded script, or `nil` if there was an error (e.g. file not found or syntax error).
1214 @retval string Error message(s), if any. Blank if no error occurred.
1216 @status current Introduced in 2.2.0
1218 ### Example
1220 ```lua
1221 fun, err = loadScript("/SCRIPTS/FUNCTIONS/print.lua")
1222 if (fun ~= nil) then
1223 fun("Hello from loadScript()")
1224 else
1225 print(err)
1230 static int luaLoadScript(lua_State * L)
1232 // this function is replicated pretty much verbatim from luaB_loadfile() and load_aux() in lbaselib.c
1233 const char *fname = luaL_optstring(L, 1, NULL);
1234 const char *mode = luaL_optstring(L, 2, NULL);
1235 int env = (!lua_isnone(L, 3) ? 3 : 0); // 'env' index or 0 if no 'env'
1236 lua_settop(L, 0);
1237 if (fname != NULL && luaLoadScriptFileToState(L, fname , mode) == SCRIPT_OK) {
1238 if (env != 0) { // 'env' parameter?
1239 lua_pushvalue(L, env); // environment for loaded function
1240 if (!lua_setupvalue(L, -2, 1)) // set it as 1st upvalue
1241 lua_pop(L, 1); // remove 'env' if not used by previous call
1243 return 1;
1245 else {
1246 // error (message should be on top of the stack)
1247 if (!lua_isstring(L, -1)) {
1248 // probably didn't find a file or had some other error before luaL_loadfile() was run
1249 lua_pushfstring(L, "loadScript(\"%s\", \"%s\") error: File not found", (fname != NULL ? fname : "nul"), (mode != NULL ? mode : "bt"));
1251 lua_pushnil(L);
1252 lua_insert(L, -2); // move nil before error message
1253 return 2; // return nil plus error message
1257 /*luadoc
1258 @function getUsage()
1260 Get percent of already used Lua instructions in current script execution cycle.
1262 @retval usage (number) a value from 0 to 100 (percent)
1264 @status current Introduced in 2.2.1
1266 static int luaGetUsage(lua_State * L)
1268 lua_pushinteger(L, instructionsPercent);
1269 return 1;
1272 /*luadoc
1273 @function resetGlobalTimer()
1275 Resets the radio global timer to 0.
1277 @status current Introduced in 2.2.2
1279 static int luaResetGlobalTimer(lua_State * L)
1281 g_eeGeneral.globalTimer = 0;
1282 storageDirty(EE_GENERAL);
1283 return 0;
1286 const luaL_Reg opentxLib[] = {
1287 { "getTime", luaGetTime },
1288 { "getDateTime", luaGetDateTime },
1289 #if defined(RTCLOCK)
1290 { "getRtcTime", luaGetRtcTime },
1291 #endif
1292 { "getVersion", luaGetVersion },
1293 { "getGeneralSettings", luaGetGeneralSettings },
1294 { "getValue", luaGetValue },
1295 { "getRAS", luaGetRAS },
1296 { "getTxGPS", luaGetTxGPS },
1297 { "getFieldInfo", luaGetFieldInfo },
1298 { "getFlightMode", luaGetFlightMode },
1299 { "playFile", luaPlayFile },
1300 { "playNumber", luaPlayNumber },
1301 { "playDuration", luaPlayDuration },
1302 { "playTone", luaPlayTone },
1303 { "playHaptic", luaPlayHaptic },
1304 { "popupInput", luaPopupInput },
1305 { "popupWarning", luaPopupWarning },
1306 { "popupConfirmation", luaPopupConfirmation },
1307 { "defaultStick", luaDefaultStick },
1308 { "defaultChannel", luaDefaultChannel },
1309 { "getRSSI", luaGetRSSI },
1310 { "killEvents", luaKillEvents },
1311 { "loadScript", luaLoadScript },
1312 { "getUsage", luaGetUsage },
1313 { "resetGlobalTimer", luaResetGlobalTimer },
1314 #if LCD_DEPTH > 1 && !defined(COLORLCD)
1315 { "GREY", luaGrey },
1316 #endif
1317 { "sportTelemetryPop", luaSportTelemetryPop },
1318 { "sportTelemetryPush", luaSportTelemetryPush },
1319 { "setTelemetryValue", luaSetTelemetryValue },
1320 #if defined(CROSSFIRE)
1321 { "crossfireTelemetryPop", luaCrossfireTelemetryPop },
1322 { "crossfireTelemetryPush", luaCrossfireTelemetryPush },
1323 #endif
1324 { NULL, NULL } /* sentinel */
1327 const luaR_value_entry opentxConstants[] = {
1328 { "FULLSCALE", RESX },
1329 { "XXLSIZE", XXLSIZE },
1330 { "DBLSIZE", DBLSIZE },
1331 { "MIDSIZE", MIDSIZE },
1332 { "SMLSIZE", SMLSIZE },
1333 { "INVERS", INVERS },
1334 { "BOLD", BOLD },
1335 { "BLINK", BLINK },
1336 { "RIGHT", RIGHT },
1337 { "LEFT", LEFT },
1338 { "PREC1", PREC1 },
1339 { "PREC2", PREC2 },
1340 { "VALUE", INPUT_TYPE_VALUE },
1341 { "SOURCE", INPUT_TYPE_SOURCE },
1342 { "REPLACE", MLTPX_REP },
1343 { "MIXSRC_FIRST_INPUT", MIXSRC_FIRST_INPUT },
1344 { "MIXSRC_Rud", MIXSRC_Rud },
1345 { "MIXSRC_Ele", MIXSRC_Ele },
1346 { "MIXSRC_Thr", MIXSRC_Thr },
1347 { "MIXSRC_Ail", MIXSRC_Ail },
1348 { "MIXSRC_SA", MIXSRC_SA },
1349 { "MIXSRC_SB", MIXSRC_SB },
1350 { "MIXSRC_SC", MIXSRC_SC },
1351 { "MIXSRC_SD", MIXSRC_SD },
1352 #if !defined(PCBX7) && !defined(PCBXLITE)
1353 { "MIXSRC_SE", MIXSRC_SE },
1354 { "MIXSRC_SG", MIXSRC_SG },
1355 #endif
1356 #if !defined(PCBXLITE)
1357 { "MIXSRC_SF", MIXSRC_SF },
1358 { "MIXSRC_SH", MIXSRC_SH },
1359 #endif
1360 { "MIXSRC_CH1", MIXSRC_CH1 },
1361 { "SWSRC_LAST", SWSRC_LAST_LOGICAL_SWITCH },
1362 #if defined(COLORLCD)
1363 { "SHADOWED", SHADOWED },
1364 { "COLOR", ZoneOption::Color },
1365 { "BOOL", ZoneOption::Bool },
1366 { "CUSTOM_COLOR", CUSTOM_COLOR },
1367 { "TEXT_COLOR", TEXT_COLOR },
1368 { "TEXT_BGCOLOR", TEXT_BGCOLOR },
1369 { "TEXT_INVERTED_COLOR", TEXT_INVERTED_COLOR },
1370 { "TEXT_INVERTED_BGCOLOR", TEXT_INVERTED_BGCOLOR },
1371 { "LINE_COLOR", LINE_COLOR },
1372 { "SCROLLBOX_COLOR", SCROLLBOX_COLOR },
1373 { "MENU_TITLE_BGCOLOR", MENU_TITLE_BGCOLOR },
1374 { "MENU_TITLE_COLOR", MENU_TITLE_COLOR },
1375 { "MENU_TITLE_DISABLE_COLOR", MENU_TITLE_DISABLE_COLOR },
1376 { "ALARM_COLOR", ALARM_COLOR },
1377 { "WARNING_COLOR", WARNING_COLOR },
1378 { "TEXT_DISABLE_COLOR", TEXT_DISABLE_COLOR },
1379 { "HEADER_COLOR", HEADER_COLOR },
1380 { "CURVE_AXIS_COLOR", CURVE_AXIS_COLOR },
1381 { "CURVE_COLOR", CURVE_COLOR },
1382 { "CURVE_CURSOR_COLOR", CURVE_CURSOR_COLOR },
1383 { "TITLE_BGCOLOR", TITLE_BGCOLOR },
1384 { "TRIM_BGCOLOR", TRIM_BGCOLOR },
1385 { "TRIM_SHADOW_COLOR", TRIM_SHADOW_COLOR },
1386 { "MAINVIEW_PANES_COLOR", MAINVIEW_PANES_COLOR },
1387 { "MAINVIEW_GRAPHICS_COLOR", MAINVIEW_GRAPHICS_COLOR },
1388 { "HEADER_BGCOLOR", HEADER_BGCOLOR },
1389 { "HEADER_ICON_BGCOLOR", HEADER_ICON_BGCOLOR },
1390 { "HEADER_CURRENT_BGCOLOR", HEADER_CURRENT_BGCOLOR },
1391 { "OVERLAY_COLOR", OVERLAY_COLOR },
1392 { "MENU_HEADER_HEIGHT", MENU_HEADER_HEIGHT },
1393 { "WHITE", (double)WHITE },
1394 { "GREY", (double)GREY },
1395 { "DARKGREY", (double)DARKGREY },
1396 { "BLACK", (double)BLACK },
1397 { "YELLOW", (double)YELLOW },
1398 { "BLUE", (double)BLUE },
1399 { "LIGHTGREY", (double)LIGHTGREY },
1400 { "RED", (double)RED },
1401 { "DARKRED", (double)DARKRED },
1402 #else
1403 { "FIXEDWIDTH", FIXEDWIDTH },
1404 #endif
1405 #if defined(PCBHORUS)
1406 { "EVT_PAGEUP_FIRST", EVT_KEY_FIRST(KEY_PGUP) },
1407 { "EVT_PAGEDN_FIRST", EVT_KEY_FIRST(KEY_PGDN) },
1408 { "EVT_TELEM_FIRST", EVT_KEY_FIRST(KEY_TELEM) },
1409 { "EVT_MODEL_FIRST", EVT_KEY_FIRST(KEY_MODEL) },
1410 { "EVT_SYS_FIRST", EVT_KEY_FIRST(KEY_RADIO) },
1411 { "EVT_RTN_FIRST", EVT_KEY_FIRST(KEY_EXIT) },
1412 #elif defined(PCBXLITE)
1413 { "EVT_DOWN_FIRST", EVT_KEY_FIRST(KEY_DOWN) },
1414 { "EVT_UP_FIRST", EVT_KEY_FIRST(KEY_UP) },
1415 { "EVT_LEFT_FIRST", EVT_KEY_FIRST(KEY_LEFT) },
1416 { "EVT_RIGHT_FIRST", EVT_KEY_FIRST(KEY_RIGHT) },
1417 { "EVT_SHIFT_FIRST", EVT_KEY_FIRST(KEY_SHIFT) },
1418 { "EVT_DOWN_BREAK", EVT_KEY_BREAK(KEY_DOWN) },
1419 { "EVT_UP_BREAK", EVT_KEY_BREAK(KEY_UP) },
1420 { "EVT_LEFT_BREAK", EVT_KEY_BREAK(KEY_LEFT) },
1421 { "EVT_RIGHT_BREAK", EVT_KEY_BREAK(KEY_RIGHT) },
1422 { "EVT_SHIFT_BREAK", EVT_KEY_BREAK(KEY_SHIFT) },
1423 { "EVT_DOWN_LONG", EVT_KEY_LONG(KEY_DOWN) },
1424 { "EVT_UP_LONG", EVT_KEY_LONG(KEY_UP) },
1425 { "EVT_LEFT_LONG", EVT_KEY_LONG(KEY_LEFT) },
1426 { "EVT_RIGHT_LONG", EVT_KEY_LONG(KEY_RIGHT) },
1427 { "EVT_SHIFT_LONG", EVT_KEY_LONG(KEY_SHIFT) },
1428 { "EVT_DOWN_REPT", EVT_KEY_REPT(KEY_DOWN) },
1429 { "EVT_UP_REPT", EVT_KEY_REPT(KEY_UP) },
1430 { "EVT_LEFT_REPT", EVT_KEY_REPT(KEY_LEFT) },
1431 { "EVT_RIGHT_REPT", EVT_KEY_REPT(KEY_RIGHT) },
1432 { "FORCE", FORCE },
1433 { "ERASE", ERASE },
1434 { "ROUND", ROUND },
1435 #elif defined(PCBTARANIS)
1436 { "EVT_MENU_BREAK", EVT_KEY_BREAK(KEY_MENU) },
1437 { "EVT_MENU_LONG", EVT_KEY_LONG(KEY_MENU) },
1438 { "EVT_PAGE_BREAK", EVT_KEY_BREAK(KEY_PAGE) },
1439 { "EVT_PAGE_LONG", EVT_KEY_LONG(KEY_PAGE) },
1440 { "EVT_PLUS_BREAK", EVT_KEY_BREAK(KEY_PLUS) },
1441 { "EVT_MINUS_BREAK", EVT_KEY_BREAK(KEY_MINUS) },
1442 { "EVT_PLUS_FIRST", EVT_KEY_FIRST(KEY_PLUS) },
1443 { "EVT_MINUS_FIRST", EVT_KEY_FIRST(KEY_MINUS) },
1444 { "EVT_PLUS_REPT", EVT_KEY_REPT(KEY_PLUS) },
1445 { "EVT_MINUS_REPT", EVT_KEY_REPT(KEY_MINUS) },
1446 #if LCD_DEPTH > 1
1447 { "FILL_WHITE", FILL_WHITE },
1448 { "GREY_DEFAULT", GREY_DEFAULT },
1449 #endif
1450 { "FORCE", FORCE },
1451 { "ERASE", ERASE },
1452 { "ROUND", ROUND },
1453 #endif
1454 { "EVT_ENTER_BREAK", EVT_KEY_BREAK(KEY_ENTER) },
1455 { "EVT_ENTER_LONG", EVT_KEY_LONG(KEY_ENTER) },
1456 { "EVT_EXIT_BREAK", EVT_KEY_BREAK(KEY_EXIT) },
1457 #if defined(ROTARY_ENCODER_NAVIGATION)
1458 { "EVT_ROT_BREAK", EVT_KEY_BREAK(KEY_ENTER) },
1459 { "EVT_ROT_LONG", EVT_KEY_LONG(KEY_ENTER) },
1460 { "EVT_ROT_LEFT", EVT_ROTARY_LEFT },
1461 { "EVT_ROT_RIGHT", EVT_ROTARY_RIGHT },
1462 #endif
1463 { "SOLID", SOLID },
1464 { "DOTTED", DOTTED },
1465 { "LCD_W", LCD_W },
1466 { "LCD_H", LCD_H },
1467 { "PLAY_NOW", PLAY_NOW },
1468 { "PLAY_BACKGROUND", PLAY_BACKGROUND },
1469 { "TIMEHOUR", TIMEHOUR },
1471 #if defined(PCBHORUS)
1472 // Adding the unit consts for the set Telemetry function adds about 1k of flash usage
1473 {"UNIT_RAW", UNIT_RAW },
1474 {"UNIT_VOLTS", UNIT_VOLTS },
1475 {"UNIT_AMPS", UNIT_AMPS },
1476 {"UNIT_MILLIAMPS", UNIT_MILLIAMPS },
1477 {"UNIT_KTS", UNIT_KTS },
1478 {"UNIT_METERS_PER_SECOND", UNIT_METERS_PER_SECOND },
1479 {"UNIT_FEET_PER_SECOND", UNIT_FEET_PER_SECOND },
1480 {"UNIT_KMH", UNIT_KMH },
1481 {"UNIT_MPH", UNIT_MPH },
1482 {"UNIT_METERS", UNIT_METERS },
1483 {"UNIT_FEET", UNIT_FEET },
1484 {"UNIT_CELSIUS", UNIT_CELSIUS },
1485 {"UNIT_FAHRENHEIT", UNIT_FAHRENHEIT },
1486 {"UNIT_PERCENT", UNIT_PERCENT },
1487 {"UNIT_MAH", UNIT_MAH },
1488 {"UNIT_WATTS", UNIT_WATTS },
1489 {"UNIT_MILLIWATTS", UNIT_MILLIWATTS },
1490 {"UNIT_DB", UNIT_DB },
1491 {"UNIT_RPMS", UNIT_RPMS },
1492 {"UNIT_G", UNIT_G },
1493 {"UNIT_DEGREE", UNIT_DEGREE },
1494 {"UNIT_RADIANS", UNIT_RADIANS },
1495 {"UNIT_MILLILITERS", UNIT_MILLILITERS },
1496 {"UNIT_FLOZ", UNIT_FLOZ },
1497 {"UNIT_HOURS", UNIT_HOURS },
1498 {"UNIT_MINUTES", UNIT_MINUTES },
1499 {"UNIT_SECONDS", UNIT_SECONDS },
1500 {"UNIT_CELLS", UNIT_CELLS},
1501 {"UNIT_DATETIME", UNIT_DATETIME},
1502 {"UNIT_GPS", UNIT_GPS},
1503 {"UNIT_BITFIELD", UNIT_BITFIELD},
1504 {"UNIT_TEXT", UNIT_TEXT},
1505 #endif
1506 { NULL, 0 } /* sentinel */