General JavaScript extensions#

Device status information#

okular.DevicesStatus()#

The function okular.DevicesStatus() will return a map of currently available client devices and their current status maps.

Example:

{
    "c04addea-54c2-4982-b27e-476d34e225ce":{ "RSSI" : 60, "Charger": 3, "BatteryLevel": 91, "Temperature": 24},
    "32021db4-74ba-5983-f27a-157d44e235ac":{ "RSSI" : 70, "Charger": 1, "BateryLevel": 13, "Temperature": 25},
  "27003600-0d47-3130-3830-333200000000":{"Battery":93,"Charger":3, "RSSI":61,"SleepMilis":8870,"Temperature":25,"TouchLastRelease":0}
}

Access information about the Visionect Server#

var okular.nm_host

Stores the IP address of the Network Manager. This is important if you wish to access services from the Network Manager.


var okular.session_uuid

Stores the device UUID. The device UUIDs are globally unique (based on a device’s micro-controller UUID).


Trigger beep sounds from JavaScript#

Warning

This feature only works with certain Visionect client devices, when coupled with an appropriate version of firmware.

Application developers can trigger different beep sounds directly from JavaScript by using the following function call:

okular.Beep(argument)

The following arguments are valid:

  • okular.BeepSoft

  • okular.BeepHard

The first one releases a silent, soft beep and the second one a louder beep.

//Example use:
okular.Beep(okular.BeepSoft)

Controlling the frontlight#

Warning

This feature only works with certain frontlight-equipped client devices. Some devices might require additional configuration via the CLI to enable remote control of LEDs.

If the device has frontlight LEDs installed, it’s possible to control their brightness from JavaScript by using:

okular.SetFrontlight(value) // Valid argument values are 0 - 100

// Example use:
okular.SetFrontlight(60);

This will set the frontlight brightness to 60%. To switch off the frontlight, set the brightness to 0.

Onscreen keyboard#

Warning

This feature only works on first generation Visionect 6” Signs. Newer client devices should implement the onscreen keyboard by using the JavaScript library.

A Visionect client device has the capability to display an on-screen keyboard if requested by the HTML rendering backend (formerly known as Okular). The backend will request the virtual keyboard to be displayed if:

  • The user clicked on a regular HTML input element of typed in a text or password.

  • The user clicked on a regular HTML textarea element.

The message that was typed by the user will be assigned to the input or textarea element value. It is also possible to apply a callback function to elements to be called after a successful text entry on the on-screen keyboard. This is done by providing an element with onKeyboardConfirm="callback_function" attribute.

<!--HTML EXAMPLE:-->
<!DOCTYPE html>
<html>
    <head>
        <script type="text/javascript">
            function kbd_check(el_id) {
                el = document.getElementById(el_id);
                    if (el) {
                        kbd_data = el.value;
                        console.log('Keyboard data:' + kbd_data);
                    } else
                        console.log('Element not found.');
                    }
        </script>
    </head>
    <body>
        <!-- input displays onscreen keyboard by default -->
        <input id = "test1" type="text" name="something" size=50 keyboard_layout="p"/>
        <!-- text area uses the callback function -->
        <textarea id = "test2" name="comments" onKeyboardConfirm='kbd_check("test2")'>
            Enter your comments here...
        </textarea>
    </body>
</html>

Keyboard layout#

Warning

This feature only works on first generation Visionect 6” Signs.

To change the layout of the keyboard (portrait or landscape), add a keyboard_layout attribute to the HTML element. The valid values for the keyboard_layout attribute are l for landscape or p for portrait view. If the keyboard_layout attribute is not set, then a default layout will be used. The default layout is predicted from the display rotation setting.

<!-- HTML EXAMPLE: -->
<input type="text" name="something" size=50 keyboard_layout="l" />

Disabling the onscreen keyboard#

Warning

This feature only works on first generation Visionect 6” Signs.

If you want to prevent the onscreen keyboard from popping up when a user registers a touch command, add a disabled attribute to the input element.

<!-- HTML EXAMPLE: -->
<form action="demo_form.asp">
  First name: <input type="text" name="fname"><br>
  Last name: <input type="text" name="lname" disabled><br>
  <!-- First name will display the keyboard popup, Last name will not. -->
  <input type="submit" value="Submit">
</form>

Button push event#

Note

Supported on Visionect Software Suite 3.4 and newer.

If one or more physical buttons are pressed on the device, then the webkit service will dispatch a “button-push” custom js event. The event provides us with source device ID and with state of all buttons.

e.detail.device - source device uuid.

e.detail.state - bit-mask, where each bit represents the ON or OFF state for up to 32 different buttons. The actual hardware will probably support a much lower number of buttons. The least significant bit represents the first button.

e.detail.active - undefined. Reserved for future use.

Following example checks whether button is pressed or released. Button index is then printed on the screen.

<body>
    <div class="content">
        <h1 align="center" id="title"><b>Checking button state: </b></h1>
        <p class="printout" id="pressed" align="center">/</p>
        <p class="printout" id="released" align="center">/</p>
    </div>
    <script type="text/javascript">
        document.addEventListener("button-push", checkButton, true);
        function checkButton(e) {
            var pt1 = document.querySelector("#pressed");
            var pt2 = document.querySelector("#released");
            for (var i=0; i<31; i++) {
                if((e.detail.active >> i) & 1 > 0) { //button index
                    if(((e.detail.state >> i) & 1) == 0) { //index if pressed
                        console.log("Button pressed: ", i);
                        pt1.innerHTML = "Button pressed: " + i.toString();
                    }
                    if (((e.detail.state >> i) & 1) == 1) { // index if released
                        console.log("Button released: ",i);
                        pt2.innerHTML = "Button released:" + i.toString();
                    }
                }
            }
        }
    </script>
</body>

Fine-grained device configuration#

The following API is designed to change client device configuration - these are the things that usually require you to connect the device to the Micro USB configuration cable and configure by using CLI.

Warning

Changing some of the following settings may lead to a dysfunctional device. Use this function only if you know what you are doing.

Syntax:

okular.NewCommand(device-id, "Param", {"Data": param-json})

The param-json must be a JSON object with the following content:

{
    "Data": [
            {
                    "Type"   : paramID,
                    "Control": paramControl,
                    "Value"  : paramValue
            },
            ...
    ]
}

The JavaScript developer should define the following constants:

  • Constants for the parameter Control key:

var ParamRead       = 0, // Create a read request
    ParamWrite      = 1, // Create a write request
    ParamReadErrro  = 2, // Error reading the requested
    ParamWriteError = 3; // Error writing requested
  • Constants for the Type key:


TCLV list#

Note

TCLV (Type-Control-Length-Value) is an encoding scheme used for device configuration.

Flag

Description

Constant

Value can’t be changed

Remote read only

Value can only be read remotely

Command

Sending TCLV with this flag triggers function on device

Remote disabled

TCLV can’t be sent remotely


Type ID

Description

Flag

0

TCLV Magic number: 0xDEADBEEF

Constant

1

TCLV table version: 2

Constant

2

Connectivity type: 0=None, 1=Wifi_PandaDS, 2=Mobile, 3=Ethernet, 4=Wifi_VTab2+Quad, 5=N/A, 6=Wifi_new

Command, remote read only

3

Wifi ACK or NACK timeout and Waiting for next block timeout [ms]

4

Wifi power saving timeout [ms]

Remote read only

5

WiFi DTIM skip interval [ms]

6

Mobile ACK or NACK timeout and Waiting for next block timeout [ms]

7

Mobile power saving timeout [ms]

Remote read only

8

Ethernet MAC address

Remote read only

9

Ethernet TCP retry count

Remote read only

10

Ethernet TCP retry timeout [100ms]

Remote read only

11

Ethernet ACK or NACK timeout and Waiting for next block timeout [ms]

12

Ethernet power saving timeout [ms]

Remote read only

13

IPv4 static IP

Remote read only

14

IPv4 static Netmask

Remote read only

15

IPv4 static Gateway

Remote read only

16

IPv4 static DNS server

Remote read only

17

IPv4 mode: 0=Static IP, 1=DHCP

Remote read only

18

Server IP

Remote read only

19

Server port

Remote read only

20

VCOM of Display 0 [mV]

21

VCOM of Display 1 [mV]

22

VCOM of Display 2 [mV]

23

VCOM of Display 3 [mV]

24

VCOM of Display 4 [mV]

25

VCOM of Display 5 [mV]

26

VCOM of Display 6 [mV]

27

VCOM of Display 7 [mV]

28

Display Type

29

Heartbeat interval [minute]

30

Network error retry interval [ms]

Remote read only

31

Proximity: offset calibration parameter

32

Proximity: threshold [%]

33

Proximity: diode current [10mA]

34

Accelerometer threshold count

35

Accelerometer debounce count

36

Battery OFF threshold [mV]

37

Battery ON threshold [mV]

38

Battery threshold count

39

Front light map 0: PWM+LDR

40

Front light map 1: PWM+LDR

41

Front light map 2: PWM+LDR

42

Front light map 3: PWM+LDR

43

Front light map 4: PWM+LDR

44

Front light map 5: PWM+LDR

45

Front light map 6: PWM+LDR

46

Front light map 7: PWM+LDR

47

Front light threshold

48

Front light mode

49

System screens: 1=Battery, 2=Not connected, 3=Battery+Not connected

50

Touch mode: 0=OFF, 1=ON, 3=ON+Beep, 5=ON+NoCalib, 7=ON+Beep+NoCalib

Command

51

Shipping mode: 0=OFF, 1=ON (Accelerometer ON), 2=ON (Accelerometer OFF)

Remote disabled

52

Sleep mode disable: 0=Sleep mode enabled, 1=Sleep mode disabled

53

Command to save parameters to flash

Command

54

Roaming mode: 0=Roaming disabled, 1=Roaming enabled

55

Roaming threshold [dBm]

56

Roaming hysteresis [dBm]

57

Writes EAP certificate chunk

Command

58

Writes EAP certificate descriptor2

Command

59

Erases EAP certificate

Command

60

EAP certificate 0 info

Constant

61

EAP certificate 1 info

Constant

62

EAP certificate 2 info

Constant

63

EAP certificate 3 info

Constant

64

EAP certificate 4 info

Constant

65

WiFi SSID

Remote read only

66

WiFi security mode: open, wpa2, wpae, wpa2e, wpa2eu

Remote read only

67

WiFi password

Command, remote read only

68

WiFi: not used. Set to 0

Remote read only

69

Mobile APN string

Remote read only

70

Mobile security mode: none, pap, chap

Remote read only

71

Mobile username

Remote read only

72

Mobile password

Remote read only

73

Mobile: not used. Set to 0

Remote read only

74

Set WiFi EAP method: 0=PEAP/MSCHAPV2, 2=TLS, 4=TTLS/MSCHAPV2, 5=FAST

Remote read only

75

Set WiFi EAP password

Remote read only

76

Set WiFi EAP username

Remote read only

77

Enable feature

Command

78

Disable feature

Command

79

Write: Play RTTTL song [text]. Read: play status: 0=Idle, 1=Playing

Command

80

Force connection establish. Read requested connection status

Command, remote disabled

81

Read connection status

Constant, command, remote disabled

82

Application name

Constant

83

Connectivity support: bit0=Wifi, bit1=Ethernet, bit2=Mobile

Constant

84

Connectivity drivers: see CLI command conn_type_list

Constant, remote disabled

85

Disable connectivity for N minutes

Constant, remote disabled

86

HW version: ID, Major, Minor, BOM

Constant, command

87

Application version: Major, Minor, Revision

Constant, command

88

Bootloader version: Major, Minor, Revision

Constant, command

89

Device UUID

Constant, command

90

Application ready status: 0:Not ready, 1:Ready

Constant, remote disabled

91

Reboot device

Command

92

Jump to application

Command, remote disabled

93

Connectivity FW version

Constant, command

95

Touch FW version

Constant, command

96

If 1, device successfully sent status packet to server, 0 otherwise

Constant, command, remote disabled

97

Frontlight LDR filter coefficient

98

Frontlight PWM filter coefficient

99

Front light sensor timeout

100

Heater low temperature limit

101

Heater high temperature limit

102

Heater operation mode

103

Extension battery threshold

104

Extension operation minimal temperature

105

Extension operation maximal temperature

106

Extension operation mode

107

Used (compiled) display type

Constant, command

108

GTIN code

Command, remote read only

109

EPD border mode: 0=server, 1=white, 2=black

Remote disabled

110

WiFi MAC

Remote read only

111

Text2speech enable

112

Text2speech voice ID

113

Text2speech speaking rate in words/minute

114

Text2speech speaking volume in dB

115

Text2speech timeout between texts in seconds

116

Converts text to speech

Command

1000

TCLV table version

Constant

1001

TCLV SCPU Frontlight Mode

1002

TCLV SCPU Frontlight map 0: PWM+LDR

1003

TCLV SCPU Frontlight map 1: PWM+LDR

1004

TCLV SCPU Frontlight map 2: PWM+LDR

1005

TCLV SCPU Frontlight map 3: PWM+LDR

1006

TCLV SCPU Frontlight map 4: PWM+LDR

1007

TCLV SCPU Frontlight map 5: PWM+LDR

1008

TCLV SCPU Frontlight map 6: PWM+LDR

1009

TCLV SCPU Frontlight map 7: PWM+LDR

1010

TCLV SCPU Frontlight PWM frequency in Hz

1011

TCLV SCPU Frontlight samplerate in sec

1012

TCLV SCPU Frontlight prefilter [0..100]

1013

TCLV SCPU Frontlight postfilter [0..100]

1014

TCLV SCPU Frontlight sensor timeout in sec

1015

TCLV SCPU Heater Mode

1016

TCLV SCPU Heater OFF temperature in °C

1017

TCLV SCPU Heater ON temperature in °C

1018

TCLV SCPU Device Mode

1019

TCLV SCPU Minimal OFF temperature in °C

1020

TCLV SCPU Maximal OFF temperature in °C

1021

TCLV SCPU Battery threshold in mV

1022

TCLV SCPU Frontlight PWM limit in %

1300

Command to push parameters to SCPU

Command

1301

Command to pull parameters from SCPU

Command

1302

Command to save parameters to SCPU

Command

2000

TCLV table version

Command

2001

TCLV DPU mode

2002

TCLV DPU VSPOS rail voltage in mV

2003

TCLV DPU VSNEG rail voltage in mV

2004

TCLV DPU VGPOS rail voltage in mV

2005

TCLV DPU VGNEG rail voltage in mV

2006

TCLV DPU VCOM1 rail voltage in mV

2007

TCLV DPU VCOM2 rail voltage in mV

2008

TCLV DPU VCOM3 rail voltage in mV

2009

TCLV DPU VCOM4 rail voltage in mV

2010

TCLV DPU sequence 0 ON: delay in ms + rail ID

2011

TCLV DPU sequence 1 ON: delay in ms + rail ID

2012

TCLV DPU sequence 2 ON: delay in ms + rail ID

2013

TCLV DPU sequence 3 ON: delay in ms + rail ID

2014

TCLV DPU sequence 4 ON: delay in ms + rail ID

2015

TCLV DPU sequence 5 ON: delay in ms + rail ID

2016

TCLV DPU sequence 6 ON: delay in ms + rail ID

2017

TCLV DPU sequence 7 ON: delay in ms + rail ID

2018

TCLV DPU sequence 0 OFF: delay in ms + rail ID

2019

TCLV DPU sequence 1 OFF: delay in ms + rail ID

2020

TCLV DPU sequence 2 OFF: delay in ms + rail ID

2021

TCLV DPU sequence 3 OFF: delay in ms + rail ID

2022

TCLV DPU sequence 4 OFF: delay in ms + rail ID

2023

TCLV DPU sequence 5 OFF: delay in ms + rail ID

2024

TCLV DPU sequence 6 OFF: delay in ms + rail ID

2025

TCLV DPU sequence 7 OFF: delay in ms + rail ID

2300

Command to push parameters to SCPU

Command

2301

Command to pull parameters from SCPU

Command

2302

Command to save parameters to SCPU

Command

Note

The commands up to 999 are supported by all devices.

The commands from 1000 up to 2000 are SCPU related and are supported by System Board 32’’ ver 0.5, 0.6 and 1.0B0

The commands from 2000 onward are in addition to SCPU also related to the DPU. These are supported by System Board 32’’ ver 1.0B1 and 2.0.

  • the Value key is a string value.

Example 1:

Get device IP address and server (gateway) IP address:

var paramPromise = okular.NewCommand("47003500-1351-3432-3434-373300000000", "Param", {"Data": [ {"Type":13, "Control":0, "Value": ""}, {"Type":18, "Control":0, "Value": ""}]})
paramPromise.then(function(data) {
    obj = JSON.parse(data);
    if (obj[0].Control == 2) {
        console.log('Error reading device IP address');
    } else {
        console.log('Device IP address is ', obj[0].Value); // convert from base64
    }
    if (obj[1].Control == 2) {
        console.log('Error reading server IP address');
    } else {
        console.log('Server IP address is ', obj[1].Value); // convert from base64
    }
});

Example 2:

Setting the device heartbeat:

changeHb = function(minutes) {
        console.log('setting heartbeat to ', minutes.toString(), ' minute(s)');
        var paramPromise = okular.NewCommand("47003500-1351-3432-3434-373300000000", "Param",
                        {"Data": [{"Type":29, "Control":1, "Value": minutes.toString()}]})
        paramPromise.then(function(data) {
                var obj = JSON.parse(data);
                if (Object.keys(obj).length == 0) {
                        document.getElementById("heartbeat").innerHTML = minutes.toString();
                        return;
                }
                var ctrl = obj[0].Control;
                var hb = obj[0].Value;
                console.log('Control:', ctrl);
                if (ctrl == 3) {
                        document.getElementById("heartbeat").innerHTML = '(write error ' + hb + ')';
                }
        });
}

Access the live view image#

Note

This section applies to the Visionect Software Suite version 2.12.4 and above.

Since Visionect Software Suite version 2.12.4, the user application can fetch live view images using the following API:

  • The same image as one seen on device display(s):

okular.image.device(id, encoder, parameters);

  • The latest rendered image for a device. The image might or might not be the same as seen on device displays. When changing content, it takes some time for a device to synchronize its displays with the Software Suite. This is the reason why the display content and the cached image might differ.

okular.image.cached(id, encoder, parameters);

  • The latest image as seen by webkit backend viewport. If back-end is in a virtual screen mode, this will show merged view for all devices:

okular.image.backend(encoder, parameters);

Parameters:

  • id Target device UUID. Typically on a single device configuration (no virtual screen), the device UUID is implicitly known and the value can be safely set to null or an empty string “”,

  • parameters Optional parameters {[scale: scale], [display: id]},

    • scale Optional number parameter for image scaling 0<scale<=4.0

    • display Optional display ID (0, 1, 2, 3, … NumDisplays-1). With okular.image.device() function, you can target a specific display.

Returns: promise

  • A resolved promise will contain:

{
      type,
      dataEncoding,
      data
};

Where:

  • type is a MIME content type, used to encode the image. The list below shows the valid type values and how they relate to the encoder function parameter value:

    • “bmp” -> “image/bmp”

    • “bmp-lz4_compress” -> “application/octet-stream” This is a normal bmp image additionally compressed with lz4 algorithm (see pierrec/node-lz4)

    • “png” -> “image/png”

    • “jpeg”-> “image/jpeg”

  • dataEncoding string with value “base64”

  • data base64 string encoded image data

Should something fail, a promise is rejected with Error.

Example:

Let’s send a captured image to some server:

<!DOCTYPE html>
  <html>
  <head>
  <script>
  function postData(json) {
      var http = new XMLHttpRequest();
      var url = "http://localhost:8666";
      http.open("POST", url, true);
      http.setRequestHeader("Content-type", "application/json");

      http.onreadystatechange = function() {//Call a function when the state changes.
          if(http.readyState == XMLHttpRequest.DONE && http.status == 200) {
               console.log(http.responseText);
          }
      }
      http.send(json);
  }

  function render() {
          okular.image.device(null, "png").then(function(img) {
              img.origin="device";
              postData(JSON.stringify(img));
           }, function (err) {
              postData(JSON.stringify({err:err.toString(), origin: "device"}));
           })
   }
   window.onload=function() {
       setTimeout(function(){
           render()
       }, 10000)
   }
   </script>

  </head>
  <body>
  <h1>My Liveview Test</h1>
  <p>
   Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
  </p>
  </body>
  </html>

Access Live-View image#

Since release 2.12.4 user application can fetch live view images using the following API:

  • okular.image.device(id, encoder, parameters); the same image as seen on device displays,

  • okular.image.cached(id, encoder, parameters); latest rendered image for device. The image might or might not be the same as seen on device displays. On content change, it takes some time for device to synchronize its displays with the server. The latter is the reason why the display content and the cached image might differ,

  • okular.image.backend(encoder, parameters); latest image as seen by the HTML backend viewport. If the backend is in virtual screen mode, this will show a merged view for all devices.

Parameters:
  • id target device UUID. Typically on a single device configuration (no virtual screen), device UUID is implicitly known and the value can be safely set to null or an empty string "",

  • parameters optional parameters {[scale: scale], [display: id]},

    • scale optional number parameter for image scaling 0<scale<=4.0

    • display optional display ID (0, 1, 2, 3, … NumDisplays-1). With okular.image.device() function, one can optionally target specific display.

Returns: promise
  • a resolved promise will contain

    {
     type,
    
         dataEncoding,
         data
    };
    
where:
  • type is a MIME content type, used to encode the image. The list below shows valid type values and how they relate to the encoder function parameter value:

    • "bmp" -> "impage/bmp"

    • "bmp-lz4_compress" -> "application/octet-stream"- This is is a normal bmp image additionally compressed with lz4 algorithm see pierrec/node-lz4

    • "pmg" -> "image/png"

    • "jpeg"-> "image/jpeg"

  • dataEncoding string with value "base64"

  • data base64 string encoded image data

or, if something fails, promise is rejected with Error

Examples#

The following app will take its own “screenshot” and will append to its DOM tree:

okular.image.backend("jpeg", {scale: 0.5}).then(function(img) {
    // img holds base64 encoded image.

    // convert base64 string to a binary string
    var binary = atob(img.data);
    // copy binary string to ArrayBuffer
    var buffer = new ArrayBuffer(img.data.length);
    var view = new Uint8Array(buffer);
    for (var i = 0; i < img.data.length; i++) {
        view[i] = binary.charCodeAt(i);
    }
    // create blob
    var blob = new Blob( [view], { type: img.type });
    // blob URL
    var objectURL = URL.createObjectURL(blob);

   // create img element and attach blob URL to it
   var image = document.createElement('img');
   image.src = objectURL;
   //append img to DOM
   document.body.appendChild(image);

   // release blob URL
   URL.revokeObjectURL(objectURL);
}, function (err) {
   console.log(err);
})

Perhaps a more practical case on how to POST a captured image to a remote server using JSON API is as follows:

<!DOCTYPE html>
<html>
<head>
<script>
function postData(json) {
    var http = new XMLHttpRequest();
    var url = "http://localhost:8666";
    http.open("POST", url, true);
    http.setRequestHeader("Content-type", "application/json");

    http.onreadystatechange = function() {//Call a function when the state changes.
        if(http.readyState == XMLHttpRequest.DONE && http.status == 200) {
             console.log(http.responseText);
        }
    }
    http.send(json);
}

function render() {
        okular.image.device(null, "png").then(function(img) {
            img.origin="device";
            postData(JSON.stringify(img));
         }, function (err) {
            postData(JSON.stringify({err:err.toString(), origin: "device"}));
         })
 }
 window.onload=function() {
     setTimeout(function(){
         render()
     }, 10000)
 }
 </script>

</head>
<body>
<h1>My Liveview Test</h1>
<p>
 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
</p>
</body>
</html>

Text to speech#

Note

Added in version 4.1.6

Text to speech function sends arbitrary text to a specific device and triggers the speech module.

Sending multiple speech requests will be queued.

Usage:

okular.Say([device id], [arbitrary 255-byte text]);