This is my Java code:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package jsscusbconnection;
import jssc.SerialPort;
import jssc.SerialPortEvent;
import jssc.SerialPortEventListener;
import jssc.SerialPortException;
/**
*
* #author Heshan
*/
public class JsscUsbConnection {
/**
* #param args the command line arguments
*/
static SerialPort serialPort;
public static void main(String[] args) {
serialPort = new SerialPort("COM15");
try {
System.out.println("port open :" + serialPort.openPort());//Open port
serialPort.setParams(SerialPort.BAUDRATE_9600,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
int mask = SerialPort.MASK_RXCHAR + SerialPort.MASK_CTS + SerialPort.MASK_DSR;//Prepare mask
serialPort.setEventsMask(mask);//Set mask
serialPort.addEventListener(new SerialPortReader());//Add SerialPortEventListener
} catch (SerialPortException ex) {
System.out.println(ex);
}
}
static class SerialPortReader implements SerialPortEventListener {
public void serialEvent(SerialPortEvent event) {
if (event.isRXCHAR()) {//If data is available
//System.out.println(event.getEventValue());
if (event.getEventValue() > 4) {//Check bytes count in the input buffer
//Read data, if 10 bytes available
try {
byte buffer[] = serialPort.readBytes(4);
System.out.println(buffer[0] + " " + buffer[1] + " " + buffer[2] + " " + buffer[3]);
} catch (SerialPortException ex) {
System.out.println(ex);
}
}
} else if (event.isCTS()) {//If CTS line has changed state
if (event.getEventValue() == 1) {//If line is ON
System.out.println("CTS - ON");
} else {
System.out.println("CTS - OFF");
}
} else if (event.isDSR()) {///If DSR line has changed state
if (event.getEventValue() == 1) {//If line is ON
System.out.println("DSR - ON");
} else {
System.out.println("DSR - OFF");
}
}
}
}
}
This is my Arduino code:
int k=0;
void setup(){
Serial.begin(9600);
}
void loop(){
Serial.println(67,BYTE);
Serial.println(98,BYTE);
Serial.println(34,BYTE);
Serial.println(108,BYTE);
Serial.flush();
}
This my output of Java code:
67 13 10 98
13 10 34 13
10 108 13 10
67 13 10 98
What are those bold values? I have no idea about these unknown values.
10 and 13 bytes are \r\n symbols. The description of the println() function from http://arduino.cc:
Prints data to the serial port as human-readable ASCII text followed by a carriage return >character (ASCII 13, or '\r') and a newline character (ASCII 10, or '\n'). This command takes >the same forms as Serial.print().
If you don't need these end line symbols use the print() function or the write() function for sending raw bytes.
13 is a carriage return. 10 is a line feed. If you're dealing with serial I/O you should really know that. 9 is a tab. 12 is a form feed. Check the ASCII table.
Related
I'm trying to control a robotic arm with a leap motion controller. Right now I'm just controlling two servos. I'm using java to read data from the leap motion, process and format it, and send it to the Arduino. The Arduino just receives the data, translates it, and sends it to the servos.
The format that I send the data to the Arduino in is, in string form:
z-rotation:shoulderPos:elbowAngle:wristAngle:clawPos
with each of these variables formatted with leading zeroes so that exactly 19 bytes are always sent to the Arduino at a time.
The issue is that data seems to be being lost in communication between the java on my laptop and the Arduino. If I send one command string, "000:180:000:000:000"
for example, the Arduino tells me that it's received "000:180:000:000:000"
and it correctly sends "000" to one servo and "180" to the second servo.
If I send a string of nine commands:
000:000:000:000:000180:000:000:000:000000:000:000:000:000180:000:000:000:000000:000:000:000:000180:000:000:000:000000:000:000:000:000180:000:000:000:000
The Arduino tells me that it's received all of the commands individually and it correctly sends all of the commands to the servos (evident by the twitching of the servos) and ends with sending "000" to both servos.
However, when I run my code with the leap motion, which effectively constantly transmits strings of 19 bytes to the Arduino, the servos just begin to twitch, moving between 0, 180, and the position that I'm sending to them. When I move my hand closer to the 100 position, the twitching servos have a net movement towards the 100 position, but never actually reaches it. The Arduino tells me that it's receiving the commands correctly for a few seconds before beginning to receive distorted messages like "0:180:0000:0018:00". I can only assume that the transmission and reception of commands are getting out of sync, but I'm not sure.
Here's my Java code:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import com.leapmotion.leap.*;
import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
class SampleListenerMain extends Listener {
//define position lock booleans
public boolean leftLock = false;
public boolean rightLock = false;
String data = "";
static DecimalFormat df = new DecimalFormat("000");
//Displacement variables
double deltaX, deltaY, deltaZ, angle;
public void onInit(Controller controller) {
System.out.println("Initialized");
}
public void onConnect(Controller controller) {
System.out.println("Connected");
controller.enableGesture(Gesture.Type.TYPE_CIRCLE);
controller.enableGesture(Gesture.Type.TYPE_KEY_TAP);
}
public void onDisconnect(Controller controller) {
System.out.println("Disconnected");
}
public void onExit(Controller controller) {
System.out.println("Exited");
}
public void onFrame(Controller controller) {
//Define position variables
double shoulderAngle, elbowAngle, wristPos, clawPos, zRotationPos, wristAngle;
//Define object variables
//Frame
Frame frame = controller.frame();
//Hands
Hand leftHand = frame.hands().leftmost();
Hand rightHand = frame.hands().rightmost();
//Arms
Arm leftArm = leftHand.arm();
Arm rightArm = rightHand.arm();
/* Control of robotic arm with Z-rotation based on the left hand, arm 'wrist' position based on the wrist,
* arm 'elbow position based on the elbow, and claw based on the fingers. 'Shoulder' is based on the left elbow
*/
//Control position locks for left hand controls and right hand controls
//Gesture gesture = new Gesture(gesture);
for(Gesture gesture : frame.gestures()) {
HandList handsForGesture = gesture.hands();
switch(gesture.type()) {
case TYPE_KEY_TAP:
System.out.println("Key tap from" + handsForGesture + " Hand");
try {
wait(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
leftLock = !leftLock;
break;
default:
System.out.println("Unrecognized gesture");
break;
}
}
//'Shoulder' control
//find angle between the left elbow and the left wrist center
Vector leftElbow = leftArm.elbowPosition();
Vector leftWrist = leftArm.wristPosition();
deltaZ = leftElbow.getZ() - leftWrist.getZ();
deltaY = leftElbow.getY() - leftWrist.getY();
angle = Math.atan(deltaY/deltaZ);
//map angle so servo can understand it
shoulderAngle = leapArm.map(angle, 0, 90, 0, 180);
//System.out.println("ShoulderPos: " + shoulderAngle);
//Write position to 'shoulder'
//Z-rotation control
Vector leftHandPos = leftHand.palmPosition();
//rotate z-axis with speed proportional to left hand X position
//map X position to motor power
zRotationPos = leapArm.map(leftHandPos.getX(), -230, 230, 0, 180);
//System.out.println("zRotationPos: " + zRotationPos);
data += df.format(zRotationPos);
data += ":" + df.format(shoulderAngle);
//write power to rotational servo
//'elbow' control
//find angle between the right elbow and right wrist center
Vector rightElbow = rightArm.elbowPosition();
Vector rightWrist = rightArm.wristPosition();
//refresh deltas and angle
deltaZ = rightElbow.getZ() - rightWrist.getZ();
deltaY = rightElbow.getY() - rightWrist.getY();
angle = Math.atan(deltaY/deltaZ);
//map angle so the servo can understand it
elbowAngle = leapArm.map(angle, -1.25, 0, 0, 180);
data+= ":" + df.format(elbowAngle);
//System.out.println("ElbowPos: " + elbowAngle);
//'wrist' control
//update vectors
rightWrist = rightArm.wristPosition();
Vector rightHandPos = rightHand.palmPosition();
//update deltas
deltaZ = rightWrist.getZ() - rightHandPos.getZ();
deltaY = rightWrist.getY() - rightHandPos.getY();
System.out.println("Wrist pos: " + rightWrist.getX() + ", " + rightWrist.getY() + ", " + rightWrist.getZ());
System.out.println("Right hand pos: " + rightHandPos.getX() + ", " + rightHandPos.getY() + ", " + rightHandPos.getZ());
angle = Math.atan(deltaY/deltaZ);
wristAngle = leapArm.map(angle, -0.5, 0.5, 0, 180);
data += ":" + df.format(wristAngle);
//System.out.println("wristAngle: " + wristAngle + " degrees");
//pinch control
//define fingers
FingerList fingerList = rightHand.fingers().fingerType(Finger.Type.TYPE_INDEX);
Finger rightIndexFinger = fingerList.get(0);
fingerList = rightHand.fingers().fingerType(Finger.Type.TYPE_THUMB);
Finger rightThumb = fingerList.get(0);
//find the distance between the bones to detect pinch
Vector rightIndexDistal = rightIndexFinger.bone(Bone.Type.TYPE_DISTAL).center();
Vector rightThumbDistal = rightThumb.bone(Bone.Type.TYPE_DISTAL).center();
//Calculate distance between joints
double distalDistance = Math.sqrt(Math.pow((rightIndexDistal.getX()-rightThumbDistal.getX()),2) + Math.pow((rightIndexDistal.getY()-rightThumbDistal.getY()),2) + Math.pow((rightIndexDistal.getZ()-rightThumbDistal.getZ()),2));
if(distalDistance <= 10) {
clawPos = 180;
} else {
clawPos = 0;
}
data += ":" + df.format(clawPos);
System.out.println("ClawPos: " + clawPos);
/* Write data to arduino
* FORMAT: z-rotation:shoulderPos:elbowAngle:wristAngle:clawPos
*/
System.out.println("Data: " + data);
/* wait for arduino to catch up ~30 packets/sec
* basically see how long the arduino takes to process one packet and flush the receiving arrays to prevent 'pollution'.
*/
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//send to arduino
leapArm.writeToArduino(data);
System.out.println("Sent");
}
}
public class leapArm implements SerialPortEventListener {
public static double map(double input, double in_min, double in_max, double out_min, double out_max) {
return ((input - in_min) * (out_max - out_min) / (in_max - in_min)) + out_min;
}
static OutputStream out = null;
static BufferedReader input;
public static void main(String[] args) {
//Connect to COM port
try
{
//Device
(new leapArm()).connect("/dev/cu.usbmodem14101");
Thread.sleep(3000);
//leapArm.writeToArduino("000:000:000:000:000180:000:000:000:000000:000:000:000:000180:000:000:000:000000:000:000:000:000180:000:000:000:000000:000:000:000:000180:000:000:000:000");
//System.out.println("sent");
}
catch ( Exception e )
{
e.printStackTrace();
System.exit(0);
}
// Create a sample listener and controller
SampleListenerMain listener = new SampleListenerMain();
Controller controller = new Controller();
// Have the sample listener receive events from the controller
controller.addListener(listener);
// Keep this process running until Enter is pressed
System.out.println("Press Enter to quit...");
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
// Remove the sample listener when done
controller.removeListener(listener);
}
void connect ( String portName ) throws Exception {
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
if ( portIdentifier.isCurrentlyOwned() )
{
System.out.println("Error: Port is currently in use");
}
else
{
CommPort commPort = portIdentifier.open(this.getClass().getName(),2000);
if ( commPort instanceof SerialPort )
{
SerialPort serialPort = (SerialPort) commPort;
serialPort.setSerialPortParams(4800,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
out = serialPort.getOutputStream();
//input = new BufferedReader(new InputStreamReader(serialPort.getInputStream()));
// add event listeners
try {
serialPort.addEventListener(this);
serialPort.notifyOnDataAvailable(true);
} catch (Exception e) {
System.err.println(e.toString());
}
}
else
{
System.out.println("Selected port is not a Serial Port");
}
}
}
public static void writeToArduino(String data)
{
String tmpStr = data;
byte bytes[] = tmpStr.getBytes();
try {
/*System.out.println("Sending Bytes: ");
for(int i = 0; i<bytes.length; i++) {
System.out.println(bytes[i]);
}*/
out.write(bytes);
} catch (IOException e) { }
}
public synchronized void serialEvent(SerialPortEvent oEvent) {
if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
String inputLine=input.readLine();
System.out.println("Received: " + inputLine);
} catch (Exception e) {
System.err.println(e.toString());
}
}
// Ignore all the other eventTypes, but you should consider the other ones.
}
}
And here's my Arduino code:
#include <SoftwareSerial.h>
#include <Servo.h>
Servo shoulder1, shoulder2;
SoftwareSerial mySerial(5,3); //RX, TX
char *strings[19];
char chars[19];
int loopno = 0;
byte index = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(4800);
mySerial.begin(9600);
mySerial.println("Started");
shoulder1.attach(2);
shoulder2.attach(4);
chars[19] = NULL;
}
void loop() {
// put your main code here, to run repeatedly:
if(Serial.available()>18) {
loopno++;
Serial.readBytes(chars, 19);
/*for(int i = 0; i< sizeof(chars); i++) {
mySerial.print("Character ");
mySerial.print(i);
mySerial.print(": ");
mySerial.println(chars[i]);
}*/
String str(chars);
/*mySerial.print("In string form: ");
mySerial.println(str);*/
char* ptr = NULL;
index = 0;
ptr = strtok(chars, ":");
while(ptr != NULL) {
/* mySerial.print("Pointer: ");
mySerial.println(ptr);*/
strings[index] = ptr;
index++;
ptr = strtok(NULL, ":");
}
//mySerial.print("shoulder1: ");
mySerial.println(atoi(strings[0]));
/*mySerial.print("shoulder2: ");
mySerial.println(atoi(strings[0]));
mySerial.print("Loop no: ");*/
mySerial.println(loopno);
shoulder1.write(atoi(strings[0]));
shoulder2.write(atoi(strings[1]));
}
flush();
}
void flush() {
for(int i = 0; i<19; i++) {
chars[i] = NULL;
strings[i] = NULL;
}
}
And here's the circuit that I'm using (The top Arduino is used for serial readout and debugging)
I'm very confused as to why this is happening. I've tried:
Decreasing the baud rate (115200 to 4800).
Sending commands one at a time or in small groups as described earlier.
Commenting out all debugging and unnecessary print statements to decrease processing time and reduce the amount of Serial calls in the Arduino program.
Commenting out all print statements in the Java code as well as rewriting my formatting and transmission code with an eye for effifiency to increase data collection -> transmission speed.
I'd appreciate it if anyone has experience with this or knows what the probelem could be!
Thanks,
Gabe
Edit:
I messed around with it when I got home and I think I might have isolated (one of) the issue(s). When I have all my debugging print statements uncommented and I send the arduino `
Tests.writeToArduino("000:000:000:000:000000:000:000:000:000180:000:000:000:000"); Tests.writeToArduino("180:000:000:000:000000:000:000:000:000000:000:000:000:000");
Tests.writeToArduino("000:000:000:000:000000:000:000:000:000000:000:000:000:000");
Tests.writeToArduino("180:000:000:000:000000:000:000:000:000000:000:000:000:000");, it starts to give me weird feedback starting at loop 7:
However, when I run my code with all the debugging statements commented out except for the first data value and the loop number, it is able to successfully keep track of 51 loops worth of data:
Tests.writeToArduino("000:000:000:000:000000:000:000:000:000180:000:000:000:000");
Tests.writeToArduino("180:000:000:000:000000:000:000:000:000000:000:000:000:000");
Tests.writeToArduino("000:000:000:000:000000:000:000:000:000000:000:000:000:000");
Tests.writeToArduino("180:000:000:000:000000:000:000:000:000000:000:000:000:000");
Tests.writeToArduino("000:000:000:000:000000:000:000:000:000000:000:000:000:000");
Tests.writeToArduino("180:000:000:000:000000:000:000:000:000000:000:000:000:000");
Tests.writeToArduino("000:000:000:000:000000:000:000:000:000000:000:000:000:000");
Tests.writeToArduino("180:000:000:000:000000:000:000:000:000000:000:000:000:000");
Tests.writeToArduino("000:000:000:000:000000:000:000:000:000000:000:000:000:000");
Tests.writeToArduino("180:000:000:000:000000:000:000:000:000000:000:000:000:000");
Tests.writeToArduino("000:000:000:000:000000:000:000:000:000180:000:000:000:000");
Tests.writeToArduino("000:000:000:000:000000:000:000:000:000180:000:000:000:000");
Tests.writeToArduino("000:000:000:000:000000:000:000:000:000180:000:000:000:000");
Tests.writeToArduino("000:000:000:000:000000:000:000:000:000180:000:000:000:000");
Tests.writeToArduino("000:000:000:000:000000:000:000:000:000180:000:000:000:000");
Tests.writeToArduino("000:000:000:000:000000:000:000:000:000180:000:000:000:000");
Tests.writeToArduino("000:000:000:000:000000:000:000:000:000777:000:000:000:000");
gives me:
This leads me to believe that either data is being lost in serial communication, as softwareserial is known to have this issue (which would make sense because less readout -> less jumbled data), or that I might be running into memory issues on the Arduino. Could either of these be the case? I'm still having the original issue, just thought these insights might help.
I found the solution: my arduino code was taking ~30ms to go through one loop but my java-side code was looping faster than that. The 64 byte arduino serial buffer was getting filled up after a few loops, and, since 64 is not a multiple of the 19 bytes I was sending, the dropped bytes would mean that the number:number format would get messed up.
If it helps anyone, I just tracked the time of the arduino loop and added a 50ms delay to the java-side code so that the arduino-side can catch up.
I am trying to send text messages from Java/Netbeans using a cell modem based on the SIM900. Using TeraTerm, I verify that I can send messages using the modem with basic AT commands. The following code attempts to use jssc to send messages.
I do not get errors and the data appears to be written to the modem, but I never receive a text message. For the phone number, I have tried both with an +, and without.
In TeraTerm, the number must be without the + to work. Many variations have been tried, and many .println's used. Still not making progress.
I hope someone can see the error of my ways.
Thanks in advance. Doug
package jssc_test;
import jssc.SerialPort;
import jssc.SerialPortException;
import jssc.SerialPortList;
public class Jssc_test {
public static SerialPort serialPort=null;
public static void main(String[] args) throws InterruptedException {
try {
String[] portNames = SerialPortList.getPortNames();
for(int i = 0; i < portNames.length; i++){
System.out.println(portNames[i]);
}
if(portNames.length < 1){
System.out.println("No ports available");
System.exit(0);
}
else{
serialPort = new SerialPort(portNames[0]);
}
System.out.println("Port opened: " + serialPort.openPort());
System.out.println("Params set: " + serialPort.setParams(9600, 8, 1, 0));
System.out.println("\"Hello World!!!\" successfully writen to port: " + serialPort.writeBytes("Hello World!!!".getBytes()));
serialPort.writeBytes(" AT+CMGF=1".getBytes());
Thread.sleep(1000);
System.out.println("bytes back = " + serialPort.readString());
serialPort.writeBytes(" AT+CMGS=\"585*******\"".getBytes()); // \r = <CR>. Tried both with and without '+'. In TeraTerm, only works without +. error if use: \r\n
//Thread.sleep(1000);
//System.out.println("bytes back = " + serialPort.readString());
//serialPort.writeBytes("0x0D".getBytes()); // send <CR>
Thread.sleep(1000);
System.out.println("bytes back = " + serialPort.readString());
serialPort.writeBytes("THIS IS A TEST from DS.".getBytes()); // placing Cntr-Z string in text did not work: \u001A
//serialPort.writeBytes("0x0D".getBytes()); // send <CR>
serialPort.writeBytes("0x1A".getBytes()); // send <ctrl>Z
Thread.sleep(1000);
System.out.println("bytes back = " + serialPort.readString());
System.out.println("Port closed: " + serialPort.closePort());
}
catch (SerialPortException ex){
System.out.println(ex);
}
} // ******************* end main ***************
} // *********************** end main class ***********************
I was able to answer the question I posed above. Below code works. The event listener does not need to be in there. The major changes that helped are defining the new line and end of file as bytes "public static final Byte eof = 0x1A, nl=0x0D;" then writing the bytes to the serialPort separately from the commands "serialPort.writeByte(nl);". I hope this helps others.
Doug
PS: I wrote a java class that may simplify sending code to the SIM900 using jssc, if anyone is interested.
package jssc_test;
//import jssc.SerialPort;
//import jssc.SerialPortException;
//import jssc.SerialPortList;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import jssc.*;
import static jssc.SerialPort.PURGE_RXCLEAR;
import static jssc.SerialPort.PURGE_TXCLEAR;
import static jssc_test.Jssc_test.serialPort;
/**
*
* #author DStockman
*/
public class Jssc_test {
public static SerialPort serialPort=null;
public static final Byte eof = 0x1A, nl=0x0D;
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws InterruptedException {
//SerialPort serialPort = null;
try {
String[] portNames = SerialPortList.getPortNames();
for(int i = 0; i < portNames.length; i++){
System.out.println(portNames[i]);
}
if(portNames.length < 1){
System.out.println("No ports available");
System.exit(0);
}
else{
serialPort = new SerialPort(portNames[0]);
}
System.out.println("Port opened: " + serialPort.openPort());
System.out.println("Params set: " + serialPort.setParams(9600, 8, 1, 0));
serialPort.writeBytes("ATI".getBytes()); // get modem ID
serialPort.writeByte(nl);
Thread.sleep(1000);
System.out.println("modem ID = " + serialPort.getEventsMask());
/*
int mask = SerialPort.MASK_RXCHAR + SerialPort.MASK_CTS + SerialPort.MASK_DSR;//Prepare mask
serialPort.setEventsMask(mask);//Set mask
try{
serialPort.addEventListener(new SerialPortReader());//Add SerialPortEventListener
}
catch (SerialPortException ex) {
System.out.println(ex);
}
*/
// just looking up settings
System.out.println("events mask = " + serialPort.getEventsMask());
System.out.println("flow control mode = " + serialPort.getFlowControlMode());
System.out.println("output buffer bytes = " + serialPort.getOutputBufferBytesCount());
//serialPort.purgePort(PURGE_RXCLEAR | PURGE_TXCLEAR);
serialPort.writeBytes(" AT+CMGF=1".getBytes());
serialPort.writeByte(nl);
Thread.sleep(1000);
System.out.println("bytes back set modem to text mode = " + serialPort.readString());
System.out.println("Success entering number: " + serialPort.writeBytes(" AT+CMGS=\"5857738696\";".getBytes())); // \r = <CR>. Tried both with and without '+'. In TeraTerm, only works without +. error if use: \r\n
serialPort.writeByte(nl);
Thread.sleep(1000);
System.out.println("bytes back after number entered = " + serialPort.readString());
serialPort.writeBytes("THIS IS A third TEST from DS 09/29/16.2.".getBytes());
serialPort.writeByte(nl);
Thread.sleep(1000);
serialPort.writeByte(eof);
Thread.sleep(1000);
System.out.println("bytes back = " + serialPort.readString());
Thread.sleep(10000);
//serialPort.purgePort(SerialPort.PURGE_TXCLEAR);
//attempt to get msgs received by modem
serialPort.writeBytes("AT+CMGL=\"ALL\"".getBytes());
serialPort.writeByte(nl);
Thread.sleep(1000);
System.out.println("bytes back = " + serialPort.readString());
System.out.println("Port closed: " + serialPort.closePort());
}
catch (SerialPortException ex){
System.out.println(ex);
}
} // ******************* end main ***************
} // *********************** end main class ***********************
class SerialPortReader implements SerialPortEventListener {
public void serialEvent(SerialPortEvent event) {
if(event.isRXCHAR()){//If data is available
if(event.getEventValue() == 1){//Check bytes count in the input buffer
//Read data, if 10 bytes available
try {
System.out.println("bytes back inside listener = " + serialPort.readString());
byte buffer[] = Jssc_test.serialPort.readBytes(10);
System.out.println("listener text:");
System.out.print(Arrays.toString(buffer));
System.out.println("End listener text:");
}
catch (SerialPortException ex) {
System.out.println(ex);
}
}
}
else if(event.isCTS()){//If CTS line has changed state
if(event.getEventValue() == 1){//If line is ON
System.out.println("CTS - ON");
}
else {
System.out.println("CTS - OFF");
}
}
else if(event.isDSR()){///If DSR line has changed state
if(event.getEventValue() == 1){//If line is ON
System.out.println("DSR - ON");
}
else {
System.out.println("DSR - OFF");
}
}
}
}
I'm trying to read card UID / HCE android device UID from my pcsc java code.
But I'm getting the following error
javax.smartcardio.CardException: sun.security.smartcardio.PCSCException: Unknown error 0x1f
Although I'm able to get ATS bit when I try to get UID I get this error.
I have refered my code from here
CODE
import java.io.Console;
import java.util.List;
import java.util.ListIterator;
import java.util.Scanner;
import javax.smartcardio.ATR;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;
public class PCSC {
private byte[] atr = null;
private String protocol = null;
private byte[] historical = null;
public CardTerminal selectCardTerminal() {
try {
// show the list of available terminals
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
ListIterator<CardTerminal> terminalsIterator = terminals
.listIterator();
CardTerminal terminal = null;
CardTerminal defaultTerminal = null;
if (terminals.size() > 1) {
System.out
.println("Please choose one of these card terminals (1-"
+ terminals.size() + "):");
int i = 1;
while (terminalsIterator.hasNext()) {
terminal = terminalsIterator.next();
System.out.print("[" + i + "] - " + terminal
+ ", card present: " + terminal.isCardPresent());
if (i == 1) {
defaultTerminal = terminal;
System.out.println(" [default terminal]");
} else {
System.out.println();
}
i++;
}
Scanner in = new Scanner(System.in);
try {
int option = in.nextInt();
terminal = terminals.get(option - 1);
} catch (Exception e2) {
// System.err.println("Wrong value, selecting default terminal!");
terminal = defaultTerminal;
}
System.out.println("Selected: " + terminal.getName());
// Console console = System.console();
return terminal;
}
} catch (Exception e) {
System.err.println("Error occured:");
e.printStackTrace();
}
return null;
}
public String byteArrayToHexString(byte[] b) {
StringBuffer sb = new StringBuffer(b.length * 2);
for (int i = 0; i < b.length; i++) {
int v = b[i] & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString().toUpperCase();
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character
.digit(s.charAt(i + 1), 16));
}
return data;
}
public Card establishConnection(CardTerminal ct) {
this.atr = null;
this.historical = null;
this.protocol = null;
System.out
.println("To establish connection, please choose one of these protocols (1-4):");
System.out.println("[1] - T=0");
System.out.println("[2] - T=1");
System.out.println("[3] - T=CL");
System.out.println("[4] - * [default]");
String p = "*";
Scanner in = new Scanner(System.in);
try {
int option = in.nextInt();
if (option == 1)
p = "T=0";
if (option == 2)
p = "T=1";
if (option == 3)
p = "T=CL";
if (option == 4)
p = "*";
} catch (Exception e) {
// System.err.println("Wrong value, selecting default protocol!");
p = "*";
}
System.out.println("Selected: " + p);
Card card = null;
try {
card = ct.connect(p);
} catch (CardException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
ATR atr = card.getATR();
System.out.println("Connected:");
System.out.println(" - ATR: " + byteArrayToHexString(atr.getBytes()));
System.out.println(" - Historical: "
+ byteArrayToHexString(atr.getHistoricalBytes()));
System.out.println(" - Protocol: " + card.getProtocol());
this.atr = atr.getBytes();
this.historical = atr.getHistoricalBytes();
this.protocol = card.getProtocol();
return card;
}
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
PCSC pcsc = new PCSC();
CardTerminal ct = pcsc.selectCardTerminal();
Card c = null;
if (ct != null) {
c = pcsc.establishConnection(ct);
CardChannel cc = c.getBasicChannel();
try {
ResponseAPDU answer = cc.transmit(new CommandAPDU(0xFF, 0xCA, 0x00, 0x00, 0x00));
System.out.println("answer: " + answer.toString());
} catch (CardException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
OUTPUT
Please choose one of these card terminals (1-3):
[1] - PC/SC terminal ACS ACR1281 1S Dual Reader ICC 0, card present: false [default terminal]
[2] - PC/SC terminal ACS ACR1281 1S Dual Reader PICC 0, card present: true
[3] - PC/SC terminal ACS ACR1281 1S Dual Reader SAM 0, card present: false
2
Selected: ACS ACR1281 1S Dual Reader PICC 0
To establish connection, please choose one of these protocols (1-4):
[1] - T=0
[2] - T=1
[3] - T=CL
[4] - * [default]
4
Selected: *
Connected:
- ATR: 3B8F8001804F0CA0000003060300030000000068
- Historical: 804F0CA00000030603000300000000
- Protocol: T=1
javax.smartcardio.CardException: sun.security.smartcardio.PCSCException: Unknown error 0x1f
at sun.security.smartcardio.ChannelImpl.doTransmit(Unknown Source)
at sun.security.smartcardio.ChannelImpl.transmit(Unknown Source)
at myPcscGoogleCode.PCSC.main(PCSC.java:186)
Caused by: sun.security.smartcardio.PCSCException: Unknown error 0x1f
at sun.security.smartcardio.PCSC.SCardTransmit(Native Method)
... 3 more
Already referred the following
Get UID of Mifare Ultralight with SCL010
Please help !
Thanks
I was able to find the solution and get the UID by changing the command APDU.
Then I converted the result byte[] to hex UID.
try {
//get UID cmd apdu
ResponseAPDU answer = cc.transmit( new CommandAPDU(new byte[] { (byte)0xFF, (byte)0xCA, (byte)0x00, (byte)0x00, (byte)0x00 } ));
System.out.println("answer: " + answer.toString());
byte[] reponseBytesArr=answer.getBytes();
System.out.println("answer byte[]");
StringBuilder sb = new StringBuilder();
for(int i=0;i<reponseBytesArr.length;i++){
//print arr
byte b =reponseBytesArr[i];
System.out.println(b);
if(i<=reponseBytesArr.length-3){
// append uid
sb.append(String.format("%02X ", b));
}
}
System.out.println("UID: "+sb.toString());
Output:
Please choose one of these card terminals (1-3):
[1] - PC/SC terminal ACS ACR1281 1S Dual Reader ICC 0, card present: false [default terminal]
[2] - PC/SC terminal ACS ACR1281 1S Dual Reader PICC 0, card present: true
[3] - PC/SC terminal ACS ACR1281 1S Dual Reader SAM 0, card present: false
2
Selected: ACS ACR1281 1S Dual Reader PICC 0
To establish connection, please choose one of these protocols (1-4):
[1] - T=0
[2] - T=1
[3] - T=CL
[4] - * [default]
4
Selected: *
Connected:
-Card Class class sun.security.smartcardio.CardImpl
- ATR: 3B8F8001804F0CA0000003060300030000000068
- Historical: 804F0CA00000030603000300000000
- Protocol: T=1
answer: ResponseAPDU: 9 bytes, SW=9000
answer byte[]
4
-28
-66
42
0
41
-128
-112
0
UID: 04 E4 BE 2A 00 29 80
Im Trying to create a simple "Game Controller" by connecting a Analog stick to Arduino and processing the sent data in a Java file using Eclipse. Here are the Codes:
Arduino:
int Ver = 0;
int Hor = 0;
int data[2];
void setup(){
Serial.begin(9600);
}
void loop(){
Ver = analogRead(A0); //511 D = 0
Hor = analogRead(A1); //515 L = 0 max = 1023
data[0] = Ver;
data[1] = Hor;
Serial.print("[");
Serial.print(data[0], DEC);
Serial.print(",");
Serial.print(data[1], DEC);
Serial.println("]");
delay(50);
}
Java:
package Controller;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.util.Enumeration;
public class AnalogStick implements SerialPortEventListener {
SerialPort serialPort;
/** The port we're normally going to use. */
private static final String PORT_NAMES[] = { "/dev/tty.usbserial-A9007UX1", // Mac OS X
"/dev/ttyUSB0", // Linux
"COM12", // Windows
};
private BufferedReader input;
private static final int TIME_OUT = 2000;
private static final int DATA_RATE = 9600;
public void initialize() {
CommPortIdentifier portId = null;
Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();
// First, Find an instance of serial port as set in PORT_NAMES.
while (portEnum.hasMoreElements()) {
CommPortIdentifier currPortId = (CommPortIdentifier) portEnum
.nextElement();
for (String portName : PORT_NAMES) {
if (currPortId.getName().equals(portName)) {
portId = currPortId;
break;
}
}
}
System.out.println("Port ID: ");
System.out.println(portId);
System.out.println("");
if (portId == null) {
System.out.println("Could not find COM port.");
return;
}
try {
// open serial port, and use class name for the appName.
serialPort = (SerialPort) portId.open(this.getClass().getName(),
TIME_OUT);
// set port parameters
serialPort.setSerialPortParams(DATA_RATE, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
// open the streams
input = new BufferedReader(new InputStreamReader(serialPort.getInputStream()));
serialPort.getOutputStream();
// add event listeners
serialPort.addEventListener(this);
serialPort.notifyOnDataAvailable(true);
} catch (Exception e) {
System.err.println(e.toString());
}
}
public synchronized void close() {
if (serialPort != null) {
serialPort.removeEventListener();
serialPort.close();
}
}
public synchronized void serialEvent(SerialPortEvent oEvent) {
if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
String inputLine = input.readLine();
//System.out.println("" + inputLine);
String[] items = inputLine.replaceAll("\\[", "").replaceAll("\\]", "").split(",");
int[] data = new int[items.length];
for (int i = 0; i < items.length; i++) {
try {
data[i] = Integer.parseInt(items[i]);
} catch (NumberFormatException nfe) {};
}
//System.out.println("Ver: " + data[0] + " Hor: " + data[1]);
Robot robot = new Robot();
if(data[0] > 511 + 10 && data[1] < 515 + 169 && data[1] > 515 - 169){
System.out.println("Up");
robot.keyPress(KeyEvent.VK_W);
}
else if(data[0] < 511 - 10 && data[1] < 515 + 169 && data[1] > 515 - 169){
System.out.println("Down");
robot.keyPress(KeyEvent.VK_S);
}
if(data[1] < 515 - 10 && data[0] < 511 + 170 && data[0] > 511 - 170){
System.out.println("Left");
robot.keyPress(KeyEvent.VK_A);
}
else if(data[1] > 515 + 10 && data[0] < 511 + 170 && data[0] > 511 - 170){
System.out.println("Right");
robot.keyPress(KeyEvent.VK_D);
}
} catch (Exception e) {
System.err.println(e.toString());
//System.out.println("failed stuff");
}
}
if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
}
}
public static void main(String[] args) throws Exception {
AnalogStick main = new AnalogStick();
main.initialize();
Thread t = new Thread() {
public void run() {
try {
Thread.sleep(5000000);
} catch (InterruptedException ie) {
}
}
};
t.start();
System.out.println("- Started -");
System.out.println("");
}
}
I press run on eclipse, and it works fine, but when I shift my mouse pointer to a different screen or try using the analog stick in a game (therefore switching screens), i get the following Error and the analog stick stops functioning within the game:
java.io.IOException: Underlying input stream returned zero bytes
I have a feeling that this error is caused by having too many data sent to the PC before the analog stick is able to process a single input line, but I am not sure since this is the first time I am working with a analog stick. Can somebody teach me
1) Why its not working as I expect it to work
2) How to fix this error
and
3) If there is a better way to program a analog stick, tell me how and/or show me a reference website
Thanks in advance
I surfed a lot of questions on the board, about tcp sockets, big-endian and little-endian format but to me nothing apllies to my case.
And I'm sorry for my bad English, I'm working on it :)
I'm loosing my mind on an unexpected behaviour in a simple client-server configuration. Here's the scenario:
Server (C++) <--- TCP socket ---> Client(Java).
Here's the client code:
package NetServ.apps.bigServer.NSLPClient;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.ArrayList;
public class Communicator {
private Socket sock;
private final int port = 6666;
private final String address="127.0.0.1";
private DataOutputStream out;
private DataInputStream in;
public Communicator(){
System.out.println("Creating communicator. Trying to bind to the tcp socket");
try {
sock = new Socket(address, port);
out=new DataOutputStream(sock.getOutputStream());
in=new DataInputStream(sock.getInputStream());
} catch (UnknownHostException e) {
System.out.println("Unable to resolv host");
e.printStackTrace();
} catch (IOException e) {
System.out.println("Generic I/O exception");
e.printStackTrace();
}
System.out.println("Communicator created");
}
public void sendRequest(Request req) throws IOException{
int cmd=0;
if(req.getCmd().equals(CommandType.tg_setup_message))
cmd=0;
if(req.getCmd().equals(CommandType.tg_remove_message))
cmd=1;
if(req.getCmd().equals(CommandType.tg_trigger_message))
cmd=2;
if(req.getCmd().equals(CommandType.tg_probe_message))
cmd=3;
byte[] buff;
Charset charset = Charset.forName("ISO-8859-1");
out.writeInt(cmd);
//out.writeUTF(req.getDstAddr().toString().substring(1));
buff = req.getDstAddr().toString().substring(1).getBytes(charset);
out.writeShort((short)buff.length);
out.write(buff, 0, buff.length);
out.writeInt(req.getProtocol());
out.writeInt(req.getSecure());
//out.writeUTF(req.getDataId());
buff = req.getDataId().getBytes(charset);
out.writeShort((short)buff.length);
out.write(buff, 0, buff.length);
//out.writeUTF(req.getUser());
buff = req.getUser().getBytes(charset);
out.writeShort((short)buff.length);
out.write(buff, 0, buff.length);
out.flush();
out.writeInt(req.getOffpath_type());
if(req.getOffpath_type()!=-1){
out.writeInt(req.getMetric_type());
String tmp = "" + req.getMetric();
//out.writeUTF(tmp);
buff = tmp.getBytes(charset);
out.writeShort((short)buff.length);
out.write(buff, 0, buff.length);
}
switch (req.getCmd()){
case tg_setup_message:
out.writeUTF(req.getUrl());
out.writeInt(req.getLifetime());
out.writeUTF(req.getParameters().toString());
break;
case tg_remove_message:
//TODO
break;
case tg_trigger_message:
//TODO
break;
case tg_probe_message:
for (Short s : req.getProbes()){
//System.out.println("Writing probe code " + s.shortValue());
out.writeShort(s.shortValue());
}
break;
}
if(req.getSignature()!=null){
out.writeInt(1);
out.writeUTF(req.getSignature());
}else{
out.writeInt(0);
}
if(req.getDep()!=null){
out.writeInt(1);
out.writeUTF(req.getDep());
}else{
out.writeInt(0);
}
if(req.getNotif()!=null){
out.writeInt(1);
out.writeUTF(req.getNotif());
}else{
out.writeInt(0);
}
if(req.getNode()!=null){
out.writeInt(1);
out.writeUTF(req.getNode());
}else{
out.writeInt(0);
}
out.flush();
//out.close();
System.out.println("request sent");
}
public ArrayList<String> rcvProbeResponse() throws IOException, SocketException{
ArrayList<String> response= new ArrayList<String>();
System.out.println("Waiting for response...");
boolean timeout=false;
int responseCode=-1;
responseCode=in.readInt();
//responseCode = in.readInt();
//System.out.println("Response code "+responseCode);
if(responseCode==1){ //response is ready! !
System.out.println("Response arriving from NSLP (code 1 )");
int responseCmdCode = in.readInt();
if(responseCmdCode!=2)
return null;
//System.out.println("Response Command Code " + responseCmdCode );
int probeSize = in.readInt();
//System.out.println("Number of probes " + probeSize);
for(int i=0; i<probeSize; i++){
//System.out.println("i: "+i);
String out = in.readUTF();
response.add(out);
}
}
in.close();
if(timeout)
return null;
else
return response;
}
}
Nothing special about that: the protocol between the entities is simply an exchange of integers, shorts and strings, that triggers the server to execute some signaling tasks (the server is the daemon of a signaling protocol).
On the other side the server is legacy code that I modified to comunicate with java.
Here's the relevant code:
[...]
// Set the current socket
communicator->setSocket(sockfd);
// FSM data structure
NetservNslpFsmData * data = new NetservNslpFsmData();
//give the address list of this node to all FSMs created by the client
data->nodeAddressList = &(param.addresses);
// Read from socket the parameters and use them
int ret;
NetservNslpCommunicator::command cmd;
ret = communicator->recvCommandFromJava(&cmd);
if (ret <= 0) {
logSocketError(sockfd, "Command");
// free up the memory allocated
delete data;
return;
}
switch(cmd){
case NetservNslpCommunicator::tg_setup_message:
DLog(param.name, "cmd set: setup");
break;
case NetservNslpCommunicator::tg_remove_message:
DLog(param.name, "cmd set: remove");
break;
case NetservNslpCommunicator::tg_probe_message:
DLog(param.name, "cmd set: probe");
break;
case NetservNslpCommunicator::tg_trigger_message:
DLog(param.name, "cmd set: trigger");
break;
}
ret = communicator->recvIPFromJava(&(data->destAddr));
DLog(param.name, "Dst Address set: "<< data->destAddr.get_ip_str());
if (ret <= 0) {
logSocketError(sockfd, "Destination IP");
// free up the memory allocated
delete data;
return;
}
[...]
int reliable = communicator->recvIntFromJava();
data->reliability = (reliable == NetservNslpCommunicator::TCP);
DLog(param.name, "Reliability set : "<< data->reliability);
int secure = communicator->recvIntFromJava();
data->security = (secure == NetservNslpCommunicator::TCP);
DLog(param.name, "Security set : "<< data->security);
data->dataId = communicator->recvStringFromJava();
DLog(param.name, "DataId : "<< data->dataId);
if (data->dataId == NULL) {
logSocketError(sockfd, "dataId");
// free up the memory allocated
delete data;
return;
}
data->user = communicator->recvStringFromJava();
DLog(param.name, "User : "<< data->user);
if (data->user == NULL) {
logSocketError(sockfd, "user");
// free up the memory allocated
delete data;
return;
}
//Receiving OffPath parameters
data->offpath_type=communicator->recvIntFromJava();
DLog(param.name, "OffType : "<< data->offpath_type);
if(data->offpath_type != -1){
data->metric_type=communicator->recvIntFromJava();
DLog(param.name, "MetricType : "<< data->metric_type);
if(data->metric_type>3|| data->metric_type<1){
logSocketError(sockfd, "metric type");
// free up the memory allocated
delete data;
return;
}
char * tmpStr = communicator->recvStringFromJava();
if (tmpStr == NULL) {
logSocketError(sockfd, "metric");
// free up the memory allocated
delete data;
return;
}
data->metric = tmpStr;
DLog(param.name, "MetricValue : "<< data->metric);
DLog(param.name, "MetricLength : "<< data->metric.length());
}
// check if socket is still alive or some errors occured
if (!communicator->isAlive(sockfd)) {
logSocketError(sockfd, "Socket not alive!");
// free up the memory allocated
delete data;
return;
}
DLog(param.name,"Reading command-specific configuration");
switch(cmd)
{
case NetservNslpCommunicator::tg_setup_message:
data->urlList.push_back(communicator->recvString());
//check if the service data is exchanged together with signaling messages
if (data->urlList.front() != NULL && (strncmp(data->urlList.front(), "file://", 7) == 0))
data->data_included = true;
data->lifetime = communicator->recvIntFromJava();
data->setupParams = communicator->recvStringFromJava();
break;
case NetservNslpCommunicator::tg_remove_message:
break;
case NetservNslpCommunicator::tg_probe_message:
{
DLog(param.name, "Reading probe codes list.");
short probe = 0;
do {
probe = communicator->recvShortFromJava();
DLog(param.name,"Probe Code " << probe);
data->probes.push_back(probe);
} while (probe != 0);
data->probes.pop_back(); //delete the last 0
if (data->probes.empty()) {
logSocketError(sockfd, "Probe list is empty!");
return;
}
break;
}
case NetservNslpCommunicator::tg_trigger_message:
data->triggerType = communicator->recvInt();
switch (data->triggerType){
case NETSERV_MESSAGETYPE_SETUP:
data->urlList.push_back(communicator->recvString());
data->lifetime = communicator->recvInt();
data->setupParams = communicator->recvString();
break;
case NETSERV_MESSAGETYPE_REMOVE:
break;
case NETSERV_MESSAGETYPE_PROBE:
{
short probe = 0;
do {
probe = communicator->recvShortFromJava();
data->probes.push_back(probe);
} while (probe != 0);
data->probes.pop_back(); //delete the last 0
break;
}
default:
ERRLog(param.name, "Trigger type not supported");
closeSocket(sockfd);
return;
}
break;
default:
logSocketError(sockfd, "Trigger type not supported!");
return;
}
DLog(param.name,"Reading optional parameters.");
// Optional parameters passing
bool addParam = 0;
addParam = communicator->recvIntFromJava();
if (addParam) {
data->signature = communicator->recvStringFromJava();
if (data->signature == NULL) {
logSocketError(sockfd, "signature");
// free up the memory allocated
delete data;
return;
}
DLog(param.name, "Message signature : "<< data->signature);
}
addParam = communicator->recvIntFromJava();
if (addParam) {
data->depList.push_back(communicator->recvStringFromJava());
if (data->depList.front() == NULL) {
logSocketError(sockfd, "dependency list");
// free up the memory allocated
delete data;
return;
}
DLog(param.name, "Message dependency list : "<< data->depList.front());
}
addParam = communicator->recvIntFromJava();
if (addParam) {
data->notification = communicator->recvStringFromJava();
if (data->notification == NULL) {
logSocketError(sockfd, "notification");
// free up the memory allocated
delete data;
return;
}
DLog(param.name, "Message notification : "<< data->notification);
}
addParam = communicator->recvIntFromJava();
if (addParam) {
data->node = communicator->recvStringFromJava();
if (data->node == NULL) {
logSocketError(sockfd, "node");
// free up the memory allocated
delete data;
return;
}
DLog(param.name, "Node destination : "<< data->node);
}
[...]
The communicator wraps the socket and uses standard calls to write and read types:
int NetservNslpCommunicator::recvCommandFromJava(NetservNslpCommunicator::command * cmd){
int code = recvIntFromJava();
cout<<"received int "<<code<<endl;
if(code>=0){
switch(code){
case 0:
*cmd=NetservNslpCommunicator::tg_setup_message;
break;
case 1:
*cmd=NetservNslpCommunicator::tg_remove_message;
break;
case 2:
*cmd=NetservNslpCommunicator::tg_trigger_message;
break;
case 3:
*cmd=NetservNslpCommunicator::tg_probe_message;
break;
}
}
return code;
}
int NetservNslpCommunicator::recvIPFromJava(protlib::hostaddress * addr){
cout<<"receiving an IP"<<endl;
char* str = recvStringFromJava();
cout<<"String received "<< str << endl;
addr->set_ipv4(str);
return 1;
}
char * NetservNslpCommunicator::recvStringFromJava(){
short length = recvShortFromJava();
cout<< "receiving a string..."<<endl<<"String length "<<length<<endl;
char * string = new char[length];
int r = 0;
int orLength=length;
while(length)
{
int r = recv(sock, string, length, 0);
if(r <= 0)
break; // Socket closed or an error occurred
length -= r;
}
string[orLength]='\0';
if(orLength==0)
return NULL;
else
return string;
}
int NetservNslpCommunicator::recvIntFromJava(){
int x = 0;
recvBuffer(sock, &x, 4);
return x;
}
short NetservNslpCommunicator::recvShortFromJava()
{
short x = 0;
recvBuffer(sock, &x, 2);
return x;
}
int NetservNslpCommunicator::recvBuffer(int sock, void * buf, size_t size)
{
int counter = 0;
// Create a pollfd struct for use in the mainloop
struct pollfd poll_fd;
poll_fd.fd = sock;
poll_fd.events = POLLIN | POLLPRI;
poll_fd.revents = 0;
int r;
while (size && !stop)
{
/* Non-blocking behavior */
// wait on number_poll_sockets for the events specified above for sleep_time (in ms)
int poll_status = poll(&poll_fd, 1/*Number of poll socket*/, 100);
if (poll_fd.revents & POLLERR) // Error condition
{
if (errno != EINTR)
cout << "NetservNslpCommunicator : " << "Poll caused error " << strerror(errno) << " - indicated by revents" << endl;
else
cout << "NetservNslpCommunicator : " << "poll(): " << strerror(errno) << endl;
}
//ignore hangups when reading from a socket
if (poll_fd.revents & POLLHUP) // Hung up
{
cout << "NetservNslpCommunicator : " << "Poll hung up" << endl;
// return -1;
}
if (poll_fd.revents & POLLNVAL) // Invalid request: fd not open
{
cout << "NetservNslpCommunicator : " << "Poll Invalid request: fd not open" << endl;
return -1;
}
switch (poll_status)
{
case -1:
if (errno != EINTR)
cout << "NetservNslpCommunicator : " << "Poll status indicates error: " << strerror(errno) << endl;
else
cout << "NetservNslpCommunicator : " << "Poll status: " << str error(errno) << endl;
break;
case 0:
if (isTriggerTimerEnabled){
counter++;
if (counter == triggerTimerValue){
isTriggerTimerEnabled = false;
return -1;
}
}
continue;
break;
default:
r = recv(sock, buf, size, 0);
if (r <= 0)
{
if (r == 0) { // connection closed
r = -1; // return an error if socket closes
cout << "NetservNslpCommunicator : " << "No data received from socket!" << endl;
stop=true;
break;
}
if (r == -1 && errno == EINTR) // received interrupt during recv, continuing
continue;
if (r == -1 && errno != EINTR) // socket error, raise exception
break;
}
if (r != -1)
size -= r;
break;
}
}
counter = 0;
isTriggerTimerEnabled = false;
return r;
}
I ask you to focus only on the tg_probe_message part. The other messages are still to implement.
The strange behaviour is: the first time the client sends a request to the server everything goes well, all values are read perfectly. Hence the server answers sending back some integer and a sequences of strings.
This is a trace (application layer only. One TCP packet per line) of what i capture on the socket:
00 //
00 //
00 //
03 // First integer
00 //
0a // Short representing string length
31:37:32:2e:31:36:2e:33:2e:32 //the string: "172.16.3.2"
00
00
00
01
00
00
00
00
00
1b
4e:65:74:53:65:72:76:2e:61:70:70:73:2e:4c:6f:69:62:46:61:6b:65:5f:30:2e:30:2e:31 //The string "NetServ.apps.LoibFake_0.0.1"
00
03
6a:61:65 //the string "jae"
00
00
00
03
00
00
00
01
00
01
31 //the string "1"
00
02
00
00
00 //
00 //
00 // 4 times
00 //
The server answers:
00:00:00:01 //response code
00:00:00:02 //response type
00:00:00:04 //number of strings to read
00:12 //str length
31:30:2e:31:30:2e:30:2e:35:20:41:43:54:49:56:45:20:31
00:12 //str length
31:30:2e:31:30:2e:30:2e:34:20:41:43:54:49:56:45:20:31
00:12 //str length
31:30:2e:31:30:2e:30:2e:33:20:41:43:54:49:56:45:20:32
00:12 //str length
31:30:2e:31:30:2e:30:2e:36:20:41:43:54:49:56:45:20:32
The second time the client sends a request (the same request) something weird occurs. This is what i captured with tcpdump during the second connection:
00 //
00 //
00 //
03 // First integer
00 //
0a // Short representing string length
31:37:32:2e:31:36:2e:33:2e:32 //the string: "172.16.3.2"
00
00
00
01
00
00
00
00
00:1b:4e:65:74:53:65:72:76:2e:61:70:70:73:2e:4c:6f:69:62:46:61:6b:65:5f:30:2e:30:2e:31:00:03:6a:61:65:00:00:00:03:00:00:00:01:00:01:31:00:02:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
With a little of patience you can recognise that the last packet contains ALL the bits of the request (same bits of the first request).
With some debuggin I can see that the command communicator->recvCommandFromJava(&cmd) returns the number 50331648 (03:00:00:00) instead of 3 (00:00:00:03) and when the command communicator->recvIPFromJava(&(data->destAddr)) is executed, which in turn calls the recvStringFromJava(), which uses the recvShortFromJava(), the short representing the string length 00:0a (10) is swapped into the little-endian 0a:00 (2560). I believe this causes the tcp to put all the data available in the next packet and to spoil the subsequent calls.
As you can see from the code I didn't adopted conversion from host-order to net-order in the server (and that is because it works fine for the first request), but it seems that conversion is required during the second request. The documentation on DataOutputStream specifies that int and short are written in big-endian. The server does not apply conversion.
Hence, in the end, this is the question:
Is it possible that C++ could change the Host-Format during execution?
How could this possibly happen?
What I can do to have predicible behaviour on the byte ordering between java client and c++ server?
Endian-ness has nothin to do with putting the data in the next packet. That's just because it's a byte stream protocol.
You have two separate problems to solve: one with ntohl() and friends, the other by continuing to read until you have all the data you're expecting.
I found a solution to my problem that works and I think is elegant enough.
Because I can't predict the behaviour of the server when it reads primitive types large more than one byte, I use a standard contract mechanism.
Every time a client wants to push commands to the server it sends a known Integer code. The server reads the integer and checks if the value is equal to the predeterminated Integer, than it can read all the values without reordering them. Otherwise it will set a flag and will read all the subsequent values swapping them with the function ntohl() and ntohs().