Transmit integer value serially using AT89S51 - c

I have written a program to transmit char value serially for AT89S51. Its is working perfectly.
Program is given below:-
#include<reg51.h>
void main()
{
TMOD=0x20;
TH1=0xFD;
SCON=0x50;
TR1=1;
while(1)
{
SBUF='A';
while(TI==0);
TI=0;
}
}
In above code char 'A' is transmitted.
Now I want to transmit an integer value and I have written a program for it.
Program is given below:-
#include<reg51.h>
void main()
{
int i=61;
TMOD=0x20;
TH1=0xFD;
SCON=0x50;
TR1=1;
while(1)
{
SBUF=i;
while(TI==0);
TI=0;
}
}
Above program is transmitting ' = ' (i.e decimal 61 corresponds to ' = ' character in ASCII).
I want to know how I can transmit an integer value.
Please guide me in this regard.

SBUF contains a single byte (i.e. char) to be transmitted. If you put 'A' there, that's what will be transmitted (in fact 0x41 will be transmitted, which corresponds to ASCII value of 'A'). When assigning a value of i into SBUF, it will be interpreted as byte regardless of type of i. This byte can be interpreted in any way the receiving party desires - it can treat it as integer or as ASCII value, it's the same as far as transmission goes; the difference is in the way the data is treated.

Icepack is right if you only wanted to transmit a char or unsigned char, but if you really wanted to transmit more than 8 bits you will have to do it byte by byte. What you are trying to do requires putting bytes into the array, and sending them over line one byte at a time (SBUF in 8051 can only hold single TX and single RX value at a time). Than you have another issue, do you transmit most significant byte first, or last? Then you should ask yourself if you wanted to transmit binary data (just spit the bytes over the wire) assuming the other party knew your data format? Or do you want to work with strings, so that number '34567' will take for example five bytes (five ASCII codes) or more if you wanted some kind of terminator, line feed, or other non printable characters, while binary it would really be an integer taking two bytes on the 8051. As you can see, your question opens to many other questions.

Related

How to code ASCII Text Based protocol over RS-232 in C

I have to implement a relatively simple communication protocol on top of RS-232.
It's an ASCII based text protocol with a couple of frame types.
Each frame looks something like this:
* ___________________________________
* | | | | |
* | SOH | Data | CRC-16 | EOT |
* |_____|_________|_________|________|
* 1B nBytes 2B 1B
Start Of Header (1 Byte)
Data (n-Bytes)
CRC-16 (2 Bytes)
EOT (End Of Transmission)
Each data-field needs to be separated by semicolon ";":
for example, for HEADER type data (contains code,ver,time,date,src,id1,id2 values):
{code};{ver};{time};{date};{src};{id1};{id2}
what is the most elegant way of implementing this in C is my question?
I have tried defining multiple structs for each type of frame, for example:
typedef struct {
uint8_t soh;
char code;
char ver;
Time_t time;
Date_t date;
char src; // Unsigned char
char id1[20]; // STRING_20
char id2[20]; // STRING_20
char crlf;
uint16_t crc;
uint8_t eot;
} stdHeader_t;
I have declared a global buffer:
uint8_t DATA_BUFF[BUFF_SIZE];
I then have a function sendHeader() in which I want to use RS-232 send function to send everything byte by byte by casting the dataBuffer to header struct and filling out the struct:
static enum_status sendHeader(handle_t *handle)
{
uint16_t len;
enum_RETURN_VALUE rs232_err = OK;
enum_status err = STATUS_OK;
stdHeader_t *header = (stdHeader_t *)DATA_BUFF;
memset(DATA_BUFF, 0, size);
header ->soh= SOH,
header ->code= HEADER,
header ->ver= 10, // TODO
header ->time= handle->time,
header ->date= handle->date,
header ->src= handle->config->source,
memset(header ->id1,handle->config->id1, strlen(handle->config->id1));
memset(header ->id2,handle->config->id2, strlen(handle->config->id1));
header ->crlf = '\r\n',
header ->crc = calcCRC();
header ->eot = EOT;
len = sizeof(stdHeader_t );
do
{
for (uint16_t i = 0; i < len; i++)
{
rs232_err= rs232_tx_send(DATA_BUFF[i], 1); // Send one byte
if (rs232_err!= OK)
{
err = STATUS_ERR;
break;
}
}
// Break do-while loop if there is an error
if (err == STATUS_ERR)
{
break;
}
} while (conditions);
return err;
}
My problem is that I do not know how to approach the problem of handling ascii text based protocol,
the above principle would work very well for byte based protocols.
Also, I do not know how to implement semicolon ";" seperation of data in the above snippet, as everything is sent byte by byte, I would need aditional logic to know when it is needed to send ";" and with current implementation, that would not look very good.
For fields id1 and id2, I am receiveing string values as a part of handle->config, they can be of any lenght, but max is 20. Because of that, with current implementation, I would be sending more than needed in case actual lenght is less than 20, but I cannot use pointers to char inside the struct, because in that case, only the pointer value would get sent.
So to sumarize, the main question is:
How to implement the above described text based protocol for rs-232 in a nice and proper way?
what is the most elegant way of implementing this (ASCII Text Based protocol) in C is my question?
Since this is ASCII, avoid endian issues of trying to map a multi-byte integer. Simply send an integer (including char) as decimal text. Likewise for floating point, use exponential notation and sufficient precision. E.g. sprintf(buf, "%.*e", DBL_DECIMAL_DIG-1, some_double);. Allow "%a" notation.
Do not use the same code for SOH and EOT. Different values reduce receiver confusion.
Send date and time using ISO 8601 as your guide. E.g. "2022-11-10", "23:38:42".
Send string with a leading/trailing ". Escape non-printable ASCII characters, and ", \, ;. Example for 10 long string 123\\;\"\xFF456 --> "123\\\;\"\xFF456".
Error check, like crazy, the received data. Reject packets of data for all sorts of reasons: field count wrong, string too long, value outside field range, bad CRC, timeout, any non-ASCII character received.
Use ASCII hex characters for CRC: 4 hex characters instead of 2 bytes.
Consider a CRC 32 or 64.
Any out-of-band input, (bytes before receiving a SOF) are silently dropped. This nicely allows an optional LF after each command.
Consider the only characters between SOH/EOT should be printable ASCII: 32-126. Escape others as needed.
Since "it's an ASCII based text protocol with a couple of frame types.", I'd expect a type field.
See What type of framing to use in serial communication for more ideas.
First of all, structs are really not good for representing data protocols. The struct in your example will be filled to the brim with padding bytes everywhere, so it is not a proper nor portable representation of the protocol. In particular, forget all about casting a struct to/from a raw uint8_t array - that's problematic for even more reasons: the first address alignment and pointer aliasing.
In case you insist on using a struct, you must write serialization/deserialization routines that manually copy to/from each member into the raw uint8_t buffer, which is the one that must be used for the actual transmission.
(De)serialization routines might not be such a bad idea anyway, because of another issue not addressed by your post: network endianess. RS-232 protocols are by tradition almost always Big Endian, but don't count on it - endianess must be documented explicitly.
My problem is that I do not know how to approach the problem of handling ascii text based protocol, the above principle would work very well for byte based protocols.
That is a minor problem compared to the above. Often it is acceptable to have a mix of raw data (essentially everything but the data payload) and ASCII text. If you want a pure ASCII protocol you could consider something like "AT commands", but they don't have much in the way of error handling. You really should have a CRC16 as well as sync bytes. Hint: preferably pick the first sync byte as something that don't match 7 bit ASCII. That is something with MSB set. 0xAA is popular.
Once you've sorted out data serialization, endianess and protocol structure, you can start to worry about details such as string handling in the payload part.
And finally, RS232 is dinosaur stuff. There's not many reasons why one shouldn't use RS422/RS485. The last argument for using RS232, "computers come with RS232 COM ports", went obsolete some 15-20 years back.
One thing your struct implementation is missing is packing. For efficiency reasons, depending on which processor your code is running on, the compiler will add padding to the structure to align on certain byte boundaries. Normally this doesn't effect you code that much, but if you are sending this data across a serial stream where every byte matters, then you will be sending random zeros across as well.
This article explains padding well, and how to pack your structures for use cases like yours
Structure Padding

How come the read function in this C program can read chinese characters and other not ASCII values?

I don't understand why I'm able to read() every character I type on my terminal but if I try to assign a non ascii value to a C variable it doesn't work.
There are three main questions below this code ->
int main (){
int fd;
fd = open("./dog.txt",O_RDONLY);
//contents of dog.txt -> 漢è hello
ssize_t r;
char b;
while( (r = read( fd, &b, sizeof(b))) > 0 ){
write(STDOUT_FILENO,&b, sizeof(b));
}
printf("\n");
//OUTPUT : 漢è hello
}
However something like this is not accepted :
int main (){
unsigned int test = '漢';
write(STDOUT_FILENO,&test,sizeof(test));
printf("\n");
}
The c program receives a series of bytes one at a time and then it sends them back one at a time to the terminal through the write system call ( the buffer in the example is 1 Byte ).
But how does the terminal know that it must "interpret" the chinese character as a group of 3 Bytes when I write()? Considering that I'm writing 1 Byte at a time it could have well interpreted each single Byte as three different 8 Bytes characters.
Is there some sort of cooperation between the process and the terminal to make this possible?
Could someone provide a straight to the point explanation of character encodings in both terminal and programs (in this case C)?
But how does the terminal know that it must "interpret" the chinese character as a group of 3 Bytes when I write() ? ...
The terminal sees a stream of bytes and tries to decode that stream into characters irrespectively of whether they were written with one write call or multiple calls.
The exact way it decodes the stream depends on the encoding used in your system. I assume that your system uses UTF-8, because that's an encoding where 漢 is represented with the sequence of the three bytes e6 bc a2 (here in hexadecimal). In UTF-8, the number of bytes the character takes is determined by its first byte. UTF-8 is actually ingenious for that and a few other reasons. For details you should refer the Wikipedia page on UTF-8.
Is there some sort of cooperation between the process and the terminal to make this possible ?
The process and the terminal both follow the system convention about which encoding to use. On UNIX systems that's determined by the value of the LANG, LC_ALL (or some other) environment variables. This might be seen as 'cooperation', but there's definitively no two-way communication between them other than the respective byte streams.
However something like this is not accepted : ...
It actually may work on some implementations. However the exact meaning of character literals (single-quoted strings) with multi-byte characters or multiple characters is not defined per the standard.
What is going to work on most UNIX systems though, is using a string literal and saving the source file in UTF-8:
char test[] = "漢";
write(STDOUT_FILENO, test, strlen(test));
printf("\n");

How to Send Hex string from uart to microcontroller and store it to integer for future "if" statement

okay, im really beg for a help here, cause im hittin a rock bottom. ive spend weeks to do this and still not able to.
i have an avr, i will recive a string containing hex value in it from UART.
ex :
0x3cffaa31
i need to split it into
0x3c
0xff
0xaa
0x31
and store it into a variable so i can do if statement with it.
how can i achieve this, please help me. i already lookin here and there on the internet yet i still lost.
uint8_t Values[4]={0};
uint8_t Loc=0;
uint32_t Mask=0xFF; //32 bits UART Rx Buffer size
for(uint8_t i=0;i<=24;i+=8)
{
Values[Loc]=(((Mask<<i) & UartRxBuf) >> i);
Loc++;
}
Help me out if I'm interpreting this wrong, but it sounds like you need to;
split the incoming string from the UART into indidual 2-character strings, each one representing a byte of hex. If you will always have 4 bytes of data, in the format that you've shown, then this will be easy. e.g. you always know that the two characters of the first byte are at index 2 and 3 of the char[] holding your input string.
Convert each two-character string to an int, so you can use them for numerical calculations. Look at strtol, which is available in AVR libc, for this http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__stdlib_1gaf8ce3b8dae3d45c34c3b172de503f7b3.html
Make sure to specify base16 for strtol-- e.g.:
long converted = strtol(digits, NULL, 16);
(where digits is a char[] containing your two hex characters, null-terminated)
UPDATE- looks like strtol doesn't care if the string has a preceding 0x, so something like this would work to get the first hex value from the raw string:
const char *raw = "0x3cffaa31";
char digits[3];
long converted;
/* Copy two bytes from 'raw', starting from index 2,
* (so we can skip the '0x') to get the string "3c" */
strncpy(digits, raw + 2, 2);
/* Make sure the new string is null-terminated */
digits[2] = '\0';
/* Convert hex string to a long. Now you can use it
* in an if-statement */
converted = strtol(digits, NULL, 16);
I donot think this is an issue
If you are using uart in 8bit mode you will recieve only one byte data at a time .
Just increase the array index every time you recieve the data
for(i = 0; i<4; i++){
while(UART_recieve_not_completed);
arr[i] = UART_RX_BUFF;}
Hope this helps

Error While Sending byte array serialy using Serial.write

Error While Sending byte array serialy using Serial.write.
byte buf[] = {125, 126, 127, 2000, 5000};
int i = Serial.write(buf, sizeof(buf));
for(int i = 0; i < (sizeof(buf) / sizeof(buf[0])); i++)
{
Serial.println(buf[i]);
}
output :
}~??125
126
127
208
136
Any while for Unknown charcters at start. I am using Arduino 1.0.5 version
They are not Unknow characters, that's what you printed with:
int i = Serial.write(buf, sizeof(buf));
Just check an ASCII table buf[0] = 125 = '{'
With write() you are writing raw data without any kind of format. Your first byte is the value 125, in binary 01111101. This byte correspond to the character { if it is intepreted as char. Your serial communication interprets the incoming byte as char, so it prints '{`.
If you want to print 125 as string on a serial communication, you have to send buf[] = {49, 50, 53}. Or you have to convert your interget into a string.
what's also wrong is that you are using the byte type with values higher than 255. Try changing to int16_t.
Characters at the start is the ASCII representation of the buff numbers you send. The arduino serial monitor monitors all the activities, and it prints out also your .write commands. .writeln which you do later, gets printed additionally after the original .write.
So what you see, is the ASCII representation of arduino sending your commands.
PS:
The numbers 2000 and 5000 don't fit into the byte, so the last two bytes that you send are probably corrupted.

How do I send an array of integers over TCP in C?

I'm lead to believe that write() can only send data buffers of byte (i.e. signed char), so how do I send an array of long integers using the C write() function in the sys/socket.h library?
Obviously I can't just cast or convert long to char, as any numbers over 127 would be malformed.
I took a look at the question, how to decompose integer array to a byte array (pixel codings), but couldn't understand it - please could someone dumb it down a little if this is what I'm looking for?
Follow up question:
Why do I get weird results when reading an array of integers from a TCP socket?
the prototype for write is:
ssize_t write(int fd, const void *buf, size_t count);
so while it writes in units of bytes, it can take a pointer of any type. Passing an int* will be no problem at all.
EDIT:
I would however, recomend that you also send the amount of integers you plan to send first so the reciever knows how much to read. Something like this (error checking omitted for brevity):
int x[10] = { ... };
int count = 10;
write(sock, &count, sizeof(count));
write(sock, x, sizeof(x));
NOTE: if the array is from dynamic memory (like you malloced it), you cannot use sizeof on it. In this case count would be equal to: sizeof(int) * element_count
EDIT:
As Brian Mitchell noted, you will likely need to be careful of endian issues as well. This is the case when sending any multibyte value (as in the count I recommended as well as each element of the array). This is done with the: htons/htonl and ntohs/ntohl functions.
Write can do what you want it to, but there's some things to be aware of:
1: You may get a partial write that's not on an int boundary, so you have to be prepared to handle that situation
2: If the code needs to be portable, you should convert your array to a specific endianess, or encode the endianess in the message.
The simplest way to send a single int (assuming 4-byte ints) is :
int tmp = htonl(myInt);
write(socket, &tmp, 4);
where htonl is a function that converts the int to network byte order. (Similarly,. when you read from the socket, the function ntohl can be used to convert back to host byte order.)
For an array of ints, you would first want to send the count of array members as an int (in network byte order), then send the int values.
Yes, you can just cast a pointer to your buffer to a pointer to char, and call write() with that. Casting a pointer to a different type in C doesn't affect the contents of the memory being pointed to -- all it does is indicate the programmer's intention that the contents of memory at that address be interpreted in a different way.
Just make sure that you supply write() with the correct size in bytes of your array -- that would be the number of elements times sizeof (long) in your case.
It would be better to have serialize/de-serialize functionality in your client /server program.
Whenever you want to send data, serialize the data into a byte buffer and send it over TCP with byte count.
When receiving data, de-serialize the data from buffer to your own interpretation .
You can interpret byte buffer in any form as you like. It can contain basic data type, objects etc.
Just make sure to take care of endianess and also alignment stuff.
Declare a character array. In each location of the array, store integer numbers, not characters.
Then you just send that.
For example:
char tcp[100];
tcp[0] = 0;
tcp[1] = 0xA;
tcp[2] = 0xB;
tcp[3] = 0xC;
.
.
// Send the character array
write(sock, tcp, sizeof(tcp));
I think what you need to come up with here is a protocol.
Suppose your integer array is:
100, 99, 98, 97
Instead of writing the ints directly to the buffer, I would "serialize" the array by turning it into a string representation. The string might be:
"100,99,98,97"
That's what would be sent over the wire. On the receiving end, you'd split the string by the commas and build the array back up.
This is more standardised, is human readable, and means people don't have to think about hi/lo byte orders and other silly things.
// Sarcasm
If you were working in .NET or Java, you'd probably encode it in XML, like this:
<ArrayOfInt><Int>100</Int><Int>99</Int><Int>98</Int><Int>97</Int></ArrayOfInt>
:)

Resources