Skip to Main Content

Embedded Technologies

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Interested in getting your voice heard by members of the Developer Marketing team at Oracle? Check out this post for AppDev or this post for AI focus group information.

Brewing Java with the Raspberry Pi

Yolande Poirier-OracleSep 4 2015 — edited Oct 13 2015

Brewing Java with the Raspberry Pi

by Stephen Chin

Interact with a USB scale to accurately measure your coffee beans and brew the perfect cup.

Published May/June 2015

The Raspberry Pi comes preloaded with Java SE Embedded 8, so getting started with Java is as simple as typing the java command at the prompt. However, the ability to run Java on your Raspberry Pi wouldn’t be complete without an application to help perfect your coffee-brewing skills. This article takes a scientific approach to coffee brewing in order to obtain the perfect “cup of Joe.”

Communicating with a USB Scale

In order to precisely measure weight for coffee brewing, we are going to use a USB shipping scale. USB communication is based on a series of pipes (logical channels) that are connected to the device via endpoints. Furthermore, these endpoints are grouped into a set of interfaces. The protocol used by most shipping scales is simple one-way communication where the current scale value is repeatedly broadcast out over a single endpoint on the first interface.

To use the scale, simply plug it into one of the host ports on your Raspberry Pi and turn it on. It consumes only 16 mA, so it can be safely powered right off the Raspberry Pi USB bus.

The vendorId and productId show up when you plug in the scale and run the dmesg command. For the Dymo M10 scale they are 0x0922 and 0x8003, respectively. For its sister scale, the Dymo M25, they are 0x0922 and 0x8004, respectively. However, any compatible Stamps.com or DymoStamp scale should work fine as long as you change the vendorId and productId in the code.

For communication with the USB scale, we are going to use the usb4java library. This is an open source, JSR 80–compliant implementation of the standard JavaX-USB specification that has support for ARM Linux distributions such as the Raspberry Pi. You can download the latest version of usb4java from the project website.

Make sure to download both the core library as well as the javax extension. From the core distribution you need to grab the following JAR files:

  • usb4java-1.2.0.jar
  • libusb4java-1.2.0-linux-arm.jar
  • commons-lang3-3.2.1.jar

And you need these additional JAR files from the usb4java-javax distribution:

  • usb-api-1.0.2.jar
  • usb4java-javax-1.2.0.jar

Place all these files in a new folder called lib in your project directory, and add them to your project dependencies in your IDE of choice.

You also need to create a properties file in the root package that tells the JavaX-USB wrapper to use usb4java as the implementation. To accomplish this, create a file called javax.usb.properties in the root package (under src), and give it the contents shown in Listing 1.

|

javax.usb.services = org.usb4java.javax.Services

|

Listing 1

To test the data returned from the scale, we are going to use a single-class implementation of the USB scale protocol that returns 60 data points and prints out the results on the command line. This will be the basis for our coffee-brewing application.

|

public static void main(String[] args) throws UsbException {
UsbScaleTest scale = UsbScaleTest.findScale();
scale.open();
try {
for (int i = 0; i < 60; i++) {
scale.syncSubmit();
}
} finally {
scale.close();
}
}

|

Listing 2

The main method for our application is shown in Listing 2 and can be summarized as follows:

  • Find a connected USB scale.
  • Open a connection to the scale.
  • For 60 data points, submit a request to retrieve data.
  • Close the scale.

To ensure the scale is closed properly even in the case of an error or exception, we are using a try finally block.

Listing 3 shows the implementation of the first method, findScale, which discovers Dymo M10 or M25 scales using the JavaX-USB API. This calls the findDevice method, which contains code that traverses the USB device tree (see Listing 4).

|

public static UsbScaleTest findScale() throws UsbException {
UsbServices services = UsbHostManager.getUsbServices();
UsbHub rootHub = services.getRootUsbHub();
// Dymo M10 Scale:
UsbDevice device = findDevice(rootHub, (short) 0x0922,
(short) 0x8003);
// Dymo M25 Scale:
if (device == null) {
device = findDevice(rootHub, (short) 0x0922,
(short) 0x8004);
}
if (device == null) {
return null;
}
return new UsbScaleTest(device);
}

|

Listing 3

|

private static UsbDevice findDevice(UsbHub hub, short
vendorId, short productId) {
for (UsbDevice device :
(List) hub.getAttachedUsbDevices()) {
UsbDeviceDescriptor desc =
device.getUsbDeviceDescriptor();
if (desc.idVendor() == vendorId &&
desc.idProduct() == productId) {
return device;
}
if (device.isUsbHub()) {
device = findDevice((UsbHub) device,
vendorId, productId);
if (device != null) {
return device;
}
}
}
return null;
}

|

Listing 4

To read data from the scale, we need to connect to the correct interface and endpoint. Fortunately, the USB scale protocol is fairly simple, so you can simply grab the first interface and endpoint and then start listening to the data coming in from the scale directly, as shown in Listing 5.

|

private void open() throws UsbException {
UsbConfiguration configuration =
device.getActiveUsbConfiguration();
iface = configuration.getUsbInterface((byte) 0);
// this allows us to steal the lock from the kernel
iface.claim(usbInterface -> true);
final List endpoints = iface.getUsbEndpoints();
// there is only 1 endpoint
pipe = endpoints.get(0).getUsbPipe();
pipe.addUsbPipeListener(this);
pipe.open();
}

|

Listing 5

Notice that we had to use the claim method that accepts a UsbInterfacePolicy. This allows us to force the kernel to detach from the USB interface so we can claim it for our application.

The implementation of syncSubmit is trivial, calling the same-named method on the UsbPipe:

|

private void syncSubmit()
throws UsbException {
pipe.syncSubmit(data);
}

|

However, the real work happens in the callback. To receive the callback, our class needs to implement the UsbPipeListener class, which has two required methods. The first is dataEventOccurred, which will be called as a result of invoking syncSubmit and contain the data returned from the scale. The second is errorEventOccurred, which will be invoked when there is a problem interfacing with the scale.

The data sent by these shipping scales is a simple byte array that contains six values. The protocol is not well documented, but it has been reverse-engineered by the open source community. The data is as follows:

  • Byte 0—Unused
  • Byte 1—Special flags: empty=2, overweight=6, negative=5 (The conditions indicated by overweight and negative are described later.)
  • Byte 2—Unit of measure: grams=2, ounces=11
  • Byte 3—Weight scale
  • Byte 4—Base weight low- order byte
  • Byte 5—Base weight high- order byte

Listing 6 shows the implementation of dataEventOccurred, which takes apart the byte array returned and prints out a human-readable scale value to the command line.

|

@Override
public void dataEventOccurred(UsbPipeDataEvent upde) {
boolean empty = data[1] == 2;
boolean overweight = data[1] == 6;
boolean negative = data[1] == 5;
boolean grams = data[2] == 2;
int scalingFactor = data[3];
int weight = (data[4] & 0xFF) + (data[5] << 8);
if (empty) {
System.out.println("EMPTY");
} else if (overweight) {
System.out.println("OVERWEIGHT");
} else if (negative) {
System.out.println("NEGATIVE");
} else {
// Use String.format b/c printf causes problems on
// remote exec
System.out.println(String.format("Weight = %,.1f%s",
scaleWeight(weight, scalingFactor),
grams ? "g" : "oz"));
}
}

|

Listing 6

Besides doing a little bit of bit-shifting to get the weight magnitude, we also need to scale the weight by the scalingFactor returned in byte 3, as shown in Listing 7.

|

private double scaleWeight(int weight, int scalingFactor) {
return weight * Math.pow(10, scalingFactor);
}

|

Listing 7

In the case of an error, the best we can do is log the error and continue (see Listing 8).

|

@Override
public void errorEventOccurred(UsbPipeErrorEvent upee) {
Logger.getLogger(UsbScaleTest.class.getName()).log(
Level.SEVERE, "Scale Error", upee);
}

|

Listing 8

And the last method to finish the scale test is the close algorithm. This simply closes the pipe and interface cleanly so that the next application that tries to use the scale will be able to access it:

|

public void close() throws
UsbException {
pipe.close();
iface.release();
}

|

Read the full article at oracle.com/javamagazine The subscription to Java Magazine is free

About the Author

chin-headshot.jpg

Stephen Chin is the lead Java community manager at Oracle, author of the upcoming Raspberry Pi with Java (McGraw-Hill), and coauthor of Pro JavaFX 8 (Apress, 2014). He is also a JavaOne content co-chair. Chin has presented keynotes at numerous Java conferences including JavaOne, where he is a four-time Rock Star Award recipient.

Comments

The language shown in the Forms runtime is the result of your NLS_LANG setting in the runtime environment configuration (e.g. default.env). However, Forms can only change the language of labels it knows about. For example, the ones you highlighted. The column titles, in your example are labels that you elected to use (or column names from the DB). As a result, we cannot change them automatically because we would have no idea if that is what you wanted to do.

You would need to programmatically change those if desired. For example:

SET_LOV_COLUMN_PROPERTY ('LOV1', 2, TITLE, 'Nombre del empleado (Spanish)');

In this example, I am running a form in English but want one column to show in Spanish. So, I use the code above to make that change at runtime.

xu meng Feb 12 2025

Thanks for your reply, your example has inspired me a bit, but it still can't meet my usage needs.
I'm confused about:
When my system language variable is Chinese, I want to make the LOV component's Query/Confirm/Cancel button appear separately in other languages. As shown in the image above.
I don't know, but you can understand what I mean.

xu meng Feb 12 2025

As shown on the picture you provided. "查询" is displayed when the Chinese environment variable is used, and "FIND" is displayed when it is used in English. At present, I want to make the button of its LOV component window display "FIND" separately in the Chinese system, but I have not found the method in the help document and the network.

As I mentioned, strings built into Forms like “Find”, “Ok”, “Cancel”, and others can be translated into the language you choose in the runtime environment using the NLS_LANG settings. Unfortunately, this cannot be changed after the application has been started. So if the app is started with for example, Chinese-Traditional it cannot later be changed to French while the form is running. This means that you must either configure your server to support multiple languages and create modules for each language. This is often the best approach. There are other ways that customers have used, but what I described here likely would require the smallest effort.

You did not mention which Forms version you are using and therefore I cannot point you to the documentation for that version. However, here is the link to the related documentation for Forms 14.1.2. The concepts are basically the same for earlier versions, although some minor improvements have been introduced in the latest release (14.1.2).

https://docs.oracle.com/en/middleware/developer-tools/forms/14.1.2/working-forms/enabling-language-detection.html

But again, for strings that do not natively belong to Forms (you created them) you would need to programmatically change them as necessary.

xu meng Feb 12 2025

OH! I will try to implement your plan first. Thank you for your patience. My Forms version is 11.1.2.

As you are likely aware, but I feel it necessary to mention in case you are not, Forms 11.1.2 was desupported many years ago. I recommend you consider upgrading to the latest Supported version in order to ensure that you can get the latest bug fixes (including security fixes), the latest features, and improvements like what I mentioned about language support and others.

Details can always be found on the Forms product page.

https://www.oracle.com/application-development/technologies/forms/forms.html

xu meng Feb 12 2025

Thank you for telling me this news. I will consider your suggestion carefully and refer to it later. Thank you for your answer.

1 - 7

Post Details

Added on Sep 4 2015
5 comments
4,433 views