Sending single characters to Telnet - printf

I have a single-threaded program that runs on a WEC2013 device. Since the device has no display I use a Telnet connection to the device.
I want to show some progress indication. For that purpose I added a printf statement:
void DoTimeConsumingCalculation()
{
for (auto step = 0; step < 100; ++step)
{
// calculate, takes about 10 seconds
CrunchNumbers();
// keep the user happy
printf(".");
}
printf("\n");
}
Instead of issuing dots during the calculation this results in a complete line that is sent to the client when the \n is sent.
I experiemented with fflush(stdout); and setvbuf(0, _IONBF,0); without any effect. Even fprintf(stderr, '.'); gives the same result.
But when I use printf(".."); I get two dots immediately. But this is not the output I need.
How can I convince the C-runtime library to send each single character?

Related

Sending a text string out a serial port in C without using printf

I'm writing 8-bit PIC microcontroller C code in Microchip's MPLAB X tool using their XC8 compiler for a demo / evaluation board for a sensor IC. The PIC I'm using has multiple serial ports, so I'm using UART2 to output only sensor data. I wanted to be able to format the output data for hex or decimal so I have stdio redirected to this port. The Rx side of UART2 is unused.
UART1 is used for command and control. This port needs to be able to output ASCII messages such as "Enter the register address:". The sensor is highly configurable, so my code initializes it with default values, but the user can halt sampling, read and write registers and re-start sampling.
Since I can't use printf when writing to UART1, I had to find another way to do this. Here's an example of what I'm doing. The file is large so I'm just cutting and pasting the relevent stuff here:
char msgString[64];
switch(command)
{
case (READCMD): // Read command
{
if (SERIAL_STATE_IDLE == serialPortState)
{
strcpy (msgString, "Input Read Address Hex Value 00-18");
Display_PrintMessage();
Display_PrintChar(prompt);
serialPortState = SERIAL_STATE_READ;
}
...
void Display_PrintMessage(void)
{
msgLength=strlen(msgString);
for (i=0; i<msgLength; ++i)
{
UART1_Write(msgString[i]);
}
Display_PrintChar(newLine);
}
void Display_PrintChar(char c)
{
UART1_Write(c);
}
I've verified that the port is working by writing some characters out to it early in main and they do appear on my terminal. I think the problem is with how I'm using the strcpy function. In the MPLAB debugger it appears that msgString is still all null characters after the strcpy executes. The strlen function may see msgString as zero length, hence, nothing prints out.
I would love to hear what you think may be wrong with my code, or, please suggest another way of accomplishing what I need.
Thanks in advance for any and all responses, they are much appreciated.
CobraRGuy

I want to parse GNSS data from NMEA sentence using STM32F0 and Truestudio

I am facing difficulty while parsing GPS data from NMEA sentence. I am using Quectel L89 GNSS module (baud rate: 115200) and Truestudio IDE. I had used CubeMX for making skeletal structure of program.
I had tried using both HAL_UART_RxCpltCallback with HAL_UART_Receive_IT and HAL_UART_Receive_IT independently. I am able to read data sometimes and sometimes not. The thing is that raw data starts from $GPRMC AND ends with $PSTMCPU while reading data from ST sometimes rec_buffer starts from $GPRMC(ok condition) and sometimes from $GNVTG(wrong condition).
How will I ensure that rec_buffer always starts from $GPRMC
I am calling this in while(1):
HAL_UART_Receive_IT(&huart2, rec_buff, 2400); // from GPS UART
I also tried this:
void HAL_UART_RxCpltCallback( UART_HandleTypeDef * huart )
{
if (huart->Instance == USART2) //GPS UART
{
HAL_UART_Transmit(&huart1, &rec_buff[0], 1, 10);
HAL_UART_Receive_IT(&huart2, rrc_buff, 2400);
}
}
L89 RAW DATA:
$GPRMC,094640.000,A,2838.86700,N,07711.56483,E,0.4,99.3,050119,,,A*5E
$GPGGA,094640.000,2838.86700,N,07711.56483,E,1,08,1.0,226.91,M,-35.9,M,,*42
$GNGNS,094640.000,2838.86700,N,07711.56483,E,ANNNNN,08,1.0,0226.9,-35.9,,*78
$GPVTG,99.3,T,,M,0.4,N,0.7,K,A*3D
$GPGST,094640.000,46.0,26.0,17.7,0.2,25.7,18.2,19.6*68
$GPGBS,094640.000,25.7,18.2,19.6,,,,*4B
$GNGSA,A,3,23,09,03,16,26,22,27,07,,,,,1.9,1.0,1.6*28
$GNGSA,A,3,,,,,,,,,,,,,1.9,1.0,1.6*22
$GPGSV,3,1,11,16,70,076,27,03,58,227,30,23,52,331,36,26,44,046,18*78
$GPGSV,3,2,11,22,44,198,21,09,24,316,34,27,19,137,19,07,16,266,27*70
$GPGSV,3,3,11,31,14,058,,14,10,112,,08,09,164,20,,,,*45
$PSTMPRES,14.7,-4.3,3.4,4.7,8.1,-34.9,-10.0,14.5,-8.6,,,,,,,,,,,,,,,,*05
$PSTMVRES,0.3,-0.0,0.0,-0.0,0.3,0.0,-0.5,-0.5,0.1,,,,,,,,,,,,,,,,*0B
$PSTMTG,2034,553618.0000,8,128199118,10,-46760.0000,002a,1025,0,0,10,2034,553618.0000,10,2034,553618.0000,6*5A
$PSTMTS,1,23,87178102.875,-44634.02,01,36,107871,1,9650745.91,13059080.41,21268551.81,-1601.01,2104.54,-615.14,-60384.66,6.69,0,0.00,0.00,0,0,0,0*01
$PSTMTS,1,09,88801609.812,-43889.80,01,34,102805,1,16028885.38,3193585.53,20890300.53,-1492.86,2210.83,804.75,142511.90,11.66,0,0.00,0.00,0,0,0,0*07
$PSTMTS,1,03,86575601.125,-48258.84,01,30,78613,1,13348561.56,22507400.16,4720188.47,-3.99,659.00,-3098.78,52480.77,6.38,0,0.00,0.00,0,0,0,0*2D
$PSTMTS,1,16,85874269.688,-47283.16,01,27,75521,1,-1886095.16,22466622.38,13519219.09,-747.65,-1602.10,2568.93,-3251.94,5.72,0,0.00,0.00,0,0,0,0*2A
$PSTMTS,1,26,3381954.688,-49146.08,00,18,53036,1,-8037289.25,15873771.62,19660297.47,-1174.67,-2217.50,1326.00,26300.98,7.53,0,0.00,0.00,0,0,0,0*3C
$PSTMTS,1,22,3609068.188,-49215.92,00,21,27316,1,10235851.56,24348322.31,-2780494.62,-516.89,-162.93,-3068.35,-184537.24,7.77,0,0.00,0.00,0,0,0,0*08
$PSTMTS,1,27,5418358.750,-44043.68,00,19,77495,1,-10438473.00,23018353.00,-7765070.44,-768.74,651.32,3023.75,-13120.58,14.66,0,0.00,0.00,0,0,0,0*23
$PSTMTS,1,07,90050798.438,-44830.90,01,27,76584,1,25565687.72,6701073.97,4735275.25,-590.77,171.17,3061.98,17221.07,16.12,0,0.00,0.00,0,0,0,0*04
$PSTMTS,1,08,454836.875,-43869.36,00,20,3383,0,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0,0.00,0.00,0,0,0,0*2B
$PSTMNOTCHSTATUS,3786017,0,2449,0,2,5420680,0,3824,0,2*57
$PSTMADCDATA,943,903,,,,,,*48
$PSTMANTENNASTATUS,0*4D
$PSTMSBAS,0,0,,,,*19
$PSTMCPU,48.64,-1,49*41
Any help/ suggestion will be appreciated.
Thanks clifford for your suggestions. I need to parse data from RMC, VTG, GGA AND GSA. I am unable to change the baud rate and also being unable to stop proprietary sentences.
There is nothing "wrong" about the output from your GNSS; it is normal for such a device to output a number of standard and proprietary sentences. It is your responsibility to either configure the module to output only the sentences you need or parse the sentences and discard those you don't want.
Send the following command to the GNSS to disable all configurable sentences but the RMC sentence:
$PSMTSETPAR,1201,0X00000040*75
You need to either send this configuration every time on start-up, or save the configuration in the non-volatile memory by:
$PSMTSAVEPAR*58
It is possible that the module will output other sentences that are not configurable (I am not familiare with the specific module); you may still need to perform parsing and filtering on the host. That is as simple as parsing the talker ID and format specifier and discarding sentences you are not interested in. There are nay number of ways to do that; your question shows no such attempt.
Your code is too "low-level" as it is - the IEC61162-1 (NMEA 0183) sentence format is line oriented, but HAL_UART_Receive_IT() will just grab a chunk of data without any knowledge of the protocol and entirely asynchronous to the sentence structure - you could as easily start and end mid-sentence. Moreover HAL_UART_RxCpltCallback() occurs in the interrupt context - that is not the place to be receiving large chunks of data and parsing it. You first need some higher-level serial I/O code that will buffer the incoming data into a queue and support line-oriented input.
You then need to process each line - discarding those that are of no interest. In that sense it is too broad a question and not specifically related to GNSS data parsing - that is not really your problem here, it is as I say lower-level than than - you need ot build a suitable serial I/O infrastructure.
Another issue is that of you wait for 2400 characters to be received, you will get the data in chunks, and only the most recently received will be valid. If real-time reception of current position is required; you cannot do it that way.
I am no ST HAL expert and would not choose to use it; it is poorly documented. What it seems you should do in HAL_UART_RxCpltCallback is place the received data into a FIFO buffer that will be processed by some other thread (i.e. not in the interrupt context); because of variable length of NMEA sentences, you will need to do that on a single character basis. A perhaps simpler method is to poll the HALL receive buffer with a zero timeout and accumulate characters until a line is available. You can then inspect that line to see if it is an RMC sentence and then process that. For example, the following allows non-blocking processing of serial input. If no other functions need be performed, the timeout can be extended to wait indefinitely for each character:
// Strictly NMEA0183 requires only 81 byte sentence buffer, but
// L89 appears to output non-compliant proprietary sentences.
static char line_buffer[1024];
int line_buffer_index = 0;
for (;;)
{
HAL_StatusTypeDef rx_status = HAL_OK ;
while( rx_status == HAL_OK )
{
// Receive a character if available (zero timeout)
char ch = 0;
HAL_StatusTypeDef rx_status = HAL_UART_Receive( &huart2, ch, 1, 0 ) ;
// If character received and not the LF following the previous CR
if( rx_status == HAL_OK && ch != `\n ` )
{
// If the end of the sentence...
if( ch == `\r ` )
{
// terminate the line
line_buffer[line_buffer_index] = `\0` ;
// Start a new line
line_buffer_index = 0 ;
// Check if line is an RMC from a GNSS
if( line_buffer_index > 6 &&
memcmp( line_buffer, "$G", 2) == 0 &&
memcmp( &line_buffer[3], "RMC", 3) == 0 )
{
// Process RMC
processRMC( line_buffer ) ;
}
}
else
{
// add character to line buffer
line_buffer[line_buffer_index] = ch ;
line_buffer_index++ ;
if( line_buffer_index > sizeof(line_buffer) - 1 )
{
// Line too long, discard all
line_buffer_index = 0 ;
}
}
}
}
// do other work here if necessary
}

Only scrolling some Text in 16x2 LCD(HD44780 display programm

I have a program which display as follows .
first Line : Volt : Over Voltage
Second Line : Current : Over Current.
In LCD the cant fully display the letter OVer Voltage or Over Current .
I just want to scroll these . But the Volt : and current : letter will be there that will not need to scroll ?
So you will need to create a routine to cycle thru the messages you want to display.
As an example, first time send "Volt: Over Volta"
Then a second later send "Volt: ver Voltag"
then "Volt: er Voltage" and so on.
The other option would be to create a routine to display the value part with a starting index after determining the len.
The following puesdo code is not compiled/tested.
char buf[17];
char label[]= "Current";
char value[]= "Over Current";
while(1)
{
if (++start_pos >= (strlen(value)+strlen(label)-16)
{
start_pos=0;
delay(500); //ms
}
snprintf(buf,16,"%s: %s",label,value[start_pos]);
puts(buf); // whatever the name of your routine to send string to LCD
if (start_pos==0)
{
delay(500); //ms
}
delay(1000); //ms NOTE: you probably want to go do some other code during this time.
}
You may want to see if you're LCD has a scroll command. Some LCDs have a character buffer where you can write data to then give a scroll command to shift which are displayed. For example I've used an LCD that can store 40 characters per line in DDRAM while displaying only 16. If I remember correctly, you have to scroll both lines at once this way, though.
If this doesn't tickle your fancy, another way is to shift your buffer in code and re-write all of it to the LCD. You can do this fast enough that it doesn't look terrible.

Ragel: avoid redundant call of "when" clause function

I'm writing Ragel machine for rather simple binary protocol, and what I present here is even more simplified version, without any error recovery whatsoever, just to demonstrate the problem I'm trying to solve.
So, the message to be parsed here looks like this:
<1 byte: length> <$length bytes: user data> <1 byte: checksum>
Machine looks as follows:
%%{
machine my_machine;
write data;
alphtype unsigned char;
}%%
%%{
action message_reset {
/* TODO */
data_received = 0;
}
action got_len {
len = fc;
}
action got_data_byte {
/* TODO */
}
action message_received {
/* TODO */
}
action is_waiting_for_data {
(data_received++ < len);
}
action is_checksum_correct {
1/*TODO*/
}
len = (any);
fmt_separate_len = (0x80 any);
data = (any);
checksum = (any);
message =
(
# first byte: length of the data
(len #got_len)
# user data
(data when is_waiting_for_data #got_data_byte )*
# place higher priority on the previous machine (i.e. data)
<:
# last byte: checksum
(checksum when is_checksum_correct #message_received)
) >to(message_reset)
;
main := (msg_start: message)*;
# Initialize and execute.
write init;
write exec;
}%%
As you see, first we receive 1 byte that represents length; then we receive data bytes until we receive needed amount of bytes (the check is done by is_waiting_for_data), and when we receive next (extra) byte, we check whether it is a correct checksum (by is_checksum_correct). If it is, machine is going to wait for next message; otherwise, this particular machine stalls (I haven't included any error recovery here on purpose, in order to simplify diagram).
The diagram of it looks like this:
$ ragel -Vp ./msg.rl | dot -Tpng -o msg.png
Click to see image
As you see, in state 1, while we receiving user data, conditions are as follows:
0..255(is_waiting_for_data, !is_checksum_correct),
0..255(is_waiting_for_data, is_checksum_correct)
So on every data byte it redundantly calls is_checksum_correct, although the result doesn't matter at all.
The condition should be as simple: 0..255(is_waiting_for_data)
How to achieve that?
How is is_checksum_correct supposed to work? The when condition happens before the checksum is read, according to what you posted. My suggestion would be to check the checksum inside message_received and handle any error there. That way, you can get rid of the second when and the problem would no longer exist.
It looks like semantic conditions are a relatively new feature in Ragel, and while they look really useful, maybe they're not quite mature enough yet if you want optimal code.

Objective-C - Arduino communication stops

I am trying to control some stepper motors using an app I am writing in Objective-C and my Arduino board. I use popen to send a byte (the letter a) to my Arduino and count the steps taken (Xc):
-(IBAction)PlusX:(id)sender{
popen("echo a > /dev/tty.usbmodem621", "r");
Xc = Xc +1;
_lXc.stringValue = #(Xc);
[NSThread sleepForTimeInterval:0.16f];
}
My Arduino reads this in the void loop and makes a step.
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
Serial.println(incomingByte);
if(incomingByte == 'a'){
MotorX->step(1, FORWARD, SINGLE);
}
}
This al works pretty much as expected. Except after byte/step/action 144 the Objective-C app keeps counting the steps correctly, however they don't appear in Arduino's serial monitor and the motor stops making steps. Is there anybody who knows why this keeps happening?
Thanks
you are opening many file (in linux everything is a file) but never closing it. Then you are creating "garbage" open file, because you can't use that file anymore, but it is there. And any OS give you a limit of maximum file open (file descriptor limit); in your case about 144.
when you "kill" your application, that descriptor get released, so you can use them again.
Also, because you don't check the descriptor is valid, you didn't debugged that after the 144 open it was giving error.
Solution (that does NOT check for error) is
pclose(popen("echo a > /dev/tty.usbmodem621", 'r'));