This project consists of web server (using libwebsockets library) and a motor control plugin for the web server that writes out and reads in packets to a motor control board as well as communicating with the server.
I have two file descriptors (fd) to a COM port on an embedded Linux board, one for writing and one for reading. They are both working individually, but I can't get them to work at the same time.
To test the read(), I hook the receive line for the COM port to a computer and send a packet of 16 bytes repeatedly from a terminal program. The bytes are received to the byte array and passed to the GUI via the web server. For this test, write is not disabled and writes out the COM port back to the terminal. The motors are not connected to the COM port for this test.
To test the write(), read is disabled and the web server is run. I can control the motors using the GUI and motor positions are reported to the server.
When both read and write are enabled, something appears to block when the web server is run. The motor cannot be enabled, nor run. As soon as I press Ctrl-C to stop the server, one or more packets from the motor appear to be processed.
The motors start to move and motor packet data is transferred to the GUI.
Is the read blocking for some reason? It works alone.
I have gotten the following outputs at various times by checking the return of the the read() call:
EINTR - Interrupted system call
EIO - Input/Output error
EBADF - Bad file descriptor
After the last tests of the read and write (separately), the only error I get is the latter. After quitting out of the program, I get a string of about 50 bad reads (I am printing perror to the terminal).
The read/write code (below) is running in a separate thread, with the packets being processed in a continuous loop. I was trying to isolate the timing for writing from fluctuations in the scheduler. The write routine is driven by a Linux timer at 8.333ms (120Hz) and SIGALRM, thus the "gotAlarm" variable.
Is this a problem with processes? I do not fork any processes and I want the web server to be the main process.
Is it a threading problem? Both the read and write have been working within the spawned thread.
Thank you for your input! Code is below.
Call to initialize fd's:
mc_tx_fd = InitPort("/dev/ttyS1", "COM2", O_WRONLY | O_NOCTTY, B115200);
mc_rx_fd = InitPort("/dev/ttyS1", "COM2", O_RDONLY | O_NOCTTY, B115200);
InitPort function:
int InitPort( char *port, char *name, int oflags, speed_t baudRate )
{
int fd, rg, rs; // File descriptor
fd = open(port, oflags); // Open the port like a file
assert(fd > 0); // Open returns -1 on error
struct termios options; // Initialize a termios struct
rg = tcgetattr(fd, &options); // Populate with current attributes
if( rg < 0 ) {
printf("Failed to get attr: %d, %s\n", fd, strerror(errno));
}
cfsetospeed (&options, baudRate); // Set baud rate out
cfsetispeed (&options, baudRate); // Set baud rate in (same as baud rate out)
options.c_cflag &= ~CSIZE; // Clear bit-length flag so it can be set
//8N1 Serial Mode
options.c_cflag &= ~CSTOPB; // Set stop bit: 1
options.c_cflag &= ~CRTSCTS; // Set flow control: none
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 2;
cfmakeraw(&options);
options.c_cflag |= (CLOCAL | CREAD); // Enable receiver, and set local mode
rs = tcsetattr(fd, TCSANOW, &options); // Set new attributes to hardware
if( rs < 0 ) {
printf("Failed to set attr: %d, %s\n", fd, strerror(errno));
}
return fd;
}
Here is the SendMotorPacket function called at the creation of a new thread:
void *SendMotorPacket( void *arg )
{
char timez, checksum;
static unsigned short rcvTimer = 0;
int flags, rcvAzPos, bytesRead, wc, wo, r, t;
while( 1 ) {
// receive byte from motor
r = read( mc_rx_fd, inByte, 1 );
if( r == -1 ) {
if( errno == EINTR ) {
continue;
} else {
perror("read");
}
}
inChar = inByte[0];
// FSM to receive packet
switch( receiveState ) {
case MOTOR_HEAD_1:
if( inChar == 0xA5 ) {
receiveState = MOTOR_HEAD_2;
checksum = 0;
}
break;
case MOTOR_HEAD_2:
if( inChar == 0x52 ) {
receiveState = MOTOR_DATA;
idx = 0;
checksum = 0xA5 + 0x52;
} else {
receiveState = MOTOR_HEAD_1;
}
break;
case MOTOR_DATA:
readPacket[idx++] = inChar;
checksum += inChar;
if( idx >= 16 ) {
receiveState = MOTOR_CHECKSUM;
}
break;
case MOTOR_CHECKSUM:
if( checksum == inChar ) {
// Check status bits and set global variables
mc.az_status = *(signed short *)&readPacket[2];
mc.el_status = *(signed short *)&readPacket[10];
// If motor is disabled, set global variable for feedback to GUI
if( !az.enable ) {
mc.az_position = *(signed int *)&readPacket[4];
}
if( !el.enable ) {
mc.el_position = *(signed int *)&readPacket[12];
}
}
receiveState = MOTOR_HEAD_1;
break;
default:
break;
}
// TRANSMIT
if( gotAlarm ) {
mc.checkSum = 0;
gotAlarm = 0;
// Call both motor position functions to update position
AzMotorPos();
ElMotorPos();
if ( az.enable ) {
mc.az_control |= 0x0001;
} else {
mc.az_control &= ~0x0001;
}
if ( el.enable ) {
mc.el_control |= 0x0001;
} else {
mc.el_control &= ~0x0001;
}
*( unsigned short * )&writePacket = mc.header;
*( signed short * )&writePacket[2] = mc.az_control;
*( signed short * )&writePacket[4] = mc.az_status;
*( signed int * )&writePacket[6] = mc.az_position;
*( signed short * )&writePacket[10] = mc.el_control;
*( signed short * )&writePacket[12] = mc.el_status;
*( signed int * )&writePacket[14] = mc.el_position;
// Calculate checksum for all bytes in packet
for( int i = 0; i < 18; i++ ) {
mc.checkSum += writePacket[i];
}
*(unsigned char * )&writePacket[18] = mc.checkSum;
write( mc_tx_fd, writePacket, MC_PACKET_SIZE );
}
}
}
Here is thread creation:
void CreatePacketThread ( void )
{
int err = -1;
err = pthread_create( &packetThread, NULL, &SendMotorPacket, NULL );
if (err != 0)
printf("\ncan't create MC SEND thread :[%s]\n\n", strerror(err));
else
printf("\nMC SEND THREAD created successfully\n\n");
}
UPDATE:
Running the web server (lwsws) and then quitting results in one EIO error:
read: Input/output error
and multiple EBADF errors:
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
About 50 of them.
I solved the problem by moving the receive routine to a separate thread. To recap, the web server (LWSWS program from libwebsockets library) is running in headless (auto start and run) mode on a Linux single board computer(SBC). The motor control code is a plugin of LWSWS and it now spawns two threads: one for sending packets out to the motor control board, and one for receiving packets.
Originally, I started with this two-thread design, but apparently had other problems at that point. Before asking this question, I had moved the receive routine into the send thread to try to get read() and write() both working.
The working combination seems to be two threads with each having its own file descriptor to the COM port. I am surprised that a mutex is not needed, as #thekamilz suggested in the comments, but pleased that it works. If I need to expand the project, I am hopeful that this design will work; with each peripheral operating on its own thread for communication.
Related
I have a microcontroller which I communicate with my windows pc, through FT232RL.
On the computer side, I am making a C-library to send and receive data, using windows API.
I have managed to:
Receive data (or multiple receives),
Transmit data (or multiple transmits),
First transmit (or multiple transmits) and then receive data (or multiple receives)
But I have not managed to:
Transmit Data and then receive.
If I receive anything, and then try to transmit, I get error. So, I guess when I receive data, there is a change in configuration of the HANDLE hComm, which I cannot find.
So the question is, what changes to my HANDLE hComm configuration when I receive data, which does not allow me to transmit after that?
Here is my code/functions and the main() that give me the error. If I run this, I get this "error 6" -screenshot of the error down below-:
#include <Windows.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
//Create Handler
HANDLE hComm = comPortSetup("\\\\.\\COM5");//change this to the com port of your mcu
//Setup the Receiver
rx_setup(hComm);
sleep(1);
char SerialBuffer[256];
//Receive data
rx_receive(hComm, &SerialBuffer);//<---- Works fine
char firstData[125] = ".";
tx_transmit(hComm, &firstData);//<----Wont work, since I received data first.
CloseHandle(hComm);//Closing the Serial Port
_getch();//press any key to close the window
}
HANDLE comPortSetup:
HANDLE comPortSetup(char ComPrt[])
{
int CharLoop=0;
HANDLE HandleCom; // Handle to the Serial port
/*----------------------------------- Opening the Serial Port --------------------------------------------*/
/*
There might be a case where one would need to use CreateFileA instead. (Depending on the compiler)
More can be found here: https://stackoverflow.com/questions/51462048/what-is-the-difference-between-createfile-and-createfilea
*/
HandleCom = CreateFile( ComPrt, // Name of the Port to be Opened
GENERIC_READ | GENERIC_WRITE, // Read/Write Access
0, // No Sharing, ports cant be shared
NULL, // No Security
OPEN_EXISTING, // Open existing port only
0, // Non Overlapped I/O (0 does not match any of the flags of dwFlagsAndAttributes.
// This means we are setting no flags or attributes (We dont care about it) https://stackoverflow.com/questions/17997608/what-does-dwflagsandattributes-0-mean-in-the-createfile-method
NULL); // Null for Comm Devices
if (HandleCom == INVALID_HANDLE_VALUE)
printf("\n Error! - Port %s can't be opened", ComPrt);
else
printf("\n Port %s Opened\n ", ComPrt);
/*------------------------------- Setting the Parameters for the SerialPort ------------------------------*/
DCB dcbSerialParams = { 0 };
// Initializing DCB structure
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
BOOL StatusFun;
StatusFun = GetCommState(HandleCom, &dcbSerialParams); //retreives the current settings
if (StatusFun == FALSE)
printf("\n Error! in GetCommState()");
dcbSerialParams.BaudRate = CBR_9600; // Setting BaudRate = 9600
dcbSerialParams.ByteSize = 8; // Setting ByteSize = 8
dcbSerialParams.StopBits = ONESTOPBIT; // Setting StopBits = 1
dcbSerialParams.Parity = NOPARITY; // Setting Parity = None
StatusFun = SetCommState(HandleCom, &dcbSerialParams); //Configuring the port according to settings in DCB
if (StatusFun == FALSE)
{
printf("\n Error! in Setting DCB Structure");
}
else
{
printf("\n Setting DCB Structure Successfull\n");
printf("\n Baudrate = %d", dcbSerialParams.BaudRate);
printf("\n ByteSize = %d", dcbSerialParams.ByteSize);
printf("\n StopBits = %d", dcbSerialParams.StopBits);
printf("\n Parity = %d", dcbSerialParams.Parity);
}
/*------------------------------------ Setting Timeouts --------------------------------------------------*/
COMMTIMEOUTS timeouts = { 0 };
//miliseconds (ms) intervals
//interval between the arrival of any two bytes. Terminates the ReadFile
timeouts.ReadIntervalTimeout = 100; //Default =50
//Total = (TimeoutMultiplier*BytesToRead + TimeoutConstant)
timeouts.ReadTotalTimeoutConstant = 10; //Default = 50
timeouts.ReadTotalTimeoutMultiplier = 20; //Default = 10
//Total = (TimeoutMultiplier*BytesToRead + TimeoutConstant)
timeouts.WriteTotalTimeoutConstant = 10; //Default = 50
timeouts.WriteTotalTimeoutMultiplier = 20; //Default = 10
if (SetCommTimeouts(HandleCom, &timeouts) == FALSE)
printf("\n Error! in Setting Time Outs");
else
printf("\n\n Setting Serial Port Timeouts Successfull");
return HandleCom;
}
rx_setup:
BOOL rx_setup(HANDLE HandleCom)
{
/*------------------------------------ Setting Receive Mask ----------------------------------------------*/
//https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setcommmask
BOOL Status;
Status = SetCommMask(HandleCom, EV_RXCHAR); //Configure Windows to Monitor the serial device for Character Reception
if (Status == FALSE)
{
printf("\n\n Error! in Setting CommMask");
}
/*------------------------------------ Setting WaitComm() Event ----------------------------------------*/
printf("\n\n Waiting for Data Reception...");
DWORD dwEventMask; // Event mask to trigger. 32-bit unsigned integer (range: 0 through 4294967295 decimal)
//Wait for the first character to be received
Status = WaitCommEvent(HandleCom, &dwEventMask, NULL); //dwEventMask Should be 1 for "A character was received and placed in the input buffer." AKA EV_RXCHAR
/*-------------------------- Program will Wait here till one Character is received ------------------------*/
if (Status == FALSE)
{
printf("\n Error! in Setting WaitCommEvent()");
exit(-1);
}
return Status;
}
rx_receive:
void rx_receive(HANDLE HandleCom, char SerialBufferFun[])
{
char TempChar; // Temperory Character
BOOL Status;
/*Receiver start*/
DWORD NoBytesRead; // Bytes read by ReadFile()
int loopArrayFun = 0;
do
{
Status = ReadFile(HandleCom,
&TempChar,
sizeof(TempChar), //No of bytes to be read
&NoBytesRead, //How many bytes were actually read
NULL);
SerialBufferFun[loopArrayFun] = TempChar;
printf("%c",SerialBufferFun[loopArrayFun]);
loopArrayFun = loopArrayFun+1;
}while (NoBytesRead > 0);//NoBytesRead = 0 when bytes are finished reading.
SerialBufferFun[loopArrayFun-3] = '\0'; //WHY -3
}
transmit:
void tx_transmit(HANDLE HandleCom, char DataToTransmit[])
{
BOOL Status;
/*----------------------------- Writing a Character to Serial Port----------------------------------------*/
//DataToTransmit should be char or byte array, otherwise write will fail
DWORD dNoOFBytestoWrite; // No of bytes to write into the port
DWORD dNoOfBytesWritten = 0; // No of bytes written to the port
dNoOFBytestoWrite = sizeof(DataToTransmit); // Calculating the no of bytes to write into the port
if (HandleCom == INVALID_HANDLE_VALUE)
{
printf("\n Invalid handle");
}
Status = WriteFile(HandleCom, // Handle to the Serialport
DataToTransmit, // Data to be written to the port
(dNoOFBytestoWrite), // No of bytes to write into the port
&dNoOfBytesWritten, // No of bytes written to the port
NULL);
if (Status != TRUE)
printf("\n\n Error %d in Writing to Serial Port",GetLastError());
}
The error I get after running the above code (Error 6. The 'Heater Driver' etc are from the MCU):
What I tried:
I noticed that the only thing that changes that could influence my transmission when receiving, is not inside rx_receive but inside rx_setup:
SetCommMask(HandleCom, EV_RXCHAR);
and:
WaitCommEvent(HandleCom, &dwEventMask, NULL);
So I tried to do SetCommMask(HandleCom, 0x00); after the reception but It did not work, I got the same error. I do not know if I need to disable the WaitCommEvent(HandleCom, &dwEventMask, NULL); though, but since it stops running when the reception is done, So it just runs inside the function and does not affect my HANDLE hComm
According to MSDN:Sample, Maybe you need to set a pin's signal state to indicate the data has been sent/received in your microcontroller program. More details reside in your serial communication transmission of data standard. And you should write code according to the result of WaitCommEvent(hCom, &dwEvtMask, &o); like the linked sample.
Found a solution! Solution:
I did not need to use the rx_setup function in the main at all!.
I am unsure of why that is, but I can receive/send data now in any sequence I want.
So my main will be:
#include <Windows.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
//Create Handler
HANDLE hComm = comPortSetup("\\\\.\\COM5");//change this to the com port of your mcu
//Setup the Receiver
//rx_setup(hComm);
sleep(1);
char SerialBuffer[256];
//Receive data
rx_receive(hComm, &SerialBuffer);//<---- Works fine
char firstData[125] = ".";
tx_transmit(hComm, &firstData);//<----Works as expected now
CloseHandle(hComm);//Closing the Serial Port
_getch();//press any key to close the window
}
I'm begining to learn linux and ALSA and I was wondering if there is a way to store the sound I record from a microphone a directly to the buffer. I read here http://www.linuxjournal.com/article/6735?page=0,2 how to make my recording program. But what I need is a little more complex. I need to record sound untill I hit a key. The reason I need this is because I'm messing with a RaspberryPI(debian on it) and to see if I could turn it into a sound monitoring/detecting device.
My main problem is now that when I try to use it to record (./Rec >name.raw ) it does nothing. It just ouputs an empty .raw file.
#define ALSA_PCM_NEW_HW_PARAMS_API
#include <termios.h>
#include <alsa/asoundlib.h>
struct termios stdin_orig; // Structure to save parameters
void term_reset() {
tcsetattr(STDIN_FILENO,TCSANOW,&stdin_orig);
tcsetattr(STDIN_FILENO,TCSAFLUSH,&stdin_orig);
}
void term_nonblocking() {
struct termios newt;
tcgetattr(STDIN_FILENO, &stdin_orig);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); // non-blocking
newt = stdin_orig;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
atexit(term_reset);
}
int main() {
int key=0;
long loops;
int rc;
int size;
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int val;
int dir;
snd_pcm_uframes_t frames;
char *buffer;
/* Open PCM device for recording (capture). */
rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE, 0);
if (rc < 0) {
fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
exit(1);
}
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(¶ms);
/* Fill it in with default values. */
snd_pcm_hw_params_any(handle, params);
/* Set the desired hardware parameters. */
/* Interleaved mode */
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
/* Signed 16-bit little-endian format */
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
/* One channel (mono) */
snd_pcm_hw_params_set_channels(handle, params, 1);
/* 16000 bits/second sampling rate */
val = 16000;
snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
/* Set period size to 2048 frames. */
frames = 2048;
snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
/* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
exit(1);
}
/* Use a buffer large enough to hold one period */
snd_pcm_hw_params_get_period_size(params, &frames, &dir);
size = frames * 2; /* 2 bytes/sample, 1 channels */
buffer = (char *) malloc(size);
while (key == 0)
{
rc = snd_pcm_readi(handle, buffer, frames);
if (rc == -EPIPE)
{
/* EPIPE means overrun */
fprintf(stderr, "overrun occurred\n");
snd_pcm_prepare(handle);
}
else if (rc < 0)
{
fprintf(stderr, "error from read: %s\n", snd_strerror(rc));
}
else if (rc != (int)frames)
{
fprintf(stderr, "short read, read %d frames\n", rc);
}
rc = write(1, buffer, size);
if (rc != size)
fprintf(stderr, "short write: wrote %d bytes\n", rc);
key = getchar();
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
free(buffer);
return 0;
}
Here is how I did that with python. Tested to work on my desktop Debian with USB Plantronics headphones. You need to install python-qt4 and python-pyaudio packages for this to work.
Also, you'll need to set your input device to microphone. In GNOME I switched both input and output devices via System Tools -> System Settings -> Sound. If you have Raspbian, not Debian on your Raspberry, as I do, it's gonna be tougher, cause there's LXDE instead of GNOME. You can use alsamixer and F6 button there to set audio cards, but the problem is that ALSA is low-level interface, while most Linuxes use some sound server on top of it, such as PulseAudio or JACK. You'll need some luck/spent time to make sure you switched input/output device to your mic/headphones.
If you use Jack microphone, plugged in through Jack input of your Raspberry Pi, note that Raspberry's Jack is input only, so you won't be able to play back your recordings and need some USB headphones to listen to your wav.
Personally, I feel that ALSA is very poorly documented (I suppose it's intentional job security) and I don't like to deal with it.
import pyaudio
import wave
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
# This is Qt part: we create a window, which has a "stop" flag.
# Stop flag defaults to False, but is set to True, when you press a key.
# Value of that flag is checked in main loop and loop exits when flag is True.
app = QApplication(sys.argv)
class MyWindow(QWidget):
def __init__(self):
super(QWidget, self).__init__()
self.stop = False
def keyPressEvent(self, event):
print "keyPressedEvent caught!"
self.stop = True
window = MyWindow()
window.show()
# This is sound processing part: we create an input stream to read from microphone.
p = pyaudio.PyAudio()
stream = p.open(format = p.get_format_from_width(2),
channels = 2,
rate=44100,
input=True,
output=False,
frames_per_buffer=1024)
# This is main loop: we iteratively poll audio and gui: audio data are stored in output_buffer,
# whereas gui is checked for stop flag value (if keyPressedEvent happened, flag will be set
# to True and break our main loop).
output_buffer = ""
while True:
app.processEvents()
data = stream.read(1024)
output_buffer += data
if window.stop: break
stream.stop_stream()
stream.close()
# Here we output contents of output_buffer as .wav file
output_wav = wave.open("output.wav", 'w')
output_wav.setparams((2, 2, 44100, len(output_buffer),"NONE","not compressed"))
output_wav.writeframesraw(output_buffer)
p.terminate()
This code shows you how to capture from ALSA in C++ in a while loop :
https://github.com/flatmax/gtkiostream/blob/master/test/ALSACaptureTest.C#L95
You can alter the loop there to record forever, replace :
while (N>0){
with
while (1){
Now you'll need some extra code. Firstly to read a character non-blocking add this to the top of the file :
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#define ngetc(c) (read (0, (c), 1))
ngetc is a non-blocking read from stdin. It returns -1 when nothing is read. It returns > 1 when you press the enter key.
So finally putting this all together, change :
while (N>0){
with
while (1){
int enter=ngetc(&ch);
if (enter>0)
break;
I'm begining to learn linux and ALSA and I was wondering if there is a way to store the sound I record from a microphone a directly to the buffer. I read here http://www.linuxjournal.com/article/6735?page=0,2 how to make my recording program. But what I need is a little more complex. I need to record sound untill I hit a key. The reason I need this is because I'm messing with a RaspberryPI(debian on it) and to see if I could turn it into a sound monitoring/detecting device.
My main problem is now that when I try to use it to record (./Rec >name.raw ) it does nothing. It just ouputs an empty .raw file.
#define ALSA_PCM_NEW_HW_PARAMS_API
#include <termios.h>
#include <alsa/asoundlib.h>
struct termios stdin_orig; // Structure to save parameters
void term_reset() {
tcsetattr(STDIN_FILENO,TCSANOW,&stdin_orig);
tcsetattr(STDIN_FILENO,TCSAFLUSH,&stdin_orig);
}
void term_nonblocking() {
struct termios newt;
tcgetattr(STDIN_FILENO, &stdin_orig);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); // non-blocking
newt = stdin_orig;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
atexit(term_reset);
}
int main() {
int key=0;
long loops;
int rc;
int size;
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int val;
int dir;
snd_pcm_uframes_t frames;
char *buffer;
/* Open PCM device for recording (capture). */
rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE, 0);
if (rc < 0) {
fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
exit(1);
}
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(¶ms);
/* Fill it in with default values. */
snd_pcm_hw_params_any(handle, params);
/* Set the desired hardware parameters. */
/* Interleaved mode */
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
/* Signed 16-bit little-endian format */
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
/* One channel (mono) */
snd_pcm_hw_params_set_channels(handle, params, 1);
/* 16000 bits/second sampling rate */
val = 16000;
snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
/* Set period size to 2048 frames. */
frames = 2048;
snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
/* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
exit(1);
}
/* Use a buffer large enough to hold one period */
snd_pcm_hw_params_get_period_size(params, &frames, &dir);
size = frames * 2; /* 2 bytes/sample, 1 channels */
buffer = (char *) malloc(size);
while (key == 0)
{
rc = snd_pcm_readi(handle, buffer, frames);
if (rc == -EPIPE)
{
/* EPIPE means overrun */
fprintf(stderr, "overrun occurred\n");
snd_pcm_prepare(handle);
}
else if (rc < 0)
{
fprintf(stderr, "error from read: %s\n", snd_strerror(rc));
}
else if (rc != (int)frames)
{
fprintf(stderr, "short read, read %d frames\n", rc);
}
rc = write(1, buffer, size);
if (rc != size)
fprintf(stderr, "short write: wrote %d bytes\n", rc);
key = getchar();
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
free(buffer);
return 0;
}
Here is how I did that with python. Tested to work on my desktop Debian with USB Plantronics headphones. You need to install python-qt4 and python-pyaudio packages for this to work.
Also, you'll need to set your input device to microphone. In GNOME I switched both input and output devices via System Tools -> System Settings -> Sound. If you have Raspbian, not Debian on your Raspberry, as I do, it's gonna be tougher, cause there's LXDE instead of GNOME. You can use alsamixer and F6 button there to set audio cards, but the problem is that ALSA is low-level interface, while most Linuxes use some sound server on top of it, such as PulseAudio or JACK. You'll need some luck/spent time to make sure you switched input/output device to your mic/headphones.
If you use Jack microphone, plugged in through Jack input of your Raspberry Pi, note that Raspberry's Jack is input only, so you won't be able to play back your recordings and need some USB headphones to listen to your wav.
Personally, I feel that ALSA is very poorly documented (I suppose it's intentional job security) and I don't like to deal with it.
import pyaudio
import wave
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
# This is Qt part: we create a window, which has a "stop" flag.
# Stop flag defaults to False, but is set to True, when you press a key.
# Value of that flag is checked in main loop and loop exits when flag is True.
app = QApplication(sys.argv)
class MyWindow(QWidget):
def __init__(self):
super(QWidget, self).__init__()
self.stop = False
def keyPressEvent(self, event):
print "keyPressedEvent caught!"
self.stop = True
window = MyWindow()
window.show()
# This is sound processing part: we create an input stream to read from microphone.
p = pyaudio.PyAudio()
stream = p.open(format = p.get_format_from_width(2),
channels = 2,
rate=44100,
input=True,
output=False,
frames_per_buffer=1024)
# This is main loop: we iteratively poll audio and gui: audio data are stored in output_buffer,
# whereas gui is checked for stop flag value (if keyPressedEvent happened, flag will be set
# to True and break our main loop).
output_buffer = ""
while True:
app.processEvents()
data = stream.read(1024)
output_buffer += data
if window.stop: break
stream.stop_stream()
stream.close()
# Here we output contents of output_buffer as .wav file
output_wav = wave.open("output.wav", 'w')
output_wav.setparams((2, 2, 44100, len(output_buffer),"NONE","not compressed"))
output_wav.writeframesraw(output_buffer)
p.terminate()
This code shows you how to capture from ALSA in C++ in a while loop :
https://github.com/flatmax/gtkiostream/blob/master/test/ALSACaptureTest.C#L95
You can alter the loop there to record forever, replace :
while (N>0){
with
while (1){
Now you'll need some extra code. Firstly to read a character non-blocking add this to the top of the file :
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#define ngetc(c) (read (0, (c), 1))
ngetc is a non-blocking read from stdin. It returns -1 when nothing is read. It returns > 1 when you press the enter key.
So finally putting this all together, change :
while (N>0){
with
while (1){
int enter=ngetc(&ch);
if (enter>0)
break;
I have been attempting to communicate with a device of mine via an RS232 serial port(in my case it is COM6). My code is supposed to write a string of ascii values to the device and then read the response, however I cannot seem to get any response. The program seems to work relatively fine when I have it write and read to a file in my computer, but not for when I designate COM6. Here is the latest edition of my code:
using namespace std;
const char ASCII[ ]= "0123456789ABCDEF";
char *Checksum (char *buffer)
{
static char Hex[10];
static int a1, a2;
register unsigned int i;
int sum;
printf("%s \n", buffer);
sum = 256;
for ( i=0 ; i<strlen(buffer) ; i++ )
{
sum-=buffer[i];
if ( sum<0 )
sum+= 256;
}
a1 = (sum & 0xF0) >> 4;
a2 = sum & 0x0F;
Hex[0] = ASCII[a1];
Hex[1] = ASCII[a2];
Hex[2] = 0;
printf("the checksum is %s \n",Hex);
return(Hex);
}
int main()
{
char data[80], input[80], *data2;
char *response;
DCB dcb;
bool retVal;
DWORD dwBytesTransferred;
DWORD byteswritten;
printf("the variable response is initially: %d\n", response);
dcb.BaudRate = CBR_19200; //19200 Baud
dcb.ByteSize = 8; //8 data bits
dcb.Parity = NOPARITY; //no parity
dcb.StopBits = ONESTOPBIT; //1 stop
//New open port area
HANDLE hPort;
if ((hPort = CreateFile ( "\\\\.\\COM6",
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)) != INVALID_HANDLE_VALUE)
{
printf("SUCCESS opening the port\n");// success
}
//GetCommState
DCB Dcb;
GetCommState (hPort, &Dcb);
Dcb.BaudRate = CBR_19200;
Dcb.StopBits = ONESTOPBIT;
Dcb.ByteSize = 8;
Dcb.Parity = NOPARITY;
Dcb.fParity = 0;
Dcb.fOutxCtsFlow = 0;
Dcb.fOutxDsrFlow = 0;
Dcb.fDsrSensitivity = 0;
Dcb.fTXContinueOnXoff = TRUE;
Dcb.fOutX = 0;
Dcb.fInX = 0;
Dcb.fNull = 0;
Dcb.fErrorChar = 0;
Dcb.fAbortOnError = 0;
Dcb.fRtsControl = RTS_CONTROL_DISABLE;
Dcb.fDtrControl = DTR_CONTROL_DISABLE;
//Flushing
FlushFileBuffers( hPort );
PurgeComm (hPort, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
COMSTAT comStat;
DWORD dwErrorFlags;
ClearCommError ( hPort, &dwErrorFlags, &comStat );
//NEW commtimeouts area
COMMTIMEOUTS CommTimeOuts;
DWORD dwTimeout = 3000; // <- set timeout in milliseconds
if(!dwTimeout)
{ // Don't use timeout -> Read the bytes already in input buffer and return immediately
CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
} else
{ // Use given timeout, wait until the requested number of bytes are read - or timeout
CommTimeOuts.ReadIntervalTimeout = 0;
CommTimeOuts.ReadTotalTimeoutConstant = dwTimeout;
}
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts (hPort, &CommTimeOuts);
printf("insert ASCII code string you wish to send:");
scanf("%s", input);
strcpy(data, "{0x02}");
strcat(data, input);
printf("%s \n", data);
data2=Checksum(data);
strcat(data, data2);
strcat(data, "{0x03}");
printf("the final sent message will be: %s \n",data);
retVal = WriteFile(hPort,data, strlen(data), &byteswritten, NULL);
printf("Number of bytes written: %d\n", byteswritten);
printf("Write Success? %d\n", retVal);
retVal=ReadFile (hPort, &response, 20, &dwBytesTransferred, NULL);
printf("Read Success? %d\n", retVal);
printf("Port Response: %d\n", response);
free(response);
return 0;
}
Summary of latest discoveries: Using the Free Serial Port Monitor that Habi suggested I now know for sure that WriteFile is functioning correctly and COM6 is in receiving the message. I'm still looking for a crossover cable to double check that the message is being transferred across the line. I figure while I try to figure that out if someone could look at this new edition and tell me if there is anything wrong, particularly in relation to the ReadFile function, it would be much appreciated. It bothers me that the Free Serial Port software is only showing the data passed from my computer and not a response from the device at all. =\
Instead of
"COM6"
try
"\\\\.\\COM6"
And I would recommend to use CreateFile(), ReadFile(), WriteFile().
To open the COM port try this:
HANDLE hComDev;
if ((hComDev = CreateFile ( "\\\\.\\COM6",
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)) != INVALID_HANDLE_VALUE)
{
// success
}
It seems that calling GetCommState() is missing in your code. Try this to configure the COM port:
DCB Dcb;
GetCommState (hComDev, &Dcb);
Dcb.BaudRate = CBR_19200;
Dcb.StopBits = ONESTOPBIT;
Dcb.ByteSize = 8;
Dcb.Parity = NOPARITY;
Dcb.fParity = 0;
Dcb.fOutxCtsFlow = 0;
Dcb.fOutxDsrFlow = 0;
Dcb.fDsrSensitivity = 0;
Dcb.fTXContinueOnXoff = TRUE;
Dcb.fOutX = 0;
Dcb.fInX = 0;
Dcb.fNull = 0;
Dcb.fErrorChar = 0;
Dcb.fAbortOnError = 0;
Dcb.fRtsControl = RTS_CONTROL_DISABLE;
Dcb.fDtrControl = DTR_CONTROL_DISABLE;
And to initially clear the COM port, I would do a reset like this before starting to send and receive bytes:
FlushFileBuffers( hComDev );
PurgeComm (hComDev, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
COMSTAT comStat;
DWORD dwErrorFlags;
ClearCommError ( hComDev, &dwErrorFlags, &comStat );
You ask for timeouts? To configure time outs try this:
COMMTIMEOUTS CommTimeOuts;
DWORD dwTimeout = .... // <- set timeout in milliseconds
if(!dwTimeout)
{ // Don't use timeout -> Read the bytes already in input buffer and return immediately
CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
}
else
{ // Use given timeout, wait until the requested number of bytes are read - or timeout
CommTimeOuts.ReadIntervalTimeout = 0;
CommTimeOuts.ReadTotalTimeoutConstant = dwTimeout;
}
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts (hComDev, &CommTimeOuts);
My code is supposed to write a string of ascii values to the device
and then read the response, however I cannot seem to get any response.
Are you sure that the bytes are really sent to the device? If you have the option, take a oscilloscope and monitor the PC's Tx line. Send some bytes and check baudrate and start/stop bits. If you have no hardware to monitor this signal, take a software based serial monitor, e.g. Free Serial Port Monitor. I have no experience with that kind of software tools but they should show you at least that the Windows drivers try to send something via the COM port of your choice.
Greetings
Habi
If you have been trying for a month then I would recommend you look at the windows function CreateFile. I have used this for serial port comms and it works fine. You might also want to look at the DCB struct and GetCommState and SetCommState functions to configure the serial port. WriteFile can be used to write data to the serial port. I cannot post all of my code as it owned by my company, but this should help you get started.
Also ClearCommError may be of help to you to query if any data is on the serial port buffer when you are waiting to read from it.
I encountered a similar problem and found the other answers helpful, but I was not receiving characters.
Ultimately I found the problem was with RTS_CONTROL_DISABLE or DTR_CONTROL_DISABLE.
By having RTS and DTR held low, the program was indicating to the modem/device that the program was not ready to receive data, and the modem was respecting this signal and dutifully not sending.
By changing these to RTS_CONTROL_ENABLE and DTR_CONTROL_ENABLE, the program indicates to the modem that data may be sent, and I began receiving characters. Not all devices respect these signals so it may work in some configurations with DTR and RTS disabled -- the modem may send data anyway.
It's possible, but you need to config com port via cmd mode command,
and there is no way to config timeout value via cmd command.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
// use mode command to config port
// mode com2 dtr=off rts=off baud=9600 parity=N data=8 stop=1 to=on
FILE *f = fopen("\\\\.\\COM2", "wb+");
setvbuf(f, NULL, _IONBF, 0);
if(f == NULL) return -1;
while(1)
{
static char p[5];
int cnt = fread(&p, 1, sizeof(p), f);
printf("read[%d]:%s\n", cnt, p);
if(p[0] == '0') break;
memset(p, 0, sizeof(p));
}
fclose(f);
return 0;
}
am trying to read sms messages via serial port in linux a from a sim card which i have placed inside a huawei 3g usb modem. i have to execute the script a number of time before some of the sms messages are displayed on the screen. At times it displays unusual characters. All i want to do is read sms messages from the sim using AT commands, c and serial port. Below is the code i am using.
int main(){
int fd;
struct termios options;
/* open the port */
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{ /* Could not open the port */
fprintf(stderr, "open_port: Unable to open /dev/ttyS1 - %s\n",strerror(errno));
}else{
printf("port opened\n");
}
fcntl(fd, F_SETFL, 0);
/* get the current options */
tcgetattr(fd, &options);
/* set raw input, 1 second timeout */
options.c_cflag |= (CLOCAL | CREAD);
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_oflag &= ~OPOST;
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 10;
/* set the options */
tcsetattr(fd, TCSANOW, &options);
char buffer[400]; /* Input buffer */
char *bufptr; /* Current char in buffer */
int nbytes; /* Number of bytes read */
int tries; /* Number of tries so far */
for (tries = 0; tries < 1; tries ++)
{
/* send an AT command*/
if (write(fd, "AT+CMGL=\"ALL\"\r", strlen("AT+CMGL=\"ALL\"\r")) < 3){
printf("command sent\n");
continue;
}
/* read characters into our string buffer*/
bufptr = buffer;
nbytes = read(fd, bufptr, buffer + sizeof(buffer) - bufptr - 1);
printf("%s\n",bufptr);
char *p;
p = strstr(buffer, "tin");
printf("%s",p);
p = strstr(buffer, "server");
if(p == NULL) printf("not from server\n");
*bufptr = '\0';
}
return 0;
}
Appearently Gnokii project supports Huawei devices -- http://wiki.gnokii.org/index.php/Huawei
I'd either go with gnokii, it's this simple:
$ gnokii --getsms
or at least look into gnokii sources, because the problem you're describing looks certainly like a synchronization or waiting-for-output problem and they almost certainly already have a nice and tested solution for that.
First of all, you need to be checking the return value of read. Basically read is never guaranteed to give you what you want. It might return early with nothing read due to your timeout or being interrupted by a signal (although you don't have any signal handlers so that's not an issue here) or give you just a partial read. You need to use the return value to advance your buffer pointer and read more until you determine you've read all the data you're looking for.
Aside from that, you should probably not just look at fixed text in an SMS to confirm the sender's id. I would sign the SMS with public key cryptography if you need to know it really came from the server you think sent it...