Language Reference¶
Recommended Optimisations¶
For small scripts, easy to read code is by far the most valuable feature. However for larger scripts, the following optimisations can be followed:
Variable names of 5 characters or less take significantly less resources, so keep variable names short.
Comments have no impact, other than taking filesystem space.
Avoid intermediate variables. Example: write
dispatch(1, a+b)
rather thanlet c = a+b; dispatch(1, c);
Generally: use small functions, with as few variables as possible.
Built-In Functions¶
let value = JSON.parse(str);
Parse JSON string and return parsed value. Can also be used to convert a string to a number.
Note: if you required a fixed number of decimal places use SQ.to_fixed()
// Convert string to a number
let n = JSON.parse("-37.165345");
// Convert JSON string to an object
let obj = JSON.parse("{key1: \"value1\"}");
let value = obj.key1;
let str = JSON.stringify(value);
Get string representation of the mJS value.
let proto = {foo: 1}; let o = Object.create(proto);
Create an object with the provided prototype.
'some_string'.slice(start, end);
Return a substring between two indices. Example: ‘abcdef’.slice(1,3) === ‘bc’;
'abc'.at(0);
Return numeric byte value at given string index. Example: ‘abc’.at(0) === 0x61;
'abc'.indexOf(substr[, fromIndex]);
Return index of first occurence of substr within the string or -1 if not found. ‘abc’.indexOf(‘bc’) === 1;
chr(n);
Return 1-byte string whose ASCII code is the integer n. If n is not numeric or outside of 0-255 range, null is returned. Example: chr(0x61) === ‘a’;
let a = [1,2,3,4,5]; a.splice(start, deleteCount, ...);
Change the contents of an array by removing existing elements and/or adding new elements. Example: let a = [1,2,3,4,5]; a.splice(1, 2, 100, 101, 102); a === [1,100,101,102,4,5];
let s = mkstr(ptrVar, length);
Create a string backed by a C memory chunk. A string s starts at memory location ptrVar, and is length bytes long.
let s = mkstr(ptrVar, offset, length, copy = false);
Like mkstr(ptrVar, length), but string s starts at memory location ptrVar + offset, and the caller can specify whether the string needs to be copied to the internal mjs buffer. By default it’s not copied.
System¶
Required library: load('api_sys.js');
void * Sys.malloc(int size)
Allocate a memory region. Memory allocated this way must be explicitly released with free().
Sys.free(void *memory_pointer)
Free memory previously allocated with malloc.
Sys.total_ram()
Return total available RAM in bytes.
Sys.free_ram()
Return free available RAM in bytes.
Sys.reboot(ms)
Reboot the system after ms milliseconds.
Sys.uptime()
Return number of seconds since last reboot.
Sys.usleep(microseconds)
Delay execution the given number of microseconds.
Config¶
Set and get values from the device’s config.
Required library: load('api_config.js');
Cfg.get(path)
- Get a value from the device’s configuration.path: the JSON path (using dot notation) of the setting to get.Examples:
Cfg.get('device.id'); // returns a string
Cfg.get('accel.interval'); // returns an integer
Cfg.get('ambient.available'); // returns a boolean
Cfg.set(obj)
- Set part of the device’s configuration. The new value is written to the internal FLASH configuration file which persists between power cycles.obj: the JSON object to set.Examples:
Cfg.set({input2: {digital: {alert: {enable: false}}}});
Warning
Incorrect use of the Cfg.set() function may lead to corruption of critical parameters, and result in bricking of the device. Use with caution.
Warning
Excessive use of Cfg.Set() will wear internal FLASH memory. The FLASH memory can typically endure 100 000 write cycles.
Timers¶
Timers and time related functions.
Required library: load('api_timer.js');
Timer.set(milliseconds, flags, handler, userdata)
- Setup timer with milliseconds timeout and handler as a callback. flags can be either 0 or Timer.REPEAT. In the latter case, the call will be repeated indefinitely (but can be cancelled with Timer.del()), otherwise it’s a one-off. Returns a numeric timer ID.
Example:
Timer.set(1000, Timer.REPEAT, function() {
// This will repeat every second
let s = "HELLO";
SERIAL.write(1, s, s.length);
}, null);
Warning
Be conscious of accidentally setting repeating timers from a function that itself is repeating (for instance from SQ.set_data_handler), as you will quickly run out of memory.
Timer.del(id)
- Cancel a previously installed timer with given id.
let id = Timer.set( ... ); // Set timer, store the id
...
Timer.del(id); // Cancel the previously set timer
id = 0; // Explicitly clear the stored id (Important!)
Timer.now()
- Return current time as double value, UNIX epoch (seconds since 1970).
Timer.tzo()
- Return the time zone offset in hours, including any daylight savings offset. The offset value is provided by the LTE network, so an active LTE network connection is required. Returns NaN if the offset is not available.
Example use:
let tzo = Timer.tzo(); if (!isNaN(tzo)) { // Do something with tzo SQ.dispatch(1, tzo); }
Timer.fmt(format, time)
- Formats the time time according to the strftime-like format specification format. A format reference for the strftime() function can be found here.
Example use:
let now = Timer.now();
let s = Timer.fmt("ISO8601 Time is: %FT%TZ", now);
SQ.dispatch(1, s); // Example output: "ISO8601 Time is: 2021-04-13T10:17:32Z"
Timer.parse(format, input)
- Reads the input string according to the given format and returns a UNIX epoch.Returns -1 if there is an error parsing the input.Format options:%m - The month number (1–12).%M - The minute (0–59).%H - The hour (0–23).%d - The day of month (1–31).%Y - The year, including century (for example, 1991).%y - The year within century (0–99).%S - The second (0–60).%T - Equivalent to %H:%M:%S.
Example use:
Timer.parse("%d %m %Y %H %M %S", "06 10 2022 15 23 45"); // Returns 1665069825.0
Timer.parse("%y/%m/%d", "22/10/06"); // Returns 1665014400.0
Timer.parse("%d.%m.%Y %T", "29.12.2018 7:05:6")); // Returns 1546067106.0
Math¶
General mathematical functions.
Required library: load('api_math.js');
Math.ceil(x)
- Rounds x upward, returning the smallest integral value that is not less than x.
Math.floor(x)
- Rounds x downward, returning the largest integral value that is not greater than x.
Math.round(x)
- Returns the integral value that is nearest to x, with halfway cases rounded away from zero.
Math.max(x, y)
- Returns the larger of its arguments: either x or y. If one of the arguments is NaN, the other is returned.
Math.min(x, y)
- Returns the smaller of its arguments: either x or y. If one of the arguments is NaN, the other is returned.
Math.abs(x)
- Returns the absolute value of x.
Math.sqrt(x)
- Returns the square root of x.
Math.exp(x)
- Returns the base-e exponential function of x, which is e raised to the power x.
Math.log(x)
- Returns the natural logarithm of x.
Math.pow(base, exponent)
- Returns base raised to the power exponent
Math.sin(x)
- Returns the sine of an angle of x radians.
Math.asin(x)
- Returns the inverse sine of an angle of x radians.
Math.cos(x)
- Returns the cosine of an angle of x radians.
Math.acos(x)
- Returns the inverse cosine of an angle of x radians.
Math.random()
- Returns a pseudo-random number from 0.0 to 1.0
Senquip¶
Functions for interacting with data collection and the device’s peripherals.
Required library: load('senquip.js');
- Constants:
SQ.INFO: 10
SQ.ALERT: 20
SQ.WARNING: 30
SQ.ALARM: 40
SQ.ON: 1
SQ.OFF: 0
- Encoding Type Constants:
SQ.U8
- Unsigned 8 bits.SQ.S8
- Signed 8 bits.SQ.U16
- Unsigned 16 bits.SQ.S16
- Signed 16 bits.SQ.U32
- Unsigned 32 bits.SQ.S32
- Signed 32 bits.SQ.FLOAT
- 4 byte floating point.SQ.DOUBLE
- 8 byte floating point.
SQ.set_data_handler(function, userdata)
- Registers a callback function that allows creation of new data parameters collected by the device.function: A callback function, invoked when a measurement cycle is completed. The completed JSON data structure is passed to the callback function.userdata: Some user data that is passed to the callback function. Set null if unused.
Note
All SQ.dispatch functions below can only be called from inside the SQ.set_data_handler callback function. Calling them outside the data handler, such as from timers or the trigger handler will not work.
SQ.dispatch_double(index, value, precision)
- Add a new double precision floating point value to the device’s data message. If called multiple times for the same index value, only the first call will be written to the outgoing data message.index: The index of the data parameter, starting from 1.value: The numerical value of the data to be added.precision: The number of decimal places to send in the data message. The value must be between 0 and 8 (inclusive). 0 means no decimal places.
SQ.dispatch_string(index, string, length)
- Add a new string to the device’s data message. If called multiple times for the same index value, only the first call will be written to the outgoing data message.index: The index of the data parameter, starting from 1.string: The string to be added.length: The number of characters in the string. Maximum 50.
SQ.dispatch_event(index, severity, message)
- Add an event (alert, warning, alarm) to the device’s data message.index: The index on the data parameter, starting from 1.severity: The severity of the event. Should be one of SQ.INFO, SQ.ALERT, SQ.WARNING or SQ.ALARMmessage: The event message string. Maximum 30 characters.
SQ.dispatch(index, value, len)
- Helper function to dispatch either a number or string. If the len parameter is omitted, a default is used. The function consists of the following mJS code:
function dispatch(index, value, len) {
if (typeof value === 'string') {
if (typeof len === 'undefined') { len = value.length; }
this.dispatch_string(index, value, len);
}
else if (typeof value === 'number') {
if (typeof len === 'undefined') { len = 1; }
this.dispatch_double(index, value, len);
}
}
SQ.set_trigger_handler(function, userdata)
- Registers a callback function that is called when a trigger button is activated from the Senquip Portal.function: A callback function, invoked when a trigger button event occurs. The trigger parameter index number is passed into the callback function. For example: 5 is passed in for tp5.userdata: Some user data that is passed to the callback function. Set null if unused.
SQ.parse(string, start, length, base, type)
- Convert a string to a number using the base given.string: The string to parse.start: The starting character index. (0 = the first character in the string).length: The number of characters in the string to parse from the start position.base: The numerical base used when parsing the string. (10 = Decimal, 16 = Hexadecimal). A special case applies when when base is (-16): the byte order will be reversed.type: (Optional) The number type used to interpret the parsed data. This parameter should be one of the encoding type constants. Default: SQ.S64Returns the parsed number, or 0 on error.
SQ.copy(dst_ptr, string)
- Copy a string into a memory location pointer to by dst_ptr. The dst_ptr would typically be acquired by using Sys.malloc().dst_ptr: Pointer to a block of memory.string: The source string to copy from.Returns the number of bytes copied from the source string.
SQ.to_fixed(number, precision)
- Convert a number into a string with the given precision.number: The number to convert.precision: The number of decimal places in the output string.Returns a string containing the printed number.Example:
SQ.to_fixed(245.741, 1)
returns245.7
SQ.encode(number, type)
- Convert a number into a binary string representation of the given type.number: The number to encode.type: The number format to use when encoding into Big Endian binary. This parameter should be one of the encoding type constants. A negative sign in front of the type will reverse the byte order and encode into Little Endian binary.Returns a string containing the binary representation of the given number.Example:
SQ.encode(1075, -SQ.U16)
returns\x33\x04
SQ.atob(string)
- Decodes and returns the base 64 encoded input string.string: A base 64 encoded string. The maximum input string length is 256 characters.Returns the decoded string.
SQ.split(string, separator)
- Takes an input string and splits it into parts using the seperator character.string: The string to splitseparator: The seperator character.Returns the split up array of strings.Example:
SQ.split('1,2,,4', ',')
returns['1', '2', undefined, '4']
SQ.crc(data, length, algorithm_id)
- Calculates a CRC on the specified input data and returns the CRC as an integer. The length parameter is optional, but should be specified if data contains a null character. The algorithm_id is optional, by default the Modbus CRC16 is calculated.data: The input string.length: (Optional) Number of bytes of the input data to use in the calculation. Default: data.lengthalgorithm_id: (Optional) An integer to specify the algorithm. Default: 1 (Modbus CRC16)Returns the CRC as an integer.Example:
SQ.crc('Hello');
returns62327
SQ.hourmeter_set(channel, hours)
- Set the hourmeter channel to the given number of hours.channel: Hourmeter channel index. According to table below:SQ.VIBRATION: Vibration HoursSQ.SUPPLY: Supply Voltage HoursSQ.MOVEMENT: GPS Movement HoursSQ.DIGITAL1: Digital 1 HoursSQ.DIGITAL2: Digital 2 HoursSQ.DIGITAL3: Digital 3 HoursSQ.STATE1: State 1 HoursSQ.STATE2: State 2 HoursSQ.STATE3: State 3 HoursSQ.STATE4: State 4 HoursSQ.STATE5: State 5 HoursSQ.STATE6: State 6 Hourshours: The new decimal value in hours. (Example: 3.75 = 3hrs 45min)Example: reset supply hours to zero:
SQ.hourmeter_set(SQ.SUPPLY, 0);
SQ.hourmeter_update(channel, active)
- For custom hourmeter use. If active is true, this function will add the number of hours since the previous update to the hourmeter channel. This function is similar to SQ.set_state however it will allow more than one state to be active at a time.channel: Hourmeter channel index. See SQ.hourmeter_set() for valid values.active: Boolean value. If true, the hourmeter will count the time since the previous call to SQ.hourmeter_update() (where active was previously either true or false) and add time to the hourmeter channel.
SQ.set_state(number)
- Set the current state of the device. State hours are calculated based on based on how long a device stays in each state. See operation section for more info.number: New state (0 to 6 inclusive)
SQ.wifi_connected()
- Returns true if the device is currently connected via WiFi.
SQ.lte_connected()
- Returns true if the device is currently connected via 4G LTE.
SQ.hibernate_mode()
- Returns true if the device is currently in hibernate mode.
SQ.keep_awake(seconds)
- Keeps the device from sleeping for the given number of seconds. Useful for custom control logic where the device may need to occasionally stay awake outside the normal cycle.seconds: Number of seconds to remain awake.
SQ.nvs_save(channel, value)
- Save a number value to non-volatile storage (NVS). The value saved can be loaded later at any point by passing the same channel index to the SQ.nvs_load() function. The saved value will persist if the device goes to sleep or hibernate.channel: Storage channel index. Valid values are from 1 to 50 inclusive.value: A number to save.
SQ.nvs_load(channel)
- Load and return a number from non-volatile storage (NVS) for the given channel.channel: Storage channel index. Valid values are from 1 to 50 inclusive.Returns the last number previously saved by SQ.nvs_save() otherwise zero. If the channel argument is out of range it will return NaN.
Note
Non Volatile Storage (NVS) is retained when the device sleeps or hibernates. However, the NVS memory is reset to zero on a physical press of the reset button or if the device enters Shutdown Mode due to power loss. The device will also save NVS memory to permanent FLASH memory every 30 minutes as a backup.
IO¶
Required library: load('senquip.js');
- Constants:
IO.OFF: 0
IO.GND: 1
IO.VIN: 2
IO.VSET: 3
IO.PULLUP: 4
IO.read_voltage(channel)
- Read the voltage in Volts for the given channel.channel: IO channel number, from 1 to 5.Returns the current in Volts, or NaN on error.
IO.read_current(channel)
- Read the current in mA for the given channel.channel: IO channel number, from 1 to 5.Returns the current in milli-Amps, or NaN on error.
IO.write(channel, mode)
- Set the IO channel to the given mode.channel: IO channel number, from 1 to 5.mode: The new mode. Valid values are:IO.OFF: All output functions disconnected. Terminal is high impedence.IO.GND: The IO terminal is internally connected to GND.IO.VIN: The IO terminal is internally connected to PWR+ terminal.IO.VSET: The IO terminal is connected to the internal voltage source.IO.PULLUP: The IO terminal is connected via a pullup resistor to 3.3V. Useful for pulse counting, or unpowered switch.Returns zero on success, other values indicate an error.
IO.vset(voltage)
- Set the value of the internal voltage source.voltage: The new voltage setting in Volts. Valid values are from 5 to 25 Volts inclusive.Returns zero on success, other values indicate an error.
IO.set_pulse_count(channel, value)
- Set the device’s pulse counter on the given channel to the given value.channel: Pulse channel index, from 1 to 5.value: The new pulse count value.
GPS¶
GPS.distance(latitude, longitude)
- Calculate the distance between the device and the given position. The device’s GPS must be enabled and it must have a valid position fix.latitude: Latitude of given position in signed decimal format.longitude: Longitude of given position in signed decimal format.Returns the distance in meters or NaN on error.Example:
let meters = SQ.distance(-32.93197, 151.77883);
GPS.get_data()
- Returns an object containing the latest internal GPS data. The GPS interval setting must be non-zero. The internal GPS data is updated at 1 Hz.
let gps = GPS.get_data(); // Available members of gps object: // gps.lat - Latitude in decimal degrees // gps.lon - Longitude in decimal degrees // gps.hdop - Horizontal dilution of precision // gps.altitude - Altitude in meters // gps.cog - Course over ground in degrees // gps.speed_km - Speed in km/h // gps.nsat - Number of satellites in fix
CAN¶
Functions for reading/writing CAN data. All settings for the CAN interface are inherited from the device’s main configuration, and the CAN Interval setting must be non-zero.
Required library: load('senquip.js');
- Constants:
CAN.STD: 0
CAN.EXT: 1
CAN.tx(channel, id, data, length, flags)
- Send a CAN message with the given id, data and data length. See Examples section for usage.channel: CAN channel to use. (1 = CAN1)id: CAN message identifier, 29-bits for Extended CAN.data: A string of up to 8 bytes for the message payload.length: Number of data bytes. The valid range is 0 to 8.flags: Message options. Set frame type (standard 11-bit or extended 29-bit) using: CAN.STD or CAN.EXTReturns true on success, or false on error.Example:
CAN.tx(1, 0x18FB1055, "\x54\x82\x13", 3, CAN.EXT);
BLE¶
Bluetooth functions. All settings for the BLE interface are inherited from the device’s main configuration.
Required library: load('senquip.js');
BLE.set_adv_data(data, length)
- Set the raw advertising data with length. Only sets the intended payload, does not start advertising. Active advertisements must be stopped before the advertising data is updated. See Examples section for usage.data: A string of up to 31 bytes for the raw BLE message payload.length: Number of data bytes. The valid range is 1 to 32.Example:
BLE.set_adv_data("\x54\x82\x13", 3);
Note
Note this function sets the raw advertising message. If the BLE specification is not followed for bytes in the message (such as adding length and type), other devices may not correctly detect the message.
BLE.adv_enable(enable)
- Stop or start BLE advertising using previously set data. Note: advertising must be stopped before the advertising data can be updated.enable: true to start advertising broadcast, false to stop.
BLE.sq_adv(data, length)
- Helper function which immediately starts advertising a ‘Manufacturer Specific’ BLE message (according to the Bluetooth Spec) using the provided data.data: A string of up to 27 bytes for the ‘Manufacturer Specific’ message payload.length: (Optional) Number of data bytes, defaults to data.length. The valid range is 1 to 27.The helper function consists of the following code:
function sq_adv(data, len) {
if (typeof len !== 'number') { len = data.length; }
// Create a Manufacturer Specific message with Senquip's ID
if (len>27) len = 27;
let s = chr(len+3) + "\xff\x71\x0a" + data;
this.adv_enable(false);
this.set_adv_data(s, s.length);
this.adv_enable(true);
}
Serial¶
Functions for reading/writing serial data over the RS232/RS485 interface. All settings for the serial port are inherited from the device’s main configuration, and the Serial 1 Interval setting must be non-zero.
Required library: load('api_serial.js');
SERIAL.write(channel, string, length)
- Write serial data out to the device’s RS232/RS485 port. This call can be used in all modes (Capture, Modbus and Scripted), however if the serial port is in use the string data will be queued until the port is available.channel: Serial channel index. (1 = Serial 1)string: The string to write.length: The number of characters in the string. Maximum length is 256 bytes.
SERIAL.read(channel)
- Read serial data from the device’s input buffer. This function is only guaranteed to work correctly when the Serial port is configured as ‘Scripting’ mode.channel: Serial channel index. (1 = Serial 1)
SERIAL.read_avail(channel)
- Return number of bytes received and waiting in the input buffer.channel: Serial channel index. (1 = Serial 1)
SERIAL.set_handler(channel, function, userdata)
- Registers a callback function that is called when there is a new data in the input buffer, however this callback function is typically not required for most applications. Serial data can be read from anywhere in a script using SERIAL.read() instead.channel: Serial channel index. (1 = Serial 1)function: The callback function which receives the following arguments:
(channel, userdata)
.userdata: Some user data that is passed to the callback function. Set null if unused.
Example:
let serial_data = "";
SERIAL.set_handler(1, function(channel) {
// Keep adding to serial_data string as characters arrive
serial_data = serial_data + SERIAL.read(channel);
// Process serial_data string here
// ...
// Clear serial_data string when completed
serial_data = "";
}, null);
Endpoint¶
Functions for sending data to custom endpoints.
Required library: load('api_endpoint.js');
MQTT.pub(topic, message, length, qos, retain)
- Publish the given message on the topic for the MQTT Endpoint connection.topic: The MQTT topic to publish on.message: The message string to publish.length: (Optional) The number of bytes in the message string. Default: message.lengthqos: (Optional) Quality of Service value for the publish. Valid values are 0, 1 or 2. Default: 1retain: (Optional) Retain flag for the message. Default: falseReturns the packet id if the message was successfully queued. A return value of zero indicates there was an error, or MQTT is not connected.Example:
MQTT.pub('data/topic', 'Message to publish')
MQTT.queued(packet_id)
- Returns true if the given packet_id is in the MQTT output queue waiting to be sent. Note: if the device goes to sleep or the output queue overflows, queued packets will be lost. The queue can hold a maximum of 5 messages.
MQTT.sub(topic, function(conn, topic, message), userdata)
- Subscribe to the given topic using the MQTT Endpoint connection and call given handler function when a message is published to the topic.QoS is fixed at 1topic: The MQTT topic to subscribe to.function: Callback function with the following arguments:conn: MQTT connection identifier.topic: The MQTT topic.message: The message data published to the topic.userdata: Some user data that is passed to the callback function. Set null if unused.
Example use:
MQTT.sub('test/topic', function(conn, topic, msg) { // Use the msg to trigger events if (msg === 'OUTPUT_ON') { // Set output ON here } }, null);
MQTT.set_lwt(topic, message, retain)
- Configure the Last Will and Testament (LWT) message for the MQTT Endpoint connection.Note: Calling this function will disconnect the MQTT connection, and reconnect with the new parameters. It is therefore recommended to call this function once at the start of a script.topic: The MQTT topic for the LWT message.message: The LWT message to publish.retain: The retain flag for the LWT message. Either: true or false.
HTTP.post(body, headers)
- Publish the body and extra headers in a HTTP POST request to the url specified in the HTTP Endpoint settings.body: The data string to put in the message body.headers: (Optional) A string of extra HTTP Headers. Each header must end with \r\n.Example:
HTTP.post('Hello World!')
Warning
Custom HTTP POST requests from the script are not retried or buffered if they fail.
UDP.send(data, length)
- Send the data in a UDP message to the url specified in the UDP Endpoint settings.data: The data string to put in the message.length: (Optional) Number of bytes of data to send. Default: data.lengthExample:
UDP.send('Hello World!')
Files¶
Functions for reading/writing files on the device.
Required library: load('api_file.js');
File.write(string, filename, mode)
- Write string data to a file on the device. If the file does not exist, it is created. The mode parameter specifies if existing data should be deleted or appended to.string: The string to write.filename: The file name to write to. Allowed special characters:
._-()
mode: (Optional) File mode. ‘w’: previous content is deleted. ‘a’: append to existing content. Default: ‘w’.Returns: number of bytes written, or zero on error.Example:let bytes = File.write('Hello World!', 'test.csv', 'a')
Warning
Excessive use of File.write() will wear internal FLASH memory. The FLASH memory can typically endure 100 000 write cycles.
File.read(filename)
- Read from a file on the device and return the whole file as a string. If the file does not exist an empty string is returned.filename: The file name to read from.Returns: contents of the file as a string.Example:
let file_data = File.read('test.csv')
Warning
Reading large files may fail if the device is low on RAM, as the entire file is placed into device memory as a string.