Machsupport Forum

Mach Discussion => Mach4 General Discussion => Topic started by: tgirard on July 24, 2015, 05:42:29 PM

Title: Making a connection through rs232 in Mach4
Post by: tgirard on July 24, 2015, 05:42:29 PM
I posted this in another thread but wanted to bring it to the top to see if anyone could help.
We have a need to have Mach make a serial connection and send and receive serial data.

I'm new to Lua but have done a little vb developmet. I've done only a couple of things that required serial com.

I searched through as much documentation as I could find in the forums with no luck (Special thanks to Scott “Poppa Bear” Shafer and Ya-Nvr-No)
McLua mc Scripting Reference SpacedOut
McLua Work Offsets and Parameters1
Pound Var List-revised1-2
Mach4 Scripting Manual
Mach4 ENUM list for mc1
Mach4 ENUM list for mc1

I installed the "Lua for windows" package which comes with the luars232.dll. I ran the example for the luars232.dll and was able to make a connection to my com ports on my machine.

I tried to do the same thing in mach4 by placing the dll in the Mach4Hobby folder (the root folder is in the PATH). I then created a new Lua script (tim.lua) and wrote the following:
rs232 = require("luars232")

 This was the result:

Compilation successful!
Output: "C:\Mach4Hobby\LuaExamples\Tims.mcc"
Waiting for client connection...
Client connected ok.
At Breakpoint line: 1 file: C:\Mach4Hobby\LuaExamples\Tims.lua
mcLua ERROR: Lua: Error while running chunk
error loading module 'luars232' from file 'C:\Mach4Hobby\luars232.dll':
   The specified module could not be found.

stack traceback:
   [C]: in ?
   [C]: in function 'require'
   [string "C:\Mach4Hobby\LuaExamples\Tims.lua"]:1: in main chunk


mcLua ERROR: Lua: Error while running chunk

Debug session finished.

It "Seems" to want to load but gets hung up somewhere. It could definitely be pilot error on my part as I do have some coding experience but far from an expert...

A simple, documented example would be the greatest help but I am more than happy to dive in to any doc that has the answer.
Thanks
-Tim
Title: Re: Making a connection through rs232 in Mach4
Post by: BR549 on July 24, 2015, 06:49:57 PM
I had tried the same type thing with using Lua sockets and got the same results. It seems there is something MISSING in Mach4 that allows it to USE the outside functions. OR none of us know the secret handshake to make it work(;-). I could be a simple PATH problem as the Sockets are LOOKING for a Lua install . It may NOT know that Mach4 IS LUA..

I was trying to get Mach4 to be able to TEXT and email. Something Mach3 does quiet well with Cypress Basic.  The ONLY way I could get it to work was to Os.execute out and from WINDOS RUN a CB/vb script that I used with Mach3 . From there it worked.  Just a wee bit clumsy to use.

(;-) TP 
Title: Re: Making a connection through rs232 in Mach4
Post by: machiner on July 25, 2015, 08:34:07 AM
FYI

i had the exact same problem trying to run luacom.dll

mcLua ERROR: Lua: Error while running chunk
error loading module 'luacom.dll' from file 'C:\Mach4Hobby\luacom.dll':
   The specified module could not be found.

Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on May 16, 2019, 10:41:06 AM
I've successfully written scripts to communicate over RS232 using ASCII protocol (not Modbus). There is essentially no documentation on it, but the coding isn't too hard.

Start with the following code placed near the top of your Screen Load script:

--Place this code at the top of the screen load script
--Open the COM port immediately upon loading Mach 4. Com port remains open until Mach 4 closes

Code: [Select]
rs232 = require("luars232")
port_name = "COM1"
local out = io.stderr

---------------------------------------------------------------
-- Initialize Communications --
---------------------------------------------------------------

--Open Serial Port 8N1 38400 baud
local e, p = rs232.open(port_name)
if e ~= rs232.RS232_ERR_NOERROR then
mc.mcCntlSetLastError(inst, "Cannot Open Serial Port")
return
end

-- set port settings
assert(p:set_baud_rate(rs232.RS232_BAUD_38400) == rs232.RS232_ERR_NOERROR)
assert(p:set_data_bits(rs232.RS232_DATA_8) == rs232.RS232_ERR_NOERROR)
assert(p:set_parity(rs232.RS232_PARITY_NONE) == rs232.RS232_ERR_NOERROR)
assert(p:set_stop_bits(rs232.RS232_STOP_1) == rs232.RS232_ERR_NOERROR)
assert(p:set_flow_control(rs232.RS232_FLOW_OFF)  == rs232.RS232_ERR_NOERROR)

This opens a serial port on your computer and allows you to read and write.

The serial write function takes a string and transmits it over the serial port opened above. It returns an error if applicable, and the length of the string written on the port.

Code: [Select]
err, len_written = p:write(string, timeout)
The Serial Read function pulls the characters from Windows' serial buffer (default 4096 bytes/characters) which keeps track of the incoming data behind the scenes. Once you read a character from the buffer, it is gone forever so reading empties the buffer. It returns an error if applicable, the data (as a string), and the length/size of the string.

Code: [Select]
err, data_read, size = p:read(read_length, timeout)

This is a very high level overview. I'd be happy explain more if this is what you are looking for.


I should add that no special packages, libraries, dll's, or anything else need to be added for this to work. Just a clean install of a recent version of Mach 4.
Title: Re: Making a connection through rs232 in Mach4
Post by: tgirard on May 16, 2019, 12:20:54 PM
You see, if you just sit at your computer and wait long enough, the answer will arrive...

Thanks for responding with a solution mcardoso!!! Even when these threads are years old, it is soo nice to have the solution, not only for myself (I don't need it now but YOU KNOW I've got to try it) and for others who run into this.

-Tim
KN6BJG
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on May 16, 2019, 12:47:10 PM
Lol, didn't even see this was 4 years old when I replied. Glad to help. Been using the serial communications to pull data from my servo drives and it is very reliable. I have a lot more info on it if anyone needs it, but it is a niche application.
Title: Re: Making a connection through rs232 in Mach4
Post by: tgirard on November 25, 2019, 07:50:33 PM
@mcardoso,
I sent a message to you earlier today. I'd really like to get this working on my machine. I did as you said but keep getting the "Cannot Open Serial Port" Error along with three "Lua:Error while running chunkattempt to call a nil valuestack traceback". I'm not a coder for a living but I can usually figure things out with minimal guidance. If you could maybe help me just get connected I'd greatly appreciate it.
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on November 25, 2019, 08:12:39 PM
OK. Lets see here. You're missing part of your error code so I'll need to do some guessing. When LUA gives you the stack traceback errors they are a roadmap to how your code failed. Try to read and understand them as they are very valuable once you learn them. The "attempt to call a nil value" usually means you forgot to define or assign a value to something. All undefined variables have the value of nil and you can even assign nil to a variable if you would like. For example the code:

Code: [Select]
print(string)
Would create the "attempt to call a nil value" error since string doesn't exist. However, the code:

Code: [Select]
string = "Hello World"
print(string)

Would run perfectly fine.

I have recently been helping another Mach 4 user with their serial code. You might find this thread useful: https://www.machsupport.com/forum/index.php?topic=42084.0


First guess is that you did not assign a global value to "port_name" up at the top. This is the string value of the name of the COM port that windows assigns. When in doubt, open Windows->Control Panel->Device Manager to see the name of the COM port. Make sure you typed the name exactly.

Second guess is that you typed the name correctly but the port doesn't exist or couldn't be opened. I like to use a terminal emulator called PuTTY to test my serial communications. Make sure you can open a port and communicate through the terminal before you bother trying with Mach 4.

Third guess, once the port is open, it has the name "p" based on the example code above. All the methods in the serial library should be called using the name "p". For example:

Code: [Select]
p:close()
p:flush()
p:read()
p:write()

Only the function call to open the port should use "port_name".

If you post the entirety of your stack traceback then we can troubleshoot further. Please make sure to compile your code in ZeroBrane studio before trying to run it in Mach 4.

Mike
Title: Re: Making a connection through rs232 in Mach4
Post by: tgirard on November 25, 2019, 08:19:19 PM
Ah, OK,  I'll double check everything and try to get to know / understand the error messaging in Mach. I am familiar with nil and I have worked with Putty so I'll review and try to figure it out. I'll also review the link in your response as well. I was just parsing through other posts you've made earlier regarding RS232.  If I could just get connected the rest will fall into place :)

THANK YOU SOOO MUCH for taking your time tonight and responding. It's rare... But really appreciated!
Title: Re: Making a connection through rs232 in Mach4
Post by: tgirard on November 25, 2019, 08:48:43 PM
Hi Mike,
So I did review everything and double checeked that I was using the right port and speed with putty. I took some screenshots just so you could see.
I also tend to use a button and try to test out code so I took a screen shot of what I was doing there.
I did shut off Putty and rebooted Mach just to be safe. I built a simple app on the Arduino to test with (See the picture)

Did I miss something
-Tim
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on November 25, 2019, 08:59:51 PM
Happy to help!

Looking great so far. Can you post a screenshot of your windows device manager showing COM4?

Also what version of Mach 4 are you using?

ALSO, I forgot. Can you post the entire LUA error message if you haven't fixed that yet?

Mike
Title: Re: Making a connection through rs232 in Mach4
Post by: tgirard on November 25, 2019, 09:06:06 PM
Attached.
any thoughts of what I might try next?
Also, If I wanted to change the baud rate or parity etc, I would just update the assert Statements right?

ex:
assert(p:set_baud_rate(rs232.RS232_BAUD_9600) == rs232.RS232_ERR_NOERROR)
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on November 25, 2019, 09:13:44 PM
OK, two thoughts here.

First, 3804 is a little old for Mach 4. Couldn't hurt to install the latest. Maybe something has changed since then? Probably not, but thinking out loud.

Second (and maybe do this first), the returned error code "e" is a numerical value. It is 0 when there is no error and something else that we can use to troubleshoot the error.

Let'd modify your button code to say the following

Code: [Select]
mc.mcCntlSetLastError(inst, "Cannot Open Serial Port. Error: "..tostring(e))
That should print the error code number in the window and we can go from there.
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on November 25, 2019, 09:29:58 PM
Preemptively, here are the error codes, extracted from the LUA source.

0=    { "RS232_ERR_NOERROR", RS232_ERR_NOERROR },
1=   { "RS232_ERR_UNKNOWN", RS232_ERR_UNKNOWN },
2=   { "RS232_ERR_OPEN", RS232_ERR_OPEN },
3=   { "RS232_ERR_CLOSE", RS232_ERR_CLOSE },
4=   { "RS232_ERR_FLUSH", RS232_ERR_FLUSH },
5=   { "RS232_ERR_CONFIG", RS232_ERR_CONFIG },
6=   { "RS232_ERR_READ", RS232_ERR_READ },
7=   { "RS232_ERR_WRITE", RS232_ERR_WRITE },
8=   { "RS232_ERR_SELECT", RS232_ERR_SELECT },
9=   { "RS232_ERR_TIMEOUT", RS232_ERR_TIMEOUT },
10=   { "RS232_ERR_IOCTL", RS232_ERR_IOCTL },
11=   { "RS232_ERR_PORT_CLOSED", RS232_ERR_PORT_CLOSED },
12=   { "RS232_ERR_BREAK", RS232_ERR_BREAK },
13=   { "RS232_ERR_FRAME", RS232_ERR_FRAME },
14=   { "RS232_ERR_PARITY", RS232_ERR_PARITY },
15=   { "RS232_ERR_RXOVERFLOW", RS232_ERR_RXOVERFLOW },
16=   { "RS232_ERR_OVERRUN", RS232_ERR_OVERRUN },

I entered the numbers by hand, but I'm pretty sure they are right.

Title: Re: Making a connection through rs232 in Mach4
Post by: tgirard on November 25, 2019, 09:33:10 PM
OK, Updated to Build 4300 and modified the error statement per your suggestion

It's spitting an error 2
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on November 25, 2019, 09:38:42 PM
OK, So it is getting an error opening the port. Hmmmmm. Is this a USB port or a hardware RS232 port?

I don't see why you couldn't use a USB port or a USB->RS232 converter, however the PC I am using had a RS232 port embedded already so I stuck to using that.

It seems like you're doing everything right? I'm a bit stumped, but now very invested in getting this working!

I know you posted about it earlier, but can you double check that you don't have PuTTY or anything else open that might be using the serial port? I usually get this error when I leave my servo configuration software open and try to use Mach.
Title: Re: Making a connection through rs232 in Mach4
Post by: tgirard on November 25, 2019, 09:40:40 PM
Wait wait.
So, I clicked the button once and it didn't error. Like a fool I clicked it a second time and got the error... LOL
I think we're good.
 
I moved the code to the Screen load script and it "seems" to be working...?
Can you give me a code example of receiving data? I'm still misunderstanding how the luars232 works and how the letter "p" is defined and used.

thanks Mike!!!!
-Tim
Title: Re: Making a connection through rs232 in Mach4
Post by: tgirard on November 25, 2019, 09:44:19 PM
When I just tried to connect with Putty It got blocked!!!
I think we're golden

I just don't understand the "p" portion of the code you wrote,
Did you encapsulate the "p" functions  into a Lua Funtion inside the SheetLoad Script to use elsewhere?

I'm inches from the finish line here :)
-Tim
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on November 25, 2019, 09:47:40 PM
Oh yeah:)

Once the port is open you aren't allowed to open it again!

Try a button on the screen with the code

Code: [Select]
p:write("Hello Arduino!\r")
Title: Re: Making a connection through rs232 in Mach4
Post by: tgirard on November 25, 2019, 10:06:34 PM
Nothing, But I don't have anything setup on the Arduino side to see and I'm not reading the buffer on that side (Nothing errored in Mach BTW)

Would you mind maybe giving me an example that reads "Hello World" from the buffer and Spits it out to a label "testLabel"
I tried this in the button with no luck (Lua Chunk error)
e = p:flush()
scr.SetProperty("testLabel","Label",p:read(12, 5))

Sooo close...
thanks -Tim
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on November 25, 2019, 10:09:06 PM
I am not an expert in programming by any stretch so this is likely a super butchered explanation...

When you call the line to open the port, you assign a pointer or address of the port to the variable "p"

From then on, your functions will use the name of the port "p" (which you can set to anything you want BTW) and call functions from the luars232 library.

As I understand... p:write() is shorthand for p.luars232.write() or something like that. It is an abbreviated form of a function call.

Once I got all my serial code working, I wrapped it up and put it in a module. I found this helpful to help keep my code organized and keep the screen load script from getting filled up with too much junk.

Signing off for the night. Check in tomorrow to see where you got.

Nice Work.

-Mike
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on November 26, 2019, 12:10:53 PM
Lets see if I can give you some examples. I'm not familiar with labels, but I can use the error bar. I'm typing this straight into the forum so please excuse and correct any syntax errors.

Example 1: Read exactly 10 characters from the serial port and print them to the error bar on screen. This assumes the port is open.

Code: [Select]
inst = mc.mcCntlGetInstance()
local string = ""  --Empty string
local len = 0

e, string, len = p:read(10,5) --Read 10 characters, save to "string", 5ms timeout, string length saved in "len"
if (e~= 0) then
    mc.mcCntlSetLastError(inst, "Read Failed, Error: "..tostring(e))
else
    mc.mcCntlSetLastError(inst, tostring(len).." characters read. Data is: "..string)
end

In this example, we want to read 10 characters. If this happens without error, we print the string length (returned from the function call) and the actual data received (not that termination characters count to the length but are not visible). If an error occurs, we print an error message with the error code.
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on November 26, 2019, 12:19:04 PM
Example 2: Read the entire buffer character by character, stop when empty. Data is concatenated into a string at the end of each loop.

Code: [Select]
inst = mc.mcCntlGetInstance()
local string = ""
local dat = ""
local len = 0

for i=0,4095,1 do
    e, dat = p:read(1,5) --1 character, 5ms timeout
    if  (e ~= 0 and e~=9) then
        mc.mcCntlSetLastError(inst, "Read Failed, Error: "..tostring(e))
    elseif (e==9) then
        mc.mcCntlSetLastError(inst, "Buffer Empty")
        break --Exit the for loop
    else
        string = string..dat
    end
end
mc.mcCntlSetLastError(inst, string.len(string).." characters received. Data is: "..string)

Here we read until getting an error 9 (Timeout) which indicates that data was not found before the timeout value expired. Timeout is a great way to handle real world latencies in network traffic.
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on November 26, 2019, 12:27:31 PM
Pretty sure I know where your error is in this code:

Code: [Select]
scr.SetProperty("testLabel","Label",p:read(12, 5))
LUA allows you to do multiple returns from a function. This means when the function exits, it can have a statement like:

Code: [Select]
myfunction()
...
return a,b,c
end

When you call this function you might do something like this:

Code: [Select]
x, y, z = myfunction()
Where x gets the value returned by "a", y the value for "b", and z the value for "c". You don't need to use all 3, however they always return in this order.

The LUA serial function read() returns 3 variables as shown below:

Code: [Select]
e, data_read, size = p:read(length, timeout)
"e" is the error code, "data_read" is the data, and "size" is the length of the returned string.

When you called this function inside another function, you only grabbed the first returned value, in this case the error code not the data you wanted.

To make this work, you'll need to do something like this:

Code: [Select]
e, string = p:read(12, 5)
scr.SetProperty("testLabel","Label",string)

Note that in this case I captured the first return "e" but did nothing with it and I didn't even bother to capture the 3rd return "size".
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on November 26, 2019, 12:35:45 PM
I think I can better phrase the answer I gave earlier about the use of the colon in the serial library.

The serial library defines the "port" as a class (as in a datatype in an object oriented programming language). When we open the port, we are also declaring that "p" is a member of the class "port". This class has methods (actions) which it can do. In our case, these are:

-close
-flush
-read
-write

To call a method (action) of a class, you can use the colon. So p:write() is the way to tell "p", member of class "port", that it should write(). This is shorthand and you could still call it normally like a function.

This is explained here in the LUA reference: https://www.lua.org/pil/16.html
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on November 26, 2019, 12:42:26 PM
Example 3: Flushing the buffer

Flushing the buffer is super simple and all it does is delete the contents of the buffer without reading them.

Lets say you open your serial port and sit for a few minutes. The whole time your Arduino has been writing "Hello World" once per second. Obviously your buffer will be very full. Lets say we want to read one line of "Hello World" and we want it to be the most recent one.

Code: [Select]
inst = mc.mcCntlGetInstance()
string = ""

p:flush()
e, string(12, 1200) --Read all 12 characters of "Hello World\r" waiting just over 1 second for them to arrive
mc.mcCntlSetLastError(inst, string)

In this code, we make sure the buffer is completely empty before we read. We allow 1.2 seconds for the data to arrive since this will capture one and only one packet from the Arduino.
Title: Re: Making a connection through rs232 in Mach4
Post by: tgirard on November 26, 2019, 08:20:16 PM
YYYYYESSSS!!!!!
So this works awesome!
I was able to read from the Arduino using the examples. I'll start setting things up to Read / Write and see how things behave. I want to also do some testing with speeds and error correction but, for now, I'm just getting the basics going.

Thank you again Mike, I really think once this gets out a little more, people will start to see just how simple / powerful this is.

More to come
-Tim
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on November 26, 2019, 08:47:49 PM
Glad it is working. Awesome job!

Sounds perfect. Something you might want to consider is adding a checksum to the end of each transmission. Something quick to calculate but unlikely to be generated by bad input data.

If you're controlling both ends of the transmission then this is great. My servo drives require this with their transmissions so I was forced to develop it. Here is that code which does some math to create a two character checksum to be appended onto the end of a string you want to transmit. On the receiving end, you take the received string, remove the last 3 characters (checksum plus carriage return), and run the same checksum code. If the string matches the string you received, then you know it is highly likely that the transmission came through correctly.

Code: [Select]

function AppendChecksum(str)
local ccSum = 0
local len = string.len(str)

for i=1,len,1 do
ccSum = ccSum + str:byte(i)
end

local output = string.format("%X", bit32.band(256-ccSum))
local checksum =  string.sub(output,-2,-1)

return str..checksum.."\r"
end


This is just one example of accomplishing this.

Feel free to keep asking questions and please share any tips that you come across for other who might want to try serial.

You've made it over the hill! Now is the fun part!
Title: Re: Making a connection through rs232 in Mach4
Post by: brandonb on November 30, 2019, 04:08:47 PM
Well done guys.

I am trying to accomplish the same. It seems that you aren't using the Serial Plugin that ships with Mach4, are you? Have anybody tried using it? I cannot find any documentation about it and I could imagine using the official plugin might be more future proof(?)

Best regards

Brandon
Title: Re: Making a connection through rs232 in Mach4
Post by: tgirard on November 30, 2019, 07:35:28 PM
@brandon. Hey Brandon. That is an old module that is there for other things. I read about it here somewhere. I shut it off all together. It's not what you need.

FYI, I have been able to build out a solid communications channel using an Arduino Uno. This has two way coms and I currently have a Jogging encoder connected which is updating a DRO. Step two is to send the DRO value back to the Arduino so that I see it on a screen.

I still need to flush this all out and then post here so that -real- developers might be able to take what I have and make it better / more bullet proof. I am going to completely document the whole process to build a serial Pendant using an Arduino.

Mike Cardoso, here in this thread, has been a tremendous help I really need to give most of the credit to him (I definitely will when I publish) but I'm trying not to bother him until I'm ready.
Title: Re: Making a connection through rs232 in Mach4
Post by: mcardoso on November 30, 2019, 08:38:49 PM
Tim,

It is no bother at all. I was actually having a blast working through your communications, so please keep posting if you have anything interesting!

Mike
Title: Re: Making a connection through rs232 in Mach4
Post by: brandonb on November 30, 2019, 08:53:51 PM
Agreed. Thank you very much for sharing your work. I'm definitely looking forward to it.

After all, information on Mach4 in this matter is still scarce. Collecting all the bits of knowledge around to forum to create a nice serial add-on is tedious. A nice guide/tutorial would definitly improve the accessability of Mach4 in general.

Best regards

Brandon