Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

250k Baud communication #15

Closed
salzig opened this issue Mar 11, 2017 · 12 comments
Closed

250k Baud communication #15

salzig opened this issue Mar 11, 2017 · 12 comments

Comments

@salzig
Copy link
Contributor

salzig commented Mar 11, 2017

Hej there,
i try to communicate with a Chinese Arduino Mega 2560 (250000 Baud).

As a precheck i used the arduino IDE serial monitor and sending "G28\r\n", which worked.

After that i tried to play around with nerves_uart in a iex session.

iex(1)> Nerves.UART.enumerate
%{"/dev/cu.Bluetooth-Incoming-Port" => %{},
  "/dev/cu.JabraREVOa400-SPPDev" => %{},
  "/dev/cu.SLAB_USBtoUART" => %{manufacturer: "Silicon Labs", product_id: 60000,
    serial_number: "0001", vendor_id: 4292}}
iex(2)> {:ok, pid} = Nerves.UART.start_link
{:ok, #PID<0.140.0>}
iex(3)> Nerves.UART.open(pid, "/dev/cu.SLAB_USBtoUART", speed: 250000, active: true)
:ok
iex(4)> Nerves.UART.write(pid, "G28\r\n")
:ok

Which doesn't result in what i was expecting. Did i miss something?

Something i observed: starting the arduino serial monitor will reset the mega2560. But it should work none the less right?

Edit: On first glimpse it looks like the provided nerves uart c-code handles custom baud rates. But i couldn't find any example using it. Maybe it's not working?

@salzig
Copy link
Contributor Author

salzig commented Mar 12, 2017

i did some more investigation.

To reset the arduino i need to toggle Nerves.UART.set_dtr(pid, boolean) twice (1. false, 2. true).

iex(1)> {:ok, pid} = Nerves.UART.start_link
{:ok, #PID<0.139.0>}
iex(2)> Nerves.UART.enumerate
%{"/dev/cu.Bluetooth-Incoming-Port" => %{},
  "/dev/cu.JabraREVOa400-SPPDev" => %{},
  "/dev/cu.SLAB_USBtoUART" => %{manufacturer: "Silicon Labs", product_id: 60000,
    serial_number: "0001", vendor_id: 4292}}
iex(3)> Nerves.UART.open(pid, "/dev/cu.SLAB_USBtoUART", active: false, speed: 250000)
:ok
iex(4)> Nerves.UART.set_dtr(pid, false) 
:ok
iex(5)> Nerves.UART.set_dtr(pid, true)
:ok
iex(6)> { :ok, message } = Nerves.UART.read(pid) 
{:ok,
 <<87, 161, 171, 15, 206, 233, 146, 126, 105, 57, 141, 172, 233, 207, 104, 10,
   243, 96, 107, 199, 173, 144, 164, 175, 165, 41, 133, 40, 61, 179, 157, 185,
   33, 172, 21, 161, 80, 253, 237>>}
iex(7)> String.codepoints(message)
["W", <<161>>, <<171>>, <<15>>, <<206>>, <<233>>, <<146>>, "~", "i", "9",
 <<141>>, <<172>>, <<233>>, <<207>>, "h", "\n", <<243>>, "`", "k", "ǭ",
 <<144>>, <<164>>, <<175>>, <<165>>, ")", <<133>>, "(", "=", <<179>>, <<157>>,
 <<185>>, "!", <<172>>, <<21>>, <<161>>, "P", <<253>>, <<237>>]

now i can see all message written by arduino on boot, but it seems like the used baud rate isn't right. I checked again with the arduino IDE, everything works fine with 250k baud there.

edit: I expect to get around 906chars, but read returns only 36.
906/36 = 25.16
so i get 1/25 number of letters as expected. Which could mean that the serialdevice is used with around 10k baud (250k/25 = 10k), maybe 9600baud.

@fhunleth
Copy link
Contributor

I suspect that the custom baud rate code isn't quite right:

https://github.com/nerves-project/nerves_uart/blob/master/src/uart_comm_unix.c#L185

I hardly ever use custom baud rates and even less so on OSX. If you're not afraid to dive into the C code, I might try hardcoding IOSSIOSPEED ioctl after calls to tcsetattr. Maybe tcsetattr is overriding the custom baud rate.

@salzig
Copy link
Contributor Author

salzig commented Mar 14, 2017

Thanks for the hint. I just checked if the call to set_custom_speed did was i was expecting. Which is does.
Right now i'm not really sure if it's maybe a driver problem, after also having a look into the Code of https://developer.apple.com/library/content/samplecode/SerialPortSample/Introduction/Intro.html
Which mentions: "The driver for the underlying serial hardware ultimately determines which baud rates can be used.".

In the end, it works with Arduino on this machine. I'll try to fix the c code, if i can.

@salzig
Copy link
Contributor Author

salzig commented Mar 14, 2017

Ok, i found two variants that work out.

First Variant: set the speed in options to the configured custom baudrate.

@@ -250,9 +250,12 @@ static int uart_config_line(int fd, const struct uart_config *config)
         // Linux lets you set custom baudrates for the B38400 option
         cfsetispeed(&options, B38400);
         cfsetospeed(&options, B38400);
+#elif defined(__APPLE__)
+        cfsetspeed(&options, config->speed);
 #endif

Second Variant: set speed via ioctl after tcsetattr

@@ -305,7 +306,10 @@ static int uart_config_line(int fd, const struct uart_config *config)
     options.c_cc[VTIME] = 0;
 
     // Set everything
-    return tcsetattr(fd, TCSANOW, &options);
+    int result = tcsetattr(fd, TCSANOW, &options);
+    speed_t sio_speed = config->speed;
+    ioctl(fd, IOSSIOSPEED, &sio_speed);
+    return result;

Which version would you prefer @fhunleth? Or do you have any feedback? Would love to create a (pull|merge)-request afterwards.

@salzig
Copy link
Contributor Author

salzig commented Mar 14, 2017

as a side note, in both variants passive mode seems not to work anymore, independent of configuration passed to UART.open it will always end in active mode.

@fhunleth
Copy link
Contributor

Awesome! (Well except for the passive mode issue - that's weird)

Code-wise I like the cfsetspeed option (first one), but I was surprised that it worked since the parameter is an enumerated type. On Linux, the enumerated types don't match the baud rates at all, but I saw that on OSX they do. I wonder if you change the to_baudrate_constant() function on __APPLE__ to just return speed;. Maybe that would fix everything since your baud rate would no longer be custom?

My concern is that this is serial driver-specific and maybe both cfsetspeed and the ioctl(fd, IOSSIOSPEED) are needed. However, I'm good for merging a PR if your setup is fixed and then if someone else runs into the issue, we can experiment with their setup.

@salzig
Copy link
Contributor Author

salzig commented Mar 15, 2017

I still need to find a bug.

iex(1)> {:ok, pid} = Nerves.UART.start_link
{:ok, #PID<0.139.0>}
iex(2)> Nerves.UART.open(pid, "/dev/cu.SLAB_USBtoUART", speed: 250000, active: true, framing: {Nerves.UART.Framing.Line, separator: "\n"}) 
{:error, :einval}
iex(3)> Nerves.UART.set_dtr(pid, false)
:ok
iex(4)> Nerves.UART.set_dtr(pid, true)
:ok
iex(5)> :timer.sleep(1000);
:ok
iex(6)> Nerves.UART.write(pid, "G28")
:ok
iex(7)> Nerves.UART.write(pid, "G1 Z0 F10000")
:ok
iex(8)> Nerves.UART.write(pid, "G28")
:ok
iex(9)> :timer.sleep(5000);
:ok
iex(10)> flush
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "start"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:Marlin1.0.0"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo: Last Updated: Feb 28 2017 20:26:44 | Author: (Leafy)"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "Compiled: Feb 28 2017"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo: Free Memory: 3634  PlannerBufferBytes: 1232"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo:Hardcoded Default Settings Loaded"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:Steps per unit:"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo:  M92 X80.00 Y80.00 Z80.00 E96.00"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:Maximum feedrates (mm/s):"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo:  M203 X200.00 Y200.00 Z200.00 E200.00"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:Maximum Acceleration (mm/s2):"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:  M201 X3000 Y3000 Z3000 E3000"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo:Acceleration: S=acceleration, T=retract acceleration"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:  M204 S3000.00 T3000.00"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo:Advanced variables: S=Min feedrate (mm/s), T=Min travel feedrate (mm/s), B=minimum segment time (ms), X=maximum XY jerk (mm/s),  Z=maximum Z jerk (mm/s),  E=maximum E jerk (mm/s)"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo:  M205 S0.00 T0.00 B20000 X20.00 Z20.00 E20.00"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:Home offset (mm):"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:  M206 X0.00 Y0.00 Z0.00"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:Endstop adjustement (mm):"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:  M666 X0.00 Y0.00 Z0.00"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:PID settings:"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:   M301 P22.20 I1.08 D114.00"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "ok"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:SD card ok"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "ok"}
:ok

as you can see, i get an {:error, :einval} for UART.open, but it works nonetheless. O.o

@fhunleth
Copy link
Contributor

When this is figured out, I'll make a new nerves_uart release. I might have some time this weekend to try 250KBaud out on my Mac, but I'm really hoping that you figure it out first! ;-)

@GregMefford GregMefford added this to Triage in Nerves Radar Mar 16, 2017
@GregMefford GregMefford moved this from Triage to In Progress in Nerves Radar Mar 17, 2017
@salzig
Copy link
Contributor Author

salzig commented Mar 17, 2017

So far i didn't manage to figure out how to solve einval, sadly.

My C-FU isn't that good, as it seems.

@fhunleth
Copy link
Contributor

Hey - I programmed an Arduino Uno to output at 250000 baud and connected it to my Mac. I think that I have something that may work for you. Could you try out my custom-speed branch? Both active and passive mode work for me. I didn't try sending data to the Uno, so I can't say that it's fully tested. The caveat is that changing baudrate, flow control, etc. after the call to open/3 will probably fail since the SIOSSIOSPEED ioctl seems to totally mess up tcsetattr. I moved the ioctl to the end of function that opening the port succeeds. Not many people change the port settings after open, so hopefully this fix satisfies your use and the majority of others using custom speeds on OSX.

@salzig
Copy link
Contributor Author

salzig commented Mar 18, 2017

Yeah, works and without the einval. Awesome!

active:

iex(1)> {:ok, pid} = Nerves.UART.start_link
{:ok, #PID<0.139.0>}
iex(2)> Nerves.UART.open(pid, "/dev/cu.SLAB_USBtoUART", speed: 250000, active: true, framing: {Nerves.UART.Framing.Line, separator: "\n"}) 
:ok
iex(3)> Nerves.UART.set_dtr(pid, false)
:ok
iex(4)> Nerves.UART.set_dtr(pid, true)
:ok
iex(5)> :timer.sleep(1000);
:ok
iex(6)> Nerves.UART.write(pid, "G28")
:ok
iex(7)> Nerves.UART.write(pid, "G1 Z0 F10000")
:ok
iex(8)> Nerves.UART.write(pid, "G28")
:ok
iex(9)> :timer.sleep(5000);
:ok
iex(10)> flush
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "start"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:Marlin1.0.0"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo: Last Updated: Feb 28 2017 20:26:44 | Author: (Leafy)"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "Compiled: Feb 28 2017"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo: Free Memory: 3634  PlannerBufferBytes: 1232"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo:Hardcoded Default Settings Loaded"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:Steps per unit:"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo:  M92 X80.00 Y80.00 Z80.00 E96.00"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:Maximum feedrates (mm/s):"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo:  M203 X200.00 Y200.00 Z200.00 E200.00"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:Maximum Acceleration (mm/s2):"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:  M201 X3000 Y3000 Z3000 E3000"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo:Acceleration: S=acceleration, T=retract acceleration"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:  M204 S3000.00 T3000.00"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo:Advanced variables: S=Min feedrate (mm/s), T=Min travel feedrate (mm/s), B=minimum segment time (ms), X=maximum XY jerk (mm/s),  Z=maximum Z jerk (mm/s),  E=maximum E jerk (mm/s)"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART",
 "echo:  M205 S0.00 T0.00 B20000 X20.00 Z20.00 E20.00"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:Home offset (mm):"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:  M206 X0.00 Y0.00 Z0.00"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:Endstop adjustement (mm):"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:  M666 X0.00 Y0.00 Z0.00"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:PID settings:"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:   M301 P22.20 I1.08 D114.00"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "ok"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "echo:SD card ok"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "ok"}
{:nerves_uart, "/dev/cu.SLAB_USBtoUART", "ok"}
:ok

passive:

iex(2)> {:ok, pid} = Nerves.UART.start_link
{:ok, #PID<0.140.0>}
iex(3)> Nerves.UART.open(pid, "/dev/cu.SLAB_USBtoUART", speed: 250000, active: false) 
:ok
iex(4)> Nerves.UART.set_dtr(pid, false)
:ok
iex(5)> Nerves.UART.set_dtr(pid, true)
:ok
iex(6)> :timer.sleep(3000);
:ok
iex(7)> Nerves.UART.read(pid)
{:ok,
 "start\necho:Marlin1.0.0\necho: Last Updated: Feb 28 2017 20:26:44 | Author: (Leafy)\nCompiled: Feb 28 2017\necho: Free Memory: 3634  PlannerBufferBytes: 1232\necho:Hardcoded Default Settings Loaded\necho:Steps per unit:\necho:  M92 X80.00 Y80.00 Z80.00 E96.00\necho:Maximum feedrates (mm/s):\necho:  M203 X200.00 Y200.00 Z200.00 E200.00\necho:Maximum Acceleration (mm/s2):\necho:  M201 X3000 Y3000 Z3000 E3000\necho:Acceleration: S=acceleration, T=retract acceleration\necho:  M204 S3000.00 T3000.00\necho:Advanced variables: S=Min feedrate (mm/s), T=Min travel feedrate (mm/s), B=minimum segment time (ms), X=maximum XY jerk (mm/s),  Z=maximum Z jerk (mm/s),  E=maximum E jerk (mm/s)\necho:  M205 S0.00 T0.00 B20000 X20.00 Z20.00 E20.00\necho:Home offset (mm):\necho:  M206 X0.00 Y0.00 Z0.00\necho:Endstop adjustement (mm):\necho:  M666 X0.00 Y0.00 Z0.00\necho:PID settings:\necho:   M301 P22.20 I1.08 D114.00\necho:SD card ok\n"}
iex(8)> Nerves.UART.write(pid, "G28\n")
:ok
iex(9)> Nerves.UART.read(pid)
{:ok, "ok\n"}
iex(10)> Nerves.UART.write(pid, "G1 Z0 F10000\n")
:ok
iex(11)> Nerves.UART.read(pid)
{:ok, "ok\n"}
iex(12)> Nerves.UART.write(pid, "G28\n")
:ok
iex(13)> Nerves.UART.read(pid)
{:ok, ""}

@fhunleth
Copy link
Contributor

Thanks for verifying! I'm going to merge and push out an updated release.

@GregMefford GregMefford moved this from In Progress to Closed in Nerves Radar Apr 6, 2017
@GregMefford GregMefford removed this from Closed in Nerves Radar May 6, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants