Open a cash drawer connected to a usb printer - linux

I have a cash drawer connected to an Epson TM-T20 connected on a USB port.
I have found an example here and applied their ideas to with the codes from here to the printer:
echo -en "\033\160\040\025" | lp -d "USB_TM-T20" -o raw
But this doesn't seem to work. Since epson provides a java library, I decided to have a look at it and decompiled it.
protected byte[] getOpenDrawerCommand()
{
byte[] arrayOfByte = new byte[5];
if (this.m_objDrawerPort.isSupportRealTimeCommand())
{
arrayOfByte[0] = 16;
arrayOfByte[1] = 20;
arrayOfByte[2] = 1;
arrayOfByte[3] = (byte)this.m_objDrawerSettings.getPinNumber(); // seems to be 0
arrayOfByte[4] = (byte)this.m_objDrawerSettings.getOnTime(true); // between 1 and 8
}
else
{
arrayOfByte[0] = 27;
arrayOfByte[1] = 112;
arrayOfByte[2] = (byte)this.m_objDrawerSettings.getPinNumber(); // seems to be 0
arrayOfByte[3] = (byte)this.m_objDrawerSettings.getOnTime(false); // [1, 255]
arrayOfByte[4] = (byte)this.m_objDrawerSettings.getOffTime(false); // [1, 255]
}
return arrayOfByte;
}
We see that the values from the keyfile seem to be correct (at least the first two). Unfortunately I was unable to find the code where it sends the data.
Do you have an idea where I can find more information? The epson website seems to be kind of sparse.
Edit:
It turns out that the connection cable was damaged and the new cable needed to be repinned in order to connect to the printer.

According to the man page, bash's echo command, as well as standalone echo, requires octal character constants to start with a leading zero. This differs slightly from C.
So, try
echo -en '\033\0160\040\025'
or just use hexadecimal.
It also looks like you're sending 4 bytes, while the Java snippet indicates that 5 are necessary.

I also have an Epson TM-T20 and found this answer and links to mostly solve my problem, but I checked the manual and found that the code to open the drawer was ESC p m t1 t2
This translated to:
echo -en '\033p011' | lp -d EPSON_TM_T20 -o raw
I used lpstat -p to find the correct printer name to use.

Related

Convert http payload to string for debugging

I am using this C++ code on an ESP8266 to check on several http accessible data. Once in a while this device acts strangelay and I would like to be able to look at the data it gets. So I collect all payloads to be able to check it via rest api. The following code ALMOST works
String payloadAll = "start ###";
http.begin(client, getLink2);
httpCode = http.GET();
if ( httpCode > 0 ) { payload = http.getString(); payloadAll = payloadAll + payload.c_str() + "### ENDE-PAYLOAD1 ###"; }
the output looks like this:
start ###Internals: DEF 1 60 192.168.40.1:1502 TCP DeviceName 192.168.40.1:1502 EXPECT idle FD 6 FUUID 5xxxxx-fxxx-9xxx-fxxx-exxxxxxxxxxx IODev SolarEdge Interval 60 LASTOPEN 1647580317.55657 MODBUSID 1 MODE master MODEL SE8K-XXXXXXXXXX### ENDE-PAYLOAD1 ###
That is the first line of the payload only. I don't know much about the formast the requested data is in but once it is in the String, I can search ind find keyword and such from the lines I don't see in the output, so I know the data is there. If I leave out the ".c_str()" part, I will only get "start ###### ENDE-PAYLOAD1 ###"
How can I get all the lines - formatting is not important - it can all be one line, I just need to be able to see it as plain text.
Thanks!

How to return pointer string with ctypes in python 2.7

I am working on implementation of new fiscal device. And it is using OPOS / UPOS library for communication. I am very new to ctypes and have no experience with C at all. However, I have managed to make it work, mostly.
But I am having issues with returning a string from generalist method DirectIO. documentation says: "This command should be used immediately after EndFiscalReceipt() to retrieve unique ID of latest receipt"
" Parameters:
– Data [in]
Ignored.
– Obj [in/out]
Value to be read."
And adds .NET example under it:
int iData = 0;
string strReferenceID = "";
fiscalPrinter.EndFiscalReceipt();
fiscalPrinter.DirectIO(CMD_EKASA_RECEIPT_ID, ref iData, ref strReferenceID);
// strReferenceID will contain latest receipt ID, e.g. "O−7DBCDA8A56EE426DBCDA8A56EE426D1A"
the first parameter (CMD_EKASA_RECEIPT_ID) is the command executed, thats why its not listed above.
However, python is not .NET and I have never been working with .NET.
I have been following instructions in ctypes doku (https://docs.python.org/2.7/library/ctypes.html), defiend this methods arguments and return in init:
self.libc.DirectIO.argtypes = [ctypes.c_int32, ctypes.c_int32, ctypes.c_char_p]
self.libc.DirectIO.restype = ctypes.c_char_p
Than tried different ways to retrieve reply string, but neither of these does work in my case:
s = ""
c_s = ctypes.c_char_p(s)
result = self.send_command(CMD_EKASA_RECEIPT_ID, 0, c_s)
p = ctypes.create_string_buffer(40)
poin = ctypes.pointer(p)
result = self.send_command(CMD_EKASA_RECEIPT_ID, 0, poin)
p = ctypes.create_string_buffer(40)
result = self.send_command(CMD_EKASA_RECEIPT_ID, 0, p)
s = ctypes.create_string_buffer('\000' * 32)
result = self.send_command(CMD_EKASA_RECEIPT_ID, 0, s)
the string object I have created is allways empty, a.k.a. "" after caling the Cmethod, just like I have created it.
However, there is one more thing, that does not make sense to me. My colleague showed me, how you can see method arguments and return in header file. For this one, there is this:
int DirectIO(int iCommand, int* piData, const char* pccString);
Which means, it returns integer? If I am not mistaken.
so, what I am thinking is, that I have to pass to the method some pointer to a string, created in python, and C will change it, into what I should read. Thus, I think my way of thinking about solution is right.
I have also tried this approach, but that does not work for me either How to pass pointer back in ctypes?
and I am starting to feel desperate. Not sure if I understand the problem correctly and looking for a solution is right place.
I have solved my problem. The whole thing was, in allocating of memory. Every example on the net that I have readed did create empty string, like s = "". But, that is not correct!
When allocated empty string "" C library have had no memory where to write result.
this was almost correct approach,
self.libc = ctypes.cdll.LoadLibrary(LIB_PATH)
self.libc.DirectIO.argtypes = [ctypes.c_int32, ctypes.c_int32, ctypes.c_char_p]
result_s = ctypes.c_char_p("")
log.info('S address: %s | S.value: "%s"' % (result_s, result_s.value))
self.libc.DirectIO(CMD_EKASA_RECEIPT_ID, 0, result_s)
log.info('S address: %s | S.value: "%s"' % (result_s, result_s.value))
returns:
S address: c_char_p(140192115373700) | S.value: ""
S address: c_char_p(140192115373700) | S.value: ""
it needed just a small modification:
self.libc = ctypes.cdll.LoadLibrary(LIB_PATH)
self.libc.DirectIO.argtypes = [ctypes.c_int32, ctypes.c_int32, ctypes.c_char_p]
result_s = ctypes.c_char_p(" " * 10)
log.info('S address: %s | S.value: %s' % (result_s, result_s.value))
self.libc.DirectIO(CMD_EKASA_RECEIPT_ID, 0, result_s)
log.info('S address: %s | S.value: %s' % (result_s, result_s.value))
now, printing result_s after calling self.libc.DirectIO does return different string, than it was before call.
S address: c_char_p(140532072777764) | S.value: " "
S address: c_char_p(140532072777764) | S.value: "0-C12A22F5"
There is linux in the tag, but OPOS does not work on linux.
Or are you working in an emulation environment such as Wine?
In any case, if you don't have the right environment, you can get into trouble with a little bit of nothing.
First, work in a Windows 32-bit environment, create something that works there, and then port it to another environment.
Since OPOS uses OLE/COM technology, the first package to use is win32com or comtypes.
UnifiedPOS is a conceptual specification and there are no actual software components.
There are three types of software that actually run: OPOS, JavaPOS, and POS for.NET.
OPOS and POS for.NET only work in a Windows 32-bit environment.
Only JavaPOS can work in a Linux environment, and it is usually only available from Java.
If you want to make something in Python, you need to create a Wrapper (or glue) library that calls Java from Python.
If the C interface UnifiedPOS(OPOS) is running on Linux without using the Windows emulator or the Wrapper for Java, it may be an original library/component created by the printer vendor with reference to UnifiedPOS.
In that case, I think that the detailed specification can only be heard from the vendor who created it.
To explain, DirectIO method and DirectIOEvent are defined as method/event that vendors can freely define and use.
Therefore, only method/event names and parameters are defined in the UnifiedPOS document.
It is necessary to ask the vendor who provides the DirectIO method/DirectIOEvent what function the specific vendor's service object has, and it is up to the vendor to determine what the parameter means is.
The OPOS specification was absorbed by UnifiedPOS from the middle, but until then it existed as a single specification.
The rest of the name is here. MCS: OPOS Releases
This is the root of the return value of the method of your library being integer.
By the way, this is the latest UnifiedPOS specification for now.
Document -- retail/17-07-32 (UnifiedPOS Retail Peripheral Architecture, Version 1.14.1)

Storing Value in Arduino Sent From Python via Serial

I have been trying to send a value from a Python program via serial to an Arduino, but I have been unable to get the Arduino to store and echo back the value to Python. My code seems to match that I've found in examples online, but for whatever reason, it's not working.
I am using Python 3.5 on Windows 10 with an Arduino Uno. Any help would be appreciated.
Arduino code:
void readFromPython() {
if (Serial.available() > 0) {
incomingIntegrationTime = Serial.parseInt();
// Collect the incoming integer from PySerial
integration_time = incomingIntegrationTime;
Serial.print("X");
Serial.print("The integration time is now ");
// read the incoming integer from Python:
// Set the integration time to what you just collected IF it is not a zero
Serial.println(integration_time);
Serial.print("\n");
integration_time=min(incomingIntegrationTime,2147483648);
// Ensure the integration time isn't outside the range of integers
integration_time=max(incomingIntegrationTime, 1);
// Ensure the integration time isn't outside the range of integers
}
}
void loop() {
readFromPython();
// Check for incoming data from PySerial
delay(1);
// Pause the program for 1 millisecond
}
Python code:
(Note this is used with a PyQt button, but any value could be typed in instead of self.IntegrationTimeInputTextbox.text() and the value is still not receieved and echoed back by Arduino).
def SetIntegrationTime(self):
def main():
# global startMarker, endMarker
#This sets the com port in PySerial to the port with the Genuino as the variable arduino_ports
arduino_ports = [
p.device
for p in serial.tools.list_ports.comports()
if 'Genuino' in p.description
]
#Set the proper baud rate for your spectrometer
baud = 115200
#This prints out the port that was found with the Genuino on it
ports = list(serial.tools.list_ports.comports())
for p in ports:
print ('Device is connected to: ', p)
# --------------------------- Error Handling ---------------------------
#Tell the user if no Genuino was found
if not arduino_ports:
raise IOError("No Arduino found")
#Tell the user if multiple Genuinos were found
if len(arduino_ports) > 1:
warnings.warn('Multiple Arduinos found - using the first')
# ---------------------------- Error Handling ---------------------------
#=====================================
spectrometer = serial.Serial(arduino_ports[0], baud)
integrationTimeSend = self.IntegrationTimeInputTextbox.text()
print("test value is", integrationTimeSend.encode())
spectrometer.write(integrationTimeSend.encode())
for i in range(10): #Repeat the following 10 times
startMarker = "X"
xDecoded = "qq"
xEncoded = "qq"
while xDecoded != startMarker: #Wait for the start marker (X))
xEncoded = spectrometer.read() #Read the spectrometer until 'startMarker' is found so the right amound of data is read every time
xDecoded = xEncoded.decode("UTF-8");
print(xDecoded);
line = spectrometer.readline()
lineDecoded = line.decode("UTF-8")
print(lineDecoded)
#=====================================
spectrometer.close()
#===========================================
#WaitForArduinoData()
main()
First, this is a problem:
incomingValue = Serial.read();
Because read() returns the first byte of incoming serial data reference. On the Arduino the int is a signed 16-bit integer, so reading only one byte of it with a Serial.read() is going to give you unintended results.
Also, don't put writes in between checking if data is available and actual reading:
if (Serial.available() > 0) {
Serial.print("X"); // Print your startmarker
Serial.print("The value is now ");
incomingValue = Serial.read(); // Collect the incoming value from
That is bad. Instead do your read immediately as this example shows:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
That's two big issues there. Take care of those and let's take a look at it after those fundamental issues are corrected.
PART 2
Once those are corrected, the next thing to do is determine which side of the serial communication is faulty. Generally what I like to do is determine one side is sending properly by having its output show up in a terminal emulator. I like TeraTerm for this.
Set your python code to send only and see if your sent values show up properly in a terminal emulator. Once that is working and you have confidence in it, you can attend to the Arduino side.

Perl Device::SerialPort

Looking for right way to detect one keyword during board boot up message.
After keyword detected, send Enter key after one second.
Kernel is Linux.
# Serial port inisialisation is finished here.
# Read boot message
($count, $result) = $ob->read(300); # at least 300 chars coming till keyword appear
if ($result =~ m/Booting_up/) {
print "send Enter ...\n";
sleep 1;
$ob->write("\r\n");
}
Thanks for hint
It appears that you are using Win32::SerialPort module, or perhaps Device::SerialPort which
provides an object-based user interface essentially identical to the one provided by the Win32::SerialPort module.
Its method read takes the number of bytes to read and returns the number read and writes them into the given string.
You may be "missing" the phrase because it's past the 300-mark, and your code doesn't read any further. Try to loop, getting a few bytes at a time and adding them up, thus building the string in small reads.
my bytes_in = 10; # length of pattern, but it does NOT ensure anything
my ($read, $result);
while (1)
{
my ($count, $read) = $ob->read( $bytes_in );
$result = $result . $read;
if ($result =~ m/Booting_up/) { # is it "Booting_up" or "Booting up" ?
print "send Enter ...\n";
sleep 1; # is this needed?
$ob->write("\r\n");
# last; # in case this is all you need to do
}
last if $count != $bytes_in; # done reading
}
I don't put the $ob->read statement in the loop condition since the documentation isn't crystal clear on how the method works. You may also be able to simply use
while ( my ($count, $read) = $ob->read( $bytes_in ) ) {
$result = $result . $read;
if ($result =~ m/Booting_up/s) {
# ...
}
last if $count != $bytes_in;
}
We read a small number of bytes at a time to prevent problems with either polling or blocking reads, brought up in comments by BenPen. See Configuration and capability methods.
You can first read those first 300 bytes that precede the pattern in one go and then start reading a few (or one) at a time, which would also lead to the quickest identification of the phrase.
This can be tweaked further but let's first see what it does as it stands (I cannot test).
Documentation also offers a few other methods which may be useful, in particular readline and streamline. As this is all rather low level there are yet other ways but if you got all else working perhaps this will be enough to complete it.
Perhaps rather index the string?
($count, $result) = $ob->read(300); # at least 300 chars coming till keyword appear
$substring = 'Booting_up';
if (index($result, $substring) != -1) {
print "send Enter ..\n";
sleep 1;
$ob->write("\r\n");
}

Merged Socket->recv with perl on Linux

Sorry for the bad English, it is not my mother tongue.
I am new to perl programming and I'm facing a tedious problem for some hours now.
I have coded a simple Client-Server using IO::Socket::INET. It works flawlessly on Windows, but is broken on Linux.
On Linux, the first recv get both of server's communication and therefor, the second one is waiting endlessly for a communication.
Running the command "perl -version" gives me this result on Windows:
This is perl 5, version 20, subversion 2 (v5.20.2) built for
MSWin32-x64-multi-t hread (with 1 registered patch, see perl -V for
more detail)
And on Linux :
This is perl 5, version 20, subversion 2 (v5.20.2) built for
x86_64-linux-gnu-thread-multi (with 42 registered patches, see perl -V
for more detail)
Here is and exemple of a server:
use IO::Socket;
my $socket= IO::Socket::INET->new( Proto => "tcp",
LocalPort => 2559,
Listen => SOMAXCONN,
Reuse => 1);
while(1)
{
print "Waiting for a client\n";
my $client = $socket->accept();
$client->send("Hello, please connect yourself");
$client->send("Username:");
$client->recv(my $username, 1024);
$client->send("Password:");
$client->recv(my $cipheredpassword, 1024);
$client->send("Thank you, Goodbye.");
$client->close();
print "Connection closed\n";
}
And here is an exemple of a client :
use IO::Socket;
use Digest::MD5 qw(md5_hex);
my $username = "";
my $password = "";
my $server = IO::Socket::INET->new( Proto => "tcp",
PeerAddr => "localhost",
PeerPort => 2559);
# Pick up both $firstServerMessage and
# $serverAskUsernameMessage on Linux
$server->recv(my $firstServerMessage, 1024);
print "$firstServerMessage\n";
# Hangs on Linux
$server->recv(my $serverAskUsernameMessage, 1024);
while($username eq "")
{
print "$serverAskUsernameMessage\n";
chomp($username = <STDIN>);
}
$server->send($username);
$server->recv(my $serverAskPasswordMessage, 1024);
while($password eq "")
{
print "$serverAskPasswordMessage\n";
chomp($password = <STDIN>);
}
my $hashedPassword = md5_hex($password);
$server->send($hashedPassword);
$server->recv(my $lastServerMessage, 1024);
print $lastServerMessage;
I know that the easy solution to this problem would be to avoid having multiple ->recv in a row, but I'm also curious to know why it is not working on Linux.
I have tried to use ->flush and ->autoflush(1), without success.
Your help and knowledge would be appreciated,
L.C.
This problem has nothing to do with the choice of operating system, or indeed the language. The problem relates to the fact of how you are using TCP.
A TCP stream is just that - a stream of bytes. It does not matter to TCP how you write those bytes - you could send in fifty 1-byte chunks, or one 50-byte, or anything inbetween. The TCP stream simply represents the bytes, without message boundaries. It's much the same as file IO - a file on disk doesn't remember the distinction between write calls, only the sum total of bytes that were transferred.
So in your server when you do
$client->send("Hello, please connect yourself");
$client->send("Username:");
It could all get merged in one segment over the wire, and will arrive in one go at the other end. This is why any TCP-based protocol provides some form of framing - be it linefeeds, or message headers that declare the size of the body, or whatever. Some way that the receiver can pull the stream apart again.
For example, you might decide to use "\n" as a message boundary. You can then send using
$client->send("Hello, please connect yourself\n");
$client->send("Username:\n");
and the receiver can use the regular readline to read these lines back out again.

Resources