Data loss when converting byte to int on Arduino - c

I have a 3-byte array that buffers incoming bytes through the Serial port. Once it is full I want to use the bytes to call a function that takes a byte and an int as a parameter. This should theoretically not be a problem, but for some reason the bytes are not being converten into an int properly. Here is the code I have:
// for serialEvent()
uint8_t buffer[3] = {0, 0, 0};
uint8_t index = 0;
void serialEvent() {
while (Serial.available()) {
if (index > 2) {
// buffer is full so process it
uint16_t argument = (uint16_t)buffer[1];
argument <<= 8;
argument |= buffer[2];
processSerial(buffer[0], argument);
index = 0;
}
buffer[index] = Serial.read();
index++;
}
}
void processSerial(uint8_t action, uint16_t argument) { ... }
The problem appears to be in the line where the first bit is shifted to the left to make space for the second one. I have tried outputting the variable over the Serial port again and after the bit shift operation, it is 0.
The same thing happens when I try to replace the bit shift operation with a multiplication by 256 (which has the same result in theory).
Irritatingly, when I assign a static value like so, everything works fine:
uint16_t argument = 0x00CD;
argument <<= 8;
Is this a type cast problem? Am I missing something here?

Have not found the solution as to why this happens, but using the word() function does exactly what I want:
uint16_t argument = word(buffer[1], buffer[2]);
processSerial(buffer[0], argument);
Annoyingly, it is defined as:
unsigned int makeWord(unsigned char h, unsigned char l) { return (h << 8) | l; }

Related

MPU6050 motion driver library function call cause hardfault exception

Hi I'm try to use mpu6050 on my stm32 project.
I copy the motion driver library from SparkFun_MPU-9250-DMP_Arduino_Library and replace arduino function like arduino_i2c_write to stm32 write function. The replace part work fine. The stm32 board did write bytes into the imu and read from it.
However, when I try to setup the dmp funtion and use the mpu_load_firmware function, I encouter a weird situation.
int mpu_load_firmware(unsigned short length, const unsigned char *firmware,
unsigned short start_addr, unsigned short sample_rate)
{
unsigned short ii;
unsigned short this_write;
/* Must divide evenly into st.hw->bank_size to avoid bank crossings. */
#define LOAD_CHUNK (16)
unsigned char cur[LOAD_CHUNK], tmp[2];
if (st.chip_cfg.dmp_loaded)
/* DMP should only be loaded once. */
return -1;
if (!firmware)
return -1;
for (ii = 0; ii < length; ii += this_write) {
this_write = min(LOAD_CHUNK, length - ii);
if (mpu_write_mem(ii, this_write, (unsigned char*)&(firmware[ii])))
return -1;
if (mpu_read_mem(ii, this_write, cur))
return -1;
if (memcmp(firmware+ii, cur, this_write))
return -2;
}
/* Set program start address. */
tmp[0] = start_addr >> 8;
tmp[1] = start_addr & 0xFF;
if (i2c_write(st.hw->addr, st.reg->prgm_start_h, 2, tmp))
return -1;
st.chip_cfg.dmp_loaded = 1;
st.chip_cfg.dmp_sample_rate = sample_rate;
return 0;
}
When the code run to the line if (mpu_write_mem(ii, this_write, (unsigned char*)&(firmware[ii]))), it cause a hardfault. And I found out that the code cannnot call the mpu_write_mem function porperly. When I start to step in the function, the hardfault exception immediately occurred. I cannot figure it out what cause the exception and how to fix it.
I've checked the pointer, &(firmware[ii]), address, it looks just fine. But the weired thing is that in the mpu_write_mem function, the argument value all differ from the mpu_load_firmware function.(mem_addr != ii, length != this_write, ...) I'm not sure if this cause the exception or the other way around.
Can anyone give me some idea? Thank you very much ~~
Here is the mpu_write_mem function, both function are in the inv_mpu.c file
int mpu_write_mem(unsigned short mem_addr, unsigned short length,
unsigned char *data)
{
unsigned char tmp[2];
if (!data)
return -1;
if (!st.chip_cfg.sensors)
return -1;
tmp[0] = (unsigned char)(mem_addr >> 8);
tmp[1] = (unsigned char)(mem_addr & 0xFF);
/* Check bank boundaries. */
if (tmp[1] + length > st.hw->bank_size)
return -1;
if (i2c_write(st.hw->addr, st.reg->bank_sel, 2, tmp))
return -1;
if (i2c_write(st.hw->addr, st.reg->mem_r_w, length, data))
return -1;
return 0;
}

String Allocated with malloc Enaccessible after Function Return

I have run into a strange bug and I cannot for the life of me get it figured out. I have a function that decodes a byte array into a string based on another encoding function. The function that decodes looks roughly like this:
char *decode_string( uint8_t *encoded_string, uint32_t length,
uint8_t encoding_bits ) {
char *sequence_string;
uint32_t idx = 0;
uint32_t posn_in_buffer;
uint32_t posn_in_cell;
uint32_t encoded_nucleotide;
uint32_t bit_mask;
// Useful Constants
const uint8_t CELL_SIZE = 8;
const uint8_t NUCL_PER_CELL = CELL_SIZE / encoding_bits;
sequence_string = malloc( sizeof(char) * (length + 1) );
if ( !sequence_string ) {
ERR_PRINT("could not allocate enough space to decode the string\n");
return NULL;
}
// Iterate over the buffer, converting one nucleotide at a time.
while ( idx < length ) {
posn_in_buffer = idx / NUCL_PER_CELL;
posn_in_cell = idx % NUCL_PER_CELL;
encoded_nucleotide = encoded_string[posn_in_buffer];
encoded_nucleotide >>= (CELL_SIZE - encoding_bits*(posn_in_cell+1));
bit_mask = (1 << encoding_bits) - 1;
encoded_nucleotide &= bit_mask;
sequence_string[idx] = decode_nucleotide( encoded_nucleotide );
// decode_nucleotide returns a char on integer input.
idx++;
}
sequence_string[idx] = '\0';
printf("%s", sequence_string); // prints the correct string
return sequence_string;
}
The bug is that the return pointer, if I try to print it, causes a segmentation fault. But calling printf("%s\n", sequence_string) inside of the function will print everything just fine. If I call the function like this:
const char *seq = "AA";
uint8_t *encoded_seq;
encode_string( &encoded_seq, seq, 2, 2);
char *decoded_seq = decode_string( encoded_seq, 2, 2);
if ( decoded_seq ) {
printf("%s\n",decoded_seq); // this crashes
if ( !strcmp(decoded_seq, seq) ) {
printf("Success!");
}
then it will crash on the print.
A few notes, the other functions seem to all work, I've tested them fairly thoroughly (i.e. decode_nucleotide, encode_string). The string also prints correctly inside the function. It is only after the function returns that it stops working.
My question is, what might cause this memory to become invalid just by returning the pointer from a function? Thanks in advance!
First (and not that important, but) in the statement:
sequence_string = malloc( sizeof(char) * (length + 1) );
sizeof(char) by definition is always == 1. so the statement becomes:
sequence_string = malloc(length + 1);
In this section of your post:
char *decoded_seq = decode_string( encoded_seq, 2, 2);
...since I cannot see your implementation of decode_string, I can only make assumptions about how you are verifying its output before returning it. I do however understand that you are expecting the return value to contain values that would be legal contents for a C string. I can also assume that because you are working with coding and decoding, that the output type is likely unsigned char. If I am correct, then a legal range of characters for an output type of unsigned char is 0-255.
You are not checking the output before sending the value to the printf statement. If the value at the memory address of decoded_seq happens to be 0, (in the range of unsigned char) your program would crash. String functions do not work well with null pointers.
You should verify the return of _decode_string_ sending it to printf
char *decoded_seq = decode_string( encoded_seq, 2, 2);
if(decoded_seq != NULL)
{
...

How to convert bytes stored inside a buffer to a variable?

So I'm reading from a file descriptor which contains an int variable in its raw byte format.
So I'm doing:
char buffer[sizeof(int)];
ssize_t sizeOfFile = read(sock_fd, buffer, sizeof(int));
int extractedInt = ???;
How can I convert that buffer to an integer? I was thinking of memcpy but was wondering if there are better ways.
You could read directly an integer
int extractedInt;
ssize_t sizeOfFile = read(sock_fd, &extractedInt, sizeof(int));
read will read the size of an int bytes, and store them into extractedInt.
If your int is actually a string in a file you want to convert to an int, the procedure is a bit different.
#define SIZE 20
char buffer[SIZE]; // ensure there is enough space for a string containing an integer
ssize_t sizeOfFile = read(sock_fd, buffer, SIZE);
int extractedInt = atoi(buffer); // convert string to integer
I can guess from your code that you're reading from the network. This is then not portable to just read a int from the buffer, in your network protocol you chose a certain endianness but you cannot expect that all the platforms where your program will run to have the same, so it will lead to bad convertions.
And other proposed solutions of asking read to return an int will lead to the same problem.
So in your case, I can only advice to iterate through your array and compute the integer by progressively placing the bytes at the right place depending on the endianness of the platform.
You can detect the endianness of the build target platform by using the macro __BYTE_ORDER__in GCC.
There is an example for network data that is big endian:
// construct an `int` with the bytes in the given buffer
// considering the buffer contains the representation
// of an int in big endian
int buffer_to_int(char* buffer, int buffer_size) {
int result = 0;
int i;
char sign = buffer[0] & 0x80;
char * res_bytes = (char*)&result; // this pointer allows to access the int bytes
int offset = sizeof(int) - buffer_size;
if( sign != 0 )
sign = 0xFF;
if( offset < 0 ) {
// not representable with a `int` type
// we chose here to return the closest representable value
if( sign == 0 ) { //positive
return INT_MAX;
} else {
return INT_MIN;
}
}
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
for(i=0; i<buffer_size; i++) {
res_bytes[i] = buffer[buffer_size-i-1]; // invert the bytes
}
for(i=0; i<offset; i++){
res_bytes[buffer_size+i] = sign;
}
#else
// same endianness, so simply copy bytes using memcpy
memcpy(&result + offset, buffer, buffer_size);
for(i=0; i<offset; i++){
res_bytes[i] = sign;
}
#endif
return result;
}

Understanding C Requirements of bits and offset [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I understand the general idea of C and how making a log file would go. Reading/writing to a file and such.
My concern is the following format that is desired:
[![enter image description here][1]][1]
I've gotten a good chunk done now but am concerned with how to append to my log file after the first record. I increment the file's record count (in the top 2 bytes) and write the first record after it. How would I then setup to add the 2nd/3rd/etc records to showup after each other?
//confirm a file exists in the directory
bool fileExists(const char* file)
{
struct stat buf;
return (stat(file, &buf) == 0);
}
int rightBitShift(int val, int space)
{
return ((val >> space) & 0xFF);
}
int leftBitShift(int val, int space)
{
return (val << space);
}
int determineRecordCount(char * logName)
{
unsigned char record[2];
FILE *fp = fopen(logName, "rb");
fread(record, sizeof(record), 1, fp);
//display the record number
int recordNum = (record[0] << 8) | record[1];
recordNum = recordNum +1;
return (recordNum);
}
void createRecord(int argc, char **argv)
{
int recordNum;
int aux = 0;
int dst;
char* logName;
char message[30];
memset(message,' ',30);
//check argument count and validation
if (argc == 7 && strcmp("-a", argv[2]) ==0 && strcmp("-f", argv[3]) ==0 && strcmp("-t", argv[5]) ==0)
{
//aux flag on
aux = 1;
logName = argv[4];
strncpy(message, argv[6],strlen(argv[6]));
}
else if (argc == 6 && strcmp("-f", argv[2]) ==0 && strcmp("-t", argv[4]) ==0)
{
logName = argv[3];
strncpy(message, argv[5],strlen(argv[5]));
}
else
{
printf("Invalid Arguments\n");
exit(0);
}
//check if log exists to get latest recordNum
if (fileExists(logName))
{
recordNum = determineRecordCount(logName);
printf("%i\n",recordNum);
}
else
{
printf("Logfile %s not found\n", logName);
recordNum = 1;
}
//Begin creating record
unsigned char record[40]; /* One record takes up 40 bytes of space */
memset(record, 0, sizeof(record));
//recordCount---------------------------------------------------------------------
record[0] = rightBitShift (recordNum, 8); /* Upper byte of sequence number */
record[1] = rightBitShift (recordNum, 0); /* Lower byte of sequence number */
//get aux/dst flags---------------------------------------------------------------
//get date and time
time_t timeStamp = time(NULL);
struct tm *date = localtime( &timeStamp );
if (date->tm_isdst)
dst = 1;
record[2] |= aux << 7; //set 7th bit
record[2] |= dst << 6; //set 6th
//timeStamp-----------------------------------------------------------------------
record[3] |= rightBitShift(timeStamp, 24);//high byte
record[4] |= rightBitShift(timeStamp, 16);
record[5] |= rightBitShift(timeStamp, 8);
record[6] |= rightBitShift(timeStamp, 0); //low byte
//leave bytes 7-8, set to 0 -----------------------------------------
record[7] = 0;
record[8] = 0;
//store message--------------------------------------------
strncpy(&record[9], message, strlen(message));
//write record to log-----------------------------------------------------------------
FILE *fp = fopen(logName, "w+");
unsigned char recordCount[4];
recordCount[0] = rightBitShift (recordNum, 8); /* Upper byte of sequence number */
recordCount[1] = rightBitShift (recordNum, 0); /* Lower byte of sequence number */
recordCount[2] = 0;
recordCount[3] = 0;
fwrite(recordCount, sizeof(recordCount), 1, fp);
fwrite(record, sizeof(record), 1, fp);
fclose(fp);
printf("Record saved successfully\n");
}
NOTE: I've never had to do this before in C, take it with a grain of salt.
This is a very specific binary formatting where each bit is precisely accounted for. It's using the Least-Significant-Bit numbering scheme (LSB 0) where the bits are numbered from 7 to 0.
Specifying that the "upper byte" comes first means this format is big-endian. The most significant bits come first. This is like how we write our numbers, four thousand, three hundred, and twenty one is 4321. 1234 would be little-endian. For example, the Number Of Records and Sequence are both 16 bit big-endian numbers.
Finally, the checksum is a number calculated from the rest of the record to verify there were no mistakes in transmission. The spec defines how to make the checksum.
Your job is to precisely reproduce this format, probably using the fixed-sized types found in stdint.h or unsigned char. For example, the sequence would be a uint16_t or unsigned char[2].
The function to produce a record might have a signature like this:
unsigned char *make_record( const char *message, bool aux );
The user only has to supply you with the message and the aux flag. The rest you can be figured out by the function. You might decide to let them pass in the timestamp and sequence. Point is, the function needs to be passed just the data, it takes care of the formatting.
This byte-ordering means you can't just write out integers, they might be the wrong size or the wrong byte order. That means any multi-byte integers must be serialized before you can write them to the record. This answer covers ways to do that and I'll be using the ones from this answer because they proved a bit more convenient.
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
unsigned char *make_record( const char *message, bool aux ) {
// Allocate and zero memory for the buffer.
// Zeroing means no risk of accidentally sending garbage.
unsigned char *buffer = calloc( 40, sizeof(unsigned char) );
// As we add to the buffer, pos will track the next byte to be written.
unsigned char *pos = buffer;
// I decided not make the user responsible for
// the sequence number. YMMV.
static uint16_t sequence = 1;
pos = serialize_uint16( pos, sequence );
// Get the timestamp and DST.
time_t timestamp = time(NULL);
struct tm *date = localtime( &timestamp );
// 2nd row is all flags and a bunch of 0s. Start with them all off.
uint8_t flags = 0;
if( aux ) {
// Flip the 7th bit on.
flags |= 0x80;
}
if( date->tm_isdst ) {
// Flip the 6th bit on.
flags |= 0x40;
}
// That an 8 bit integer has no endianness, this is to ensure
// pos is consistently incremented.
pos = serialize_uint8(pos, flags);
// I don't know what their timestamp format is.
// This is just a guess. It's probably wrong.
pos = serialize_uint32(pos, (uint32_t)timestamp);
// "Spare" is all zeros.
// The spec says this is 3 bytes, but only gives it bytes
// 7 and 8. I'm going with 2 bytes.
pos = serialize_uint16(pos, 0);
// Copy the message in, 30 bytes.
// strncpy() does not guarantee the message will be null
// terminated. This is probably fine as the field is fixed width.
// More info about the format would be necessary to know for sure.
strncpy( pos, message, 30 );
pos += 30;
// Checksum the first 39 bytes.
// Sorry, I don't know how to do 1's compliment sums.
pos = serialize_uint8( pos, record_checksum( buffer, 39 ) );
// pos has moved around, but buffer remains at the start
return buffer;
}
int main() {
unsigned char *record = make_record("Basset hounds got long ears", true);
fwrite(record, sizeof(unsigned char), 40, stdout);
}
At this point my expertise is exhausted, I've never had to do this before. I'd appreciate folks fixing up the little mistakes in edits and suggesting better ways to do it in the comments, like what to do with the timestamp. And maybe someone else can cover how to do 1's compliment checksums in another answer.
As a byte is composed by 8 bits (from 0 to 7) you can use bitwise operations to modify them as asked in your specifications. Take a look for general information (https://en.wikipedia.org/wiki/Bitwise_operations_in_C). As a preview, you can use >> or << operators to determine which bit to modify, and use logical operators | and & to set it's values.

Function sscanf must be assigned to variable otherwise strange behavior

Consider this code:
#define TRANSLATOR_requestElectricityMeterWrite() do{addr = word_getAddress(); value = word_getValue(); }while(0)
uint16_t value;
uint8_t addr;
bool dispatcher(void)
{
TRANSLATOR_requestElectricityMeterWrite();
return true;
} // AFTER this point (during debug) program goes to default handler
int main(void)
{
if(dispatcher())
continue;
. . . .
. . . .
}
uint16_t word_getValue(void)
{
uint16_t value;
sscanf("ABCD", "%4x", (unsigned int *)&value);
return value;
}
uint8_t word_getAddress(void)
{
uint8_t address;
sscanf("00", "%2x", (unsigned int *)&address);
;
return address;
}
When the code above is run, the statement inside if causes program to crash(goes to some default handler).
But when I change the two(word_getValue and word_getAddres) functions to this:
uint16_t word_getValue(void)
{
uint16_t value;
int i = 0;i++;
i = sscanf(WORD_getValueString(), "%4x", (unsigned int *)(&value));
return value;
}
uint8_t word_getAddress(void)
{
uint8_t address;
int i = 0;i++;
i = sscanf(WORD_getNameString(), "%2x", (unsigned int *)(&address));
return address;
}
It works. The addition if the dummy i seems to solve that problem. But why doesn't it work the other way?
GNU ARM v4.8.3 toolchain
Both functions invoke undefined behavior, hence anything can happen. Adding an extra local variable changes the location of the destination variable, hiding the effect of its incorrect size.
sscanf("ABCD", "%4x", (unsigned int *)&value);
sscanf will store sizeof(unsigned int) bytes (probably 4) into variable value, which has only 2 bytes.
sscanf(WORD_getNameString(), "%2x", (unsigned int *)(&address));
Will store sizeof(unsigned int) bytes into variable address, which has only 1 byte.
The easiest way to fix this problem is to parse into an unsigned int and store the parsed value to the destination separately, or simply return the value:
uint16_t word_getValue(void) {
unsigned int value;
if (sscanf(WORD_getValueString(), "%4x", &value) == 1)
return value;
// could not parse a value, return some default value or error code
return 0;
}
uint8_t word_getAddress(void) {
unsigned int address;
if (sscanf(WORD_getNameString(), "%2x", &address) == 1)
return address;
// could not parse a value, return some default value or error code
return 0;
}
You might also want to verify if the parsed value is within range for the destination type, but since you limit the parse to respectively 4 and 2 hex digits, overflow cannot happen.
%x format requires unsigned argument (suppose it's uint32_t on your platform). If you pass uint16_t or uint8_t it can corrupt memory. In your case it corrupt stack and overwrites return address. Try use %4hx for uint16_t and %2hhx for uint8_t.

Resources