String parsing, turn LED on/off based on received serial data - c

I am receiving in the serial port from python a string in this format (0,77,88,55).
I have the first number randomized to either be 0 or 1, the rest are fixed.
import random
import serial
import time
last_time = 0
test_list = [0,1]
serialSTM32 = serial.Serial('COM22',9600,writeTimeout=0)
while True:
if (time.time() - last_time) > 2:
random_num = random.choice(test_list)
#string = str(random_num)
string = "(" + str(random_num) + "," + "77" + "," + "88" + "," + "55" + ")"
serialSTM32.write(string.encode('utf-8'))
print(string)
last_time = time.time()
serialSTM32.flushInput()
The STM32 MCU is receiving through the virtual serial COM port the data through a variable in the "usbd_cdc_if.c" file:
uint8_t bufferVariable = 0;
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
bufferVariable = Buf[0];
return (USBD_OK);
}
The bufferVariable is passed to the "main.c" file using:
extern uint8_t bufferVariable;
The "main.c" is then parsing the data, splitting the data and placing the first number which is '0' or '1' in a variable named "first", the second number which is '77' in a variable named "second" etc.
I am taking the "first" variable to see if it is '1' to turn on the LED, but if it is '0' to turn off the LED.
Program waits for start market "(" and keeps reading until it reaches end marker ")", then parses the data in between the two. **Works well on Arduino from parsing tutorial a while back, i converted everything to work on STM32 MCU....
**For this STM32 controller, previously, I tested without the parsing, only receiving '1' or '0' through serial to turn on/off the LED which works.....but now I am using the format (0,77,88,55) which includes parsing the data then getting the value extracted from that format. **This part is not working....LED does not turn on or off.
"main.c" file code:
#define buffer_size 40
char input_buffer[buffer_size];
const char start_marker = '(';
const char end_marker = ')';
uint8_t bytes_received = 0;
uint8_t read_in_progress = 0;
char* grab_value(char *data, char separator, int index) {
int found = 0;
int string_index[] = { 0, -1 };
int maximum_index = strlen(data) - 1;
for (int i = 0; i <= maximum_index && found <= index; i++) {
if (data[i] == separator || i == maximum_index) {
found++;
string_index[0] = string_index[1] + 1;
string_index[1] = (i == maximum_index) ? i + 1 : i;
}
}
if (found > index) {
data[string_index[1]] = '\0';
return &data[string_index[0]];
} else {
return NULL;
}
}
int first, second, third, fourth;
extern uint8_t bufferVariable;
int main(void) {
while (1) {
if (bufferVariable == end_marker) {
read_in_progress = 0;
input_buffer[bytes_received] = '\0';
}
if (read_in_progress) {
input_buffer[bytes_received++] = bufferVariable;
if (bytes_received == buffer_size) {
bytes_received = buffer_size - 1;
}
}
if (bufferVariable == start_marker) {
bytes_received = 0;
read_in_progress = 1;
}
char *str_first = grab_value(input_buffer, ',', 0);
char *str_second = grab_value(input_buffer, ',', 1);
char *str_third = grab_value(input_buffer, ',', 2);
char *str_fourth = grab_value(input_buffer, ',', 3);
if (str_first) {
first = atoi(str_first);
}
if (str_second) {
second = atoi(str_second);
}
if (str_third) {
third = atoi(str_third);
}
if (str_fourth) {
fourth = atoi(str_fourth);
}
printf("first: %d\n", first);
if (first == '0') {
HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_SET);
} else if (first == '1') {
HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_RESET);
}
}
}

first == '0'
It is completely wrong. '0' is not zero integer value only 48 in ASCII representing the character '0'.
Do not reinvent the wheel.
After the whole sting reception simply:
if(sscanf(inputBuffer, "(%d,%d,%d,%d)", &first, &second, &third, &fourth) == 4)
{
if(!first) HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_SET);
else HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_RESET);
}
STM32 uCs have much more resources than AVR uCs used in Arduino and you do not need any "tricks"
Only make sure that inputBuffer is null character terminated

Disclaimer, this is my personal preference:
For embedded, you should never use blocking I/O, always buffered, interrupt-driven I/O. I cannot say a single thing in favor of blocking I/O except it's simpler.
You say the problem is, that the LED doesn't toggle.
Let's look at the facts.
This check controls whether or not to toggle the LED:
if (first == '0') {
HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_SET);
} else if (first == '1') {
HAL_GPIO_WritePin(GPIOK, GPIO_PIN_3, GPIO_PIN_RESET);
}
I notice that you're comparing an integer with the char-values for 0 and 1 and not the integer values. You're essentially checking if first == 48/49 instead of 0/1 (see integer values https://www.asciitable.com/)
At the same time, you're converting the received string to an integer by doing
first = atoi(str_first);
Compare first against 0 or 1 instead of '0' and '1' (which are 48 and 49 respectively).

Related

Working with byte array I get an "invalid conversion from char* to byte"

I am reading and writing to an RFID tag using MFRC522.h
I can currently read the UID of a card and dump it to "UIDChar"
The UID of a card typically is 8 characters.
UID Example: 467EE9A9
I can use the mfrc522.MIFARE_SetUid function to write this UID to a new card. In order to do this I have to set the newUID to:
0x46,0x7E,0xE9,0xA9f
I have written this into my code.
What I am wanting to do is convert the UID string into a byte array so that I can use that in place of my manually written 0x46,0x7E,0xE9,0xA9.
I use the convert function to convert the UID into that format.
It can that be displayed with "buf".
Serial.println(buf);
Now my problem. If I replace the
byte newUid[] = {0x46,0x7E,0xE9,0xA9f};
with
byte newUid[] = {buf};
I get the error
invalid conversion from 'char*' to 'byte {aka unsigned char}'
How can I set my "newUid" as "buf"?
#define SS_PIN 0 //D2
#define RST_PIN 2 //D1
#include <SPI.h>
#include <MFRC522.h>
/* For RFID */
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance.
char buf[40]; // For string to byte array convertor
void convert(char *s)
{
int i, j, k;
buf[0] = 0x0;
for (j = 0, i = 0, k = 0; j < strlen(s); j++)
{
if (i++ == 0) {
buf[k++] = '0';
buf[k++] = 'x';
}
buf[k++] = s[j];
if (i == 2) {
if(j != strlen(s) -1) buf[k++] = ',';
i = 0;
}
}
buf[k] = 0x0;
}
void clone() {
/* RFID Read */
// Look for new cards
if ( ! mfrc522.PICC_IsNewCardPresent())
{
return;
}
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial())
{
return;
}
//Show UID on serial monitor
Serial.println();
Serial.print(" UID tag :");
// Very basic UID dump
unsigned int hex_num;
hex_num = mfrc522.uid.uidByte[0] << 24;
hex_num += mfrc522.uid.uidByte[1] << 16;
hex_num += mfrc522.uid.uidByte[2] << 8;
hex_num += mfrc522.uid.uidByte[3];
// Get UID
int NFC_id = (int)hex_num;
Serial.print(NFC_id, HEX);
// Convert UID to string using an int and a base (hexadecimal)
String stringUID = String(NFC_id, HEX);
char UIDChar[10];
stringUID.toCharArray(UIDChar,10);
delay(1000);
Serial.println();
// Convert to uppercase
for (int i = 0; i < strlen(UIDChar); i++ )
{
if ( UIDChar[i] == NULL ) break;
UIDChar[i] = toupper(UIDChar[i]);
}
//Serial.print( &UIDChar[0] );
Serial.println();
convert(UIDChar);
Serial.println(buf);
/* RFID Write */
// Set new UID
// Change your UID hex string to 4 byte array
// I get error if I use byte newUid[] = {buf};
/* ERROR HERE */
byte newUid[] = {0x46,0x7E,0xE9,0xA9};
if ( mfrc522.MIFARE_SetUid(newUid, (byte)4, true) ) {
Serial.println( "Wrote new UID to card." );
}
// Halt PICC and re-select it so DumpToSerial doesn't get confused
mfrc522.PICC_HaltA();
if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) {
return;
}
// Dump the new memory contents
Serial.println( "New UID and contents:" );
mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
}
void setup() {
Serial.begin ( 115200 );
/* RFID */
SPI.begin(); // Initiate SPI bus
mfrc522.PCD_Init(); // Initiate MFRC522
clone();
}
void loop() {
}
When you write
byte newUid[] = {buf};
you are trying to initialise newUid with a single element (there's only one item inside your {}), and that element is buf, which is a char* (or a char[]). That's why you get the error - you are trying to assign an array with one char* to a variable whose elements are bytes.
Without reading your full code in detail, I don't know why you are trying to do this assignment, rather than just use your buf array as it is. But to fix the problem, you probably just want to use
byte* newUid = buf;

How to continually print output inside a switch statement?

I have been trying to continually print the PWM output of pin 3 inside the switch statement condition but it only prints once. Can I continually print it in serial monitor until it meets the second conditon? or use a while loop? or a if else ?
Here is my code I also have a code with a similar function but it uses if else but still it only prints once
void loop() {
// if there's any serial available, read it:
while (Serial.available() > 0) {
int InitVal = Serial.parseInt();
int red = Serial.parseInt();
switch(InitVal) {
case 1:
if (Serial.read() == '\n') {
analogWrite(redPin, red);
Serial.println(red);
Serial.write(red);
}
break;
case 0:
analogWrite(redPin, 0);
Serial.println(0);
Serial.write(0);
break;
}
}
}
I'am planning to inter-phase this with a GUI . A GUI sends ascii to the arduino reads it then sends the output value to the GUI.
Example
1.GUI sends [1,123] : 1 = the trigger point for the switch statement ; 123 = PWM value.
Arduino receives instructions and it prints out the pwm value
GUI receives pwm value and displays it
Revised code: Stuck at the last while loop maybe i could use a threading function in arduino so that the last while loop would be satisfied/dissatisfied?
void loop() {
int InitVal = 0;
// if there's any serial available, read it:
while (Serial.available() > 0) {
int InitVal = Serial.parseInt();
int red = Serial.parseInt();
switch(InitVal) {
case 1:
if (Serial.read() == '\n') {
InitVal = 1;
//analogWrite(redPin, red);
//Serial.println(red);
// Serial.write(red);
}
break;
case 0:
InitVal = 0;
//analogWrite(redPin, 0);
//Serial.println(0);
//Serial.write(0);
break;
}
if (InitVal) /* when enabled, blink leds */ {
delay(20);
while (InitVal == 1) /* loop forever */{
Serial.println(red);
Serial.write(red);
delay(20);
}
}
}
}
I discarded Serial.parseInt() function, removed the switch statments and followed #Arno Bozo advise on serial listening while following this tutorial on http://forum.arduino.cc/index.php?topic=396450.0
I came up with what I want and here is the code
const int redPin = 3;
const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars]; // temporary array for use when parsing
// variables to hold the parsed data
boolean newData = false;
int InitVal = 0; // change to init value or red
int red = 0;
void setup() {
// initialize serial:
Serial.begin(9600);
// make the pins outputs:
pinMode(redPin, OUTPUT);
}
void loop() {
recvWithStartEndMarkers();
if (newData == true) {
strcpy(tempChars, receivedChars);
// this temporary copy is necessary to protect the original data
// because strtok() used in parseData() replaces the commas with \0
parseData();
One();
newData = false;
}
else {
Zero();
}
}
///////////////////// ///////////////////// /////////////////////
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial.available() > 0 && newData == false) {
rc = Serial.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
///////////////////// ///////////////////// /////////////////////
void parseData() { // split the data into its parts
char * strtokIndx; // this is used by strtok() as an index
strtokIndx = strtok(tempChars,","); // get the first part - the string
InitVal = atoi(strtokIndx); // copy it to messageFromPC
strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
red = atoi(strtokIndx); // convert this part to an integer
}
///////////////////// ///////////////////// /////////////////////
void One() {
if (InitVal == 0){
delay(20);
Serial.println(0);
delay(20);
}
}
///////////////////// ///////////////////// /////////////////////
void Zero() {
if (InitVal == 1){
delay(20);
Serial.println(red);
delay(20);
}
}
In Summary the code works like this
1.In serial monitor send this <1,123> : 1 = the trigger point for the switch statement ; 123 = PWM value.
Arduino receives instructions and it prints out the pwm value
If you send <0,123> it prints a zero once
I post a refined code here. The architecture may be reused for serial treatment. I have written it as an example for people I meet and who are learning with arduino.
I have made comments and explanation of ways to avoid delay. Here it is used to print current value of pwm every 1s, without stopping with a delay(1000).
#include <Arduino.h>
// with schedule(f,i) , the function f() will be called every i ms
// schedule(f,i) lines are put in loop() function
// f is of type void f(void)
#define schedule(f,i) {static unsigned long l=0;unsigned long c=millis();if((unsigned long)(c-l)>=i){l=c;f();}}
const int ledPin = 13;
void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
}
boolean newCommandHasArrived=false, newParsedCommand=false;
String personalSerialBuffer=""; // char[] would be better; but String are so convenient
enum ECommand {ecmdNoPwm=0, ecmdPwm=1, ecmdBad=10 };
ECommand cmd=ecmdNoPwm;
int cmdArg=0;
boolean readSerialBuffer(String &personalSerialBuffer);
boolean parseCommand(String &apersonalSerialBuffer, ECommand &acmd, int &acmdArg);
void executeCommand(ECommand acmd, int &acmdArg);
void printCurrentValue() {Serial.println(String("cval:") + cmdArg);}
void loop() {
// transfer serial buffer in personal buffer
newCommandHasArrived = readSerialBuffer(personalSerialBuffer);
if (newCommandHasArrived) {
newCommandHasArrived = false;
newParsedCommand = parseCommand(personalSerialBuffer, cmd, cmdArg);
}
if (newParsedCommand) {
newParsedCommand = false;
executeCommand(cmd, cmdArg);
}
// I print current value every 1000ms
//delay(1000); // you can often use delay without pb, but it is a bad usage
// Here I provide you with a quick way to execute a task every 1000ms
{
const unsigned long delayBetweenExecution=1000;
static unsigned long lastTime=0;
unsigned long current = millis();
// note that C++ says that overflow on unsigned is well defined
// it calculates modulo arithmetic
if ((unsigned long)(millis() - lastTime) >= delayBetweenExecution) {
lastTime = current;
Serial.println(String("cval:") + cmdArg);
}
}
// We can make it shorter thanks to a macro:
// but you have to define a void function(void) that uses only global variable
// because it has no argument :
// void printCurrentValue() {Serial.print(String("cval:") + cmdArg);}
//schedule(printCurrentValue, 1000);
}
boolean readSerialBuffer(String &personalSerialBuffer) {
if (Serial.available() > 0) {
personalSerialBuffer.concat(Serial.readString());
}
// the frame is considered finished, if it ends with \n
if (personalSerialBuffer.endsWith("\n"))
return true;
else
return false;
}
boolean parseCommand(String &apersonalSerialBuffer, ECommand &acmd, int &acmdArg) {
// format [ 1, 123]\n
// I omit [ then I read first int : 1
// Note: I cannot detect if no int is found because it will return 0 that is a valid cmd
int readCmd = apersonalSerialBuffer.substring(1).toInt();
// conversion readCmd to acmd
switch (readCmd) {
case 0:
acmd = ecmdNoPwm; break;
case 1:
acmd = ecmdPwm; break;
default:
Serial.println(String("new command unknown: ") +
apersonalSerialBuffer);
apersonalSerialBuffer = "";
return false;
}
// find beginning of 2nd part, separated by ','
int sepPos = apersonalSerialBuffer.indexOf(',');
// no ',' : indexOf returns -1
if (sepPos == -1) {
Serial.println(String("new command could not be parsed: ") +
apersonalSerialBuffer);
apersonalSerialBuffer = "";
return false;
}
// Note: I cannot detect if no int is found because it will return 0 that is a valid cmd
acmdArg = apersonalSerialBuffer.substring(sepPos+1).toInt();
// All is fine
// I have to reset buffer before leaving
apersonalSerialBuffer = "";
return true;
}
void executeCommand(ECommand acmd, int &acmdArg) {
switch(acmd) {
case ecmdNoPwm:
// I erase acmdArg
acmdArg = 0;
analogWrite(ledPin, acmdArg);
Serial.println("cmd no pwm");
break;
case ecmdPwm:
analogWrite(ledPin, acmdArg);
Serial.print("cmd pwm:"); Serial.println(acmdArg);
break;
default:
analogWrite(ledPin, 0);
Serial.println("Bad cmd");
}
}

receive/transmit over rs232 with arm lpc2148 on sparkfun logomatic

I am trying to program the logomatic by sparkfun, and yes I have used their forum with no responses, and having some issues. I am trying to send characters to the UART0 and I want the logomatic to respond with specific characters and not just an echo. For example, I send 'ID?' over the terminal (using RealTerm), and the logomatic sends back '1'. All it will so now is echo.
I am using c with programmers notepad with the WinARM toolchain. The following snippet is from the main.c file. I only included this, because I am fairly certain that this is where my problem lies
void Initialize(void)
{
rprintf_devopen(putc_serial0);
PINSEL0 = 0xCF351505;
PINSEL1 = 0x15441801;
IODIR0 |= 0x00000884;
IOSET0 = 0x00000080;
S0SPCR = 0x08; // SPI clk to be pclk/8
S0SPCR = 0x30; // master, msb, first clk edge, active high, no ints
}
Notice the rprintf_devopen function, below is from the rprintf.c file, and due to my mediocre skills, I do not understand this bit of code. If I comment out the rprintf_devopen in main, the chip never initializes correctly.
static int (*putcharfunc)(int c);
void rprintf_devopen( int(*put)(int) )
{
putcharfunc = put;
}
static void myputchar(unsigned char c)
{
if(c == '\n') putcharfunc('\r');
putcharfunc(c);
}
Now, below is from the serial.c file. So my thought was that I should be able to just call one of these putchar functions in main.c and that it would work, but it still just echoes.
int putchar_serial0 (int ch)
{
if (ch == '\n')
{
while (!(U0LSR & 0x20));
U0THR = CR; // output CR
}
while (!(U0LSR & 0x20));
return (U0THR = ch);
}
// Write character to Serial Port 0 without \n -> \r\n
int putc_serial0 (int ch)
{
while (!(U0LSR & 0x20));
return (U0THR = ch);
}
// Write character to Serial Port 1 without \n -> \r\n
int putc_serial1 (int ch)
{
while (!(U1LSR & 0x20));
return (U1THR = ch);
}
void putstring_serial0 (const char *string)
{
char ch;
while ((ch = *string))
{
putchar_serial0(ch);
string++;
}
}
I have tried calling the different putchar functions in main, also with the rprintf_devopen. Still just echoes. I have altered the putchar functions and still just echoes. I have tried just writing to the U0THR register in main.c and no luck. Keep in mind that I am still a student and my major is electrical engineering, so the only programming classes that I have taken are intro to c, and an intro to vhdl. I am more of a math and physics guy. I was working on this for an internship I was doing. The internship ended, but it just bugs me that I cannot figure this out. Honestly, working on this program taught me more that the c class that I took. Anyways, I appreciate any help that can be offered, and let me know if you want to see the entire code.
Below is an update to the question. This function is in main.c
static void UART0ISR(void)
{
char temp;
trig = 13; //This is where you set the trigger character in decimal, in this case a carriage return.
temp = U0RBR; //U0RBR is the receive buffer on the chip, refer to datasheet.
if(temp == query1[counter1]) //This segment looks for the characters "ID?" from the U0RBR
{ //query1 is defined at the top of the program
counter1++;
if(counter1 >= 3)
{
flag1 = 1; //This keeps track of whether or not query1 was found
counter1 = 0;
stat(1,ON);
delay_ms(50);
stat(1,OFF);
RX_in = 0;
temp = 0;
//rprintf("\n\rtransmission works\n");
putc_serial1(49);
}
}
if(temp == query2[counter2] && flag1 == 1) //This segment looks for "protov?" from the U0RBR, but only after query1 has been found
{
counter2++;
if(counter2 >= 7)
{
flag2 = 1; //This keeps track of whether or not query2 was found
counter2 = 0;
stat(1,ON);
delay_ms(50);
stat(1,OFF);
RX_in = 0;
temp = 0;
putc_serial1(49);
}
}
if(temp == stop[counter3]) //This if segment looks for certain characters in the receive buffer to stop logging
{
counter3++;
if(counter3 >= 2)
{
flagstop = 1; //This flagstop keeps track of whether or not stop was found. When the stop characters are found,
flag1 = 0; //the query1 and query2 flags will be reset. So, in order to log again these queries must be sent again
flag2 = 0; //this may seem obvious, but deserves mention.
counter3 = 0;
stat(1,ON);
delay_ms(500);
stat(1,OFF);
RX_in = 0;
temp = 0;
}
flagstop = 0; //Reset the stop flag in order to wait once again for the query 1&2
}
if(RX_in == 0)
{
memset (RX_array1, 0, 512); // This clears the RX_array to make way for new data
memset (RX_array2, 0, 512);
}
if(RX_in < 512 && flag1 == 1 && flag2 == 1) //We cannot log data until we see both flags 1 & 2 and after we see these flags,
{ //we must then see the trigger character "carriage return"
RX_array1[RX_in] = temp;
RX_in++;
if(temp == trig)
{
RX_array1[RX_in] = 10; // delimiters
log_array1 = 1;
RX_in = 0;
}
}
else if(RX_in >= 512 && flag1 == 1 && flag2 == 1) //This else if is here in case the RX_in is greater than 512 because the RX_arrays are defined to
{ //be of size 512. If this happens we don't want to lose data, so we must put the overflow into another register.
RX_array2[RX_in - 512] = temp;
RX_in++;
RX_array1[512] = 10; // delimiters
RX_array1[512 + 1] = 13;
log_array1 = 1;
if(RX_in == 1024 || temp == trig)
{
RX_array2[RX_in - 512] = 10; // delimiters
log_array2 = 1;
RX_in = 0;
}
}
temp = U0IIR; // have to read this to clear the interrupt
VICVectAddr = 0;
}

Missing events on ReadConsoleInput in windows shell?

As I proceed in my (possibly vain) attempt to reimplement a curses style library that supports both *nix and windows under an MIT license, I've stumbled onto a problem reading terminal import using the windows api.
Basically, I don't get all the events I expect to, and I don't know why.
First I setup the terminal to be in non-buffering mode:
DWORD mode;
HANDLE hstdin = GetStdHandle( STD_INPUT_HANDLE );
// Save old mode
GetConsoleMode(hstdin, &mode);
// Set to no line-buffering, no echo, no special-key-processing
SetConsoleMode(hstdin, 0);
Then I use PeekConsoleInput and ReadConsoleInput in a loop to have a non blocking key press input; the equivalent of using termios.h and select on stdin in linux:
__EXPORT int sterm_read(void *state) {
DWORD dwRead;
INPUT_RECORD inRecords[1];
PeekConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &inRecords[0], 1, &dwRead);
if (dwRead > 0) {
ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &inRecords[0], 1, &dwRead);
if (inRecords[0].EventType == KEY_EVENT) {
if (inRecords[0].Event.KeyEvent.bKeyDown) {
return inRecords[0].Event.KeyEvent.wVirtualKeyCode;
}
}
}
return -1;
}
Ignore the state variable; that's so the api can accept an arbitrary state struct on various platforms.
Now if I try to use this code:
#include <sterm.h>
#include <stdio.h>
#define assert(v, msg) if (!v) { printf("FAILED! %s", msg); return 1; }
int main(void) {
void *state = sterm_init();
int i;
char c;
for (;;) {
if ((c = sterm_read(state)) == 81) { // ie. press q to exit
break;
}
if (c != -1) {
sterm_write(state, &c, 1); // This is a thin wrapper around _write(1, ...)
}
}
sterm_shutdown(state);
return 0;
}
It almost works. I get the input character I press pushed out to the terminal... mostly.
Probably every 10th character press is recorded. If I type quickly, the API 'loses' events, and I get "HEO WLD" instead of "HELLO WORLD".
What's going on? Does ReadConsoleInput somehow clear the input buffer?
Am I doing something wrong? It seems almost like I'm only getting events based on a race condition which is 'is key pressed when PeekConsoleInput is called'.
...but surely that shouldn't be the case? The point of using these buffered I/O interfaces (instead of GetAsyncKeyState) is that the events should be buffered right?
Help!
I also have discovered that events are not guaranteed to stay around to be read.
This makes sense because otherwise the OS would need to provide lots and lots of buffering space.
The best I can do to deal with this is this code to do my own buffering
but clearly pastes of more than 128 characters will often fail :
static int g_eaten_ct = 0; /* Re-eaten char */
static int g_eaten_ix = -1;
static int g_eaten[128];
void reeat(int c)
{ g_eaten_ct += 1;
g_eaten[g_eaten_ix + g_eaten_ct] = c; /* save the char for later */
}
void flush_typah()
{
g_eaten_ct = 0;
g_eaten_ix = -1;
while (_kbhit())
(void)ttgetc();
}
int ttgetc()
{ if (g_eaten_ct > 0)
{ g_eaten_ct -= 1;
return g_eaten[++g_eaten_ix];
}
{ int totalwait = g_timeout_secs;
int oix = -1;
while (1)
{ int got,need;
const DWORD lim = 1000;
INPUT_RECORD rec[32];
int cc = WaitForSingleObject(g_ConsIn, lim);
switch(cc)
{ case WAIT_OBJECT_0:
need = sizeof(g_eaten)/sizeof(g_eaten[0]) - oix;
if (need > 32)
need = 32;
cc = ReadConsoleInput(g_ConsIn,&rec[0],need,(DWORD*)&got);
if (cc && got > 0)
break;
#if _DEBUG
{ DWORD errn = GetLastError();
if (errn != 6)
mlwrite("%pError %d %d ", cc, errn);
}
#endif
continue;
case WAIT_TIMEOUT:
#if _DEBUG
if (g_got_ctrl)
{ g_got_ctrl = false;
return (int)(CTRL | 'C');
}
#endif
if (--totalwait == 0) // -w opt
exit(2);
// drop through
default:continue;
}
{ int ix = -1;
while (++ix < got)
{ INPUT_RECORD * r = &rec[ix];
if (r->EventType == KEY_EVENT && r->Event.KeyEvent.bKeyDown)
{ int ctrl = 0;
int keystate = r->Event.KeyEvent.dwControlKeyState;
if (keystate & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
{ ctrl |= CTRL;
g_chars_since_ctrl = 0;
}
{ int chr = r->Event.KeyEvent.wVirtualKeyCode;
if (in_range(chr, 0x10, 0x12))
continue; /* shifting key only */
if (keystate & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
ctrl |= ALTD;
else
chr = r->Event.KeyEvent.uChar.AsciiChar & 0xff;
if (/*chr != 0x7c && */ (chr | 0x60) != 0x7c) // | BSL < or ^ BSL
{ int vsc = r->Event.KeyEvent.wVirtualScanCode;
if (in_range(vsc, SCANK_STT, 0x58))
{ ctrl |= SPEC;
chr = scantokey[vsc - SCANK_STT];
}
// else if (in_range(vsc, 2, 10) && chr == 0)
// chr = '0' - 1 + vsc;
}
if ((keystate & SHIFT_PRESSED) && ctrl) // exclude e.g. SHIFT 7
ctrl |= SHFT;
g_eaten[++oix] = ctrl | (chr == 0xdd ? 0x7c : chr);
++g_chars_since_ctrl;
}}
else if (r->EventType == MENU_EVENT)
{ /*loglog1("Menu %x", r->Event.MenuEvent.dwCommandId);*/
}
}
if (got == need && oix < sizeof(g_eaten) / sizeof(int))
{ PeekConsoleInput(g_ConsIn, &rec[0], 1, (DWORD*)&got);
if (got > 0)
continue;
}
if (oix >= 0)
{ g_eaten_ct = oix;
g_eaten_ix = 0;
return g_eaten[0];
}
}}
}}

Parsing code for GPS NMEA string

i am trying to parse the incoming GPGGA NMEA GPS string using Arduino uno and below code.
What i am trying to do is that i am using only GPGGA NMEA string to get the values of Latitude, longitude and altitude.In my below code, i had put certain checks to check if incoming string is GPGGA or not, and then store the further string in a array which can be further parsed suing strtok function and all the 3 GPS coordinates can be easily find out.
But i am unable to figure out how to store only GPGGA string and not the further string.I am using a for loop but it isn't working.
I am not trying to use any library.I had came across certain existing codes like this.
Here is the GPGGA string information link
i am trying to have following functionlity
i) Check if incoming string is GPGGA
ii) If yes, then store the following string upto EOL or upto * (followed by checksum for the array) in a array, array length is variable(i am unable to find out solution for this)
iii) Then parse the stored array(this is done, i tried this with a different array)
#include <SoftwareSerial.h>
SoftwareSerial mySerial(10,11); // 10 RX / 11 TX
void setup()
{
Serial.begin(9600);
mySerial.begin(9600);
}
void loop()
{
uint8_t x;
char gpsdata[65];
if((mySerial.available()))
{
char c = mySerial.read();
if(c == '$')
{char c1 = mySerial.read();
if(c1 == 'G')
{char c2 = mySerial.read();
if(c2 == 'P')
{char c3 = mySerial.read();
if(c3 == 'G')
{char c4 = mySerial.read();
if(c4 == 'G')
{char c5 = mySerial.read();
if(c5 == 'A')
{for(x=0;x<65;x++)
{
gpsdata[x]=mySerial.read();
while (gpsdata[x] == '\r' || gpsdata[x] == '\n')
{
break;
}
}
}
else{
Serial.println("Not a GPGGA string");
}
}
}
}
}
}
}
Serial.println(gpsdata);
}
Edit 1:
Considering Joachim Pileborg, editing the for loop in the code.
I am adding a pic to show the undefined output of the code.
Input for the code:
$GPGGA,092750.000,5321.6802,N,00630.3372,W,1,8,1.03,61.7,M,55.2,M,,*76
$GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A
$GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70
$GPGSV,3,2,11,02,39,223,19,13,28,070,17,26,23,252,,04,14,186,14*79
$GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76
$GPRMC,092750.000,A,5321.6802,N,00630.3372,W,0.02,31.66,280511,,,A*43
$GPGGA,092751.000,5321.6802,N,00630.3371,W,1,8,1.03,61.7,M,55.3,M,,*75
$GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A
$GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70
$GPGSV,3,2,11,02,39,223,16,13,28,070,17,26,23,252,,04,14,186,15*77
$GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76
$GPRMC,092751.000,A,5321.6802,N,00630.3371,W,0.06,31.66,280511,,,A*45
After a quick check of the linked article on the NMEA 0183 protocol, this jumped out at me:
<CR><LF> ends the message.
This means, that instead of just read indiscriminately from the serial port, you should be looking for that sequence. If found, you should terminate the string, and break out of the loop.
Also, you might want to zero-initialize the data string to begin with, to easily see if there actually is any data in it to print (using e.g. strlen).
You could use some functions from the C library libnmea. Theres functions to split a sentence into values by comma and then parse them.
Offering this as a suggestion in support of what you are doing...
Would it not be useful to replace all of the nested if()s in your loop with something like:
EDIT added global string to copy myString into once captured
char globalString[100];//declare a global sufficiently large to hold you results
void loop()
{
int chars = mySerial.available();
int i;
char *myString;
if (chars>0)
{
myString = calloc(chars+1, sizeof(char));
for(i=0;i<chars;i++)
{
myString[i] = mySerial.read();
//test for EOF
if((myString[i] == '\n') ||(myString[i] == '\r'))
{
//pick this...
myString[i]=0;//strip carriage - return line feed(or skip)
//OR pick this... (one or the other. i.e.,I do not know the requirements for your string)
if(i<chars)
{
myString[i+1] = mySerial.read() //get remaining '\r' or '\n'
myString[i+2]=0;//add null term if necessary
}
break;
}
}
if(strstr(myString, "GPGGA") == NULL)
{
Serial.println("Not a GPGGA string");
//EDIT
strcpy(globalString, "");//if failed, do not want globalString populated
}
else
{ //EDIT
strcpy(globalString, myString);
}
}
//free(myString) //somewhere when you are done with it
}
Now, the return value from mySerial.available() tells you exactly how many bytes to read, you can read the entire buffer, and test for validity all in one.
I have a project that will need to pull the same information out of the same sentence.
I got this out of a log file
import serial
import time
ser = serial.Serial(1)
ser.read(1)
read_val = ("nothing")
gpsfile="gpscord.dat"
l=0
megabuffer=''
def buffThis(s):
global megabuffer
megabuffer +=s
def buffLines():
global megabuffer
megalist=megabuffer.splitlines()
megabuffer=megalist.pop()
return megalist
def readcom():
ser.write("ati")
time.sleep(3)
read_val = ser.read(size=500)
lines=read_val.split('\n')
for l in lines:
if l.startswith("$GPGGA"):
if l[:len(l)-3].endswith("*"):
outfile=open('gps.dat','w')
outfile.write(l.rstrip())
outfile.close()
readcom()
while 1==1:
readcom()
answer=raw_input('not looping , CTRL+C to abort')
The result is this:
gps.dat
$GPGGA,225714.656,5021.0474,N,00412.4420,W,0,00,50.0,0.0,M,18.0,M,0.0,0000*5B
Using "malloc" every single time you read a string is an enormous amount of computational overhead. (And didn't see the corresponding free() function call. Without that, you never get that memory back until program termination or system runs out of memory.) Just pick the size of the longest string you will ever need, add 10 to it, and declare that your string array size. Set once and done.
There are several C functions for getting substrings out of a string, strtok() using the coma is probably the least overhead.
You are on an embedded microcontroller. Keep it small, keep overhead down. :)
#include <stdio.h>
#include <string.h>
#define GNSS_HEADER_LENGTH 5
#define GNSS_PACKET_START '$'
#define GNSS_TOKEN_SEPARATOR ','
#define bool int
#define FALSE 0
#define TRUE 1
//To trim a string contains \r\n
void str_trim(char *str){
while(*str){
if(*str == '\r' || *str == '\n'){
*str = '\0';
}
str++;
}
}
/**
* To parse GNSS data by header and the index separated by comma
*
* $GPGSV,1,1,03,23,39,328,30,18,39,008,27,15,33,035,33,1*5A
* $GNRMC,170412.000,V,,,,,,,240322,,,N,V*2D
* $GNGGA,170412.000,,,,,0,0,,,M,,M,,*57
*
* #data_ptr the pointer points to gps data
* #header the header for parsing GPGSV
* #repeat_index the header may repeat for many lines
* so the header index is for identifying repeated header
* #token_index is the index of the parsing data separated by ","
* the start is 1
* #result to store the result of the parser input
*
* #result bool - parsed successfully
**/
bool parse_gnss_token(char *data_ptr, char *header, int repeat_index, int token_index, char *result) {
bool gnss_parsed_result = FALSE; // To check GNSS data parsing is success
bool on_header = FALSE;
// For header
int header_repeat_counter = 0;
int header_char_index = 0; // each char in header index
// For counting comma
int counted_token_index = 0;
// To hold the result character index
bool data_found = FALSE;
char *result_start = result;
char header_found[10];
while (*data_ptr) {
// 1. Packet start
if (*data_ptr == GNSS_PACKET_START) {
on_header = TRUE;
header_char_index = 0; // to index each character in header
data_found = FALSE; // is data part found
data_ptr++;
}
// 2. For header parsing
if (on_header) {
if (*data_ptr == GNSS_TOKEN_SEPARATOR || header_char_index >= GNSS_HEADER_LENGTH) {
on_header = FALSE;
} else {
header_found[header_char_index] = *data_ptr;
if (header_char_index == GNSS_HEADER_LENGTH - 1) { // Now Header found
header_found[header_char_index + 1] = '\0';
on_header = FALSE;
if (!strcmp(header, header_found)) {
// Some headers may repeat - to identify it set the repeat index
if (header_repeat_counter == repeat_index) {
//printf("Header: %s\r\n", header_found );
data_found = TRUE;
}
header_repeat_counter++;
}
}
header_char_index++;
}
}
// 3. data found
if (data_found) {
// To get the index data separated by comma
if (counted_token_index == token_index && *data_ptr != GNSS_TOKEN_SEPARATOR) {
// the data to parse
*result++ = *data_ptr;
gnss_parsed_result = TRUE;
}
if (*data_ptr == GNSS_TOKEN_SEPARATOR) { // if ,
counted_token_index++; // The comma counter for index
}
// Break if the counted_token_index(token_counter) greater than token_index(search_token)
if (counted_token_index > token_index) {
break;
}
}
// Appending \0 to the end
*result = '\0';
// To trim the data if ends with \r or \n
str_trim(result_start);
// Input data
data_ptr++;
}
return gnss_parsed_result;
}
int main()
{
char res[100];
char *nem = "\
$GNRMC,080817.000,A,0852.089246,N,07636.289920,E,0.00,139.61,270322,,,A,V*04\r\n\\r\n\
$GNGGA,080817.000,0852.089246,N,07636.289920,E,1,5,1.41,11.246,M,-93.835,M,,*5E\r\n\
$GNVTG,139.61,T,,M,0.00,N,0.00,K,A*2F\r\n\
$GNGSA,A,3,30,19,17,14,13,,,,,,,,1.72,1.41,0.98,1*0A\r\n\
$GNGSA,A,3,,,,,,,,,,,,,1.72,1.41,0.98,3*02\r\n\
$GNGSA,A,3,,,,,,,,,,,,,1.72,1.41,0.98,6*07\r\n\
$GPGSV,3,1,12,06,64,177,,30,60,138,15,19,51,322,18,17,42,356,27,1*68\r\n\
$GPGSV,3,2,12,14,36,033,17,07,34,142,17,13,32,267,17,02,21,208,,1*6C\r\n\
$GPGSV,3,3,12,15,05,286,,01,05,037,,03,03,083,,20,02,208,,1*6B\r\n\
$GAGSV,1,1,00,7*73\r\n\
$GIGSV,1,1,00,1*7D\r\n\
$GNGLL,0852.089246,N,07636.289920,E,080817.000,A,A*43\r\n\
$PQTMANTENNASTATUS,1,0,1*4F\r\n";
printf("Parsing GNRMC\r\n");
printf("===============\r\n");
for(int i=1;i<=16;i++){
parse_gnss_token(nem, "GNRMC", 0, i, res);
printf("Index: %d, Result: %s\r\n", i, res);
}
printf("Parsing GNVTG (First Parameter)\r\n");
printf("================================");
// GNVTG - Header, 0 - Repeat Index(if header is repeating), 1 - Value Index,
parse_gnss_token(nem, "GNVTG", 0, 1, res);
printf("\r\nGNVTG: %s\r\n", res);
return 0;
}

Resources