The registry on the JNIOR is what defines how it is configured. Registry keys hold information that persist past reboots, allowing applications to set them and save settings that have been created. This information can be connection settings, device identification, versioning, etc. Below is a quick example that prints out a specific registry key’s information, and then prompts the user to enter a new value for it.
In this example the registry key being referenced on the JNIOR called AppData/RegistyTest. This registry key doesn’t exist the first time the application is run, and it will print out “Registry not created yet”. It will then prompt the user to enter a value for it, and once that’s done it will exit the application. Running the application a second time will show that the value entered for the registry key will now be what’s displayed for its value.
package registryexample;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import com.integpg.system.JANOS;
import java.io.IOException;
public class RegistryExample {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String stringRegistry = JANOS.getRegistryString("AppData/RegistryTest", "Registry not created yet.");
System.out.println(String.format("Current registry value was: %s", stringRegistry));
System.out.print("Enter new string value for registry: ");
String registryValue = br.readLine();
JANOS.setRegistryString("AppData/RegistryTest", registryValue);
System.out.println("Exiting");
}
}
I put the built jar file of this example application into the JNIOR’s flash folder and ran it from the Web UI’s console tab. As you can see it, the registry key report empty the first the application is run, and the second time its run the registry key contains the value enter for it the first time it was run.
Here shows the registry key we created in the Registry Tab of the JNIOR Web UI.
There could be a want for an application that performs different functionality, completing multiple operations at the same time. The JNIOR accomplishes this with threading. A thread is a sequence of instructions within a program that can be executed independently of other code. The JNIOR is able to handle threading via the default JAVA library. Below are two quick examples of threading, with one of them using synchronization.
Non-Synchronized Threading
In this first example, there is a main method that creates two threads, each supplying it an array of words that will be used to create sentences and print them out. Each new thread calls a function called slowStringBuffer, taking the words in their string array’s they’ve been supplied and adding them to the string buffer in that function. Since the threads run at the same time, they add their words to the string buffer simultaneously. This results in nonsense being printed out for the sentences, showing that synchronization in our next example is useful in avoiding this issue.
package ThreadingExample;
public class ThreadingExample {
private static final StringBuffer STRING_BUFFER = new StringBuffer();
public static void main(String[] args) {
newThread(new String[]{ "The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog" });
newThread(new String[]{ "I", "scream,", "you", "scream,", "we", "all", "scream", "for", "ice", "cream" });
}
private static void newThread(String[] wordArray) {
new Thread(new Runnable() {
@Override
public void run() {
try {
slowStringBufffer(wordArray);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}).start();
}
/**
* A perfect example of needing to synchronize code is when a shared resource or object is
* being used. Here we will use a shared StringBuffer object. Without synchronization the
* words get added to the shared StringBuffer at the same time and are all mixed by the time we
* print it out. If we simply synchronize this method then the sentences are built one at a
* time.
*
* @param wordArray
*/
private static void slowStringBuffer(String[] wordArray) throws InterruptedException {
// reset the length
STRING_BUFFER.setLength(0);
// add each word to the string buffer. we will use a small delay to simulate the need
// for synchronization
for (String word : wordArray) {
if (0 != STRING_BUFFER.length()) STRING_BUFFER.append(" ");
STRING_BUFFER.append(word);
Thread.sleep(10);
}
// finally we add a period and print out our sentence
STRING_BUFFER.append(".");
System.out.println(STRING_BUFFER.toString());
}
}
I put the built jar file of this example application into the JNIOR’s flash folder and ran it from the Web UI’s console tab. As you can see it, the threads compete against each other to build their sentences, resulting in nonsense being printed out.
Synchronized Threading
In this second example, the only change that is made is that the slowStringBuffer function is made synchronized. This results in one thread taking control first, making sure it prints out its sentence completely before the next thread can start its sentence. This results in two complete sentences being generated.
package SynchronizedThreadingExample;
public class SynchronizedThreadingExample {
private static final StringBuffer STRING_BUFFER = new StringBuffer();
public static void main(String[] args) {
newThread(new String[]{ "The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog" });
newThread(new String[]{ "I", "scream,", "you", "scream,", "we", "all", "scream", "for", "ice", "cream" });
}
private static void newThread(String[] wordArray) {
new Thread(new Runnable() {
@Override
public void run() {
try {
slowStringBuffer(wordArray);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}).start();
}
/**
* A perfect example of needing to synchronize code is when a shared resource or object is
* being used. Here we will use a shared StringBuffer object. Without synchronization the
* words get added to the shared StringBuffer at the same time and are all mixed by the time we
* print it out. If we simply synchronize this method then the sentences are built one at a
* time.
*
* @param wordArray
*/
private static synchronized void slowStringBuffer(String[] wordArray) throws InterruptedException {
// reset the length
STRING_BUFFER.setLength(0);
// add each word to the string buffer. we will use a small delay to simulate the need
// for synchronization
for (String word : wordArray) {
if (0 != STRING_BUFFER.length()) STRING_BUFFER.append(" ");
STRING_BUFFER.append(word);
Thread.sleep(10);
}
// finally we add a period and print out our sentence
STRING_BUFFER.append(".");
System.out.println(STRING_BUFFER.toString());
}
}
I put the built jar file of this example application into the JNIOR’s flash folder and ran it from the Web UI’s console tab. In this example two complete sentences are formed and printed.
On every JNIOR, there is a System Message Pump that is constantly circulating any message sent between applications. Each message sent around the system has a unique type that identifies the message. This is done so when an application receives a message, it can determine if the message was meant for it. If the message was for that application, it consumes the message and reacts accordingly for the what the application is doing. If its not meant for it, then it simply re-broadcasts the message so the next application can evaluate the message until the correct application receives it.
Message Types
As far as the different types of messages that can be sent, there are two groups. The first group is the system messages, which are predefined and reserved only for the OS to use when its performing checks and tasks. Message numbers below 1024 (0x400) are reserved for the system. The most common example of this is the SM_PROBE (0x02) message. This is a message that isn’t supposed to be consumed by other applications and is supposed to make its way back to the OS after its sent. If the message doesn’t come back, then it knows somethings wrong, and an application is consuming messages it shouldn’t be. It fixes this by restarting the OS. Here are other predefined system examples the JNIOR OS uses. The other group of messages are user defined messages. These messages can have any message number at or above 1024 (0x400), and can be given any message a user would wish to define for the message.
Message Example
Below is an example of two applications communicating with one another using a user defined message. They use the System class to declare a MessagePump and SystemMsg objects. The first application opens the message pump and permanently loops, only pausing the loop when its listening for the message. When it receives a message with type 1600 it prints the string in the message and continues the loop again. The second application opens the message pump, sends a user defined message containing a string and the message number 1600, and then closes the pump.
package messagepumpexample;
import com.integpg.system.MessagePump;
import com.integpg.system.SystemMsg;
import static java.lang.Thread.sleep;
import java.util.Vector;
public class MessagePumpExample {
private static final MessagePump MESSAGE_PUMP = new MessagePump();
private static final Vector LISTENERS = new Vector<>();
public static void addListener(MessagePumpListener listener) {
synchronized (LISTENERS) {
LISTENERS.addElement(listener);
}
}
public static void main(String[] args) throws InterruptedException {
MESSAGE_PUMP.open();
while (true) {
System.out.println("looping");
// read all messages from the message pump
SystemMsg systemMsg = MESSAGE_PUMP.getMessage();
// we must repost as fast as we can
MESSAGE_PUMP.postMessage(systemMsg);
String message = new String(systemMsg.msg);
if (systemMsg.type == 1600) {
System.out.println("Recieved command 1600.\n");
System.out.println(String.format("The message was: %s\n", message));
}
sleep(10);
}
}
}
package messagepumpexamplesender;
import com.integpg.system.MessagePump;
import com.integpg.system.SystemMsg;
import static java.lang.Thread.sleep;
public class MessagePumpExampleSender {
private static final MessagePump MESSAGE_PUMP = new MessagePump();
public static void main(String[] args) throws InterruptedException {
MESSAGE_PUMP.open();
SystemMsg systemMsg = new SystemMsg();
systemMsg.type = 1600;
systemMsg.msg = "This message is from the MessagePumpExampleSender".getBytes();
MESSAGE_PUMP.postMessage(systemMsg);
sleep(5000);
MESSAGE_PUMP.close();
}
}
I put the built jar files of these example applications into the JNIOR’s flash folder and ran one from from the Web UI’s console tab and the other from a command line connection. As shown below, one application constantly loops through itself, pausing when its trying to get a message from the message pump and then printing the message out when it does. The other sends the message the first one is listening for.
NOTE: In the picture above, the message only prints out once, while other times it just goes through the loop without printing the message. This is because we only handle one type of message, 1600. If it continues through the loop without printing a message, it means it got a message with a number type that wasn’t 1600. The other message its getting is most likely the SM_PROBE (0x02) message.
Many times we write an application that should be the only instance of that application running. That application may monitor inputs, control outputs, send email, or perform logic. Whatever that application is doing, it should be the only one doing that particular thing at any given time.
The ensure that this happens we use the JANOS.registerProcess(id) method. This method returns the number of processes that have been registered with that given id. That id can be any unique identifier given to that application. It can be a name or a random UUID.
If the registerProcess() method returns a value greater than one then we will exit our application. This will leave the first instance running. Here is an example.
// make sure there is only 1 instance of this application running
if (1 < JANOS.registerProcess(Application.getAppName())) {
JANOS.syslog("Another instance of " + Application.getAppName() + " is running");
}
JNIORs can connect with other devices via Ethernet connections. Defining a port and IP, the JNIOR can listen or send to a device with those Ethernet settings using the IO and Net classes of the JANOS Runtime library. Receiving data uses a DataInputStream and sending data uses a DataOutputStream.
TCP
Below is a very short example, that shows a quick reference to the IO class to create a socket to open a DataOutputStream. Once that is open I can send a message to an external device. The socket only requires the IP address and the port number. In this example the IP is 10.0.0.17 and the port is 9222. Once the DataOutputStream is created using the socket, I send a string out it. I use a TCP server I created that listens on that IP and port number to see if it receives the data sent.
package ethernetexample;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class TCPSocketExample {
//declare output stream and socket objects for an ethernet connection
public static OutputStream outputStream = null;
public static Socket socket = null;
public static void main(String[] args) throws IOException {
//set IP and port number
int portOut = 9222;
String IP = "10.0.0.17";
try {
//set socket and then create outputstream with socket
socket = new Socket(IP, portOut);
outputStream = new DataOutputStream(socket.getOutputStream());
} catch (NullPointerException e) {
}
try {
//send message out the outputstream
outputStream.write("This was sent from an ethernet connection.".getBytes());
outputStream.flush();
} catch (NullPointerException e) {}
}
}
I put the built jar file of this example application into the JNIOR’s flash folder and ran it from the Web UI’s console tab. After it has successfully run, I check my TCP server and see that the information was successfully received.
UDP
Another example of Ethernet control uses a UDP connection versus a TCP connection. Another example is below, using the Datagram Socket and Packet classes from the Java.net package. This creates a UDP connection from the JNIOR to any UDP server listening and sends it a string.
package ethernetexample2;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class UDPExample {
public static DatagramSocket udpsocket;
public static InetAddress address;
public static byte[] buf;
public static void main(String[] args) throws UnknownHostException, IOException {
udpsocket = new DatagramSocket();
address = InetAddress.getByName("10.0.0.17");
try {
buf = "This was sent from a UDP connection.".getBytes();
DatagramPacket packet = new DatagramPacket(buf, buf.length, address, 9222);
udpsocket.send(packet);
} catch (NullPointerException e) {
}
}
}
Just like before, I put the built jar file of this example application into the JNIOR’s flash folder and ran it from the Web UI’s console tab. After it has successfully run, I check a UDP server I created and see that the information was successfully received.
Applications can log information they are generating into a .log file to view. This feature can be crucial to some users, letting them be able to monitor data that constantly needs supervision. Log files can roll over as well, which happens when a log file runs out of space. When this occurs, a new empty file with the same name is created to log more data, while the old file that has no more space gets a .bat extension added to the end of the file name. This allows for data to be retained longer before being overwritten.
To use logging in a software application on the JNIOR, you will need to use the JANOS class of the JANOS runtime library. With it you can define a log file name and what you want to log to it. If the log file exists at the location you specify, it will add what enter to it. If the log file doesn’t exist, it will create the log file, and then log what you enter to it. Below is a short example of creating/adding to a log file that exists in the temp folder of the JNIOR.
package loggingexample;
import com.integpg.system.JANOS;
public class LoggingExample {
public static void main(String[] args) {
//log message to log file
JANOS.logfile("/temp/loggingExample.log", "This will create a log in"
+ " the temp folder of the JNIOR called loggingExample");
}
}
I put the built jar file of this example application into the JNIOR’s flash folder and ran it from the Web UI’s console tab. As you can see after running it, going to the temp directory of the JNIOR shows the loggingExample.log file.
Series 4 JNIORs have a varying amount of inputs and outputs depending on what model you have. The 410 has 8 inputs and 8 outputs, the 412 and 412DMX have 4 inputs and 12 outputs, and the 414 has 12 inputs and 4 outputs. Each relay contact rating is 1A @ 24VDC and the voltage source must be in the range of 5 – 30V AC or DC. The JNIOR uses a 2-piece terminal connector system for all power and I/O wiring connections allowing for easy installation and/or removal of the JNIOR.
Controlling Relay Outputs
When controlling the JNIOR’s I/O, the JANOS class can be used to get/manipulate their statuses. In the following example the JANOS class is used to pulse outputs, get the output states mask, check the status of an output, and check an outputs usage meter.
package outputcontrolexample;
import com.integpg.system.JANOS;
import java.io.IOException;
import static java.lang.Thread.sleep;
public class OutputControlExample {
public static void main(String[] args) throws IOException, InterruptedException {
//Pulse output 1 for 3 seconds
JANOS.setOutputPulsed(1, 1, 3000);
sleep(4000);
//Pulse output 2 and 3 for 3 seconds
JANOS.setOutputPulsed(6, 6, 3000);
//print out the current mask of outputs that are on.
System.out.println(String.format("current output states are %d\n", JANOS.getOutputStates()));
sleep(4000);
//Check if output 1 is on
System.out.println(String.format("Is output 1 on: %s\n", String.valueOf(JANOS.isOutputSet(0))));
//print out usage meter value for output 1, values may change for different model JNIOR:
System.out.println(String.format("Current usage meter value for output 1: %d\n", JANOS.getUsageMeter(8)));
}
}
I put the built jar file of this example application into the JNIOR’s flash folder and ran it from the Web UI’s console tab. As you can see it displays different data about the outputs at the time of the application running.
Getting Outputs from the IO Log
Another way to interact with the I/O on the JNIOR is via the Iolog Monitor. The Iolog Monitor is an object that can be checked for I/O events that have occurred on the JNIOR. There is the IoEvent class and Iolog class. An IoEvent is when an input or output state changed on the JNIOR, and the Iolog is an object that contains all the IoEvents from a certain timestamp. The example below pulses an output, and then shows how the Iolog has recorded that output pulse as an IoEvent.
package outputcontrolexample2;
import com.integpg.system.IoEvent;
import com.integpg.system.Iolog;
import com.integpg.system.JANOS;
import java.io.IOException;
import static java.lang.Thread.sleep;
import java.text.SimpleDateFormat;
public class OutputControlExample2 {
public static void main(String[] args) throws IOException, InterruptedException {
//gets the current time as a long.
long now = System.currentTimeMillis();
JANOS.setOutputPulsed(8, 8, 3000);
sleep(4000);
//iolog object for getting I/O events
//refresh Iolog so getting output events from it are only output evetns
//from when this application start AKA the outputs we just pulsed.
Iolog iolog = new Iolog();
iolog.refresh(now);
IoEvent[] events = iolog.getOutputEvents();
//for each output event in the iolog, print the output states at that
//time and the timestamp it occured.
if (events.length > 0) {
//SimpleDateFormat object that formats the timestamps of the IO events
//into a caladender date and time.
SimpleDateFormat date = new SimpleDateFormat("MM/dd/yy HH:mm:ss.SSS");
for (int i = iolog.getOutputEvents().length - 1; i >= 0; i--) {
System.out.println(String.format("States: %d @ %s\n",
events[i].states, date.format(events[i].timestamp)));
}
} else {
System.out.println("no output events...\n");
}
}
}
I put the built jar file of this example application into the JNIOR’s flash folder and ran it from the Web UI’s console tab. As you can see it recorded the output change where output 8 turned on and again when it turned back off.
Excluding the 412DMX, each JNIOR model has a COM port (labelled RS-232) and an AUX port (labelled AUX Serial). Both are DB-9F Female 9-pin D-sub connectors. The AUX port has 4 active signals and the COM port 2. The pin assignments are as follows:
Each port provides at least a 3-wire RS-232 interface. A 3-wire connection contains only the Transmit (Tx), Receive (Rx) and Signal Ground (GND) circuits. The COM port is used for diagnostics, but the AUX port on the 410 is RS-485 capable. These ports can be taken over to send and receive messages from these ports.
Creating a Serial Connection
When creating applications to control a serial port, the Comm and IO classes are what can be used to control them from the JANOS runtime library. Using this class you can create Input and Output streams for either the AUX or COM ports to receive or send out data via the serial ports. Below is a very short example, that shows a quick reference to the Comm class to open the AUX serial port and after establishing an Output Stream sends out a string. I opened a serial connection from my computer using command line and connected to the AUX port of the JNIOR to see if the data went through successfully.
package serialportexample;
import com.integpg.comm.AUXSerialPort;
import com.integpg.comm.PortInUseException;
import com.integpg.comm.SerialPort;
import java.io.IOException;
import java.io.OutputStream;
public class SerialPortExample {
public static SerialPort AUX = null;
public static OutputStream outputStream = null;
public static void main(String[] args) throws PortInUseException, IOException {
//create aux port object and open an aux port connection.
AUX = new AUXSerialPort();
AUX.open();
//create object for aux outputstream and send message out aux port.
outputStream = AUX.getOutputStream();
outputStream.write("This was sent from the AUX port.".getBytes());
}
}
I put the built jar file of this example application into the JNIOR’s flash folder and ran it from the Web UI’s console tab. After it has successfully run, I checked my serial connection to the AUX port of that JNIOR and saw the message went through.
Serial Connection Output
NOTE: AUX and COM ports can’t be opened more then one time, if they are then a PortInUseException error is thrown. An example of this happening is shown below where a serial port was opened twice.
The available expansion modules for the JNIOR are as follows: the Control Panel, Temperature Probe, Environmental Sensor, 4 Relay Output, 10volt, 4-20ma, and 3 Channel LED Dimmer. These expansion modules all have ways to be manipulated through the JANOS runtime library. Each Expansion Module works on any JNIOR logic controller model and contains the following key features:
On-board microprocessor for rapid, effective analog signal sampling
Automatic recognition of the module by the main JNIOR logic controller
Flexibility to utilize a mix of modules
Can be used in addition to the JNIOR temperature sensors
Web-based configuration via the main JNIOR web page
Easily integrated into all the JNIOR communication methods
No separate power supply necessary – all power received via the Sensor Port
2-year warranty
Controlling I/O and Reporting Data
When creating applications to control expansion modules, the SensorPort and JANOS classes are what can be used to control them from the JANOS runtime library. Using this class you can get an expansion modules type, check their inputs, check/set their outputs, get the temperature, set LEDs, and check the external device list of expansion modules. Below is an example, that gets each module connected to the JNIOR, and then reports on their data.
package expansionmoduleexample;
import com.integpg.sensor.SensorPort;
import com.integpg.system.JANOS;
import java.io.IOException;
public class ExpansionModuleExample {
public static void main(String[] args) throws IOException {
int maModule = 0;
int voltModule = 0;
int tempProbe = 0;
int fourRout = 0;
long[] devices;
devices = SensorPort.externalDeviceList();
for (int i = 0; i < devices.length; i++) {
if (Long.toHexString(devices[i]).endsWith("fe")) {
maModule++;
}
if (Long.toHexString(devices[i]).endsWith("fd")) {
voltModule++;
}
if (Long.toHexString(devices[i]).endsWith("fb")) {
tempProbe++;
}
if (Long.toHexString(devices[i]).endsWith("28")) {
fourRout++;
}
}
System.out.println(String.format("%d 4-20ma modules connected", maModule));
System.out.println(String.format("%d 10-volt modules connected", voltModule));
System.out.println(String.format("%d Temperature probes connected", tempProbe));
System.out.println(String.format("%d 4-rout modules connected\n", fourRout));
if (maModule != 0) {
int y = 0;
int z = 0;
try {
for (int i = 0; i < maModule; i++) {
System.out.println(String.format("4-20ma module %d", i + 1));
Double maInput1 = SensorPort.get420In(y);
System.out.println(String.format("Input %d: %.3f", y + 1, maInput1));
y++;
Double maInput2 = SensorPort.get420In(y);
System.out.println(String.format("Input %d: %.3f", y + 1, maInput2));
y++;
Double maInput3 = SensorPort.get420In(y);
System.out.println(String.format("Input %d: %.3f", y + 1, maInput3));
y++;
Double maInput4 = SensorPort.get420In(y);
System.out.println(String.format("Input %d: %.3f", y + 1, maInput4));
y++;
Double maOutput1 = SensorPort.get420Out(z);
System.out.println(String.format("Output %d: %.3f", z + 1, maOutput1));
z++;
Double maOutput2 = SensorPort.get420Out(z);
System.out.println(String.format("Output %d: %.3f\n", z + 1, maOutput2));
z++;
}
} catch (IOException e) {
JANOS.logfile("/expansionmoduleexample.log", "Expansion module was removed."
+ " Remove connected expansion modules from JNIOR. Then perform"
+ " extern -r command to reset saved expansion modules registry keys."
+ " Reconnect expansion modules and run application again.");
}
}
if (voltModule != 0) {
int y = 0;
int z = 0;
try {
for (int i = 0; i < voltModule; i++) {
System.out.println(String.format("10volt module %d", i + 1));
Double voltInput1 = SensorPort.get10vIn(y);
System.out.println(String.format("Input %d: %.3f", y + 1, voltInput1));
y++;
Double voltInput2 = SensorPort.get10vIn(y);
System.out.println(String.format("Input %d: %.3f", y + 1, voltInput2));
y++;
Double voltInput3 = SensorPort.get10vIn(y);
System.out.println(String.format("Input %d: %.3f", y + 1, voltInput3));
y++;
Double voltInput4 = SensorPort.get10vIn(y);
System.out.println(String.format("Input %d: %.3f", y + 1, voltInput4));
y++;
Double voltOutput1 = SensorPort.get10vOut(z);
System.out.println(String.format("Output %d: %.3f", z + 1, voltOutput1));
z++;
Double voltOutput2 = SensorPort.get10vOut(z);
System.out.println(String.format("Output %d: %.3f\n", z + 1, voltOutput2));
z++;
}
} catch (IOException e) {
JANOS.logfile("/expansionmoduleexample.log", "Expansion module was removed."
+ " Remove connected expansion modules from JNIOR. Then perform"
+ " extern -r command to reset saved expansion modules registry keys."
+ " Reconnect expansion modules and run application again.");
}
}
if (tempProbe != 0) {
try {
for (int i = 0; i < tempProbe; i++) {
System.out.println(String.format("temp probe %d temperature", i + 1));
Double temp = SensorPort.getTemp(i);
System.out.println(String.format("%.3f\n", temp));
}
} catch (IOException e) {
JANOS.logfile("/expansionmoduleexample.log", "Expansion module was removed."
+ " Remove connected expansion modules from JNIOR. Then perform"
+ " extern -r command to reset saved expansion modules registry keys."
+ " Reconnect expansion modules and run application again.");
}
}
if (fourRout != 0) {
try {
String getAllOutputs = Integer.toString(SensorPort.getOutputStates());
System.out.println(String.format("%s is the relay state mask for JNIOR and 4-rout module", getAllOutputs));
System.out.println("Now setting outputs");
SensorPort.setOutputStates(15, 15);
} catch (IOException e) {
JANOS.logfile("/expansionmoduleexample.log", "Expansion module was removed."
+ " Remove connected expansion modules from JNIOR. Then perform"
+ " extern -r command to reset saved expansion modules registry keys."
+ " Reconnect expansion modules and run application again.");
}
}
}
}
I put the built jar file of this example application into the JNIOR’s flash folder and ran it from the Web UI’s console tab. As you can see it prints the expansion modules connected along with info about the data they are reporting.
JANOS v2.0 removed the classic command line registry editor. The regedit application brings back that command line functionality within an add-on application. regedit starts by displaying the root registry directory, which is where the registry starts for the JNIOR. Unlike how editing the registry currently works with Series 4 units, the regedit application displays the full registry with a number value for each folder/key in the command line. regedit allows you to either display a folders contents, edit/delete a registry key, or add a new folder/key.
Adding a key
When inside a folder or in the root directory key, you can add a new key. Simply enter the name of the key you wish to create. You’ll then be prompted to add new values to this key. Because the JNIOR registry can’t contain empty keys in it, if you don’t enter a value for the key you are creating the key will instantly after being created. After entering value for the key, it will display what the key with its value. After hitting ‘y’ to accept the key. it will be created and redisplay your current folder’s registry keys.
Adding a folder
Adding a new folder can be done when you add a new key. When adding a new key, you add the folder name beforehand to set the folder the key resides in. For example, if you wanted to create a folder with the name ‘testing’ that contains a key named ‘key’, you would type ‘testing/key’ and then enter a value for that key. A key must be created with a new folder, since folders can’t be empty in the JNIOR registry. Just like a key, if an empty folder is added it is instantly deleted.
Viewing a folders content
When a number entered is associated with a registry folder, regedit will ‘enter’ that folder by displaying the folders/keys inside of it and giving each one a number. You can then repeat the process of entering another registry folder or then edit a registry key, the only difference being that you can go back to the previous folder you were in by typing the number associated with the ‘<previous>’ value.
Editing/Deleting a key
When a number is entered that is associated with a registry key, regedit will display the key and its value and prompt you to either change, edit, or remove the key. To do one of these of options, you’ll just enter the first character for any of these options (‘c’ to change, ‘e’ to edit, ‘r’ to remove). The change option allows you to re-enter a brand new value for the key you are editing. The edit option is similar to the change option, but leaves the previous value the key had for you to edit. Lastly, the remove option simply removes the key from the registry. You’ll be prompted one additional time to confirm removing the key before the remove goes through.
Deleting a whole folder
While inside a registry folder, typing the * key will allow you to delete the entire folder you are in. Be careful using this to make sure you don’t delete necessary folders on the JNIOR.
Were you a fan of the classic way you could the configure registry? With the RegEdit application, you can edit the registry the same way you would in the past. Simply download and update your JNIOR with the RegEdit update project and run the application from the command line by typing ‘regedit’. Click here for more information.
The biggest one is that series 4 JNIORs will use the WebSockets protocol during an Update Project. WebSockets use the HTTP port, which is normally port 80. This port is usually forwarded so that you can get to the WEB UI. FTP and Telnet are not used with Series 4 JNIORs.
You can also open multiple update projects at the same time. You can push one setup to a set of JNIORs and then push a different setup to a different set of JNIORs at the same time. Care must be taken to not push conflicting updates to the same JNIOR.
Multiple snapshots can be taken at the same time.
This version also allows you to select multiple JNIORs in the Beacon tab. Right clicking and selecting an action from the popup menu will apply to all of the selected JNIORs. This is especially useful if you want to take snapshots of all of your units at once.
You can right click on a unit in beacon to get the Network Capture.
This post will go over how to create an entity in Home Assistant that will keep track of a JNIOR temperature probe using MQTT.
Setup MQTT on the JNIOR
Before beginning to setup Home Assistant, you’ll want to make sure that you have MQTT setup on your JNIOR. Here is a post you can follow to setup MQTT on the JNIOR.
Setup Home Assistant
Now to start configuring Home Assistant, you’ll need to make sure you have a working setup of it. Here is the installation walkthrough on Home Assistant’s website. Once you have successfully setup a working setup of Home Assistant, you should be able to access its Configuration Web Page at homeassistant.local:8123 in a Web Browser. It should look like this when you access it.
Add MQTT Integration
After you’ve setup Home Assistant, you can now begin to configure Home Assistant to monitor a JNIOR Output. To do this the first thing we need to do is setup the MQTT integration. To do this you’ll go to your settings and select Devices and Services.
Once there, select Add Integration at the bottom of the page and you’ll search for the MQTT integration and add it to your current integrations. Once MQTT is added to your integrations, you’ll then need to configure it to use the same MQTT Broker as your JNIOR device. Here we are using broker.hivemq.com.
Get File Editor Add-on
Select next and finish configuring the MQTT integration. After its completed, the next thing we can do is setup an MQTT Sensor Entity. This will represent an on/off status of our JNIOR Output. To do this, we are going to need to get an add-on for Home Assistant. To do this we will select Setting again and go to the Add-ons.
Once in Add-ons, you’ll look for the add-on called File Editor. You’ll select that add-on and install it. (Adding it to your sidebar in its installation settings is helpful also.) This is needed to edit your configuration.yaml file. What is configuration. yaml? The configuration.yaml file is the main YAML file used by Home Assistant to gather all of the information about the users desired configuration. In this file we will define the MQTT Sensor, and we need the file editor add-on to do so.
Configure .yaml File
Once the File Editor add-on is installed we’ll go to it using the sidebar in Home Assistant. The File Editor will display all the files in Home Assistant, and the one we are interested in is the configuration.yaml file explained earlier. In this file we are going to add the MQTT Sensor platform inside it. Here is a picture of a configuration.yaml file after adding the temperature probe as an MQTT sensor.
NOTE: If using the example above, make sure to replace the serial number of the JNIOR above with your JNIOR’s serial number.
# Loads default set of integrations. Do not remove.
default_config:
# Text to speech
tts:
- platform: google_translate
mqtt:
sensor:
- name: MQTT_Tempprobe_Sensor
state_topic: "jnior/718040125/status/tempprobe/1/fahrenheit"
unit_of_measurement: "Fahrenheit"
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
Once this is added to your configuration.yaml file, head over to the developer tools above the settings option in the Home Assistant sidebar. On this page click the ‘check configuration’ button, and if the configuration is valid, you’ll then select the ‘restart’ button.
Setup Home Assistant Dashboard
Once Home Assistant has restarted, we can now go to the overview page of Home Assistant and edit our Dashboard. In the Dashboard we are going to click on the kebab menu and select edit Dashboard. This will allow us to add entities that will display the statuses of our JNIOR’s I/O sensors.
With the Dashboard Editor open, we’ll add a new card to the Dashboard. The card we want to add is the Entities Card, as this will let us display the MQTT Sensors we declared in the configuration.yaml file while leaving room to add more. This will also allow us to show how our JNIOR’s temperature probe is being monitored.
Inside in the Entities card, we’ll select the MQTT Sensor we made. Optionally, you can change certain attributes of the card by clicking the pencil icon next to the entity.
Save this Card Configuration, and you should now have a dashboard with your entity. When the JNIOR’s temperature probe changes, so will your entity on your homepage!
This post will go over how to create an entity in Home Assistant that will keep track of a JNIOR Output using MQTT.
Setup MQTT on the JNIOR
Before beginning to setup Home Assistant, you’ll want to make sure that you have MQTT setup on your JNIOR. Here is a post you can follow to setup MQTT on the JNIOR.
Setup Home Assistant
Now to start configuring Home Assistant, you’ll need to make sure you have a working setup of it. Here is the installation walkthrough on Home Assistant’s website. Once you have successfully setup a working setup of Home Assistant, you should be able to access its Configuration Web Page at homeassistant.local:8123 in a Web Browser. It should look like this when you access it.
Add MQTT Integration
After you’ve setup Home Assistant, you can now begin to configure Home Assistant to monitor a JNIOR Output. To do this the first thing we need to do is setup the MQTT integration. To do this you’ll go to your settings and select Devices and Services.
Once there, select Add Integration at the bottom of the page and you’ll search for the MQTT integration and add it to your current integrations. Once MQTT is added to your integrations, you’ll then need to configure it to use the same MQTT Broker as your JNIOR device. Here we are using broker.hivemq.com.
Get File Editor Add-on
Select next and finish configuring the MQTT integration. After its completed, the next thing we can do is setup an MQTT Binary Sensor Entity. This will represent an on/off status of our JNIOR Output. To do this, we are going to need to get an add-on for Home Assistant. To do this we will select Setting again and go to the Add-ons.
Once in Add-ons, you’ll look for the add-on called File Editor. You’ll select that add-on and install it. (Adding it to your sidebar in its installation settings is helpful also.) This is needed to edit your configuration.yaml file. What is configuration. yaml? The configuration.yaml file is the main YAML file used by Home Assistant to gather all of the information about the users desired configuration. In this file we will define the MQTT Binary Sensors, and we need the file editor add-on to do so.
Configure .yaml File
Once the File Editor add-on is installed we’ll go to it using the sidebar in Home Assistant. The File Editor will display all the files in Home Assistant, and the one we are interested in is the configuration.yaml file explained earlier. In this file we are going to add the MQTT Binary Sensor platform inside it. With the MQTT application on the JNIOR, topics on the Broker can be updated two different ways. The first is by updating a specific topic on the broker, and the other is using a JSON payload to update multiple topics at once. To show both ways, here is a picture of a configuration.yaml file after adding both ways to it. (This setup will be looking at Output 1 on the JNIOR, since in the state_topic lines it specifies Output 1)
NOTE: If using the example above, make sure to replace the serial number of the JNIOR above with your JNIOR’s serial number.
# Loads default set of integrations. Do not remove.
default_config:
# Text to speech
tts:
- platform: google_translate
mqtt:
binary_sensor:
- name: MQTT_JNIOR_Single_Topic
state_topic: "jnior/718040125/status/digital/outputs/1/state"
payload_on: "true"
payload_off: "false"
- name: MQTT_JNIOR_JSON_Payload
state_topic: "jnior/718040125/status/digital/outputs/1"
value_template: "{{ 'ON' if value_json.State == 'true' else 'OFF' }}"
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
Once this is added to your configuration.yaml file, head over to the developer tools above the settings option in the Home Assistant sidebar. On this page click the ‘check configuration’ button, and if the configuration is valid, you’ll then select the ‘restart’ button.
Setup Home Assistant Dashboard
Once Home Assistant has restarted, we can now go to the overview page of Home Assistant and edit our Dashboard. In the Dashboard we are going to click on the kebab menu and select edit Dashboard. This will allow us to add entities that will display the statuses of our JNIOR’s I/O sensors.
With the Dashboard Editor open, we’ll add a new card to the Dashboard. The card we want to add is the Entities Card, as this will let us display both MQTT Binary Sensors we declared in the configuration.yaml file. This will allow us to show how our JNIOR’s output is being monitored.
Inside in the Entities card, we’ll select the two MQTT Binary Sensors we made. In this example I also edit each of the entities to use different icons. This is optional, but you can change certain attributes of the card by clicking the pencil icon next to either entity.
Save this Card Configuration, and you should now have a dashboard with your entities. When the JNIOR’s output changes, so will your entities on your homepage! (Depending on if you chose the JSON Payload, Single_Topic, or both in the JNIOR’s MQTT application, only one may change.)
This post will go over installing MQTT onto a JNIOR, and then connecting to an MQTT Broker.
To start, download the JNIOR Support Tool and MQTT update project if you haven’t already. You’ll run the JNIOR Support Tool once its installed, and then go to the update tab. Here you’ll click ‘open project’ and select the MQTT update project. (do not unzip before opening in the support tool) Once its opened in the support tool you’ll select ‘publish’ and in the dialog box select the JNIOR you want to publish to and hit okay.
Once this is complete, you’ll want to go to the JNIOR web page for MQTT by going to a web browser and entering your JNIOR’s IP followed by /mqtt. (For example if your JNIOR’s IP is 10.0.0.110, in your web browser you would to go the URL 10.0.0.110/mqtt.) The webpage will look like this.
Once on this page, all you need to do is enter an MQTT Broker Host and its Port. You can use a Broker you already have, or if you don’t have one there are public Brokers that you can use. (Examples are test.mosquitto.org or https://www.hivemq.com/public-mqtt-broker which have their Host Names and Port Values on those pages.) You’ll save these changes, and afterward click the connect button. If the Status says connected, then MQTT is all setup on the JNIOR! If not, make sure you entered the correct settings for your Broker, and saved them before trying to connect.
The JNIOR is used in a variety of industries. Here the JNIOR is used in a clean energy solution, a residential Solar installation. This solar installation went live in the evening of July 7th, 2022. This is the second solar installation for this owner. The previous installation generated power for over a decade. It is still in operation but for a new owner.
How?
The JNIOR is tasked with communicating with the four inverters to collect the amount of power that they produce. Once every 10 seconds the application on the JNIOR sequentially communicates with each inverter to request the current power, daily yield and total yield. The communication is done via MODBUS TCP. Once the data is read, it is tucked away in 2 locations as individual reading and totalized. The data is stored in the registry as a system key that is used for real-time reporting. The data is also stored in a readings.dat log file. This file is moved to the flash filesystem at the end of every day for historical analysis. Only the previous 7 days are maintained in the flash filesystem. The files are numbered by the day of the week to ensure that Sunday overwrites last Sunday, Monday overwrites last Monday and so on.
The real-time data is served from the registry using a php script that generates a JSON format. That JSON response is requested and consumed by a linux server that is responsible for collecting and storing that data in a MYSQL database for long term storage.
Why?
Fun! While the data can be used to check that the power company is paying you for the kWhs generated beyond what is consumed by the house, this page is generally watched as a sort of game to see how days throughout the year stack up against each other.
While INTEG does not have a Home Assistant integration for the JNIOR, you can use the MQTT Integration to work with the JNIOR and Home Assistant quite easily. The MQTT Binary Sensor platform and MQTT Sensor platform can allow you to setup MQTT Sensors to monitor values on the JNIOR. Home Assistant can also publish topics to the MQTT Broker that the JNIOR is subscribed to.
With the release of JANOS 2.2, the bundled software that comes with JNIORs now have their own webpages! You can access them by going to a web browser and in the URL enter the JNIOR’s IP with ‘/bundled’ after it. As stated before, to access these web pages though you’ll need JANOS version 2.2 or later to access them. Here is a link that briefly goes over the Bundled Software Web Pages. Let us know what you think of it!
Web Pages have been added for Bundled Software Applications! These web pages have been implemented to make configuring the Bundled Software easier. This post will go over the different tabs the Bundled Software Pages have. To start, there are 5 tabs available on the Bundled Software Pages. Other then the On Boot page, each tab should display different registry settings to configure a different application in the Bundled Software.
On Boot Page
The On Boot tab is the opening tab when the Bundled Software Pages are loaded. Instead of letting you edit the registry keys of one of the applications like the other tabs, this page allows you to set which Bundled Software Applications will run on boot. Keep in mind that when setting one of the these applications to not start on boot, you won’t be able to access that applications tab in the Bundled Software Pages until its enabled again.
Serial Control
Serial Control can accept one or more connections from an external device and this connection can be over the serial port and/or the Ethernet port. The Serial Control tab allows you to edit the registry keys of Serial Control from the web page. Below is a quick explanation for each field.
Incoming Termination String – Sets the string that Serial Control looks for at the end of each message sent to it to know if that is the end of message being sent.
Outgoing Termination String – Sets the string that Serial Control adds to the end of each message it sends. The external device being sent to needs to know to look for this Outgoing Termination String.
Send Counts – Determines if the JNIOR reports each time an input changes to the external device connected through the Serial Control Connection.
Send Date Stamp – Determines if each report of an I/O change on the JNIOR reported through the Serial Control Connection gets appended with the current data and time.
Send Unsolicited I/O Alerts – Determines if any Alert such as I/O Counts or Date Stamps should be allowed through the Serial Control Connection.
Serial Port – Sets the Serial connection that Serial Control connects on.
TCP Port – Sets the TCP port that Serial Control connects on.
UDP Port – Sets the TCP port that Serial Control connects on.
Serial-to-Ethernet
Serial-to-Ethernet lets you setup a connection between a device that can communicate via ethernet and a device that can only connect serially. The web page allows you to configure the port for the ethernet connection, the serial port of the serial connection, the host address of the device that the JNIOR will attempt to connect to if the external device only listens for TCP connections, and if the connection is maintained after being established.
Modbus
Modbus lets you setup a connection to the JNIOR using the Modbus Protocol. The Modbus tab on the Bundled Software Pages allows you to setup the port the Modbus Protocol uses, if login is required, and the amount of time in seconds without communication on the Modbus connection for it to timeout.
Slaving
Slaving lets you control the I/O on one JNIOR by monitoring the I/O of another. There are two tables on the Slaving tab, one for inputs and the other for outputs. Each input and output and on the JNIOR will be displayed in these tables, and from there can be set the I/O point they are slaved to, followed by the information of the external JNIOR that has that I/O point.
This release was issued to support manufacturing and component changes forced by supply chain issues. Because of this, an update to the OS was required to handle this change. Units made June 22nd, 2022 or later will NOT be able to roll back to earlier version of JANOS.
Corrected bug in creating multidimensional arrays
Improved DEFLATE compression performance with binary files
Corrected issue with ‘%’ sign when logging to the syslog
Fixed dropped serial character when port is closed
We have released JANOS v1.9 which has several changes and bug fixes. Most notably with TCP packet generation. While it is not wrong to send data in multiple TCP packets, many devices incorrectly implement TCP clients and fail when this case arises. This version adjusted the release of socket data to reduce the chances that a message might be split across separate TCP packets. This had been a recent issue with some MODBUS client devices.
Added support for Reverse LLMNR allowing network scanners to label IP addresses with hostnames
Adjusted the release of socket data to reduce the chances that a message might be split across separate TCP packets
Corrected Sockets race condition that caused occasional reception delays and blocking
Fixed the incorrect signed display of large input counter values in JRMON and JMP Protocol
Adds a BEACON announcement in the event of an IP conflict
Improved NONCE management eliminating possible issues in the presence of port scanners
Eliminated chance of buffer overrun occurring in FTP transfer
Beacon REBOOT now works in all cases
Eliminated potential difficulty in obtaining network capture file
We have released JANOS v1.8 which adds the JMP Protocol. The JANOS Management Protocol (JMP) is essentially the JSON message interface utilized by JANOS Websockets. The JMP Protocol has been exposed on its own TCP/IP port. This encloses the JSON messaging in a JSON array along with the JSON Object length making reception of the messages easy. The JMP Protocol will be used by the QSC Q-SYS JNIOR component.
Implements the JANOS Management Protocol JMP
Adds "Block" command to JSON interfaces
Web Server limits the effects of extremely slow connections.