Improve multi (#7136)
[opentx.git] / radio / src / lua / api_general.cpp
blobea2ce80a411d2a6ab7ab5e08da9fcf69004b5861
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"
27 #include "telemetry/multi.h"
29 #if defined(PCBX12S)
30 #include "lua/lua_exports_x12s.inc" // this line must be after lua headers
31 #elif defined(RADIO_T16)
32 #include "lua/lua_exports_t16.inc"
33 #elif defined(PCBX10)
34 #include "lua/lua_exports_x10.inc"
35 #elif defined(PCBX9E)
36 #include "lua/lua_exports_x9e.inc"
37 #elif defined(RADIO_X7)
38 #include "lua/lua_exports_x7.inc"
39 #elif defined(RADIO_T12)
40 #include "lua/lua_exports_t12.inc"
41 #elif defined(PCBX9LITES)
42 #include "lua/lua_exports_x9lites.inc"
43 #elif defined(PCBX9LITE)
44 #include "lua/lua_exports_x9lite.inc"
45 #elif defined(PCBXLITES)
46 #include "lua/lua_exports_xlites.inc"
47 #elif defined(PCBXLITE)
48 #include "lua/lua_exports_xlite.inc"
49 #elif defined(PCBTARANIS)
50 #include "lua/lua_exports_x9d.inc"
51 #endif
53 #if defined(SIMU)
54 #define RADIO_VERSION FLAVOUR "-simu"
55 #else
56 #define RADIO_VERSION FLAVOUR
57 #endif
59 #define FIND_FIELD_DESC 0x01
61 #define KEY_EVENTS(xxx, yyy) \
62 { "EVT_"#xxx"_FIRST", EVT_KEY_FIRST(yyy) }, \
63 { "EVT_"#xxx"_BREAK", EVT_KEY_BREAK(yyy) }, \
64 { "EVT_"#xxx"_LONG", EVT_KEY_LONG(yyy) }, \
65 { "EVT_"#xxx"_REPT", EVT_KEY_REPT(yyy) }
67 /*luadoc
68 @function getVersion()
70 Return OpenTX version
72 @retval string OpenTX version (ie "2.1.5")
74 @retval multiple (available since 2.1.7) returns 5 values:
75 * (string) OpenTX version (ie "2.1.5")
76 * (string) radio type: `x12s`, `x10`, `x9e`, `x9d+`, `x9d` or `x7`.
77 If running in simulator the "-simu" is added
78 * (number) major version (ie 2 if version 2.1.5)
79 * (number) minor version (ie 1 if version 2.1.5)
80 * (number) revision number (ie 5 if version 2.1.5)
82 @status current Introduced in 2.0.0, expanded in 2.1.7, radio type strings changed in 2.2.0
84 ### Example
86 This example also runs in OpenTX versions where the function returned only one value:
88 ```lua
89 local function run(event)
90 local ver, radio, maj, minor, rev = getVersion()
91 print("version: "..ver)
92 if radio then print ("radio: "..radio) end
93 if maj then print ("maj: "..maj) end
94 if minor then print ("minor: "..minor) end
95 if rev then print ("rev: "..rev) end
96 return 1
97 end
99 return { run=run }
101 Output of the above script in simulator:
103 version: 2.1.7
104 radio: taranis-simu
105 maj: 2
106 minor: 1
107 rev: 7
110 static int luaGetVersion(lua_State * L)
112 lua_pushstring(L, VERSION);
113 lua_pushstring(L, RADIO_VERSION);
114 lua_pushnumber(L, VERSION_MAJOR);
115 lua_pushnumber(L, VERSION_MINOR);
116 lua_pushnumber(L, VERSION_REVISION);
117 return 5;
120 /*luadoc
121 @function getTime()
123 Return the time since the radio was started in multiple of 10ms
125 @retval number Number of 10ms ticks since the radio was started Example:
126 run time: 12.54 seconds, return value: 1254
128 The timer internally uses a 32-bit counter which is enough for 497 days so
129 overflows will not happen.
131 @status current Introduced in 2.0.0
133 static int luaGetTime(lua_State * L)
135 lua_pushunsigned(L, get_tmr10ms());
136 return 1;
139 static void luaPushDateTime(lua_State * L, uint32_t year, uint32_t mon, uint32_t day,
140 uint32_t hour, uint32_t min, uint32_t sec)
142 uint32_t hour12 = hour;
144 if (hour == 0) {
145 hour12 = 12;
147 else if (hour > 12) {
148 hour12 = hour - 12;
150 lua_createtable(L, 0, 8);
151 lua_pushtableinteger(L, "year", year);
152 lua_pushtableinteger(L, "mon", mon);
153 lua_pushtableinteger(L, "day", day);
154 lua_pushtableinteger(L, "hour", hour);
155 lua_pushtableinteger(L, "min", min);
156 lua_pushtableinteger(L, "sec", sec);
157 lua_pushtableinteger(L, "hour12", hour12);
158 if (hour < 12) {
159 lua_pushtablestring(L, "suffix", "am");
161 else {
162 lua_pushtablestring(L, "suffix", "pm");
166 /*luadoc
167 @function getDateTime()
169 Return current system date and time that is kept by the RTC unit
171 @retval table current date and time, table elements:
172 * `year` (number) year
173 * `mon` (number) month
174 * `day` (number) day of month
175 * `hour` (number) hours
176 * `hour12` (number) hours in US format
177 * `min` (number) minutes
178 * `sec` (number) seconds
179 * `suffix` (text) am or pm
181 static int luaGetDateTime(lua_State * L)
183 struct gtm utm;
184 gettime(&utm);
185 luaPushDateTime(L, utm.tm_year + TM_YEAR_BASE, utm.tm_mon + 1, utm.tm_mday, utm.tm_hour, utm.tm_min, utm.tm_sec);
186 return 1;
189 /*luadoc
190 @function getRtcTime()
192 Return current RTC system date as unix timstamp (in seconds since 1. Jan 1970)
194 Please note the RTC timestamp is kept internally as a 32bit integer, which will overflow
195 in 2038.
197 @retval number Number of seconds elapsed since 1. Jan 1970
200 #if defined(RTCLOCK)
201 static int luaGetRtcTime(lua_State * L)
203 lua_pushunsigned(L, g_rtcTime);
204 return 1;
206 #endif
208 static void luaPushLatLon(lua_State* L, TelemetrySensor & telemetrySensor, TelemetryItem & telemetryItem)
209 /* result is lua table containing members ["lat"] and ["lon"] as lua_Number (doubles) in decimal degrees */
211 lua_createtable(L, 0, 5);
212 lua_pushtablenumber(L, "lat", telemetryItem.gps.latitude * 0.000001); // floating point multiplication is faster than division
213 lua_pushtablenumber(L, "pilot-lat", telemetryItem.pilotLatitude * 0.000001);
214 lua_pushtablenumber(L, "lon", telemetryItem.gps.longitude * 0.000001);
215 lua_pushtablenumber(L, "pilot-lon", telemetryItem.pilotLongitude * 0.000001);
217 int8_t delay = telemetryItem.getDelaySinceLastValue();
218 if (delay >= 0)
219 lua_pushtableinteger(L, "delay", delay);
220 else
221 lua_pushtablenil(L, "delay");
224 static void luaPushTelemetryDateTime(lua_State* L, TelemetrySensor & telemetrySensor, TelemetryItem & telemetryItem)
226 luaPushDateTime(L, telemetryItem.datetime.year, telemetryItem.datetime.month, telemetryItem.datetime.day,
227 telemetryItem.datetime.hour, telemetryItem.datetime.min, telemetryItem.datetime.sec);
230 static void luaPushCells(lua_State* L, TelemetrySensor & telemetrySensor, TelemetryItem & telemetryItem)
232 if (telemetryItem.cells.count == 0)
233 lua_pushinteger(L, (int)0); // returns zero if no cells
234 else {
235 lua_createtable(L, telemetryItem.cells.count, 0);
236 for (int i = 0; i < telemetryItem.cells.count; i++) {
237 lua_pushnumber(L, i + 1);
238 lua_pushnumber(L, telemetryItem.cells.values[i].value * 0.01f);
239 lua_settable(L, -3);
244 void luaGetValueAndPush(lua_State* L, int src)
246 getvalue_t value = getValue(src); // ignored for GPS, DATETIME, and CELLS
248 if (src >= MIXSRC_FIRST_TELEM && src <= MIXSRC_LAST_TELEM) {
249 div_t qr = div(src-MIXSRC_FIRST_TELEM, 3);
250 // telemetry values
251 if (TELEMETRY_STREAMING() && telemetryItems[qr.quot].isAvailable()) {
252 TelemetrySensor & telemetrySensor = g_model.telemetrySensors[qr.quot];
253 switch (telemetrySensor.unit) {
254 case UNIT_GPS:
255 luaPushLatLon(L, telemetrySensor, telemetryItems[qr.quot]);
256 break;
257 case UNIT_DATETIME:
258 luaPushTelemetryDateTime(L, telemetrySensor, telemetryItems[qr.quot]);
259 break;
260 case UNIT_TEXT:
261 lua_pushstring(L, telemetryItems[qr.quot].text);
262 break;
263 case UNIT_CELLS:
264 if (qr.rem == 0) {
265 luaPushCells(L, telemetrySensor, telemetryItems[qr.quot]);
266 break;
268 // deliberate no break here to properly return `Cels-` and `Cels+`
269 default:
270 if (telemetrySensor.prec > 0)
271 lua_pushnumber(L, float(value)/telemetrySensor.getPrecDivisor());
272 else
273 lua_pushinteger(L, value);
274 break;
277 else {
278 // telemetry not working, return zero for telemetry sources
279 lua_pushinteger(L, (int)0);
282 else if (src == MIXSRC_TX_VOLTAGE) {
283 lua_pushnumber(L, float(value) * 0.1f);
285 else {
286 lua_pushinteger(L, value);
291 Return field data for a given field name
293 bool luaFindFieldByName(const char * name, LuaField & field, unsigned int flags)
295 // TODO better search method (binary lookup)
296 for (unsigned int n=0; n<DIM(luaSingleFields); ++n) {
297 if (!strcmp(name, luaSingleFields[n].name)) {
298 field.id = luaSingleFields[n].id;
299 if (flags & FIND_FIELD_DESC) {
300 strncpy(field.desc, luaSingleFields[n].desc, sizeof(field.desc)-1);
301 field.desc[sizeof(field.desc)-1] = '\0';
303 else {
304 field.desc[0] = '\0';
306 return true;
310 // search in multiples
311 unsigned int len = strlen(name);
312 for (unsigned int n=0; n<DIM(luaMultipleFields); ++n) {
313 const char * fieldName = luaMultipleFields[n].name;
314 unsigned int fieldLen = strlen(fieldName);
315 if (!strncmp(name, fieldName, fieldLen)) {
316 unsigned int index;
317 if (len == fieldLen+1 && isdigit(name[fieldLen])) {
318 index = name[fieldLen] - '1';
320 else if (len == fieldLen+2 && isdigit(name[fieldLen]) && isdigit(name[fieldLen+1])) {
321 index = 10 * (name[fieldLen] - '0') + (name[fieldLen+1] - '1');
323 else {
324 continue;
326 if (index < luaMultipleFields[n].count) {
327 if(luaMultipleFields[n].id == MIXSRC_FIRST_TELEM)
328 field.id = luaMultipleFields[n].id + index*3;
329 else
330 field.id = luaMultipleFields[n].id + index;
331 if (flags & FIND_FIELD_DESC) {
332 snprintf(field.desc, sizeof(field.desc)-1, luaMultipleFields[n].desc, index+1);
333 field.desc[sizeof(field.desc)-1] = '\0';
335 else {
336 field.desc[0] = '\0';
338 return true;
343 // search in telemetry
344 field.desc[0] = '\0';
345 for (int i=0; i<MAX_TELEMETRY_SENSORS; i++) {
346 if (isTelemetryFieldAvailable(i)) {
347 char sensorName[TELEM_LABEL_LEN+1];
348 int len = zchar2str(sensorName, g_model.telemetrySensors[i].label, TELEM_LABEL_LEN);
349 if (!strncmp(sensorName, name, len)) {
350 if (name[len] == '\0') {
351 field.id = MIXSRC_FIRST_TELEM + 3*i;
352 field.desc[0] = '\0';
353 return true;
355 else if (name[len] == '-' && name[len+1] == '\0') {
356 field.id = MIXSRC_FIRST_TELEM + 3*i + 1;
357 field.desc[0] = '\0';
358 return true;
360 else if (name[len] == '+' && name[len+1] == '\0') {
361 field.id = MIXSRC_FIRST_TELEM + 3*i + 2;
362 field.desc[0] = '\0';
363 return true;
369 return false; // not found
372 /*luadoc
373 @function sportTelemetryPop()
375 Pops a received SPORT packet from the queue. Please note that only packets using a data ID within 0x5000 to 0x50FF
376 (frame ID == 0x10), as well as packets with a frame ID equal 0x32 (regardless of the data ID) will be passed to
377 the LUA telemetry receive queue.
379 @retval nil queue does not contain any (or enough) bytes to form a whole packet
381 @retval multiple returns 4 values:
382 * sensor ID (number)
383 * frame ID (number)
384 * data ID (number)
385 * value (number)
387 @status current Introduced in 2.2.0
389 static int luaSportTelemetryPop(lua_State * L)
391 if (!luaInputTelemetryFifo) {
392 luaInputTelemetryFifo = new Fifo<uint8_t, LUA_TELEMETRY_INPUT_FIFO_SIZE>();
393 if (!luaInputTelemetryFifo) {
394 return 0;
398 if (luaInputTelemetryFifo->size() >= sizeof(SportTelemetryPacket)) {
399 SportTelemetryPacket packet;
400 for (uint8_t i=0; i<sizeof(packet); i++) {
401 luaInputTelemetryFifo->pop(packet.raw[i]);
403 lua_pushnumber(L, packet.physicalId);
404 lua_pushnumber(L, packet.primId);
405 lua_pushnumber(L, packet.dataId);
406 lua_pushunsigned(L, packet.value);
407 return 4;
410 return 0;
413 #define BIT(x, index) (((x) >> index) & 0x01)
414 uint8_t getDataId(uint8_t physicalId)
416 uint8_t result = physicalId;
417 result += (BIT(physicalId, 0) ^ BIT(physicalId, 1) ^ BIT(physicalId, 2)) << 5;
418 result += (BIT(physicalId, 2) ^ BIT(physicalId, 3) ^ BIT(physicalId, 4)) << 6;
419 result += (BIT(physicalId, 0) ^ BIT(physicalId, 2) ^ BIT(physicalId, 4)) << 7;
420 return result;
423 /*luadoc
424 @function sportTelemetryPush()
426 This functions allows for sending SPORT telemetry data toward the receiver,
427 and more generally, to anything connected SPORT bus on the receiver or transmitter.
429 When called without parameters, it will only return the status of the output buffer without sending anything.
431 @param sensorId physical sensor ID
433 @param frameId frame ID
435 @param dataId data ID
437 @param value value
439 @retval boolean data queued in output buffer or not.
441 @status current Introduced in 2.2.0
444 static int luaSportTelemetryPush(lua_State * L)
446 if (!IS_FRSKY_SPORT_PROTOCOL()) {
447 lua_pushboolean(L, false);
448 return 1;
451 if (lua_gettop(L) == 0) {
452 lua_pushboolean(L, outputTelemetryBuffer.isAvailable());
453 return 1;
456 uint16_t dataId = luaL_checkunsigned(L, 3);
458 if (outputTelemetryBuffer.isAvailable()) {
459 for (uint8_t i=0; i<MAX_TELEMETRY_SENSORS; i++) {
460 TelemetrySensor & sensor = g_model.telemetrySensors[i];
461 if (sensor.id == dataId) {
462 if (sensor.frskyInstance.rxIndex == TELEMETRY_ENDPOINT_SPORT) {
463 SportTelemetryPacket packet;
464 packet.physicalId = getDataId(luaL_checkunsigned(L, 1));
465 packet.primId = luaL_checkunsigned(L, 2);
466 packet.dataId = dataId;
467 packet.value = luaL_checkunsigned(L, 4);
468 outputTelemetryBuffer.pushSportPacketWithBytestuffing(packet);
470 else {
471 outputTelemetryBuffer.sport.physicalId = getDataId(luaL_checkunsigned(L, 1));
472 outputTelemetryBuffer.sport.primId = luaL_checkunsigned(L, 2);
473 outputTelemetryBuffer.sport.dataId = dataId;
474 outputTelemetryBuffer.sport.value = luaL_checkunsigned(L, 4);
476 outputTelemetryBuffer.setDestination(sensor.frskyInstance.rxIndex);
477 lua_pushboolean(L, true);
478 return 1;
482 // sensor not found, we send the frame to the SPORT line
484 SportTelemetryPacket packet;
485 packet.physicalId = getDataId(luaL_checkunsigned(L, 1));
486 packet.primId = luaL_checkunsigned(L, 2);
487 packet.dataId = dataId;
488 packet.value = luaL_checkunsigned(L, 4);
489 outputTelemetryBuffer.pushSportPacketWithBytestuffing(packet);
490 #if defined(PXX2)
491 uint8_t destination = (IS_INTERNAL_MODULE_ON() ? INTERNAL_MODULE : EXTERNAL_MODULE);
492 outputTelemetryBuffer.setDestination(isModulePXX2(destination) ? (destination << 2) : TELEMETRY_ENDPOINT_SPORT);
493 #else
494 outputTelemetryBuffer.setDestination(TELEMETRY_ENDPOINT_SPORT);
495 #endif
496 lua_pushboolean(L, true);
497 return 1;
501 lua_pushboolean(L, false);
502 return 1;
505 #if defined(PXX2)
506 /*luadoc
507 @function accessTelemetryPush()
509 This functions allows for sending SPORT / ACCESS telemetry data toward the receiver,
510 and more generally, to anything connected SPORT bus on the receiver or transmitter.
512 When called without parameters, it will only return the status of the output buffer without sending anything.
514 @param module module index (0 = internal, 1 = external)
516 @param rxUid receiver index
518 @param sensorId physical sensor ID
520 @param frameId frame ID
522 @param dataId data ID
524 @param value value
526 @retval boolean data queued in output buffer or not.
528 @status current Introduced in 2.3
532 bool getDefaultAccessDestination(uint8_t & destination)
534 for (uint8_t i=0; i<MAX_TELEMETRY_SENSORS; i++) {
535 TelemetrySensor & sensor = g_model.telemetrySensors[i];
536 if (sensor.type == TELEM_TYPE_CUSTOM) {
537 TelemetryItem sensorItem = telemetryItems[i];
538 if (sensorItem.isFresh()) {
539 destination = sensor.frskyInstance.rxIndex;
540 return true;
544 return false;
547 static int luaAccessTelemetryPush(lua_State * L)
549 if (lua_gettop(L) == 0) {
550 lua_pushboolean(L, outputTelemetryBuffer.isAvailable());
551 return 1;
554 if (outputTelemetryBuffer.isAvailable()) {
555 int8_t module = luaL_checkinteger(L, 1);
556 uint8_t rxUid = luaL_checkunsigned(L, 2);
557 uint8_t destination;
559 if (module < 0) {
560 if (!getDefaultAccessDestination(destination)) {
561 lua_pushboolean(L, false);
562 return 1;
565 else {
566 destination = (module << 2) + rxUid;
569 outputTelemetryBuffer.sport.physicalId = getDataId(luaL_checkunsigned(L, 3));
570 outputTelemetryBuffer.sport.primId = luaL_checkunsigned(L, 4);
571 outputTelemetryBuffer.sport.dataId = luaL_checkunsigned(L, 5);
572 outputTelemetryBuffer.sport.value = luaL_checkunsigned(L, 6);
573 outputTelemetryBuffer.setDestination(destination);
574 lua_pushboolean(L, true);
575 return 1;
578 lua_pushboolean(L, false);
579 return 1;
581 #endif
583 #if defined(CROSSFIRE)
584 /*luadoc
585 @function crossfireTelemetryPop()
587 Pops a received Crossfire Telemetry packet from the queue.
589 @retval nil queue does not contain any (or enough) bytes to form a whole packet
591 @retval multiple returns 2 values:
592 * command (number)
593 * packet (table) data bytes
595 @status current Introduced in 2.2.0
597 static int luaCrossfireTelemetryPop(lua_State * L)
599 if (!luaInputTelemetryFifo) {
600 luaInputTelemetryFifo = new Fifo<uint8_t, LUA_TELEMETRY_INPUT_FIFO_SIZE>();
601 if (!luaInputTelemetryFifo) {
602 return 0;
606 uint8_t length = 0, data = 0;
607 if (luaInputTelemetryFifo->probe(length) && luaInputTelemetryFifo->size() >= uint32_t(length)) {
608 // length value includes the length field
609 luaInputTelemetryFifo->pop(length);
610 luaInputTelemetryFifo->pop(data); // command
611 lua_pushnumber(L, data);
612 lua_newtable(L);
613 for (uint8_t i=1; i<length-1; i++) {
614 luaInputTelemetryFifo->pop(data);
615 lua_pushinteger(L, i);
616 lua_pushinteger(L, data);
617 lua_settable(L, -3);
619 return 2;
622 return 0;
625 /*luadoc
626 @function crossfireTelemetryPush()
628 This functions allows for sending telemetry data toward the TBS Crossfire link.
630 When called without parameters, it will only return the status of the output buffer without sending anything.
632 @param command command
634 @param data table of data bytes
636 @retval boolean data queued in output buffer or not.
638 @status current Introduced in 2.2.0
640 static int luaCrossfireTelemetryPush(lua_State * L)
642 if (telemetryProtocol != PROTOCOL_TELEMETRY_CROSSFIRE) {
643 lua_pushboolean(L, false);
644 return 1;
647 if (lua_gettop(L) == 0) {
648 lua_pushboolean(L, outputTelemetryBuffer.isAvailable());
650 else if (outputTelemetryBuffer.isAvailable()) {
651 uint8_t command = luaL_checkunsigned(L, 1);
652 luaL_checktype(L, 2, LUA_TTABLE);
653 uint8_t length = luaL_len(L, 2);
654 outputTelemetryBuffer.pushByte(MODULE_ADDRESS);
655 outputTelemetryBuffer.pushByte(2 + length); // 1(COMMAND) + data length + 1(CRC)
656 outputTelemetryBuffer.pushByte(command); // COMMAND
657 for (int i=0; i<length; i++) {
658 lua_rawgeti(L, 2, i+1);
659 outputTelemetryBuffer.pushByte(luaL_checkunsigned(L, -1));
661 outputTelemetryBuffer.pushByte(crc8(outputTelemetryBuffer.data+2, 1 + length));
662 outputTelemetryBuffer.setDestination(TELEMETRY_ENDPOINT_SPORT);
663 lua_pushboolean(L, true);
665 else {
666 lua_pushboolean(L, false);
668 return 1;
670 #endif
672 /*luadoc
673 @function getFieldInfo(name)
675 Return detailed information about field (source)
677 The list of valid sources is available:
679 | OpenTX Version | Radio |
680 |----------------|-------|
681 | 2.0 | [all](http://downloads-20.open-tx.org/firmware/lua_fields.txt) |
682 | 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) |
683 | 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) |
685 @param name (string) name of the field
687 @retval table information about requested field, table elements:
688 * `id` (number) field identifier
689 * `name` (string) field name
690 * `desc` (string) field description
691 * 'unit' (number) unit identifier [Full list](../appendix/units.html)
693 @retval nil the requested field was not found
695 @status current Introduced in 2.0.8, 'unit' field added in 2.2.0
697 static int luaGetFieldInfo(lua_State * L)
699 const char * what = luaL_checkstring(L, 1);
700 LuaField field;
701 bool found = luaFindFieldByName(what, field, FIND_FIELD_DESC);
702 if (found) {
703 lua_newtable(L);
704 lua_pushtableinteger(L, "id", field.id);
705 lua_pushtablestring(L, "name", what);
706 lua_pushtablestring(L, "desc", field.desc);
707 if (field.id >= MIXSRC_FIRST_TELEM && field.id <= MIXSRC_LAST_TELEM) {
708 TelemetrySensor & telemetrySensor = g_model.telemetrySensors[(int)((field.id-MIXSRC_FIRST_TELEM)/3)];
709 lua_pushtableinteger(L, "unit", telemetrySensor.unit);
711 else {
712 lua_pushtablenil(L, "unit");
714 return 1;
716 return 0;
719 /*luadoc
720 @function getValue(source)
722 Returns the value of a source.
724 The list of fixed sources:
726 | OpenTX Version | Radio |
727 |----------------|-------|
728 | 2.0 | [all](http://downloads-20.open-tx.org/firmware/lua_fields.txt) |
729 | 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) |
730 | 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) |
733 In OpenTX 2.1.x the telemetry sources no longer have a predefined name.
734 To get a telemetry value simply use it's sensor name. For example:
735 * Altitude sensor has a name "Alt"
736 * to get the current altitude use the source "Alt"
737 * to get the minimum altitude use the source "Alt-", to get the maximum use "Alt+"
739 @param source can be an identifier (number) (which was obtained by the getFieldInfo())
740 or a name (string) of the source.
742 @retval value current source value (number). Zero is returned for:
743 * non-existing sources
744 * for all telemetry source when the telemetry stream is not received
745 * far all non allowed sensors while FAI MODE is active
747 @retval table GPS position is returned in a table:
748 * `lat` (number) latitude, positive is North
749 * `lon` (number) longitude, positive is East
750 * `pilot-lat` (number) pilot latitude, positive is North
751 * `pilot-lon` (number) pilot longitude, positive is East
753 @retval table GPS date/time, see getDateTime()
755 @retval table Cells are returned in a table
756 (except where no cells were detected in which
757 case the returned value is 0):
758 * table has one item for each detected cell:
759 * key (number) cell number (1 to number of cells)
760 * value (number) current cell voltage
762 @status current Introduced in 2.0.0, changed in 2.1.0, `Cels+` and
763 `Cels-` added in 2.1.9
765 @notice Getting a value by its numerical identifier is faster then by its name.
766 While `Cels` sensor returns current values of all cells in a table, a `Cels+` or
767 `Cels-` will return a single value - the maximum or minimum Cels value.
769 static int luaGetValue(lua_State * L)
771 int src = 0;
772 if (lua_isnumber(L, 1)) {
773 src = luaL_checkinteger(L, 1);
775 else {
776 // convert from field name to its id
777 const char *name = luaL_checkstring(L, 1);
778 LuaField field;
779 bool found = luaFindFieldByName(name, field);
780 if (found) {
781 src = field.id;
784 luaGetValueAndPush(L, src);
785 return 1;
788 /*luadoc
789 @function getRAS()
791 Return the RAS value or nil if no valid hardware found
793 @retval number representing RAS value. Value bellow 0x33 (51 decimal) are all ok, value above 0x33 indicate a hardware antenna issue.
794 This is just a hardware pass/fail measure and does not represent the quality of the radio link
796 @notice RAS was called SWR in the past
798 @status current Introduced in 2.2.0
800 static int luaGetRAS(lua_State * L)
802 if (isRasValueValid()) {
803 lua_pushinteger(L, telemetryData.swrInternal.value());
805 else {
806 lua_pushnil(L);
808 return 1;
811 /*luadoc
812 @function getTxGPS()
814 Return the internal GPS position or nil if no valid hardware found
816 @retval table representing the current radio position
817 * `lat` (number) internal GPS latitude, positive is North
818 * `lon` (number) internal GPS longitude, positive is East
819 * 'numsat' (number) current number of sats locked in by the GPS sensor
820 * 'fix' (boolean) fix status
821 * 'alt' (number) internal GPS altitude in 0.1m
822 * 'speed' (number) internal GPSspeed in 0.1m/s
823 * 'heading' (number) internal GPS ground course estimation in degrees * 10
824 * 'hdop' (number) internal GPS horizontal dilution of precision
826 @status current Introduced in 2.2.2
828 static int luaGetTxGPS(lua_State * L)
830 #if defined(INTERNAL_GPS)
831 lua_createtable(L, 0, 7);
832 lua_pushtablenumber(L, "lat", gpsData.latitude * 0.000001);
833 lua_pushtablenumber(L, "lon", gpsData.longitude * 0.000001);
834 lua_pushtableinteger(L, "numsat", gpsData.numSat);
835 lua_pushtableinteger(L, "alt", gpsData.altitude);
836 lua_pushtableinteger(L, "speed", gpsData.speed);
837 lua_pushtableinteger(L, "heading", gpsData.groundCourse);
838 lua_pushtableinteger(L, "hdop", gpsData.hdop);
839 if (gpsData.fix)
840 lua_pushtableboolean(L, "fix", true);
841 else
842 lua_pushtableboolean(L, "fix", false);
843 #else
844 lua_pushnil(L);
845 #endif
846 return 1;
850 /*luadoc
851 @function getFlightMode(mode)
853 Return flight mode data.
855 @param mode (number) flight mode number to return (0 - 8). If mode parameter
856 is not specified (or contains invalid value), then the current flight mode data is returned.
858 @retval multiple returns 2 values:
859 * (number) (current) flight mode number (0 - 8)
860 * (string) (current) flight mode name
862 @status current Introduced in 2.1.7
864 static int luaGetFlightMode(lua_State * L)
866 int mode = luaL_optinteger(L, 1, -1);
867 if (mode < 0 || mode >= MAX_FLIGHT_MODES) {
868 mode = mixerCurrentFlightMode;
870 lua_pushnumber(L, mode);
871 char name[sizeof(g_model.flightModeData[0].name)+1];
872 zchar2str(name, g_model.flightModeData[mode].name, sizeof(g_model.flightModeData[0].name));
873 lua_pushstring(L, name);
874 return 2;
877 /*luadoc
878 @function playFile(name)
880 Play a file from the SD card
882 @param path (string) full path to wav file (i.e. “/SOUNDS/en/system/tada.wav”)
883 Introduced in 2.1.0: If you use a relative path, the current language is appended
884 to the path (example: for English language: `/SOUNDS/en` is appended)
886 @status current Introduced in 2.0.0, changed in 2.1.0
888 static int luaPlayFile(lua_State * L)
890 const char * filename = luaL_checkstring(L, 1);
891 if (filename[0] != '/') {
892 // relative sound file path - use current language dir for absolute path
893 char file[AUDIO_FILENAME_MAXLEN+1];
894 char * str = getAudioPath(file);
895 strncpy(str, filename, AUDIO_FILENAME_MAXLEN - (str-file));
896 file[AUDIO_FILENAME_MAXLEN] = 0;
897 PLAY_FILE(file, 0, 0);
899 else {
900 PLAY_FILE(filename, 0, 0);
902 return 0;
905 /*luadoc
906 @function playNumber(value, unit [, attributes])
908 Play a numerical value (text to speech)
910 @param value (number) number to play. Value is interpreted as integer.
912 @param unit (number) unit identifier [Full list]((../appendix/units.html))
914 @param attributes (unsigned number) possible values:
915 * `0 or not present` plays integral part of the number (for a number 123 it plays 123)
916 * `PREC1` plays a number with one decimal place (for a number 123 it plays 12.3)
917 * `PREC2` plays a number with two decimal places (for a number 123 it plays 1.23)
919 @status current Introduced in 2.0.0
922 static int luaPlayNumber(lua_State * L)
924 int number = luaL_checkinteger(L, 1);
925 int unit = luaL_checkinteger(L, 2);
926 unsigned int att = luaL_optunsigned(L, 3, 0);
927 playNumber(number, unit, att, 0);
928 return 0;
931 /*luadoc
932 @function playDuration(duration [, hourFormat])
934 Play a time value (text to speech)
936 @param duration (number) number of seconds to play. Only integral part is used.
938 @param hourFormat (number):
939 * `0 or not present` play format: minutes and seconds.
940 * `!= 0` play format: hours, minutes and seconds.
942 @status current Introduced in 2.1.0
944 static int luaPlayDuration(lua_State * L)
946 int duration = luaL_checkinteger(L, 1);
947 bool playTime = (luaL_optinteger(L, 2, 0) != 0);
948 playDuration(duration, playTime ? PLAY_TIME : 0, 0);
949 return 0;
952 /*luadoc
953 @function playTone(frequency, duration, pause [, flags [, freqIncr]])
955 Play a tone
957 @param frequency (number) tone frequency in Hz (from 150 to 15000)
959 @param duration (number) length of the tone in milliseconds
961 @param pause (number) length of the silence after the tone in milliseconds
963 @param flags (number):
964 * `0 or not present` play with normal priority.
965 * `PLAY_BACKGROUND` play in background (built in vario function uses this context)
966 * `PLAY_NOW` play immediately
968 @param freqIncr (number) positive number increases the tone pitch (frequency with time),
969 negative number decreases it. The frequency changes every 10 milliseconds, the change is `freqIncr * 10Hz`.
970 The valid range is from -127 to 127.
972 @status current Introduced in 2.1.0
974 static int luaPlayTone(lua_State * L)
976 int frequency = luaL_checkinteger(L, 1);
977 int length = luaL_checkinteger(L, 2);
978 int pause = luaL_checkinteger(L, 3);
979 int flags = luaL_optinteger(L, 4, 0);
980 int freqIncr = luaL_optinteger(L, 5, 0);
981 audioQueue.playTone(frequency, length, pause, flags, freqIncr);
982 return 0;
985 /*luadoc
986 @function playHaptic(duration, pause [, flags])
988 Generate haptic feedback
990 @param duration (number) length of the haptic feedback in milliseconds
992 @param pause (number) length of the silence after haptic feedback in milliseconds
994 @param flags (number):
995 * `0 or not present` play with normal priority
996 * `PLAY_NOW` play immediately
998 @status current Introduced in 2.2.0
1000 static int luaPlayHaptic(lua_State * L)
1002 #if defined(HAPTIC)
1003 int length = luaL_checkinteger(L, 1);
1004 int pause = luaL_checkinteger(L, 2);
1005 int flags = luaL_optinteger(L, 3, 0);
1006 haptic.play(length, pause, flags);
1007 #else
1008 UNUSED(L);
1009 #endif
1010 return 0;
1013 /*luadoc
1014 @function killEvents(key)
1016 Stops key state machine. See [Key Events](../key_events.md) for the detailed description.
1018 @param key (number) key to be killed, can also include event type (only the key part is used)
1020 @status current Introduced in 2.0.0
1023 static int luaKillEvents(lua_State * L)
1025 uint8_t key = EVT_KEY_MASK(luaL_checkinteger(L, 1));
1026 // prevent killing maskable keys (only in telemetry scripts)
1027 // TODO add which tpye of script is running before p_call()
1028 if (IS_MASKABLE(key)) {
1029 killEvents(key);
1031 return 0;
1034 #if LCD_DEPTH > 1 && !defined(COLORLCD)
1035 /*luadoc
1036 @function GREY()
1038 Returns gray value which can be used in LCD functions
1040 @retval (number) a value that represents amount of *greyness* (from 0 to 15)
1042 @notice Only available on Taranis
1044 @status current Introduced in 2.0.13
1046 static int luaGrey(lua_State * L)
1048 int index = luaL_checkinteger(L, 1);
1049 lua_pushunsigned(L, GREY(index));
1050 return 1;
1052 #endif
1054 /*luadoc
1055 @function getGeneralSettings()
1057 Returns (some of) the general radio settings
1059 @retval table with elements:
1060 * `battWarn` (number) radio battery range - warning value
1061 * `battMin` (number) radio battery range - minimum value
1062 * `battMax` (number) radio battery range - maximum value
1063 * `imperial` (number) set to a value different from 0 if the radio is set to the
1064 IMPERIAL units
1065 * `language` (string) radio language (used for menus)
1066 * `voice` (string) voice language (used for speech)
1067 * `gtimer` (number) radio global timer in seconds (does not include current session)
1069 @status current Introduced in 2.0.6, `imperial` added in TODO,
1070 `language` and `voice` added in 2.2.0, gtimer added in 2.2.2.
1073 static int luaGetGeneralSettings(lua_State * L)
1075 lua_newtable(L);
1076 lua_pushtablenumber(L, "battWarn", (g_eeGeneral.vBatWarn) * 0.1f);
1077 lua_pushtablenumber(L, "battMin", (90+g_eeGeneral.vBatMin) * 0.1f);
1078 lua_pushtablenumber(L, "battMax", (120+g_eeGeneral.vBatMax) * 0.1f);
1079 lua_pushtableinteger(L, "imperial", g_eeGeneral.imperial);
1080 lua_pushtablestring(L, "language", TRANSLATIONS);
1081 lua_pushtablestring(L, "voice", currentLanguagePack->id);
1082 lua_pushtableinteger(L, "gtimer", g_eeGeneral.globalTimer);
1083 return 1;
1086 /*luadoc
1087 @function getGlobalTimer()
1089 Returns radio timers
1091 @retval table with elements:
1092 * `gtimer` (number) radio global timer in seconds
1093 * `session` (number) radio session in seconds
1094 * `ttimer` (number) radio throttle timer in seconds
1095 * `tptimer` (number) radio throttle percent timer in seconds
1097 @status current Introduced added in 2.3.0.
1100 static int luaGetGlobalTimer(lua_State * L)
1102 lua_newtable(L);
1103 lua_pushtableinteger(L, "total", g_eeGeneral.globalTimer + sessionTimer);
1104 lua_pushtableinteger(L, "session", sessionTimer);
1105 lua_pushtableinteger(L, "throttle", s_timeCumThr);
1106 lua_pushtableinteger(L, "throttlepct", s_timeCum16ThrP/16);
1107 return 1;
1110 /*luadoc
1111 @function popupInput(title, event, input, min, max)
1113 Raises a pop-up on screen that allows uses input
1115 @param title (string) text to display
1117 @param event (number) the event variable that is passed in from the
1118 Run function (key pressed)
1120 @param input (number) value that can be adjusted by the +/­- keys
1122 @param min (number) min value that input can reach (by pressing the -­ key)
1124 @param max (number) max value that input can reach
1126 @retval number result of the input adjustment
1128 @retval "OK" user pushed ENT key
1130 @retval "CANCEL" user pushed EXIT key
1132 @notice Use only from stand-alone and telemetry scripts.
1134 @status current Introduced in 2.0.0
1137 /* TODO : fix, broken by popups rewrite
1138 static int luaPopupInput(lua_State * L)
1140 event_t event = luaL_checkinteger(L, 2);
1141 warningInputValue = luaL_checkinteger(L, 3);
1142 warningInputValueMin = luaL_checkinteger(L, 4);
1143 warningInputValueMax = luaL_checkinteger(L, 5);
1144 warningText = luaL_checkstring(L, 1);
1145 warningType = WARNING_TYPE_INPUT;
1146 runPopupWarning(event);
1147 if (warningResult) {
1148 warningResult = 0;
1149 lua_pushstring(L, "OK");
1151 else if (!warningText) {
1152 lua_pushstring(L, "CANCEL");
1154 else {
1155 lua_pushinteger(L, warningInputValue);
1157 warningText = NULL;
1158 return 1;
1162 /*luadoc
1163 @function popupWarning(title, event)
1165 Raises a pop-up on screen that shows a warning
1167 @param title (string) text to display
1169 @param event (number) the event variable that is passed in from the
1170 Run function (key pressed)
1172 @retval "CANCEL" user pushed EXIT key
1174 @notice Use only from stand-alone and telemetry scripts.
1176 @status current Introduced in 2.2.0
1178 static int luaPopupWarning(lua_State * L)
1180 event_t event = luaL_checkinteger(L, 2);
1181 warningText = luaL_checkstring(L, 1);
1182 warningType = WARNING_TYPE_ASTERISK;
1183 runPopupWarning(event);
1184 if (!warningText) {
1185 lua_pushstring(L, "CANCEL");
1187 else {
1188 warningText = NULL;
1189 lua_pushnil(L);
1191 return 1;
1194 /*luadoc
1195 @function popupConfirmation(title, event)
1197 Raises a pop-up on screen that asks for confirmation
1199 @param title (string) text to display
1201 @param event (number) the event variable that is passed in from the
1202 Run function (key pressed)
1204 @retval "CANCEL" user pushed EXIT key
1206 @notice Use only from stand-alone and telemetry scripts.
1208 @status current Introduced in 2.2.0
1210 static int luaPopupConfirmation(lua_State * L)
1212 event_t event = luaL_checkinteger(L, 2);
1213 warningText = luaL_checkstring(L, 1);
1214 warningType = WARNING_TYPE_CONFIRM;
1215 runPopupWarning(event);
1216 if (!warningText) {
1217 lua_pushstring(L, warningResult ? "OK" : "CANCEL");
1219 else {
1220 warningText = NULL;
1221 lua_pushnil(L);
1223 return 1;
1226 /*luadoc
1227 @function defaultStick(channel)
1229 Get stick that is assigned to a channel. See Default Channel Order in General Settings.
1231 @param channel (number) channel number (0 means CH1)
1233 @retval number Stick assigned to this channel (from 0 to 3)
1235 @status current Introduced in 2.0.0
1237 static int luaDefaultStick(lua_State * L)
1239 uint8_t channel = luaL_checkinteger(L, 1);
1240 lua_pushinteger(L, channelOrder(channel+1)-1);
1241 return 1;
1244 /*luadoc
1245 @function setTelemetryValue(id, subID, instance, value [, unit [, precision [, name]]])
1247 @param id Id of the sensor, valid range is from 0 to 0xFFFF
1249 @param subID subID of the sensor, usually 0, valid range is from 0 to 7
1251 @param instance instance of the sensor (SensorID), valid range is from 0 to 0xFF
1253 @param value fed to the sensor
1255 @param unit unit of the sensor [Full list](../appendix/units.html)
1257 @param precision the precision of the sensor
1258 * `0 or not present` no decimal precision.
1259 * `!= 0` value is divided by 10^precision, e.g. value=1000, prec=2 => 10.00.
1261 @param name (string) Name of the sensor if it does not yet exist (4 chars).
1262 * `not present` Name defaults to the Id.
1263 * `present` Sensor takes name of the argument. Argument must have name surrounded by quotes: e.g., "Name"
1265 @retval true, if the sensor was just added. In this case the value is ignored (subsequent call will set the value)
1267 @notice All three parameters `id`, `subID` and `instance` can't be zero at the same time. At least one of them
1268 must be different from zero.
1270 @status current Introduced in 2.2.0
1272 static int luaSetTelemetryValue(lua_State * L)
1274 uint16_t id = luaL_checkunsigned(L, 1);
1275 uint8_t subId = luaL_checkunsigned(L, 2) & 0x7;
1276 uint8_t instance = luaL_checkunsigned(L, 3);
1277 int32_t value = luaL_checkinteger(L, 4);
1278 uint32_t unit = luaL_optunsigned(L, 5, 0);
1279 uint32_t prec = luaL_optunsigned(L, 6, 0);
1281 char zname[4];
1282 const char* name = luaL_optstring(L, 7, NULL);
1283 if (name != NULL && strlen(name) > 0) {
1284 str2zchar(zname, name, 4);
1286 else {
1287 zname[0] = hex2zchar((id & 0xf000) >> 12);
1288 zname[1] = hex2zchar((id & 0x0f00) >> 8);
1289 zname[2] = hex2zchar((id & 0x00f0) >> 4);
1290 zname[3] = hex2zchar((id & 0x000f) >> 0);
1292 if (id | subId | instance) {
1293 int index = setTelemetryValue(PROTOCOL_TELEMETRY_LUA, id, subId, instance, value, unit, prec);
1294 if (index >= 0) {
1295 TelemetrySensor &telemetrySensor = g_model.telemetrySensors[index];
1296 telemetrySensor.id = id;
1297 telemetrySensor.subId = subId;
1298 telemetrySensor.instance = instance;
1299 telemetrySensor.init(zname, unit, prec);
1300 lua_pushboolean(L, true);
1302 else {
1303 lua_pushboolean(L, false);
1305 return 1;
1307 lua_pushboolean(L, false);
1308 return 1;
1311 /*luadoc
1312 @function defaultChannel(stick)
1314 Get channel assigned to stick. See Default Channel Order in General Settings
1316 @param stick (number) stick number (from 0 to 3)
1318 @retval number channel assigned to this stick (from 0 to 3)
1320 @retval nil stick not found
1322 @status current Introduced in 2.0.0
1324 static int luaDefaultChannel(lua_State * L)
1326 uint8_t stick = luaL_checkinteger(L, 1);
1327 for (int i=1; i<=4; i++) {
1328 int tmp = channelOrder(i) - 1;
1329 if (tmp == stick) {
1330 lua_pushinteger(L, i-1);
1331 return 1;
1334 lua_pushnil(L);
1335 return 1;
1338 /*luadoc
1339 @function getRSSI()
1341 Get RSSI value as well as low and critical RSSI alarm levels (in dB)
1343 @retval rssi RSSI value (0 if no link)
1345 @retval alarm_low Configured low RSSI alarm level
1347 @retval alarm_crit Configured critical RSSI alarm level
1349 @status current Introduced in 2.2.0
1351 static int luaGetRSSI(lua_State * L)
1353 lua_pushunsigned(L, min((uint8_t)99, TELEMETRY_RSSI()));
1354 lua_pushunsigned(L, g_model.rssiAlarms.getWarningRssi());
1355 lua_pushunsigned(L, g_model.rssiAlarms.getCriticalRssi());
1356 return 3;
1359 /*luadoc
1360 @function chdir(directory)
1362 Change the working directory
1364 @param directory (string) New working directory
1366 @status current Introduced in 2.3.0
1370 static int luaChdir(lua_State * L)
1372 const char * directory = luaL_optstring(L, 1, nullptr);
1373 f_chdir(directory);
1374 return 0;
1377 /*luadoc
1378 @function loadScript(file [, mode], [,env])
1380 Load a Lua script file. This is similar to Lua's own [loadfile()](https://www.lua.org/manual/5.2/manual.html#pdf-loadfile)
1381 API method, but it uses OpenTx's optional pre-compilation feature to save memory and time during load.
1383 Return values are same as from Lua API loadfile() method: If the script was loaded w/out errors
1384 then the loaded script (or "chunk") is returned as a function. Otherwise, returns nil plus the error message.
1386 @param file (string) Full path and file name of script. The file extension is optional and ignored (see `mode` param to control
1387 which extension will be used). However, if an extension is specified, it should be ".lua" (or ".luac"), otherwise it is treated
1388 as part of the file name and the .lua/.luac will be appended to that.
1390 @param mode (string) (optional) Controls whether to force loading the text (.lua) or pre-compiled binary (.luac)
1391 version of the script. By default OTx will load the newest version and compile a new binary if necessary (overwriting any
1392 existing .luac version of the same script, and stripping some debug info like line numbers).
1393 You can use `mode` to control the loading behavior more specifically. Possible values are:
1394 * `b` only binary.
1395 * `t` only text.
1396 * `T` (default on simulator) prefer text but load binary if that is the only version available.
1397 * `bt` (default on radio) either binary or text, whichever is newer (binary preferred when timestamps are equal).
1398 * Add `x` to avoid automatic compilation of source file to .luac version.
1399 Eg: "tx", "bx", or "btx".
1400 * Add `c` to force compilation of source file to .luac version (even if existing version is newer than source file).
1401 Eg: "tc" or "btc" (forces "t", overrides "x").
1402 * Add `d` to keep extra debug info in the compiled binary.
1403 Eg: "td", "btd", or "tcd" (no effect with just "b" or with "x").
1405 @notice
1406 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).
1407 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()).
1409 @param env (integer) See documentation for Lua function loadfile().
1411 @retval function The loaded script, or `nil` if there was an error (e.g. file not found or syntax error).
1413 @retval string Error message(s), if any. Blank if no error occurred.
1415 @status current Introduced in 2.2.0
1417 ### Example
1419 ```lua
1420 fun, err = loadScript("/SCRIPTS/FUNCTIONS/print.lua")
1421 if (fun ~= nil) then
1422 fun("Hello from loadScript()")
1423 else
1424 print(err)
1429 static int luaLoadScript(lua_State * L)
1431 // this function is replicated pretty much verbatim from luaB_loadfile() and load_aux() in lbaselib.c
1432 const char *fname = luaL_optstring(L, 1, NULL);
1433 const char *mode = luaL_optstring(L, 2, NULL);
1434 int env = (!lua_isnone(L, 3) ? 3 : 0); // 'env' index or 0 if no 'env'
1435 lua_settop(L, 0);
1436 if (fname != NULL && luaLoadScriptFileToState(L, fname , mode) == SCRIPT_OK) {
1437 if (env != 0) { // 'env' parameter?
1438 lua_pushvalue(L, env); // environment for loaded function
1439 if (!lua_setupvalue(L, -2, 1)) // set it as 1st upvalue
1440 lua_pop(L, 1); // remove 'env' if not used by previous call
1442 return 1;
1444 else {
1445 // error (message should be on top of the stack)
1446 if (!lua_isstring(L, -1)) {
1447 // probably didn't find a file or had some other error before luaL_loadfile() was run
1448 lua_pushfstring(L, "loadScript(\"%s\", \"%s\") error: File not found", (fname != NULL ? fname : "nul"), (mode != NULL ? mode : "bt"));
1450 lua_pushnil(L);
1451 lua_insert(L, -2); // move nil before error message
1452 return 2; // return nil plus error message
1456 /*luadoc
1457 @function getUsage()
1459 Get percent of already used Lua instructions in current script execution cycle.
1461 @retval usage (number) a value from 0 to 100 (percent)
1463 @status current Introduced in 2.2.1
1465 static int luaGetUsage(lua_State * L)
1467 lua_pushinteger(L, instructionsPercent);
1468 return 1;
1471 /*luadoc
1472 @function resetGlobalTimer([type])
1474 Resets the radio global timer to 0.
1476 @param (optional) : if set to 'all', throttle ,throttle percent and session timers are reset too
1477 if set to 'session', radio session timer is reset too
1478 if set to 'ttimer', radio throttle timer is reset too
1479 if set to 'tptimer', radio throttle percent timer is reset too
1481 @status current Introduced in 2.2.2, param added in 2.3
1483 static int luaResetGlobalTimer(lua_State * L)
1485 size_t length;
1486 const char * option = luaL_optlstring(L, 1, "total", &length);
1487 if (!strcmp(option, "all")) {
1488 g_eeGeneral.globalTimer = 0;
1489 sessionTimer = 0;
1490 s_timeCumThr = 0;
1491 s_timeCum16ThrP = 0;
1493 else if (!strcmp(option, "total")) {
1494 g_eeGeneral.globalTimer = 0;
1495 sessionTimer = 0;
1497 else if (!strcmp(option, "session")) {
1498 sessionTimer = 0;
1500 else if (!strcmp(option, "throttle")) {
1501 s_timeCumThr = 0;
1503 else if (!strcmp(option, "throttlepct")) {
1504 s_timeCum16ThrP = 0;
1506 storageDirty(EE_GENERAL);
1507 return 0;
1510 /*luadoc
1511 @function multiBuffer(address[,value])
1513 This function reads/writes the Multi protocol buffer to interact with a protocol².
1515 @param address to read/write in the buffer
1516 @param (optional): value to write in the buffer
1518 @retval buffer value (number)
1520 @status current Introduced in 2.3.2
1522 #if defined(MULTIMODULE)
1523 uint8_t * Multi_Buffer = nullptr;
1525 static int luaMultiBuffer(lua_State * L)
1527 uint8_t address = luaL_checkunsigned(L, 1);
1528 if (!Multi_Buffer)
1529 Multi_Buffer = (uint8_t *) malloc(MULTI_BUFFER_SIZE);
1531 if (!Multi_Buffer || address >= MULTI_BUFFER_SIZE) {
1532 lua_pushinteger(L, 0);
1533 return 0;
1535 uint16_t value = luaL_optunsigned(L, 2, 0x100);
1536 if (value < 0x100) {
1537 Multi_Buffer[address] = value;
1539 lua_pushinteger(L, Multi_Buffer[address]);
1540 return 1;
1542 #endif
1544 /*luadoc
1545 @function serialWrite(str)
1546 @param str (string) String to be written to the serial port.
1548 Writes a string to the serial port. The string is allowed to contain any character, including 0.
1550 @status current Introduced in TODO
1552 static int luaSerialWrite(lua_State * L)
1554 const char * str = luaL_checkstring(L, 1);
1555 size_t len = lua_rawlen(L, 1);
1557 if (!str || len < 1)
1558 return 0;
1560 #if !defined(SIMU)
1561 #if defined(USB_SERIAL)
1562 if (getSelectedUsbMode() == USB_SERIAL_MODE) {
1563 size_t wr_len = len;
1564 const char* p = str;
1565 while(wr_len--) usbSerialPutc(*p++);
1567 #endif
1568 #if defined(AUX_SERIAL)
1569 if (auxSerialMode == UART_MODE_LUA) {
1570 size_t wr_len = len;
1571 const char* p = str;
1572 while(wr_len--) auxSerialPutc(*p++);
1574 #endif
1575 #else
1576 debugPrintf("luaSerialWrite: %.*s",len,str);
1577 #endif
1579 return 0;
1582 const luaL_Reg opentxLib[] = {
1583 { "getTime", luaGetTime },
1584 { "getDateTime", luaGetDateTime },
1585 #if defined(RTCLOCK)
1586 { "getRtcTime", luaGetRtcTime },
1587 #endif
1588 { "getVersion", luaGetVersion },
1589 { "getGeneralSettings", luaGetGeneralSettings },
1590 { "getGlobalTimer", luaGetGlobalTimer },
1591 { "getValue", luaGetValue },
1592 { "getRAS", luaGetRAS },
1593 { "getTxGPS", luaGetTxGPS },
1594 { "getFieldInfo", luaGetFieldInfo },
1595 { "getFlightMode", luaGetFlightMode },
1596 { "playFile", luaPlayFile },
1597 { "playNumber", luaPlayNumber },
1598 { "playDuration", luaPlayDuration },
1599 { "playTone", luaPlayTone },
1600 { "playHaptic", luaPlayHaptic },
1601 // { "popupInput", luaPopupInput },
1602 { "popupWarning", luaPopupWarning },
1603 { "popupConfirmation", luaPopupConfirmation },
1604 { "defaultStick", luaDefaultStick },
1605 { "defaultChannel", luaDefaultChannel },
1606 { "getRSSI", luaGetRSSI },
1607 { "killEvents", luaKillEvents },
1608 { "chdir", luaChdir },
1609 { "loadScript", luaLoadScript },
1610 { "getUsage", luaGetUsage },
1611 { "resetGlobalTimer", luaResetGlobalTimer },
1612 #if LCD_DEPTH > 1 && !defined(COLORLCD)
1613 { "GREY", luaGrey },
1614 #endif
1615 #if defined(PXX2)
1616 { "accessTelemetryPush", luaAccessTelemetryPush },
1617 #endif
1618 { "sportTelemetryPop", luaSportTelemetryPop },
1619 { "sportTelemetryPush", luaSportTelemetryPush },
1620 { "setTelemetryValue", luaSetTelemetryValue },
1621 #if defined(CROSSFIRE)
1622 { "crossfireTelemetryPop", luaCrossfireTelemetryPop },
1623 { "crossfireTelemetryPush", luaCrossfireTelemetryPush },
1624 #endif
1625 #if defined(MULTIMODULE)
1626 { "multiBuffer", luaMultiBuffer },
1627 #endif
1628 { "serialWrite", luaSerialWrite },
1629 { nullptr, nullptr } /* sentinel */
1632 const luaR_value_entry opentxConstants[] = {
1633 { "FULLSCALE", RESX },
1634 { "XXLSIZE", XXLSIZE },
1635 { "DBLSIZE", DBLSIZE },
1636 { "MIDSIZE", MIDSIZE },
1637 { "SMLSIZE", SMLSIZE },
1638 { "INVERS", INVERS },
1639 { "BOLD", BOLD },
1640 { "BLINK", BLINK },
1641 { "RIGHT", RIGHT },
1642 { "LEFT", LEFT },
1643 { "CENTER", CENTERED },
1644 { "PREC1", PREC1 },
1645 { "PREC2", PREC2 },
1646 { "VALUE", INPUT_TYPE_VALUE },
1647 { "SOURCE", INPUT_TYPE_SOURCE },
1648 { "REPLACE", MLTPX_REP },
1649 { "MIXSRC_FIRST_INPUT", MIXSRC_FIRST_INPUT },
1650 { "MIXSRC_Rud", MIXSRC_Rud },
1651 { "MIXSRC_Ele", MIXSRC_Ele },
1652 { "MIXSRC_Thr", MIXSRC_Thr },
1653 { "MIXSRC_Ail", MIXSRC_Ail },
1654 { "MIXSRC_SA", MIXSRC_SA },
1655 { "MIXSRC_SB", MIXSRC_SB },
1656 { "MIXSRC_SC", MIXSRC_SC },
1657 { "MIXSRC_SD", MIXSRC_SD },
1658 #if !defined(PCBX7) && !defined(PCBXLITE) && !defined(PCBX9LITE)
1659 { "MIXSRC_SE", MIXSRC_SE },
1660 { "MIXSRC_SG", MIXSRC_SG },
1661 #endif
1662 #if !defined(PCBXLITE) && !defined(PCBX9LITE)
1663 { "MIXSRC_SF", MIXSRC_SF },
1664 { "MIXSRC_SH", MIXSRC_SH },
1665 #endif
1666 { "MIXSRC_CH1", MIXSRC_CH1 },
1667 { "SWSRC_LAST", SWSRC_LAST_LOGICAL_SWITCH },
1668 { "MAX_SENSORS", MAX_TELEMETRY_SENSORS },
1669 #if defined(COLORLCD)
1670 { "SHADOWED", SHADOWED },
1671 { "COLOR", ZoneOption::Color },
1672 { "BOOL", ZoneOption::Bool },
1673 { "STRING", ZoneOption::String },
1674 { "CUSTOM_COLOR", CUSTOM_COLOR },
1675 { "TEXT_COLOR", TEXT_COLOR },
1676 { "TEXT_BGCOLOR", TEXT_BGCOLOR },
1677 { "TEXT_INVERTED_COLOR", TEXT_INVERTED_COLOR },
1678 { "TEXT_INVERTED_BGCOLOR", TEXT_INVERTED_BGCOLOR },
1679 { "LINE_COLOR", LINE_COLOR },
1680 { "SCROLLBOX_COLOR", SCROLLBOX_COLOR },
1681 { "MENU_TITLE_BGCOLOR", MENU_TITLE_BGCOLOR },
1682 { "MENU_TITLE_COLOR", MENU_TITLE_COLOR },
1683 { "MENU_TITLE_DISABLE_COLOR", MENU_TITLE_DISABLE_COLOR },
1684 { "ALARM_COLOR", ALARM_COLOR },
1685 { "WARNING_COLOR", WARNING_COLOR },
1686 { "TEXT_DISABLE_COLOR", TEXT_DISABLE_COLOR },
1687 { "HEADER_COLOR", HEADER_COLOR },
1688 { "CURVE_AXIS_COLOR", CURVE_AXIS_COLOR },
1689 { "CURVE_COLOR", CURVE_COLOR },
1690 { "CURVE_CURSOR_COLOR", CURVE_CURSOR_COLOR },
1691 { "TITLE_BGCOLOR", TITLE_BGCOLOR },
1692 { "TRIM_BGCOLOR", TRIM_BGCOLOR },
1693 { "TRIM_SHADOW_COLOR", TRIM_SHADOW_COLOR },
1694 { "MAINVIEW_PANES_COLOR", MAINVIEW_PANES_COLOR },
1695 { "MAINVIEW_GRAPHICS_COLOR", MAINVIEW_GRAPHICS_COLOR },
1696 { "HEADER_BGCOLOR", HEADER_BGCOLOR },
1697 { "HEADER_ICON_BGCOLOR", HEADER_ICON_BGCOLOR },
1698 { "HEADER_CURRENT_BGCOLOR", HEADER_CURRENT_BGCOLOR },
1699 { "OVERLAY_COLOR", OVERLAY_COLOR },
1700 { "MENU_HEADER_HEIGHT", MENU_HEADER_HEIGHT },
1701 { "WHITE", (double)WHITE },
1702 { "GREY", (double)GREY },
1703 { "DARKGREY", (double)DARKGREY },
1704 { "BLACK", (double)BLACK },
1705 { "YELLOW", (double)YELLOW },
1706 { "BLUE", (double)BLUE },
1707 { "LIGHTGREY", (double)LIGHTGREY },
1708 { "RED", (double)RED },
1709 { "DARKRED", (double)DARKRED },
1710 #else
1711 { "FIXEDWIDTH", FIXEDWIDTH },
1712 #endif
1714 // Virtual events
1715 #if defined(ROTARY_ENCODER_NAVIGATION)
1716 { "EVT_VIRTUAL_PREV", EVT_ROTARY_LEFT },
1717 { "EVT_VIRTUAL_NEXT", EVT_ROTARY_RIGHT },
1718 { "EVT_VIRTUAL_DEC", EVT_ROTARY_LEFT },
1719 { "EVT_VIRTUAL_INC", EVT_ROTARY_RIGHT },
1720 #elif defined(PCBX9D) || defined(PCBX9DP) // key reverted between field nav and value change
1721 { "EVT_VIRTUAL_PREV", EVT_KEY_FIRST(KEY_PLUS) },
1722 { "EVT_VIRTUAL_PREV_REPT", EVT_KEY_REPT(KEY_PLUS) },
1723 { "EVT_VIRTUAL_NEXT", EVT_KEY_FIRST(KEY_MINUS) },
1724 { "EVT_VIRTUAL_NEXT_REPT", EVT_KEY_REPT(KEY_MINUS) },
1725 { "EVT_VIRTUAL_DEC", EVT_KEY_FIRST(KEY_MINUS) },
1726 { "EVT_VIRTUAL_DEC_REPT", EVT_KEY_REPT(KEY_MINUS) },
1727 { "EVT_VIRTUAL_INC", EVT_KEY_FIRST(KEY_PLUS) },
1728 { "EVT_VIRTUAL_INC_REPT", EVT_KEY_REPT(KEY_PLUS) },
1729 #else
1730 { "EVT_VIRTUAL_PREV", EVT_KEY_FIRST(KEY_UP) },
1731 { "EVT_VIRTUAL_PREV_REPT", EVT_KEY_REPT(KEY_UP) },
1732 { "EVT_VIRTUAL_NEXT", EVT_KEY_FIRST(KEY_DOWN) },
1733 { "EVT_VIRTUAL_NEXT_REPT", EVT_KEY_REPT(KEY_DOWN) },
1734 { "EVT_VIRTUAL_DEC", EVT_KEY_FIRST(KEY_DOWN) },
1735 { "EVT_VIRTUAL_DEC_REPT", EVT_KEY_REPT(KEY_DOWN) },
1736 { "EVT_VIRTUAL_INC", EVT_KEY_FIRST(KEY_UP) },
1737 { "EVT_VIRTUAL_INC_REPT", EVT_KEY_REPT(KEY_UP) },
1738 #endif
1740 #if defined(NAVIGATION_9X) || defined(NAVIGATION_XLITE)
1741 { "EVT_VIRTUAL_PREV_PAGE", EVT_KEY_LONG(KEY_LEFT) },
1742 { "EVT_VIRTUAL_NEXT_PAGE", EVT_KEY_BREAK(KEY_LEFT) },
1743 { "EVT_VIRTUAL_MENU", EVT_KEY_BREAK(KEY_RIGHT) },
1744 { "EVT_VIRTUAL_MENU_LONG", EVT_KEY_LONG(KEY_RIGHT) },
1745 { "EVT_VIRTUAL_ENTER", EVT_KEY_BREAK(KEY_ENTER) },
1746 { "EVT_VIRTUAL_ENTER_LONG", EVT_KEY_LONG(KEY_ENTER) },
1747 { "EVT_VIRTUAL_EXIT", EVT_KEY_BREAK(KEY_EXIT) },
1748 #elif defined(NAVIGATION_X7) || defined(NAVIGATION_X9D)
1749 { "EVT_VIRTUAL_PREV_PAGE", EVT_KEY_LONG(KEY_PAGE) },
1750 { "EVT_VIRTUAL_NEXT_PAGE", EVT_KEY_BREAK(KEY_PAGE) },
1751 { "EVT_VIRTUAL_MENU", EVT_KEY_BREAK(KEY_MENU) },
1752 { "EVT_VIRTUAL_MENU_LONG", EVT_KEY_LONG(KEY_MENU) },
1753 { "EVT_VIRTUAL_ENTER", EVT_KEY_BREAK(KEY_ENTER) },
1754 { "EVT_VIRTUAL_ENTER_LONG", EVT_KEY_LONG(KEY_ENTER) },
1755 { "EVT_VIRTUAL_EXIT", EVT_KEY_BREAK(KEY_EXIT) },
1756 #elif defined(NAVIGATION_HORUS)
1757 #if defined(KEYS_GPIO_REG_PGUP)
1758 { "EVT_VIRTUAL_PREV_PAGE", EVT_KEY_BREAK(KEY_PGUP) },
1759 { "EVT_VIRTUAL_NEXT_PAGE", EVT_KEY_BREAK(KEY_PGDN) },
1760 #else
1761 { "EVT_VIRTUAL_PREV_PAGE", EVT_KEY_LONG(KEY_PGDN) },
1762 { "EVT_VIRTUAL_NEXT_PAGE", EVT_KEY_BREAK(KEY_PGDN) },
1763 #endif
1764 { "EVT_VIRTUAL_MENU", EVT_KEY_BREAK(KEY_MODEL) },
1765 { "EVT_VIRTUAL_MENU_LONG", EVT_KEY_LONG(KEY_MODEL) },
1766 { "EVT_VIRTUAL_ENTER", EVT_KEY_BREAK(KEY_ENTER) },
1767 { "EVT_VIRTUAL_ENTER_LONG", EVT_KEY_LONG(KEY_ENTER) },
1768 { "EVT_VIRTUAL_EXIT", EVT_KEY_BREAK(KEY_EXIT) },
1769 #endif
1771 #if defined(KEYS_GPIO_REG_EXIT)
1772 { "EVT_EXIT_BREAK", EVT_KEY_BREAK(KEY_EXIT) },
1773 #endif
1775 #if defined(KEYS_GPIO_REG_ENTER)
1776 KEY_EVENTS(ENTER, KEY_ENTER),
1777 #endif
1779 #if defined(KEYS_GPIO_REG_MENU)
1780 KEY_EVENTS(MENU, KEY_MENU),
1781 #endif
1783 #if defined(KEYS_GPIO_REG_RIGHT) && defined(NAVIGATION_HORUS)
1784 KEY_EVENTS(TELEM, KEY_TELEM),
1785 #elif defined(KEYS_GPIO_REG_RIGHT)
1786 KEY_EVENTS(RIGHT, KEY_RIGHT),
1787 #endif
1789 #if defined(KEYS_GPIO_REG_UP) && defined(NAVIGATION_HORUS)
1790 KEY_EVENTS(MODEL, KEY_MODEL),
1791 #elif defined(KEYS_GPIO_REG_UP)
1792 KEY_EVENTS(UP, KEY_UP),
1793 #endif
1795 #if defined(KEYS_GPIO_REG_LEFT) && defined(NAVIGATION_HORUS)
1796 KEY_EVENTS(SYS, KEY_RADIO),
1797 #elif defined(KEYS_GPIO_REG_LEFT)
1798 KEY_EVENTS(LEFT, KEY_LEFT),
1799 #endif
1801 #if defined(KEYS_GPIO_REG_DOWN) && defined(NAVIGATION_HORUS)
1802 { "EVT_RTN_FIRST", EVT_KEY_BREAK(KEY_EXIT) },
1803 #else
1804 KEY_EVENTS(DOWN, KEY_DOWN),
1805 #endif
1807 #if defined(KEYS_GPIO_REG_PGUP)
1808 KEY_EVENTS(PAGEUP, KEY_PGUP),
1809 #endif
1811 #if defined(KEYS_GPIO_REG_PGDN)
1812 KEY_EVENTS(PAGEDN, KEY_PGDN),
1813 #endif
1815 #if defined(KEYS_GPIO_REG_PAGE)
1816 KEY_EVENTS(PAGE, KEY_PAGE),
1817 #endif
1819 #if defined(KEYS_GPIO_REG_SHIFT)
1820 KEY_EVENTS(SHIFT, KEY_SHIFT),
1821 #endif
1823 #if defined(KEYS_GPIO_REG_PLUS)
1824 KEY_EVENTS(PLUS, KEY_PLUS),
1825 #endif
1827 #if defined(KEYS_GPIO_REG_MINUS)
1828 KEY_EVENTS(MINUS, KEY_MINUS),
1829 #endif
1831 #if defined(ROTARY_ENCODER_NAVIGATION)
1832 KEY_EVENTS(ROT, KEY_ENTER),
1833 { "EVT_ROT_LEFT", EVT_ROTARY_LEFT },
1834 { "EVT_ROT_RIGHT", EVT_ROTARY_RIGHT },
1835 #endif
1837 #if LCD_DEPTH > 1 && !defined(COLORLCD)
1838 { "FILL_WHITE", FILL_WHITE },
1839 { "GREY_DEFAULT", GREY_DEFAULT },
1840 #endif
1842 #if LCD_W <= 212
1843 { "FORCE", FORCE },
1844 { "ERASE", ERASE },
1845 { "ROUND", ROUND },
1846 #endif
1848 { "SOLID", SOLID },
1849 { "DOTTED", DOTTED },
1850 { "LCD_W", LCD_W },
1851 { "LCD_H", LCD_H },
1852 { "PLAY_NOW", PLAY_NOW },
1853 { "PLAY_BACKGROUND", PLAY_BACKGROUND },
1854 { "TIMEHOUR", TIMEHOUR },
1856 #if defined(PCBHORUS)
1857 // Adding the unit consts for the set Telemetry function adds about 1k of flash usage
1858 {"UNIT_RAW", UNIT_RAW },
1859 {"UNIT_VOLTS", UNIT_VOLTS },
1860 {"UNIT_AMPS", UNIT_AMPS },
1861 {"UNIT_MILLIAMPS", UNIT_MILLIAMPS },
1862 {"UNIT_KTS", UNIT_KTS },
1863 {"UNIT_METERS_PER_SECOND", UNIT_METERS_PER_SECOND },
1864 {"UNIT_FEET_PER_SECOND", UNIT_FEET_PER_SECOND },
1865 {"UNIT_KMH", UNIT_KMH },
1866 {"UNIT_MPH", UNIT_MPH },
1867 {"UNIT_METERS", UNIT_METERS },
1868 {"UNIT_FEET", UNIT_FEET },
1869 {"UNIT_CELSIUS", UNIT_CELSIUS },
1870 {"UNIT_FAHRENHEIT", UNIT_FAHRENHEIT },
1871 {"UNIT_PERCENT", UNIT_PERCENT },
1872 {"UNIT_MAH", UNIT_MAH },
1873 {"UNIT_WATTS", UNIT_WATTS },
1874 {"UNIT_MILLIWATTS", UNIT_MILLIWATTS },
1875 {"UNIT_DB", UNIT_DB },
1876 {"UNIT_RPMS", UNIT_RPMS },
1877 {"UNIT_G", UNIT_G },
1878 {"UNIT_DEGREE", UNIT_DEGREE },
1879 {"UNIT_RADIANS", UNIT_RADIANS },
1880 {"UNIT_MILLILITERS", UNIT_MILLILITERS },
1881 {"UNIT_FLOZ", UNIT_FLOZ },
1882 {"UNIT_MILLILITERS_PER_MINUTE", UNIT_MILLILITERS_PER_MINUTE },
1883 {"UNIT_HOURS", UNIT_HOURS },
1884 {"UNIT_MINUTES", UNIT_MINUTES },
1885 {"UNIT_SECONDS", UNIT_SECONDS },
1886 {"UNIT_CELLS", UNIT_CELLS},
1887 {"UNIT_DATETIME", UNIT_DATETIME},
1888 {"UNIT_GPS", UNIT_GPS},
1889 {"UNIT_BITFIELD", UNIT_BITFIELD},
1890 {"UNIT_TEXT", UNIT_TEXT},
1891 #endif
1892 { nullptr, 0 } /* sentinel */