Skip to main content
Skip table of contents

Kipling Hacks and Hidden Features

Kipling is an open-source application (for versions before v3.3.0). Its source code is published to GitHub in a project called labjack_kipling (ljswitchboard-project_manager for versions before v3.2.0). The application is written in Node.js and uses the GUI front-end tool NW.js (previously known as node-webkit) on versions before v3.3.0. For version v.3.3.0, Kipling started using Electron as a front-end tool instead of NW.js. The Electron version of Kipling is not currently open-source, but we plan on making the newer Kipling versions open-source again. However, the core codebase was not changed when switching to Electron and the projects are very similar. Sometimes the README.md files descriptions lag behind how the application works and what versions of packages it uses. It has a lot of moving parts but one really cool feature about NW.js and Electron applications (in newer versions look at the SDK builds) is the "developer tools" interface. NW.js, as well as GitHub's Electron, are both based on Google's Chromium open-source application and run JavaScript/HTML/CSS files as a desktop application. These applications integrate features of Node.js into them which allow applications to perform native system calls such as file I/O, networking, and most importantly, communicate with .dll/.so/.dylib files. This lets the application use our LJM driver to communicate with and control devices. Both of these tools are really nice for developing responsive, cross-platform applications. For a quick getting started tutorial with LJM and Node.js, look at our Node.js Examples page.

Given that Kipling is an open-source project and it provides access to the back-end for development purposes, we have had a few customer questions that have been solved or assisted by informing them on how to open the developer tools and run a few commands. These commands may or may not change in the future so don't be surprised if they don't work in the future. Feel free to email us if you really need to dig into Kipling's back-end to accomplish something or have questions. Also, feel free to post on GitHub – we will receive your questions and do our best to address them. If you want to contribute to the application, please let us know.

Opening the Developer Tools

Press the ctrl+shift+alt+c keys to open a second window. If this window isn't familiar to you, Google search "google chrome developer tools" to learn more. The key tab that this webpage addresses and utilizes is the console which lets us type and execute JavaScript code.

Opening a device by IP address

In Kipling 3.1.4 you can navigate to the device selector tab and type:

CODE
activeModule.viewGen.onConnect({'connectionType':{'dt': [device type number T7: 7],'ct':[connection type number usb:1, eth:3, wifi: 4], 'ipAddress':'[device ip address ex: 192.168.1.2]'},device:{'isMockDevice':false,'serialNumber':[device serial number ex: 470010111]}}).then(pr,pe)

// Example T7, Ethernet, IP:192.168.1.2, SN: 470010111:
activeModule.viewGen.onConnect({'connectionType':{'dt':7, 'ct':3, 'ipAddress':'192.168.1.2'}, device:{'isMockDevice':false,'serialNumber':470010111}}).then(pr,pe)

// Can be simplified to: T7, Ethernet, IP:192.168.1.2
activeModule.viewGen.onConnect({'connectionType':{'dt':7, 'ct':3, 'ipAddress':'192.168.1.2'}, device:{'isMockDevice':false}}).then(pr,pe)

// To make the command feel more like opening a device run:
activeModule.viewGen.onConnect({'connectionType':{'dt':7, 'ct':3, 'ipAddress':'192.168.1.2'}, device:{'isMockDevice':false}}).then(function(res){MODULE_LOADER.loadModuleByName(MODULE_LOADER.current_module_data.name);console.log(res);},pe)

// Many of the function calls return promises as implemented by the npm library "q". There are two
// globally defined functions "pr" and "pe" that print the result, and print the error.

Looking up Error Codes

You can run a line of code to look up an LJM error code to get its definition. This is particularly useful when developing Lua Scripts because errors are reported as numbers. These numbers can be deciphered and help determine the issue at hand.

CODE
modbus_map.getErrorInfo(1236)
// or
modbus_map.getErrorInfo("LJME_CANNOT_OPEN_DEVICE")

Looking up Register Information

You can run a line of code to look up an LJM error code to get what its meaning is, for example:

CODE
modbus_map.getAddressInfo(0)
// or
modbus_map.getAddressInfo("AIN0")

Device Control

Not all modules (tabs, ex: Device Selector, Device Info, etc.) support this but several do. You can run various commands that communicate directly with the device. One module that does is the Device Info module. For more details look at the ljswitchboard-ljm_device_curator GitHub repository.

CODE
// Read one register 
sdModule.activeDevice.iRead('AIN0').then(pr,pe) 

// Read many addresses in one modbus transaction 
sdModule.activeDevice.iReadMany(['AIN0','AIN1']).then(pr,pe)

// Perform multiple single read calls aka multiple modbus reads
sdModule.activeDevice.iReadMultiple(['AIN0','AIN1']).then(pr,pe)

// Write to one register.
sdModule.activeDevice.iWrite('DAC0',0).then(pr,pe)

Configure the RTC Timestamp

While you are on a tab that lets you communicate with the device (for example the "Lua Script Debugger" tab, you can configure the T7s RTC with your computer's current time with the following script. More information about the T7s RTC can be found in section 19.0 RTC (T7-Pro Only)

CODE
sdModule.activeDevice.write('RTC_SET_TIME_S',(new Date()).getTime()/1000).then(pr,pe)

To verify that this worked properly, run the "Read RTC" script. The returned timestamp should reflect the current UTC time.

Download Data from a Digit

Most likely requires Kipling 3.1.5 and higher. Possibly requires Kipling 3.1.6 and higher. While you are on a tab that lets you communicate with the device (for example the "Device Info" tab), you can download the data that a Digit has logged. More information about the Digit's logging abilities can be found in the Digit Datasheet.

CODE
sdModule.activeDevice.callFunc('readDigitLoggedData',[]).then(function(data){
    console.log('Downloaded data from Digit, creating file.');
    // var dataRaw = JSON.stringify(data, null, 4);
    var txtData = '';
    try {
    var basicDataToSave = {};
    Object.keys(data).forEach(function(key) {
        if(key !== 'data') {
            basicDataToSave[key] = data[key];
        }
    });
    txtData += 'Items Logged: ';
    if(data.logParams.itemsLogged.temperature) {
        txtData += 'Temperature';
    }
    if(data.logParams.itemsLogged.temperature) {
        txtData += 'Humidity';
    }
    if(data.logParams.itemsLogged.temperature) {
        txtData += 'Light';
    }
    txtData += '\nNum Saved Readings, ' + data.logParams.storedBytes.numReadings;
    txtData += '\nLog Interval: ' + data.logParams.logInterval.time;
    txtData += '\nStart Time: ' + data.logParams.startTime.time;
    txtData += '\nStart Time value: ' + data.logParams.startTime.value;
    txtData += '\nHeaders:\nTime, Temperature (C), Temperature (' + data.temperatureUnits+ '), Humidity (%RH), Light (lux), warning, pwrFail, reset, usbActive\n';
 
    data.data.forEach(function(dataPoint) {
        txtData += dataPoint.time.toString() + ', ';
        txtData += dataPoint.temperatureC.toString() + ', ';
        txtData += dataPoint.temperature.toString() + ', ';
        txtData += dataPoint.humidity.toString() + ', ';
        txtData += dataPoint.light.toString() + ', ';
        txtData += dataPoint.sysFlags.warning.toString() + ', ';
        txtData += dataPoint.sysFlags.pwrFail.toString() + ', ';
        txtData += dataPoint.sysFlags.reset.toString() + ', ';
        txtData += dataPoint.sysFlags.usbActive.toString() + '\n';
    });
    } catch(err) {
        console.log('Error making data...', err);
    }
    var fp = path.join('C:','Users','chris','DGT_' + sdModule.activeDevice.savedAttributes.serialNumber.toString() + '_data.txt');
    // var fpRaw = path.join('C:','Users','chris','DGT_' + sdModule.activeDevice.savedAttributes.serialNumber.toString() + '_raw_data.txt');
 
    try {
        fs.writeFileSync(fp, txtData, {'flag': 'w'});
        // fs.writeFileSync(fpRaw, dataRaw, {'flag': 'w'});
    } catch(err) {
        console.log('Error creating file', err);
    }
    console.log('Finished making file');
},pe);

A message should print out saying Finished Making File when finished. You can configure where the data is saved by changing the line of code that defines var fp.

Manually Running Check for Hardware Issues

In rare cases relating to the "bit-pop" issues, it may be necessary to manually execute Kipling's check hardware issues function. Unless these steps are followed exactly, this test is not valid.

  1. With Kipling closed, the device connected to the computer over USB, and with ALL ELECTRONICS disconnected from the device, Open Kipling.

  2. Using a DMM, switch to "Volts" mode, and check to make sure the voltage difference between the VS and GND screw terminals is higher than 4.7-4.8V. Do this by inserting the probe tips into the screw terminals and then clamping down on them by tightening the terminals.

  3. After Kipling opens, connect to the device over USB.

  4. Navigate to the "Analog Outputs" tab.

  5. Open the developer tools window

  6. In the console tab, run the following command and check the results. If necessary, report them to upport@labjack.com for diagnosis.

Command

CODE
// After properly navigating to the "Device Updater" tab WITHOUT GOING ANYWHERE ELSE...
// Check for hardware issues.
sdModule.activeDevice.checkForHardwareIssues().then(pr,pe)

Expected Response

CODE
// The output should look something like this if it works correctly:
Result: {
  "overallResult": true,
  "overallMessage": "No HW issues detected",
  "shortMessage": "OK",
  "calibrationInfoValid": true,
  "testResults": {
    "flashVerification": {
      "name": "Check Cal Constants",
      "description": "Make sure saved device calibration constants are somewhat reasonable and not blank.",
      "status": true,
      "executed": true,
      "testMessage": "Device calibration constants are in range.",
      "shortMessage": "Cal Constants OK"
    },
    "ain15NoiseCheck": {
      "name": "AIN15 Noise Check",
      "description": "Verify that AIN15 readings at resolutions 1 and 9 aren't static and are close to zero.",
      "status": true,
      "executed": true,
      "testMessage": "HS & HR Converter AIN15 values are OK",
      "shortMessage": "GND Readings OK"
    },
    "temperatureBoundsCheck": {
      "name": "Check Temperature Sensor",
      "description": "Verify that device is reporting a temperature within the device's operating range (-40C to +85C).",
      "status": true,
      "executed": true,
      "testMessage": "Device calibration constants are in range.",
      "shortMessage": "Temp Readings OK"
    },
    "rawTemperatureNoiseCheck": {
      "name": "AIN14 Noise Check",
      "description": "Verify that AIN14 readings at resolution 1 and 9 aren't static.",
      "status": true,
      "executed": true,
      "testMessage": "HS & HR Converter AIN14 values are OK",
      "shortMessage": "No AIN14 Issues Detected"
    },
    "proOnlyHSHRComparisonCheck": {
      "name": "HS/HR Comparison Test",
      "description": "Compare HS and HR converter results.",
      "status": true,
      "executed": true,
      "testMessage": "HS/HR Ground Comparison Check Passes",
      "shortMessage": "HS Converter is OK"
    }
  }
}
JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.