My question is largely a clarification question, I am trying to communicate through a serial port in a bit bang sort of sense. Whereas I want to output out of the serial port exactly what I write to it..
If I write 0-0-0-1, I would want the output on the wire to be 0-0-0-1.
The reason I ask this question is because it seems like using the DCB object to configure the port, I have to set all sorts of settings like Stop Bits and Parity?
Is there a way to configure this port as 'raw' as possible? I don't want anything being sent that I don't send.. if that makes sense..
//Open the serial port
hComm = CreateFile( "\\\\.\\COM1", // Name of the Port to be Opened
GENERIC_WRITE, // Write Access
0, // No Sharing, ports cant be shared
NULL, // No Security
OPEN_EXISTING, // Open existing port only
0, // Non Overlapped I/O
NULL); // Null for Comm Devices
DCB dcbSerialParams = { 0 }; // Initializing DCB structure
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
//retreives the current settings
Status = GetCommState(hComm, &dcbSerialParams);
if(!Status){
//Error in GetCommState!
}
dcbSerialParams.BaudRate = 10000; // Setting BaudRate = 10000
dcbSerialParams.ByteSize = 8; // Setting ByteSize = 8
dcbSerialParams.StopBits = ONESTOPBIT; // Setting StopBits = 1
dcbSerialParams.Parity = ODDPARITY;
I wasn't quite sure from the question article what you were having problems with.
Are you worried about the bit order of the MSB/LSB or the endianness of 4-byte integers? Or will data be added or deleted or changed by operating as a terminal?
Please add the exact content.
Bit order is a hardware standard specification issue so you don't have to worry about it.
The endian problem seems to be lacking in your understanding.
Serial port communication is basically byte-by-byte communication.
You need to set the data in a byte array and send it, and the received data is a byte array.
It is a problem on the application program side to give them meanings such as 4-byte integers and single/double precision floating point numbers.
Then, looking at your source code, it seems that you are calling the Windows Win32 API directly.
Communications Functions
The Win32 API and device drivers only have raw mode.
Unlike Unix tty devices, it doesn't have any features like character completion/conversion/newline waiting buffering or control code equivalents. (Unless you explicitly specify XON / XOFF flow control)
On Windows, those features aren't APIs, but libraries/framworks that run on them.
Basically, each setting such as DCB and timeout must be done according to the specifications of the device with which you are communicating. Even if you say "all settings", the number of items will fit on one page.
DCB structure (winbase.h)
Related
I have a /dev/ttyUSB device and a /dev/ttyMFD device that I need to stream to logfiles. For the USB device I could use termios and configure it through that. This was pretty straight forward and there was a bit of documentation for this as well.
I can't seem to find any for the MFD though. Some places call it a MultiFuctionDevice and others call it the Medfield High Speed UART device.
Which is correct in the first place?
And secondly, can I open it in the same way that I open up a regular ttyUSB device?
Here is the snippit I use to open USB devices.
int fd = open(USBDEVICE0, O_RDWR);
struct termios io;
memset(&io, 0, sizeof(io));
io.c_iflag = 0;
io.c_oflag = 0;
io.c_cflag = CS8|CREAD|CLOCAL; // 8n1, see termios.h for more information
io.c_lflag = 0;
// TODO -- Since we are operating in non-blocking mode; confirm VMIN and VTIME settings have no effect on duration of the read() call.
io.c_cc[VMIN] = 1;
io.c_cc[VTIME] = 5;
speed_t speedSymbol = B921600;
cfsetospeed(&io, speedSymbol);
cfsetispeed(&io, speedSymbol);
int retVal;
retVal = tcsetattr(fd, TCSANOW, &io);
tcflush(fd, TCIOFLUSH);
usleep(100);
EDIT
For anyone who comes across this, there is one caveat. You must open the device in raw mode and dump everything into a log file. Parsing must be done post. Everything will come out as raw data but if you try to do any kind of configuration, the devices buffer will not be able to capture all the data, hold it, and process it in time before more data comes along.
MFD in Linux kernel is common abbreviation to Multi-Functional Device, Legacy serial driver for Edison abuses that and uses it's own interpretation as you mentioned: Medfield. In the upstream kernel the abbreviation MID is used regarding to Mobile Internet Device. In particular the serial driver is called drivers/tty/serial/8250_mid.c. See https://en.wikipedia.org/wiki/Mobile_Internet_device.
Yes, you may do the same operations as you did on top of /dev/ttyUSBx.
I have the following code:
QSerialPort arduPort("COM5");
arduPort.setBaudRate(QSerialPort::Baud9600);
arduPort.setDataBits(QSerialPort::Data8);
arduPort.setParity(QSerialPort::NoParity);
arduPort.setStopBits(QSerialPort::OneStop);
arduPort.setFlowControl(QSerialPort::NoFlowControl);
arduPort.open(QSerialPort::ReadWrite);
cout<<arduPort.isReadable()<<endl;
cout<<arduPort.isWritable()<<endl;
arduPort.write("a");
QByteArray s=arduPort.readAll();
cout<<QString(s).toStdString()<<endl;
And the next code in Arduino:
int inByte = 0;
void setup()
{
Serial.begin(9600);
while(!Serial){;}
int i=0;
}
void loop()
{
if(Serial.read()=='a')
Serial.write('b');
}
First I send an 'a' to the Arduino, and the ARduino must respond with 'b'. But when I read the port of the Arduino, I recieve '' only.
Anyone knows why I recieve '' instead of 'b'? Thanks for your time.
Update: See bottom of this answer for the answer. TL;DR: You have so set the baud rate (and presumably all the other settings) after you open the port.
I believe this is a bug in the Windows implementation of QSerialPort. I haven't been able to narrow down the cause yet but I have the following symptoms:
Load the Arduino (Uno in my case; Leonardo may behave very differently) with the ASCII demo. Unplug and replug the Arduino. Note that the TX light doesn't come on.
Connect to it with Putty or the Arduino serial port monitor. This resets the Arduino and then prints the ASCII table. The TX light is on continuously as expected.
Unplug/replug the Arduino and this time connect to it with a QSerialPort program. This time despite the port being opened ok the TX light never comes on and readyRead() is never triggered. Also note that the Arduino is not reset because by default QSerialPort does not change DTR. If you do QSerialPort::setDataTerminalReady(false); then pause for 10ms then set it true it will reset the Arduino as expected but it still doesn't transmit.
Note that if you have an Arduino program that transmits data continuously (ASCII example stops), if you open the port with putty so that it starts transmitting and then open it with QSerialPort without unplugging the cable it will work! However as soon as you unplug/plug the cable it stops working again.
This makes me suspect that putty is setting some serial port option that is required by the arduino and reset when you replug the cable. QSerialPort obviously doesn't change this value.
Here are the settings used by Putty as far as I can tell:
dcb.fBinary = TRUE;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fDsrSensitivity = FALSE;
dcb.fTXContinueOnXoff = FALSE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fErrorChar = FALSE;
dcb.fNull = FALSE;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.fAbortOnError = FALSE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.BaudRate = ...;
dcb.ByteSize = ...;
And by QSerialPort:
dcb.fBinary = TRUE;
dcb.fDtrControl = unchanged!
dcb.fDsrSensitivity = unchanged!
dcb.fTXContinueOnXoff = unchanged!
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fErrorChar = FALSE;
dcb.fNull = FALSE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fAbortOnError = FALSE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = unchanged!
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.BaudRate = ...;
dcb.ByteSize = ...;
So I think it must be one of those unchanged values which makes the Arduino think that it isn't connected. From the DCB documentation I suspect fTxContinueOnXoff.
Ok I am going to write a little program to read these settings and see what changes.
Update 1
Ok I wrote my program and made the following discovery. The differences after running putty and just my Qt program were:
BaudRate: THIS WASN'T SET BY QT!!!!!!! It turns out you can only set the baud rate after you open the port.. Otherwise it is left at the previous value which is 0 when you first plug in the cable.
fDtrControl: Set to 1 by Putty, left at 0 by Qt.
fOutX and fInX: Both also set to 1 by Putty and left at 0 by Qt.
After moving all my set...() function calls after the open it worked perfectly. I didn't have to fiddle with DtrControl or Out/InX. (Although I have also set DTR high manually.)
Update 2
While setting all the parameters I thought it would be a good idea to set the error policy to 'skip'. DON'T DO THIS! LEAVE IT ON IGNORE! Otherwise it fucks everything up and adds weird delays to all your communications.
Setting the ports before open is not quite possible before Qt 5.2. The reason is that the original design was a bit too low-level for the class rather properly object oriented. I had been considered for a long-time whether to change it and in the end I actually decided to do so.
I have just submitted a change that is now under code review that will make your original concept work, too.
Here you can find the details:
Make it possible to set the port values before opening
The summary can be read here for the change:
Make it possible to set the port values before opening
This patch also changes the behavior of the open method. We do not use port
detection anymore for good. It has been a broken concept, and it is very
unlikely that anyone has ever relied on it. If anyone had done that, they would
be in trouble anyway, getting noisy warnings needlessly and all that.
The other option was also considered to keep this behavior optionally as the
default, but that would complicate the API too much without not much gain.
The default port settings are now also the sane 9600,8,N,1, and no flow control.
Also please note that the serial port is closed automatically in the open method
if any of the settings fails.
Please update your Qt version (at least to Qt 5.2) or you can backport the change yourself. Then, it is possible to write this code and actually it is even recommended:
QSerialPort arduPort("COM5");
arduPort.setBaudRate(QSerialPort::Baud9600);
arduPort.setDataBits(QSerialPort::Data8);
arduPort.setParity(QSerialPort::NoParity);
arduPort.setStopBits(QSerialPort::OneStop);
arduPort.setFlowControl(QSerialPort::NoFlowControl);
arduPort.open(QSerialPort::ReadWrite);
BaudRate: THIS WASN'T SET BY QT!!!!!!! It turns out you can only set
the baud rate after you open the port.. Otherwise it is left at the
previous value which is 0 when you first plug in the cable.
Yes, it is true. But it has been fixed and will be available in Qt 5.3
fDtrControl: Set to 1 by Putty, left at 0 by Qt.
No. Qt do not touch the DTR signal at opening. This signal will be cleared only when was set to DTR_CONTROL_HANDSHAKE. Because QtSerialPort do not support the DTR/DSR flow control. So, in any case you can control the DTR by means of QSerialPort::setDataTerminalReady(bool).
PS: I mean current Qt 5.3 release
fOutX and fInX: Both also set to 1 by Putty and left at 0 by Qt.
This flag's are used only when you use QSerialPort::Software flow control (Xon/Xoff). But you are use the QSerialPort::NoFlowControl (as I can see from your code snippet), so, all right. So, please check that you use Putty with the "None" flow control too.
While setting all the parameters I thought it would be a good idea to
set the error policy to 'skip'.
Please use only QSerialPort::Ignore policy (by default). Because other policies is deprecated (all policies) and will be removed in future.
UPD:
dcb.fRtsControl = RTS_CONTROL_ENABLE;
Ahh, seems that your Arduino expect that RTS signal should be enabled by default. In this case you should to use QSerialPort::setRequestToSend(bool). But it is possible only in QSerialPort::NoFlowControl mode.
I.e. RTS always will be in RTS_CONTROL_DISABLE or RTS_CONTROL_HANDSHAKE after opening of port (depends on your FlowControl settings, QSerialPort::setFlowControl() ).
I recently got my hands on an Arduino (Uno), and I was wondering something.
I've got no external volume changer for my speakers, so I thought, maybe hook up a potentiometer to the Arduino, and then use that to control the volume (in Windows). But is that possible?
To read a value of an Arduino pin using maybe Visual C, C++ (or some more 'multi-platform' language)? And then using that to set the volume level in Windows (and if it's possible also in Linux).
I thought it might be possible, because if you use:
Serial.println(analogRead([pin with potentiometer]));
You can get the values of the potentiometer to the pc (via USB). So is there any way to read those values in C or C++?
I know how to set the volume in Windows via C or C++, I only need to know if there is a way to read out the values of a potentiometer in a (Visual) C or C++ script.
Definitely. And using exactly the same method: serial communication. Since I'm not a great Windows expert, I can't write you a complete Windows example, but here are some snippets that may get you started on a Unix (Linux, OS X, etc.):
Code on the Arduino:
#define POT_PIN 1
void setup()
{
Serial.begin(9600); // 9600 baud is more than enough
}
void loop()
{
int pot = analogRead(POT_PIN);
unsigned char byte = (pot + 2) >> 2; // round and divide by 4
Serial.write(pot);
delay(100); // or do something useful
}
Code on the computer:
#include <termios.h>
struct termios tio;
memset(&tio, 0, sizeof(tio));
// Open serial port in mode `8N1', non-blocking
tio.c_cflag = CS8 | CREAD | CLOCAL;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 5;
int fd = open(device, O_RDONLY);
cfsetospeed(&tio, B9600);
cfsetispeed(&tio, B9600);
tcsetattr(fd, TCSANOW, &tio);
while (1) {
unsigned char byte;
read(fd, &byte, 1);
use_some_library_to_set_volume(byte);
}
There are some things to consider.
You want the PC to listen to your "potentiometer Arduino" only. You don't want it to listen to any random USB data coming at any port. There is a need to create a data protocol which is somewhat unique to your device. If you just spam analog readings you'll get some raw data like "123 128 133 145", which could mean anything and come from any kind of device.
When you type Serial.whatever, the Arduino is likely spitting out an UART signal. I don't know a thing about Arduino, but hopefully it has either RS-232 or USB circuitry on board. You will need a matching port on the PC, the traditional RS-232 9 pin d-sub connector is getting increasingly rare on modern computers. You might need a RS-232 to USB converter.
You need a mean to determine which COM port the device is connected to. USB ports and traditional RS-232 ports work the same, as far as the PC is concerned. Either let the user pick the port manually, or design a more intelligent algorithm to find the port where your device is connected.
Study the Windows API for all the serial port routines. Documentation and examples at MSDN.
I am attempting to use boost asio for serial communication. I am currently working in Windows, but will eventually be moving the code into Linux. When ever I restart my computer data sent from the program is not what it should be (for example I send a null followed by a carriage return and get "00111111 10000011" in binary) and it is not consistent (multiple nulls yield different binary).
However, as soon as I use any other program to send any data to the serial port and run the program again it works perfectly. I think I must be missing something in the initialization of the port, but my research has not turned anything up.
Here is how I am opening the port:
// An IOService to get the socket to work
boost::asio::io_service *io;
// An acceptor for getting connections
boost::shared_ptr<boost::asio::serial_port> port;
// Cnstructor Functions
void Defaults() {
io = new boost::asio::io_service();
// Set Default Commands
command.prefix = 170;
command.address = 3;
command.xDot[0] = 128;
command.xDot[1] = 128;
command.xDot[2] = 128;
command.throtle = 0;
command.button8 = 0;
command.button16 = 0;
command.checkSum = 131;
}
void Defaults(char * port, int baud) {
Defaults();
// Setup the serial port
port.reset(new boost::asio::serial_port(*io,port));
port->set_option( boost::asio::serial_port_base::baud_rate( baud ));
// This is for testing
printf("portTest: %i\n",(int)port->is_open());
port->write_some(boost::asio::buffer((void*)"\0", 1));
boost::asio::write(*port, boost::asio::buffer((void*)"\0", 1));
boost::asio::write(*port, boost::asio::buffer((void*)"\r", 1));
boost::asio::write(*port, boost::asio::buffer((void*)"\r", 1));
Sleep(2000);
}
Edit: In an attempt to remove unrelated code I accidentally deleted the the line where I set the baud rate, I added it back. Also, I am checking the output with a null-modem and Docklight. Aside from the baud rate I am using all of the default serial settings specified for a boost serial port (I have also tried explicitly setting them with no effect).
You haven't said how you're checking what's being sent, but it's probably a baud rate mismatch between the two ends.
It looks like you're missing this:
port->set_option( boost::asio::serial_port_base::baud_rate( baud ) );
Number of data bits, parity, and start and stop bits will also need configuring if they're different to the default values.
If you still can't sort it, stick an oscilloscope on the output and compare the waveform of the sender and receiver. You'll see something like this.
This is the top search result when looking for this problem, so I thought I'd give what I believe is the correct answer:
Boost Asio is bugged as of this writing and does not set default values for ports on the Windows platform. Instead, it grabs the current values on the port and then bakes them right back in. After a fresh reboot, the port hasn't been used yet, so its Windows-default values are likely not useful for communication (byte size typically defaults to 9, for example). After you use the port with a program or library that sets the values correctly, the values Asio picks up afterward are correct.
To solve this until Asio incorporates the fix, just set everything on the port explicitly, ie:
using boost::asio::serial_port;
using boost::asio::serial_port_base;
void setup_port(serial_port &serial, unsigned int baud)
{
serial.set_option(serial_port_base::baud_rate(baud));
serial.set_option(serial_port_base::character_size(8));
serial.set_option(serial_port_base::flow_control(serial_port_base::flow_control::none));
serial.set_option(serial_port_base::parity(serial_port_base::parity::none));
serial.set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::one));
}
How do I receive layer 2 packets in POSIXy C++? The packets only have src and dst MAC address, type/length, and custom formatted data. They're not TCP or UDP or IP or IGMP or ARP or whatever - they're a home-brewed format given unto me by the Hardware guys.
My socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW) never returns from its recvfrom().
I can send fine, I just can't receive no matter what options I fling at the network stack.
(Platform is VxWorks, but I can translate POSIX or Linux or whatever...)
receive code (current incarnation):
int s;
if ((s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) < 0) {
printf("socket create error.");
return -1;
}
struct ifreq _ifr;
strncpy(_ifr.ifr_name, "lltemac0", strlen("lltemac0"));
ioctl(s, IP_SIOCGIFINDEX, &_ifr);
struct sockaddr_ll _sockAttrib;
memset(&_sockAttrib, 0, sizeof(_sockAttrib));
_sockAttrib.sll_len = sizeof(_sockAttrib);
_sockAttrib.sll_family = AF_PACKET;
_sockAttrib.sll_protocol = IFT_ETHER;
_sockAttrib.sll_ifindex = _ifr.ifr_ifindex;
_sockAttrib.sll_hatype = 0xFFFF;
_sockAttrib.sll_pkttype = PACKET_HOST;
_sockAttrib.sll_halen = 6;
_sockAttrib.sll_addr[0] = 0x00;
_sockAttrib.sll_addr[1] = 0x02;
_sockAttrib.sll_addr[2] = 0x03;
_sockAttrib.sll_addr[3] = 0x12;
_sockAttrib.sll_addr[4] = 0x34;
_sockAttrib.sll_addr[5] = 0x56;
int _sockAttribLen = sizeof(_sockAttrib);
char packet[64];
memset(packet, 0, sizeof(packet));
if (recvfrom(s, (char *)packet, sizeof(packet), 0,
(struct sockaddr *)&_sockAttrib, &_sockAttribLen) < 0)
{
printf("packet receive error.");
}
// code never reaches here
I think the way to do this is to write your own Network Service that binds to the MUX layer in the VxWorks network stack. This is reasonably well documented in the VxWorks Network Programmer's Guide and something I have done a number of times.
A custom Network Service can be configured to see all layer 2 packets received on a network interface using the MUX_PROTO_SNARF service type, which is how Wind River's own WDB protocol works, or packets with a specific protocol type.
It is also possible to add a socket interface to your custom Network Service by writing a custom socket back-end that sits between the Network Service and the socket API. This is not required if you are happy to do the application processing in the Network Service.
You haven't said which version of VxWorks you are using but I think the above holds for VxWorks 5.5.x and 6.x
Have you tried setting the socket protocol to htons(ETH_P_ALL) as prescribed in packet(7)? What you're doing doesn't have much to do with IP (although IPPROTO_RAW may be some wildcard value, dunno)
I think this is going to be a bit tougher problem to solve than you expect. Given that it's not IP at all (or apparently any other protocol anything will recognize), I don't think you'll be able to solve your problem(s) entirely with user-level code. On Linux, I think you'd need to write your own device agnostic interface driver (probably using NAPI). Getting it to work under VxWorks will almost certainly be non-trivial (more like a complete rewrite from the ground-up than what most people would think of as a port).
Have you tried confirming via Wireshark that a packet has actually been sent from the other end?
Also, for debugging, ask your hardware guys if they have a debug pin (you can attach to a logic analyzer) that they can assert when it receives a packet. Just to make sure that the hardware is getting the packets fine.
First you need to specify the protocol as ETH_P_ALL so that your interface gets all the packet. Set your socket to be on promiscuous mode. Then bind your RAW socket to an interface before you perform a receive.