Adafruit SHT31-D and Raspberry Pi2 -- Unable to read data from sensor - c++

hopefully one of you out there can help me!
I am trying to use the Adafruit SHT31-D (an i2c device) board with my Pi2. I am going off of this datasheet to guide my coding efforts. I am using Wiring Pi (wiringpi.com) to facilitate things.
I am able to successfully open a connection to the device, and sending commands seems to work fine, but I am unable to read data back! Here is the little mini library I have put together. I am hoping that one of you might have some experience with this sort of thing and be able to help me see where I've gone wrong.
To rule out any possible issues with the sensor hardware, I have tested it with my Arduino UNO and it works without issues.
Here is my C++ code:
SHT3x.h
#pragma once
/* Sensor Commands */
#define DEFAULT_SHT_ADDR 0x44
#define MEAS_HREP_STRETCH 0x2C06
#define MEAS_MREP_STRETCH 0x2C0D
#define MEAS_LREP_STRETCH 0x2C10
#define MEAS_HREP 0x2400
#define MEAS_MREP 0x240B
#define MEAS_LREP 0x2416
#include <cstdint>
class SHT3x {
public:
SHT3x(const uint8_t& i2cAddr);
float readHumidity(const uint16_t& command) const;
float readTempC(const uint16_t& command) const;
float readTempF(const uint16_t& command) const;
private:
int8_t _fd;
uint8_t _header;
uint32_t getMeasurement(const uint16_t& command) const;
void sendCommand(const uint16_t& command) const;
uint32_t receiveData(void) const;
};
SHT3x.cpp
#include <stdexcept>
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include "SHT3x.h"
SHT3x::SHT3x(const uint8_t& i2cAddr) {
_fd = wiringPiI2CSetup(i2cAddr);
_header = i2cAddr << 1;
if (_fd < 0) {
throw std::runtime_error("Unable to connect");
}
}
float SHT3x::readHumidity(const uint16_t& command) const {
uint32_t raw_data = getMeasurement(command);
if (!raw_data) {
throw std::runtime_error("Bad Reading.");
}
uint16_t raw_humidity = raw_data & 0xFFFF;
float humidity = 100.0 * ((float) raw_humidity / (float) 0xFFFF);
return humidity;
}
float SHT3x::readTempC(const uint16_t& command) const {
uint32_t raw_data = getMeasurement(command);
if (!raw_data) {
throw std::runtime_error("Bad Reading.");
}
uint16_t raw_temp = raw_data >> 16;
float tempC = -45.0 + (175.0 * ((float) raw_temp / (float) 0xFFFF));
return tempC;
}
float SHT3x::readTempF(const uint16_t& command) const {
uint32_t raw_data = getMeasurement(command);
if (!raw_data) {
throw std::runtime_error("Bad Reading.");
}
uint16_t raw_temp = raw_data >> 16;
float tempF = -49.0 + (315.0 * ((float) raw_temp / (float) 0xFFFF));
return tempF;
}
uint32_t SHT3x::getMeasurement(const uint16_t& command) const {
try {
sendCommand(command);
} catch (std::runtime_error& e) {
throw;
}
return receiveData();
}
void SHT3x::sendCommand(const uint16_t& command) const {
// break command into bytes
uint8_t MSB = command >> 8;
uint8_t LSB = command & 0xFF;
// send header
int8_t ack = wiringPiI2CWrite(_fd, _header);
// send command
ack &= wiringPiI2CWrite(_fd, MSB);
ack &= wiringPiI2CWrite(_fd, LSB);
// handle errors
if (ack) {
throw std::runtime_error("Sending command failed.");
}
}
uint32_t SHT3x::receiveData(void) const {
uint32_t data;
// send header
uint8_t read_header = _header | 0x01;
int8_t ack = wiringPiI2CWrite(_fd, read_header);
// handle errors
if (ack) throw std::runtime_error("Unable to read data.");
// read data
data = wiringPiI2CRead(_fd);
for (size_t i = 0; i < 4; i++) {
printf("Data: %d\n", data);
data <<= 8;
if (i != 1) {
data |= wiringPiI2CRead(_fd);
} else {
wiringPiI2CRead(_fd); // skip checksum
}
}
wiringPiI2CRead(_fd); // second checksum
return data;
}

The SHT31 uses 16bit read and write, rather than using 2 8bit writes you might be better off using wiringpi's 16bit write. wiringPiI2CWriteReg16(). Same thing applies to the read.
Below is a very early copy of what I've done to read the sht31-d on a PI. It has no dependencies except i2c-dev. Heater enable/disable is not working, but softreset, clearstatus, getserial & get temp/humid are all fine.
/*
* Referances
* https://www.kernel.org/doc/Documentation/i2c/dev-interface
* https://github.com/adafruit/Adafruit_SHT31
* https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/Humidity_and_Temperature_Sensors/Sensirion_Humidity_and_Temperature_Sensors_SHT3x_Datasheet_digital.pdf
*
* This depends on i2c dev lib
* sudo apt-get install libi2c-dev
*
* Below is also a good one to have, but be careful i2cdump from the below cause the sht31 interface to become unstable for me
* and requires a hard-reset to recover correctly.
* sudo apt-get install i2c-tools
*
* on PI make sure below 2 commands are in /boot/config.txt
* dtparam=i2c_arm=on
* dtparam=i2c1_baudrate=10000
* I know we are slowing down the baurate from optimal, but it seems to be the most stable setting in my testing.
* add another 0 to the above baudrate for max setting, ie dtparam=i2c1_baudrate=100000
*/
#include <linux/i2c-dev.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <elf.h>
#include <unistd.h>
#define SHT31_INTERFACE_ADDR 1
#define SHT31_DEFAULT_ADDR 0x44
#define SHT31_READ_SERIALNO 0x3780
#define SHT31_MEAS_HIGHREP_STRETCH 0x2C06 // Doesn't work on PI
#define SHT31_MEAS_MEDREP_STRETCH 0x2C0D // Seems to work on PI but shouldn't
#define SHT31_MEAS_LOWREP_STRETCH 0x2C10 // Seems to work on PI but shouldn't
#define SHT31_MEAS_HIGHREP 0x2400 // Doesn't work on PI
#define SHT31_MEAS_MEDREP 0x240B
#define SHT31_MEAS_LOWREP 0x2416
#define SHT31_READSTATUS 0xF32D
#define SHT31_CLEARSTATUS 0x3041
#define SHT31_SOFTRESET 0x30A2
#define SHT31_HEATER_ENABLE 0x306D
#define SHT31_HEATER_DISABLE 0x3066
#define CHECK_BIT(var,pos) (((var)>>(pos)) & 1)
/*
* delay:
* Wait for some number of milliseconds
*********************************************************************************
*/
void delay (unsigned int howLong)
{
struct timespec sleeper, dummy ;
sleeper.tv_sec = (time_t)(howLong / 1000) ;
sleeper.tv_nsec = (long)(howLong % 1000) * 1000000 ;
nanosleep (&sleeper, &dummy) ;
}
/*
*
* CRC-8 formula from page 14 of SHT spec pdf
*
* Test data 0xBE, 0xEF should yield 0x92
*
* Initialization data 0xFF
* Polynomial 0x31 (x8 + x5 +x4 +1)
* Final XOR 0x00
*/
uint8_t crc8(const uint8_t *data, int len)
{
const uint8_t POLYNOMIAL = 0x31;
uint8_t crc = 0xFF;
int j;
int i;
for (j = len; j; --j ) {
crc ^= *data++;
for ( i = 8; i; --i ) {
crc = ( crc & 0x80 )
? (crc << 1) ^ POLYNOMIAL
: (crc << 1);
}
}
return crc;
}
/*
*
* buffer should return with data read, size defined by readsize
*********************************************************************************
*/
int writeandread(int fd, uint16_t sndword, uint8_t *buffer, int readsize)
{
int rtn;
uint8_t snd[3];
// Split the 16bit word into two 8 bits that are flipped.
snd[0]=(sndword >> 8) & 0xff;
snd[1]=sndword & 0xff;
rtn = write(fd, snd, 2);
if ( rtn != 2 ) {
return 1;
}
if (readsize > 0) {
delay(10);
rtn = read(fd, buffer, readsize);
if ( rtn < readsize) {
return 2;
}
}
return 0;
}
void printserialnum(int file)
{
uint8_t buf[10];
int rtn;
rtn = writeandread(file, SHT31_READ_SERIALNO, buf, 6);
if (rtn != 0)
printf("ERROR:- Get serial i2c %s failed\n",(rtn==1?"write":"read"));
else {
if (buf[2] != crc8(buf, 2) || buf[5] != crc8(buf+3, 2))
printf("WARNING:- Get serial CRC check failed, don't trust result\n");
uint32_t serialNo = ((uint32_t)buf[0] << 24)
| ((uint32_t)buf[1] << 16)
| ((uint32_t)buf[3] << 8)
| (uint32_t)buf[4];
printf("Serial# = %d\n",serialNo);
}
}
void printtempandhumidity(int file)
{
uint8_t buf[10];
int rtn;
rtn = writeandread(file, SHT31_MEAS_MEDREP_STRETCH, buf, 6);
if (rtn != 0)
printf("ERROR:- Get temp/humidity i2c %s failed\n",(rtn==1?"write":"read"));
else {
if ( buf[2] != crc8(buf, 2) || buf[5] != crc8(buf+3, 2))
printf("WARNING:- Get temp/humidity CRC check failed, don't trust results\n");
uint16_t ST, SRH;
ST = buf[0];
ST <<= 8;
ST |= buf[1];
SRH = buf[3];
SRH <<= 8;
SRH |= buf[4];
double stemp = ST;
stemp *= 175;
stemp /= 0xffff;
stemp = -45 + stemp;
double stempf = ST;
stempf *= 315;
stempf /= 0xffff;
stempf = -49 + stempf;
printf("Temperature %.2fc - %.2ff\n",stemp,stempf);
double shum = SRH;
shum *= 100;
shum /= 0xFFFF;
printf("Humidity %.2f%%\n",shum);
}
}
void printBitStatus(uint16_t stat)
{
printf("Status\n");
printf(" Checksum status %d\n", CHECK_BIT(stat,0));
printf(" Last command status %d\n", CHECK_BIT(stat,1));
printf(" Reset detected status %d\n", CHECK_BIT(stat,4));
printf(" 'T' tracking alert %d\n", CHECK_BIT(stat,10));
printf(" 'RH' tracking alert %d\n", CHECK_BIT(stat,11));
printf(" Heater status %d\n", CHECK_BIT(stat,13));
printf(" Alert pending status %d\n", CHECK_BIT(stat,15));
}
void printstatus(int file)
{
uint8_t buf[10];
int rtn;
rtn = writeandread(file, SHT31_READSTATUS, buf, 3);
if (rtn != 0)
printf("ERROR:- readstatus %s failed\n",(rtn==1?"write":"read"));
else {
if ( buf[2] != crc8(buf, 2))
printf("WARNING:- Get status CRC check failed, don't trust results\n");
uint16_t stat = buf[0];
stat <<= 8;
stat |= buf[1];
printBitStatus(stat);
}
}
void clearstatus(int file)
{
if( writeandread(file, SHT31_CLEARSTATUS, NULL, 0) != 0)
printf("ERROR:- sht31 clear status failed\n");
else
printf("Clearing status - ok\n");
}
void softreset(int file)
{
if( writeandread(file, SHT31_SOFTRESET, NULL, 0) != 0)
printf("ERROR:- sht31 soft reset failed\n");
else
printf("Soft reset - ok\n");
}
void enableheater(int file)
{
if( writeandread(file, SHT31_HEATER_ENABLE, NULL, 0) != 0)
printf("ERROR:- sht31 heater enable failed\n");
else
printf("Enabiling heater - ok\n");
}
void disableheater(int file)
{
if( writeandread(file, SHT31_HEATER_DISABLE, NULL, 0) != 0)
printf("ERROR:- sht31 heater enable failed\n");
else
printf("Disableing heater - ok\n");
}
int main()
{
int file;
char filename[20];
snprintf(filename, 19, "/dev/i2c-%d", SHT31_INTERFACE_ADDR);
file = open(filename, O_RDWR);
if (file < 0) {
printf("ERROR:- Can't open %s\n",filename);
exit(1);
}
if (ioctl(file, I2C_SLAVE, SHT31_DEFAULT_ADDR) < 0) {
printf("ERROR:- Connecting to sht31 I2C address 0x%02hhx\n", SHT31_DEFAULT_ADDR);
exit(1);
}
softreset(file);
printtempandhumidity(file);
printstatus(file);
close(file);
return 0;
}

Related

MAX31865 C++ Class on Raspberry Pi with pigpio SPI functions

I am looking for some guidance on integrating a MAX31865 board into my Raspberry Pi project with a Qt front end. The board contains 4 MAX31865 chips with separate CS pins. I have confirmed the chips / RTDs are working correctly with both Arduino and STM32 boards, but for some reason I can't get the same output from Raspberry Pi. I'm wondering if the incorrect output has to do with the pigpio spiWrite / spiRead / spiXfer functions using (char *) for the rx / tx buffers rather than uint8_t like most other libraries.
For example, I'll get 92.5 (roughly 92.406) on chips with RTDs attached, and -403.636 on chips without RTDs attached.
Regardless, I'm not getting resistance changes at all on the sensor.
I'm trying to adapt some code I have used for the STM32 chipset into a C++ class and it looks OK, but would love a second (or more) pair of eyes if possible.
Here is my max31865.cpp file (For simplicity, i included the #defines here instead of the .h file)
#include "math.h"
#include "rpigpio.h"
#define MAX31865_CONFIG_REG 0x00
#define MAX31865_CONFIG_BIAS 0x80
#define MAX31865_CONFIG_MODEAUTO 0x40
#define MAX31865_CONFIG_MODEOFF 0x00
#define MAX31865_CONFIG_1SHOT 0x20
#define MAX31865_CONFIG_3WIRE 0x10
#define MAX31865_CONFIG_24WIRE 0x00
#define MAX31865_CONFIG_FAULTSTAT 0x02
#define MAX31865_CONFIG_FILT50HZ 0x01
#define MAX31865_CONFIG_FILT60HZ 0x00
#define MAX31865_RTDMSB_REG 0x01
#define MAX31865_RTDLSB_REG 0x02
#define MAX31865_HFAULTMSB_REG 0x03
#define MAX31865_HFAULTLSB_REG 0x04
#define MAX31865_LFAULTMSB_REG 0x05
#define MAX31865_LFAULTLSB_REG 0x06
#define MAX31865_FAULTSTAT_REG 0x07
#define MAX31865_FAULT_HIGHTHRESH 0x80
#define MAX31865_FAULT_LOWTHRESH 0x40
#define MAX31865_FAULT_REFINLOW 0x20
#define MAX31865_FAULT_REFINHIGH 0x10
#define MAX31865_FAULT_RTDINLOW 0x08
#define MAX31865_FAULT_OVUV 0x04
#define MAX31865_FAULT_NONE 0x00
#define RTD_A 3.9083e-3
#define RTD_B -5.775e-7
#define _MAX31865_RREF 402.0f
#define _MAX31865_RNOMINAL 100.0f
#define MAX31865_WIRES 3
#define MAX31865_FILTERHZ 50
MAX31865::MAX31865(uint8_t spiHandle, uint8_t cs_pin)
{
this->_spiHandle = spiHandle;
this->_cs_pin = cs_pin;
gpioSetMode(this->_cs_pin, PI_OUTPUT);
gpioWrite(this->_cs_pin, PI_HIGH);
MAX31865_setWires(MAX31865_WIRES);
MAX31865_enableBias(false);
MAX31865_autoConvert(false);
MAX31865_clearFault();
MAX31865_setFilter(MAX31865_FILTERHZ);
}
void MAX31865::MAX31865_readRegisterN(uint8_t address, uint8_t *buffer, uint8_t n) {
uint8_t tmp = 0xFF;
address &= 0x7F;
gpioWrite(this->_cs_pin, PI_LOW);
spiWrite(this->_spiHandle, (char *) &address, 1);
while (n--) {
spiXfer(this->_spiHandle, (char*) &tmp, (char *) buffer, 1);
buffer++;
}
gpioWrite(this->_cs_pin, PI_HIGH);
}
uint8_t MAX31865::MAX31865_readRegister8(uint8_t address) {
uint8_t ret = 0;
MAX31865_readRegisterN(address, &ret, 1);
return ret;
}
uint16_t MAX31865::MAX31865_readRegister16(uint8_t address) {
uint8_t buffer[2] = {0, 0};
MAX31865_readRegisterN(address, buffer, 2);
uint16_t ret = buffer[0];
ret <<= 8;
ret |= buffer[1];
return ret;
}
void MAX31865::MAX31865_writeRegister8(uint8_t address, uint8_t data) {
gpioWrite(this->_cs_pin, PI_LOW);
address |= 0x80;
spiWrite(this->_spiHandle, (char*) &address, 1);
spiWrite(this->_spiHandle, (char*) &data, 1);
gpioWrite(this->_cs_pin, PI_HIGH);
}
void MAX31865::MAX31865_readFault(){
this->_fault = MAX31865_readRegister8(MAX31865_FAULTSTAT_REG);
}
void MAX31865::MAX31865_clearFault() {
uint8_t t = MAX31865_readRegister8(MAX31865_CONFIG_REG);
t &= ~0x2C;
t |= MAX31865_CONFIG_FAULTSTAT;
MAX31865_writeRegister8(MAX31865_CONFIG_REG, t);
}
void MAX31865::MAX31865_enableBias(bool enable) {
uint8_t t = MAX31865_readRegister8(MAX31865_CONFIG_REG);
if (enable) {
t |= MAX31865_CONFIG_BIAS;
}
else {
t &= ~MAX31865_CONFIG_BIAS;
}
MAX31865_writeRegister8(MAX31865_CONFIG_REG, t);
}
void MAX31865::MAX31865_autoConvert(bool enable) {
uint8_t t = MAX31865_readRegister8(MAX31865_CONFIG_REG);
if (enable) {
t |= MAX31865_CONFIG_MODEAUTO;
}
else {
t &= ~MAX31865_CONFIG_MODEAUTO;
}
MAX31865_writeRegister8(MAX31865_CONFIG_REG, t);
}
void MAX31865::MAX31865_setWires(uint8_t numWires) {
uint8_t t = MAX31865_readRegister8(MAX31865_CONFIG_REG);
if (numWires == 3) {
t |= MAX31865_CONFIG_3WIRE;
}
else {
t &= ~MAX31865_CONFIG_3WIRE;
}
MAX31865_writeRegister8(MAX31865_CONFIG_REG, t);
}
void MAX31865::MAX31865_setFilter(uint8_t filterHz) {
uint8_t t = MAX31865_readRegister8(MAX31865_CONFIG_REG);
if (filterHz == 50) {
t |= MAX31865_CONFIG_FILT50HZ;
}
else {
t &= ~MAX31865_CONFIG_FILT50HZ;
}
MAX31865_writeRegister8(MAX31865_CONFIG_REG, t);
}
uint16_t MAX31865::MAX31865_readRTD() {
MAX31865_clearFault();
MAX31865_enableBias(true);
std::this_thread::sleep_for (std::chrono::milliseconds(10));
uint8_t t = MAX31865_readRegister8(MAX31865_CONFIG_REG);
t |= MAX31865_CONFIG_1SHOT;
MAX31865_writeRegister8(MAX31865_CONFIG_REG, t);
std::this_thread::sleep_for (std::chrono::milliseconds(10));
uint16_t rtd = MAX31865_readRegister16(MAX31865_RTDMSB_REG);
MAX31865_enableBias(false);
rtd >>= 1;
return rtd;
}
void MAX31865::MAX31865_readTemp() {
float Z1, Z2, Z3, Z4, Rt, temp;
Rt = MAX31865_readRTD();
Rt /= 32768;
Rt *= _MAX31865_RREF;
Z1 = -RTD_A;
Z2 = RTD_A * RTD_A - (4 * RTD_B);
Z3 = (4 * RTD_B) / _MAX31865_RNOMINAL;
Z4 = 2 * RTD_B;
temp = Z2 + (Z3 * Rt);
temp = (sqrtf(temp) + Z1) / Z4;
if (temp >= 0) {
MAX31865_readFault();
MAX31865_compareFault();
// Assume the temperature is correct
this->_MAX31865_tempC = temp;
this->_MAX31865_tempF = (this->_MAX31865_tempC * 9.0f / 5.0f) + 32.0f;
}
else {
Rt /= _MAX31865_RNOMINAL;
Rt *= 100;
float rpoly = Rt;
temp = -242.02;
temp += 2.2228 * rpoly;
rpoly *= Rt; // square
temp += 2.5859e-3 * rpoly;
rpoly *= Rt; // ^3
temp -= 4.8260e-6 * rpoly;
rpoly *= Rt; // ^4
temp -= 2.8183e-8 * rpoly;
rpoly *= Rt; // ^5
temp += 1.5243e-10 * rpoly;
MAX31865_readFault();
MAX31865_compareFault();
this->_MAX31865_tempC = temp;
this->_MAX31865_tempF = (this->_MAX31865_tempC * 9.0f / 5.0f) + 32.0f;
}
}
void MAX31865::MAX31865_compareFault() {
this->_faultText = "Unknown error has occured. Refer to the MAX31865 datasheet.";
if (this->_fault == MAX31865_FAULT_NONE)
this->_faultText = "No errors detected";
if (this->_fault == MAX31865_FAULT_HIGHTHRESH)
this->_faultText = "Measured resistance greater than High Fault Threshold value.";
if (this->_fault == MAX31865_FAULT_LOWTHRESH)
this->_faultText = "Measured resistance less than Low Fault Threshold value.";
if (this->_fault == MAX31865_FAULT_REFINLOW)
this->_faultText = "vREFIN > 0.85 x vBIAS.";
if (this->_fault == MAX31865_FAULT_REFINHIGH)
this->_faultText = "vRERFIN < 0.85 X vBIAS (FORCE - open).";
if (this->_fault == MAX31865_FAULT_RTDINLOW)
this->_faultText = "vRTRIN- < 0.85 X vBIAS (FORCE - open).";
if (this->_fault == MAX31865_FAULT_OVUV)
this->_faultText = "Any protected input voltage > vDD or < GND1.";
}
Here is the initialization code and temperature reading functions
int spiHandle = 0;
spiHandle = spiOpen(SPI_CHANNEL, SPI_SPEED, 0);
qDebug() << "[DEBUG] SPI Handle Open. Handle: " << spiHandle;
MAX31865 MAX31865_1(spiHandle, MAX31865_1_GPIO);
qDebug() << "[DEBUG] MAX31865 Initialized.";
volatile float _temp1_F = 0.0f;
MAX31865_1.MAX31865_readTemp();
qDebug() << "[DEBUG] 1 Temp F: " << MAX31865_1.read_MAX31865TempF();
_temp1_F = MAX31865_1.read_MAX31865TempF();
qDebug() << "[DEBUG] 1 Fault: " << MAX31865_1.read_MAX31865FaultText();
RPIDataStructure->set_tempMAX_1(_temp1_F);
Really appreciate any and all help on this project. Thank you.

FLASH_If_Write and HardFault

I am using stm32f4 and I am trying to write some data on dual bank flash.
I am calling the below function in my code after the clean flash.
uint32_t totalBlockNumber;
uint32_t blockNumber;
uint8_t blockData[512];
uint32_t frameDataCount;
(frameDataCount always > 8)
Flash_Write_Start(totalBlockNumber,blockNumber,(frameDataCount - 8), blockData);
Than this is the Flash_Write_Start function:
void Flash_Write_Start( uint32_t totalBlockNumber, uint32_t blockNumber, uint32_t packet_length, uint8_t * blockData)
{
HAL_StatusTypeDef errorCheck = 1;
uint32_t bankActive;
if(fwFrameCounter == blockNumber)
{
errorCheck = FLASH_If_Write(Flash_Write_Address, blockData, packet_length);
}
if(errorCheck != FLASHIF_OK)
{
SerialPrint("FLASH ERROR\n");
}
Not every time but I have a hardfault, when code comes in FLASH_If_Write(Flash_Write_Address, blockData, packet_length); function. What is the reason for ?
FLASHIF_StatusTypeDef FLASH_If_Write(uint32_t destination, uint32_t *p_source, uint32_t length)
{
FLASHIF_StatusTypeDef status ;
HAL_StatusTypeDef halErrorFlag;
/* Unlock the Flash to enable the flash control register access *************/
if (HAL_FLASH_Unlock() != HAL_OK)
{
return FLASHIF_CRCKO;
}
uint32_t i ;
status = FLASHIF_OK;
/* DataLength must be a multiple of 32 bit */
for ( i = 0U; (i < (length >> 2U)) && (destination <= (USER_FLASH_END_ADDRESS - 4U)); i++) //
{
/* Device voltage range supposed to be [2.7V to 3.6V], the operation will
be done by word */
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (destination + (i * 4U)), *(uint32_t *)(p_source + i)) == HAL_OK)
{
/* Check the written value */
if (*(__IO uint32_t *)(destination + (i * 4U)) != *(uint32_t *)(p_source + i))
{
status = FLASHIF_WRITINGCTRL_ERROR;
}
/* Increment FLASH destination address */
// destination += 4;
}
else
{
/* Error occurred while writing data in Flash memory */
status = FLASHIF_WRITING_ERROR;
break;
}
// SerialPrint(" 4 \n");
if (status != FLASHIF_OK)
{
break;
}
}
/* Lock the Flash to disable the flash control register access (recommended
to protect the FLASH memory against possible unwanted operation) *********/
halErrorFlag = HAL_FLASH_Lock();
if( halErrorFlag != HAL_OK)
{
}
return status;
}

Arduino Wire requestFrom freezes

I'm trying to program a class to control the MPU6050 with the Arduino Wire library but when I run the code in my Arduino mini it freezes after a few seconds.
There is the code of the library and a test sketch:
// Include Wire Library for I2C
#include <Wire.h>
enum MPU6050_filter {_256Hz, _188Hz, _98Hz, _42Hz, _20Hz, _10Hz, _5Hz};
enum MPU6050_gyro {_250dps, _500dps, _1000dps, _2000dps};
enum MPU6050_accel {_2g, _4g, _8g, _16Hz};
class MPU6050
{
public:
MPU6050 ();
bool start (bool AD0_value);
void goToSleep ();
void stopSleeping ();
void setFilterVal (MPU6050_filter filter_val);
void setGyroRange (MPU6050_gyro range);
void setAccelRange (MPU6050_accel range);
bool dataAvailable ();
void getLastGyroData (float& gx, float& gy, float& gz);
void getRawGyroData (int& gx, int& gy, int& gz);
private:
void writeRegister (byte address, byte data);
byte readRegister (byte address);
void readData (byte start_address, byte bytes, byte* data);
float convertGyroToDPS (int gyro);
bool AD0_val;
MPU6050_filter filter;
MPU6050_accel accel_range;
MPU6050_gyro gyro_range;
unsigned long last_read;
const unsigned long min_read_time = 1;
};
MPU6050::MPU6050 () : AD0_val(false),
filter(_256Hz),
accel_range(_2g),
gyro_range(_250dps) {}
bool MPU6050::start (bool AD0_value)
{
AD0_val = AD0_value;
// init sample rate div to 0 (max sample rate)
writeRegister(0x19, 0);
// activate FIFO for gyroscope data
writeRegister(0x23, 0x70);
// clear config setup register
writeRegister(0x6B, 0);
// setup the register
writeRegister(0x37, 0x10);
// set interrupt by data ready
writeRegister(0x38, 0x01);
}
void MPU6050::goToSleep ()
{
byte prev_data = readRegister(0x6B);
prev_data = (prev_data | 0x40);
writeRegister(0x6B, prev_data);
}
void MPU6050::stopSleeping ()
{
byte prev_data = readRegister(0x6B);
prev_data = (prev_data & 0xBF);
writeRegister(0x6B, prev_data);
}
void MPU6050::setFilterVal (MPU6050_filter filter_val)
{
int val;
if (filter_val == _256Hz) val = 0;
else if (filter_val == _188Hz) val = 1;
else if (filter_val == _98Hz) val = 2;
else if (filter_val == _42Hz) val = 3;
else if (filter_val == _20Hz) val = 4;
else if (filter_val == _10Hz) val = 5;
else val = 6;
byte data = readRegister(0x1A);
data = (data & 0xF8) | (val & 0x07);
writeRegister(0x1A, data);
filter = filter_val;
}
void MPU6050::setAccelRange (MPU6050_accel range)
{
byte value;
if (range == _2g) value = 0;
else if (range == _4g) value = 1;
else if (range == _8g) value = 2;
else value = 3;
byte reg_value = readRegister(0x1C);
reg_value = (reg_value & 0xE0) | (value << 3);
writeRegister(0x1C, reg_value);
accel_range = range;
}
void MPU6050::setGyroRange (MPU6050_gyro range)
{
byte value;
if (range == _250dps) value = 0;
else if (range == _500dps) value = 1;
else if (range == _1000dps) value = 2;
else value = 3;
byte reg_value = readRegister(0x1B);
reg_value = (reg_value & 0xE0) | (value << 3);
writeRegister(0x1B, reg_value);
gyro_range = range;
}
bool MPU6050::dataAvailable ()
{
return (readRegister(0x3A) & 0x01);
}
void MPU6050::getLastGyroData (float& gx, float& gy, float& gz)
{
int raw_x, raw_y, raw_z;
getRawGyroData(raw_x, raw_y, raw_z);
gx = convertGyroToDPS(raw_x);
gy = convertGyroToDPS(raw_y);
gz = convertGyroToDPS(raw_z);
}
void MPU6050::getRawGyroData (int& gx, int& gy, int& gz)
{
byte* data = new byte[6];
readData(0x43, 6, data);
gx = data[0] << 8 | data[1];
gy = data[2] << 8 | data[3];
gz = data[4] << 8 | data[5];
delete data;
}
void MPU6050::writeRegister (byte address, byte data)
{
Wire.beginTransmission(0x68 + AD0_val);
Wire.write(address);
Wire.write(data);
Wire.endTransmission();
}
byte MPU6050::readRegister (byte address)
{
byte data_buff = 0x00;
Wire.beginTransmission(byte(0x68 + AD0_val));
//Send the requested starting register
Wire.write(address);
//End the transmission
Wire.endTransmission(false);
//Request 14 bytes from the MPU-6050
Wire.requestFrom(byte(0x68 + AD0_val), byte(0x01), byte(true));
unsigned long initial_time = millis();
//Wait until all the bytes are received
while(Wire.available() == 0 and millis() < initial_time + 5);
if (millis() < initial_time + 5)
{
// read the data
data_buff = Wire.read();
}
// end the transmission
Wire.endTransmission();
return data_buff;
}
void MPU6050::readData (byte start_address, byte bytes, byte* data)
{
Wire.beginTransmission(byte(0x68 + AD0_val));
//Send the requested starting register
Wire.write(start_address);
//End the transmission
Wire.endTransmission(false);
//Request 14 bytes from the MPU-6050
Wire.requestFrom(byte(0x68 + AD0_val), bytes, byte(true));
//Wait until all the bytes are received
while(Wire.available() < bytes);
for (int i = 0; i < bytes; i++)
data[i] = Wire.read();
Wire.endTransmission();
}
float MPU6050::convertGyroToDPS (int gyro)
{
if (gyro_range == _250dps) return float(gyro)/131.0;
else if (gyro_range == _500dps) return float(gyro)/65.5;
else if (gyro_range == _1000dps) return float(gyro)/32.8;
else return float(gyro)/16.4;
}
#define SHOW_EACH 50
MPU6050 chip;
unsigned long last_shown = 0;
unsigned this_fps = 0;
unsigned last_fps = 0;
unsigned last_time = 0;
unsigned total_fps = 0;
float g_x, g_y, g_z;
void setup()
{
Serial.begin(115200);
Serial.println("--------");
chip.setFilterVal(_256Hz);
chip.setGyroRange(_250dps);
chip.start(false);
}
void loop()
{
if (chip.dataAvailable())
chip.getLastGyroData(g_x, g_y, g_z);
++this_fps;
++total_fps;
if (millis()/1000 != last_time)
{
last_time = millis()/1000;
last_fps = this_fps;
this_fps = 0;
}
if (millis() - last_shown >= SHOW_EACH)
{
last_shown = millis();
Serial.print(g_x);
Serial.print(" ");
Serial.print(g_y);
Serial.print(" ");
Serial.print(g_y);
Serial.print(" ");
Serial.print(last_fps);
Serial.print(" ");
Serial.println(total_fps);
}
}
Some testing with Serial.println points to the function requestFrom from the Wire library. What can be the cause?
Sorry that i write this as an answer, but I can't write comments yet.
1st. There are multiple requestFrom() calls in your code, so it would be better to specify where does the problem occure exactly (if you can).
2nd. Are you completely sure that, it's the requestFrom() where your code hang. In readData() there is a while() just after requestFrom(). Maybe it hangs there, as the other device don't send enough bytes (for some reasons).
Anyway this might help a litle (link), here they recommend to always check the return value of endTransmission().

Data transfer over sockets[TCP] how to pack multiple integer in c/c++ and transfer the data with send() recv()?

I'm making a small client/server based game, on linux in c/c++ and I need to send the player turn to the server.
Here is my problem.
I want to send two integers to the server and sometimes it works perfectly, but sometimes the server receives both integer in the first recv() and its stuck.
I know that the best way is to package the messages.
The problem is I don't know how the syntax should look like.
In theory--> the player input would be like an int column = 4 and a second int row = 1 and I package the message as 4|1 or something like this. Then I send from client to server and encode it on the server.
An example would be great or maybe some advice how stuff like this is handled probably.
I'm still very new to socket programming.
Here is how my function looks like:
Client:
#define BUFFER 512
void send_turn_to_server(int sock, int row, int column)
{
// sends row to server from player turn
char char_row[BUFFER];
sprintf(char_row, "%d", row);
char *message_from_client = char_row;
int len, bytes_sent_row;
len = strlen(message_from_client);
if (sendall(sock, message_from_client, &len) == -1)
{
perror("sendall");
printf("We only sent %d bytes because of the error!\n", len);
}
char char_column[BUFFER];
int bytes_sent_column;
//sends column from player turn
//sprintf converts the int to char
sprintf(char_column, "%d", column);
char *column_from_client = char_column;
len = strlen(column_from_client);
if (sendall(sock, column_from_client, &len) == -1)
{
perror("sendall");
printf("We only sent %d bytes because of the error!\n", len);
}
cout << "send_turn_to_server_complete" << endl;
}
Here I use a function from Beej's Guide to Network Programming, so I can be sure the whole buffer is sent.
Client:
int sendall(int s, char *buf, int *len)
{
int total = 0; // how many bytes we've sent
int bytesleft = *len; // how many we have left to send
int n;
while (total < *len)
{
n = send(s, buf + total, bytesleft, 0);
if (n == -1)
{
break;
}
total += n;
bytesleft -= n;
}
*len = total; // return number actually sent here
return n == -1 ? -1 : 0; // return -1 on failure, 0 on success
}
Server:
int receive_player_turn(int sock, int &int_row, int &int_column)
{
int byte_count;
char buf[BUFFER];
byte_count = recv(sock, buf, sizeof buf, 0);
cout << "The row from player: " << buf << endl;
//The C library function int atoi(const char *str) converts the string argument str to an integer (type int).
int_row = atoi(buf);
//cleans the buffer
bzero(buf, sizeof(buf));
byte_count = recv(sock, buf, sizeof buf, 0);
cout << buf << endl;
cout << "The column from player: " << buf << endl;
//converts the char string to an int
int_column = atoi(buf);
cout << endl
<< "receive player turn worked" << endl
<< "players turn was in the row " << int_row << " and in the column " << int_column + 1 << endl;
return int_row, int_column;
}
output correct from server:
Player connected: SchleichsSalaticus
The row from player: 7
4
The column from player: 4
receive player turn worked
players turn was in the row 7 and in the column 5
7 4
output wrong from server:
Player connected: SchleichsSalaticus
The row from player: 74
The issue is that TCP is a continuous stream, with no concept of the start or end of a ”message” because it is not message-based.
Most times, people use a very simple ”framing protocol” whereby you always send a 4-byte header on every transfer which tells the recipient how many bytes to read, then you send that many bytes as your message.
Use htonl() to send the 4-byte header in network byte order then you will be interoperable. There is a very similar example here.
One possible solution could be defining a format for the message that client send to server. For example you could define a protocol as follow:
[4 bytes length of your message][2 bytes for first player][2 bytes for second one] and in server side you should at first in rcv function get 4 bytes and extract the length of the arrived message and based on the receiving length(L) call again the rcv function with size L after that you should parse received messaged and extract the turn of each players.
If all your messages are expected to be of same length, then you do not need a message header. Something like that given below should work fine. In general you should be prepared to receive less or more than your expected message, as well as for one message to be split across many receives.
Also, I would recommend one function that receives bytes making no assumption about what they mean, and another that interprets them. Then the first one can be applied more broadly.
Treat the following only as pseudo code. not tested.
// use a buffer length double of MESSAGE_LENGTH.
static int offset = 0; // not thread safe.
// loop to receive a message.
while(offset < MESSAGE_LENGTH) {
byte_count = recv(sock, &buf[offset], (sizeof(buf)-offset), 0);
if(byte_count > 0) {
offset += byte_count;
}
else {
// add error handling here. close socket.
break out of loop
}
}
// process buf here, but do not clear it.
// received message always starts at buf[0].
if(no receive error above) {
process_received_message(buf); //
}
// move part of next message (if any) to start of buffer.
if(offset > MESSAGE_LENGTH) {
// copy the start of next message to start of buffer.
// and remember the new offset to avoid overwriting them.
char* pSrc = &buf[MESSAGE_LENGTH];
char* pSrcEnd = &buf[offset];
char* pDest = buf;
while(pSrc < pSrcEnd){
*pDest++ = *pSrc++;
} //or memcpy.
offset -= MESSAGE_LENGTH;
}
else {
offset = 0;
}
On many hardware architectures, integers and other types have alignment requirements. The compiler normally takes care of this, but when in a buffer, unaligned accesses can be an issue. Furthermore, the server and the client might not use the same byte order.
Here is a set of inline helper functions you can use to pack and unpack integer types to/from a buffer:
/* SPDX-License-Identifier: CC0-1.0 */
#ifndef PACKING_H
#define PACKING_H
#include <stdint.h>
/* Packing and unpacking unsigned and signed integers in
little-endian byte order.
Works on all architectures and OSes when compiled
using a standards-conforming C implementation, C99 or later.
*/
static inline void pack_u8(unsigned char *dst, uint8_t val)
{
dst[0] = val & 255;
}
static inline void pack_u16(unsigned char *dst, uint16_t val)
{
dst[0] = val & 255;
dst[1] = (val >> 8) & 255;
}
static inline void pack_u24(unsigned char *dst, uint32_t val)
{
dst[0] = val & 255;
dst[1] = (val >> 8) & 255;
dst[2] = (val >> 16) & 255;
}
static inline void pack_u32(unsigned char *dst, uint32_t val)
{
dst[0] = val & 255;
dst[1] = (val >> 8) & 255;
dst[2] = (val >> 16) & 255;
dst[3] = (val >> 24) & 255;
}
static inline void pack_u40(unsigned char *dst, uint64_t val)
{
dst[0] = val & 255;
dst[1] = (val >> 8) & 255;
dst[2] = (val >> 16) & 255;
dst[3] = (val >> 24) & 255;
dst[4] = (val >> 32) & 255;
}
static inline void pack_u48(unsigned char *dst, uint64_t val)
{
dst[0] = val & 255;
dst[1] = (val >> 8) & 255;
dst[2] = (val >> 16) & 255;
dst[3] = (val >> 24) & 255;
dst[4] = (val >> 32) & 255;
dst[5] = (val >> 40) & 255;
}
static inline void pack_u56(unsigned char *dst, uint64_t val)
{
dst[0] = val & 255;
dst[1] = (val >> 8) & 255;
dst[2] = (val >> 16) & 255;
dst[3] = (val >> 24) & 255;
dst[4] = (val >> 32) & 255;
dst[5] = (val >> 40) & 255;
dst[6] = (val >> 48) & 255;
}
static inline void pack_u64(unsigned char *dst, uint64_t val)
{
dst[0] = val & 255;
dst[1] = (val >> 8) & 255;
dst[2] = (val >> 16) & 255;
dst[3] = (val >> 24) & 255;
dst[4] = (val >> 32) & 255;
dst[5] = (val >> 40) & 255;
dst[6] = (val >> 48) & 255;
dst[7] = (val >> 56) & 255;
}
static inline void pack_i8(unsigned char *dst, int8_t val)
{
pack_u8((uint8_t)val);
}
static inline void pack_i16(unsigned char *dst, int16_t val)
{
pack_u16((uint16_t)val);
}
static inline void pack_i24(unsigned char *dst, int32_t val)
{
pack_u24((uint32_t)val);
}
static inline void pack_i32(unsigned char *dst, int32_t val)
{
pack_u32((uint32_t)val);
}
static inline void pack_i40(unsigned char *dst, int64_t val)
{
pack_u40((uint64_t)val);
}
static inline void pack_i48(unsigned char *dst, int64_t val)
{
pack_u48((uint64_t)val);
}
static inline void pack_i56(unsigned char *dst, int64_t val)
{
pack_u56((uint64_t)val);
}
static inline void pack_i64(unsigned char *dst, int64_t val)
{
pack_u64((uint64_t)val);
}
static inline uint8_t unpack_u8(const unsigned char *src)
{
return (uint_fast8_t)(src[0] & 255);
}
static inline uint16_t unpack_u16(const unsigned char *src)
{
return (uint_fast16_t)(src[0] & 255)
| ((uint_fast16_t)(src[1] & 255) << 8);
}
static inline uint32_t unpack_u24(const unsigned char *src)
{
return (uint_fast32_t)(src[0] & 255)
| ((uint_fast32_t)(src[1] & 255) << 8)
| ((uint_fast32_t)(src[2] & 255) << 16);
}
static inline uint32_t unpack_u32(const unsigned char *src)
{
return (uint_fast32_t)(src[0] & 255)
| ((uint_fast32_t)(src[1] & 255) << 8)
| ((uint_fast32_t)(src[2] & 255) << 16)
| ((uint_fast32_t)(src[3] & 255) << 24);
}
static inline uint64_t unpack_u40(const unsigned char *src)
{
return (uint_fast64_t)(src[0] & 255)
| ((uint_fast64_t)(src[1] & 255) << 8)
| ((uint_fast64_t)(src[2] & 255) << 16)
| ((uint_fast64_t)(src[3] & 255) << 24)
| ((uint_fast64_t)(src[4] & 255) << 32);
}
static inline uint64_t unpack_u48(const unsigned char *src)
{
return (uint_fast64_t)(src[0] & 255)
| ((uint_fast64_t)(src[1] & 255) << 8)
| ((uint_fast64_t)(src[2] & 255) << 16)
| ((uint_fast64_t)(src[3] & 255) << 24)
| ((uint_fast64_t)(src[4] & 255) << 32)
| ((uint_fast64_t)(src[5] & 255) << 40);
}
static inline uint64_t unpack_u56(const unsigned char *src)
{
return (uint_fast64_t)(src[0] & 255)
| ((uint_fast64_t)(src[1] & 255) << 8)
| ((uint_fast64_t)(src[2] & 255) << 16)
| ((uint_fast64_t)(src[3] & 255) << 24)
| ((uint_fast64_t)(src[4] & 255) << 32)
| ((uint_fast64_t)(src[5] & 255) << 40)
| ((uint_fast64_t)(src[6] & 255) << 48);
}
static inline uint64_t unpack_u64(const unsigned char *src)
{
return (uint_fast64_t)(src[0] & 255)
| ((uint_fast64_t)(src[1] & 255) << 8)
| ((uint_fast64_t)(src[2] & 255) << 16)
| ((uint_fast64_t)(src[3] & 255) << 24)
| ((uint_fast64_t)(src[4] & 255) << 32)
| ((uint_fast64_t)(src[5] & 255) << 40)
| ((uint_fast64_t)(src[6] & 255) << 48)
| ((uint_fast64_t)(src[7] & 255) << 56);
}
static inline int8_t unpack_i8(const unsigned char *src)
{
return (int8_t)(src[0] & 255);
}
static inline int16_t unpack_i16(const unsigned char *src)
{
return (int16_t)unpack_u16(src);
}
static inline int32_t unpack_i24(const unsigned char *src)
{
uint_fast32_t u = unpack_u24(src);
/* Sign extend to 32 bits */
if (u & 0x800000)
u |= 0xFF000000;
return (int32_t)u;
}
static inline int32_t unpack_i32(const unsigned char *src)
{
return (int32_t)unpack_u32(src);
}
static inline int64_t unpack_i40(const unsigned char *src)
{
uint_fast64_t u = unpack_u40(src);
/* Sign extend to 64 bits */
if (u & UINT64_C(0x0000008000000000))
u |= UINT64_C(0xFFFFFF0000000000);
return (int64_t)u;
}
static inline int64_t unpack_i48(const unsigned char *src)
{
uint_fast64_t u = unpack_i48(src);
/* Sign extend to 64 bits */
if (u & UINT64_C(0x0000800000000000))
u |= UINT64_C(0xFFFF000000000000);
return (int64_t)u;
}
static inline int64_t unpack_i56(const unsigned char *src)
{
uint_fast64_t u = unpack_u56(src);
/* Sign extend to 64 bits */
if (u & UINT64_C(0x0080000000000000))
u |= UINT64_C(0xFF00000000000000);
return (int64_t)u;
}
static inline int64_t unpack_i64(const unsigned char *src)
{
return (int64_t)unpack_u64(src);
}
#endif /* PACKING_H */
When packed, these values are in two's complement little-endian byte order.
pack_uN() and unpack_uN() work with unsigned integers from 0 to 2N-1, inclusive.
pack_iN() and unpack_iN() work with signed integers from -2N-1 to 2N-1-1, inclusive.
Let's consider a simple binary protocol, where each message starts with two bytes: first one the total length of this message, and the second one identifying the type of the message.
This has the nice feature that if something odd happens, it is always possible to resynchronize by sending at least 256 zeroes. Each zero is an invalid length for the message, so they should just be skipped by the receiver. You probably won't need this, but it may come in handy someday.
To receive a message of this form, we can use the following function:
/* Receive a single message.
'fd' is the socket descriptor, and
'msg' is a buffer of at least 255 chars.
Returns -1 with errno set if an error occurs,
or the message type (0 to 255, inclusive) if success.
*/
int recv_message(const int fd, unsigned char *msg)
{
ssize_t n;
msg[0] = 0;
msg[1] = 0;
/* Loop to skip zero bytes. */
do {
do {
n = read(fd, msg, 1);
} while (n == -1 && errno == EINTR);
if (n == -1) {
/* Error; errno already set. */
return -1;
} else
if (n == 0) {
/* Other end closed the socket. */
errno = EPIPE;
return -1;
} else
if (n != 1) {
errno = EIO;
return -1;
}
} while (msg[0] == 0);
/* Read the rest of the message. */
{
unsigned char *const end = msg + msg[0];
unsigned char *ptr = msg + 1;
while (ptr < end) {
n = read(fd, ptr, (size_t)(end - ptr));
if (n > 0) {
ptr += n;
} else
if (n == 0) {
/* Other end closed socket */
errno = EPIPE;
return -1;
} else
if (n != -1) {
errno = EIO;
return -1;
} else
if (errno != EINTR) {
/* Error; errno already set */
return -1;
}
}
}
/* Success, return message type. */
return msg[1];
}
In your own code, you can use the above like this:
unsigned char buffer[256];
switch(receive_message(fd, buffer)) {
case -1:
if (errno == EPIPE) {
/* The other end closed the connection */
} else {
/* Other error; see strerror(errno). */
}
break or return or abort;
case 0: /* Exit/cancel game */
break or return or abort;
case 4: /* Coordinate message */
int x = unpack_i16(buffer + 2);
int y = unpack_i16(buffer + 4);
/* x,y is the coordinate pair; do something */
break;
default:
/* Ignore all other message types */
}
where I randomly chose 0 as the abort-game message type, and 4 as the coordinate message type.
Instead of scattering such statements here and there in your client, put it in a function. You could also consider using a finite-state machine to represent the game state.
To send messages, you can use a helper function like
/* Send one or more messages; does not verify contents.
Returns 0 if success, -1 with errno set if an error occurs.
*/
int send_message(const int fd, const void *msg, const size_t len)
{
const unsigned char *const end = (const unsigned char *)msg + len;
const unsigned char *ptr = (const unsigned char *)msg;
ssize_t n;
while (ptr < end) {
n = write(fd, ptr, (size_t)(end - ptr));
if (n > 0) {
ptr += n;
} else
if (n != -1) {
/* C library bug, should not occur */
errno = EIO;
return -1;
} else
if (errno != EINTR) {
/* Other error */
return -1;
}
}
return 0;
}
so that sending an abort game (type 0) message would be
int send_abort_message(const int fd)
{
unsigned char buffer[2] = { 1, 0 };
return send_message(fd, buffer, 2);
}
and sending a coordinate (type 4) message would be e.g.
int send_coordinates(const int fd, const int x, const int y)
{
unsigned char buffer[2 + 2 + 2];
buffer[0] = 6; /* Length in bytes/chars */
buffer[1] = 4; /* Type */
pack_i16(buffer + 2, x);
pack_i16(buffer + 4, y);
return send_message(fd, buffer, 6);
}
If the game is not turn-based, you won't want to block in the sends or receives, like the above functions do.
Nonblocking I/O is the way to go. Essentially, you'll have something like
static int server_fd = -1;
static size_t send_size = 0;
static unsigned char *send_data = NULL;
static size_t send_next = 0; /* First unsent byte */
static size_t send_ends = 0; /* End of buffered data */
static size_t recv_size = 0;
static unsigned char *recv_data = NULL;
static size_t recv_next = 0; /* Start of next message */
static size_t recv_ends = 0; /* End of buffered data */
and you set the server_fd nonblocking using e.g. fcntl(server_fd, F_SETFL, O_NONBLOCK);.
A communicator function will try to send and receive as much data as possible. It will return 1 if it sent anything, 2 if it received anything, 3 if both, 0 if neither, and -1 if an error occurred:
int communicate(void) {
int retval = 0;
ssize_t n;
while (send_next < send_ends) {
n = write(server_fd, send_data + send_next, send_ends - send_next);
if (n > 0) {
send_next += n;
retval |= 1;
} else
if (n != -1) {
/* errno already set */
return -1;
} else
if (errno == EAGAIN || errno == EWOULDBLOCK) {
/* Cannot send more without blocking */
break;
} else
if (errno != EINTR) {
/* Error, errno set */
return -1;
}
}
/* If send buffer became empty, reset it. */
if (send_next >= send_ends) {
send_next = 0;
send_ends = 0;
}
/* If receive buffer is empty, reset it. */
if (recv_next >= recv_ends) {
recv_next = 0;
recv_ends = 0;
}
/* Receive loop. */
while (1) {
/* Receive buffer full? */
if (recv_ends + 256 > recv_ends) {
/* First try to repack. */
if (recv_next > 0) {
memmove(recv_data, recv_data + recv_next, recv_ends - recv_next);
recv_ends -= recv_next;
recv_next = 0;
}
if (recv_ends + 256 > recv_ends) {
/* Allocate 16k more (256 messages!) */
size_t new_size = recv_size + 16384;
unsigned char *new_data;
new_data = realloc(recv_data, new_size);
if (!new_data) {
errno = ENOMEM;
return -1;
}
recv_data = new_data;
recv_size = new_size;
}
}
/* Try to receive incoming data. */
n = read(server_fd, recv_data + recv_ends, recv_size - recv_ends);
if (n > 0) {
recv_ends += n;
retval |= 2;
} else
if (n == 0) {
/* Other end closed the connection. */
errno = EPIPE;
return -1;
} else
if (n != -1) {
errno = EIO;
return -1;
} else
if (errno == EAGAIN || errno == EWOULDBLOCK) {
break;
} else
if (errno != EINTR) {
return -1;
}
}
return retval;
}
When there is nothing to do, and you want to wait for a short while (some milliseconds), but interrupt the wait whenever more I/O can be done, use
/* Wait for max 'ms' milliseconds for communication to occur.
Returns 1 if data received, 2 if sent, 3 if both, 0 if neither
(having waited for 'ms' milliseconds), or -1 if an error occurs.
*/
int communicate_wait(int ms)
{
struct pollfd fds[1];
int retval;
/* Zero timeout is "forever", and we don't want that. */
if (ms < 1)
ms = 1;
/* We try communicating right now. */
retval = communicate();
if (retval)
return retval;
/* Poll until I/O possible. */
fds[0].fd = server_fd;
if (send_ends > send_next)
fds[0].events = POLLIN | POLLOUT;
else
fds[0].events = POLLIN;
fds[0].revents = 0;
poll(fds, 1, ms);
/* We retry I/O now. */
return communicate();
}
To process messages received thus far, you use a loop:
while (recv_next < recv_ends && recv_next + recv_data[recv_next] <= recv_ends) {
if (recv_data[recv_next] == 0) {
recv_next++;
continue;
}
/* recv_data[recv_next+0] is the length of the message,
recv_data[recv_next+1] is the type of the message. */
switch (recv_data[recv_next + 1]) {
case 4: /* Coordinate message */
if (recv_data[recv_next] >= 6) {
int x = unpack_i16(recv_data + recv_next + 2);
int y = unpack_i16(recv_data + recv_next + 4);
/* Do something with x and y ... */
}
break;
/* Handle other message types ... */
}
recv_next += recv_data[recv_next];
}
Then you recalculate game state, update the display, communicate some more, and repeat.

Debug Error - abort() has been called (gcz2tga)

Complete C++/C newbie here. I recompiled gcz2tga (I did not make this code, just found it on the webs) into a debug .exe file using Visual Studio 2019. Everything works well until it gets to "split_images" and then the program spits out this error:
Debug Error!
Program: C:\Users\Harrison\source\repos\gcz2tga\Debug\gcz2tga.exe
abort() has been called
(Press Retry to debug the application)
When I hit Retry, the program closes. The code is set up like this:
/* gcz2tga.c: Slice up a directory full of GCZ (texture) files into TGA files.
*
* Credit goes to afwefwe for reverse-engineering the texture format
* and LZSS compression */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/* Assuming x86, usual endian crap is not accounted for */
typedef unsigned char u8_t;
typedef unsigned short u16_t;
typedef unsigned int u32_t;
struct image
{
unsigned int width, height;
u8_t *planes;
};
struct clip
{
short x, y, w, h;
};
static u16_t swab16(u16_t in)
{
/* GC headers are big-endian */
return ((in & 0xFF) << 8) | (in >> 8);
}
static void unpack_gc(struct image *out, u8_t *in, size_t out_sz)
{
unsigned int npixels;
unsigned int i;
unsigned int j;
u16_t *pixels;
u16_t *pheight;
u16_t *pwidth;
u16_t *pmagic;
u16_t pixel;
pmagic = (u16_t *) in;
pheight = (u16_t *) (in + 14);
pwidth = (u16_t *) (in + 12);
if (*pmagic != 0x4347) {
fprintf(stderr, "(FAIL: Invalid header)\n");
exit(EXIT_FAILURE);
}
/* Set up output image struct */
in += 24;
out->width = swab16(*pwidth);
out->height = swab16(*pheight);
out->planes = malloc(out->width * out->height * 4);
/* Clamp W/H (don't know why this is necessary but it is) */
out->width = out->width > 1024 ? 1024 : out->width;
out->height = out->height > 1024 ? 1024 : out->height;
fprintf(stderr, "(%dx%d)", out->width, out->height);
/* Unpack pixels */
pixels = (u16_t *) in;
npixels = out->width * out->height;
if (out_sz > npixels * 4) {
/* Deep image (i.e. 32-bit) */
memcpy(out->planes, pixels, npixels * 4);
} else {
/* Shallow image (i.e. 16-bit) */
for (i = 0, j = 0 ; i < npixels ; i++) {
pixel = pixels[i];
out->planes[j++] = ((pixel ) & 0x1F) << 3; /* B */
out->planes[j++] = ((pixel >> 5) & 0x1F) << 3; /* G */
out->planes[j++] = ((pixel >> 10) ) << 3; /* R */
out->planes[j++] = pixel & 0x8000 ? 0xFF : 0x00; /* A */
}
}
}
static u8_t *expand_lzss(u8_t *lzss, size_t *pout_sz)
{
static u8_t ring[0x1000];
unsigned int ring_pos = 0x0FEE;
unsigned int chunk_offset;
unsigned int chunk_length;
u32_t control_word = 1;
size_t length;
u8_t cmd1;
u8_t cmd2;
u8_t *out;
u8_t *pos;
u8_t *in;
/* Header = 32 bit unpacked file length */
length = *((u32_t *) lzss);
*pout_sz = length;
if (length > 8000000) {
fprintf(stderr, "(FAIL: Unreasonably large expanded size %d)\n",
length);
exit(EXIT_FAILURE);
}
out = malloc(length * 2); /* Seems to overrun */
pos = out;
in = lzss + 4;
while (length > 0) {
if (control_word == 1) {
/* Read a control byte */
control_word = 0x100 | *in++;
}
/* Decode a byte according to the current control byte bit */
if (control_word & 1) {
/* Straight copy */
*pos++ = *in;
ring[ring_pos] = *in++;
ring_pos = (ring_pos + 1) % 0x1000;
length--;
} else {
/* Reference to data in ring buffer */
cmd1 = *in++;
cmd2 = *in++;
chunk_length = (cmd2 & 0x0F) + 3;
chunk_offset = ((cmd2 & 0xF0) << 4) | cmd1;
for ( ; chunk_length > 0 ; chunk_length--) {
/* Copy historical data to output AND current ring pos */
*pos++ = ring[chunk_offset];
ring[ring_pos] = ring[chunk_offset];
/* Update counters */
chunk_offset = (chunk_offset + 1) % 0x1000;
ring_pos = (ring_pos + 1) % 0x1000;
length--;
}
}
/* Get next control bit */
control_word >>= 1;
}
return out;
}
static void readfile(const char *filename, u8_t **data, long *nbytes)
{
FILE *f;
f = fopen(filename, "rb");
if (f == NULL) abort();
fseek(f, 0, SEEK_END);
*nbytes = ftell(f);
fseek(f, 0, SEEK_SET);
*data = malloc(*nbytes);
fread(*data, *nbytes, 1, f);
fclose(f);
}
void put8(FILE *f, unsigned char val)
{
fwrite(&val, 1, 1, f);
}
void put16(FILE *f, unsigned short val)
{
fwrite(&val, 2, 1, f);
}
void split_images(const char *in_dir, const char *out_dir,
struct image *images, int nimages)
{
struct clip *clips;
char filename[512];
long nbytes;
u8_t *data;
char *name;
FILE *f;
int i;
int j;
int k;
/* Read file and get TOC */
sprintf(filename, "%s/system.idx", in_dir);
readfile(filename, &data, &nbytes);
clips = (struct clip *) (data + 0x01BC);
name = (char *) (data + 8 + *((long *) data));
/* Guess how many clips there are with a heuristic */
for (i = 0 ; clips[i].w != 0 && clips[i].h != 0 ; i++) {
sprintf(filename, "%s/%s.tga", out_dir, name);
name += strlen(name) + 3;
f = fopen(filename, "wb");
if (f == NULL) abort();
/* Locate the correct source image */
j = 0;
while (clips[i].y > images[j].height) {
clips[i].y -= images[j].height;
j++;
}
/* Write header */
put8(f, 0); put8(f, 0); put8(f, 2);
put16(f, 0); put16(f, 0); put8(f, 0);
put16(f, 0); put16(f, 0); put16(f, clips[i].w); put16(f, clips[i].h);
put8(f, 32); put8(f, 32);
/* Write scanlines */
for (k = 0 ; k < clips[i].h ; k++) {
if (clips[i].y == images[j].height) {
clips[i].y = 0;
j++;
}
fwrite(images[j].planes + ((images[j].width * clips[i].y) +
clips[i].x) * 4, clips[i].w, 4, f);
clips[i].y++;
}
/* Close output file */
fclose(f);
}
/* Cleanup */
free(data);
}
int main(int argc, char **argv)
{
char *in_dir;
char *out_dir;
struct image images[32];
char filename[256];
unsigned int i;
long filesize;
u8_t *lzss, *gc;
size_t out_sz;
FILE *f;
/* Usage */
if (argc != 3) {
fprintf(stderr, "Usage: %s [indir] [outdir]\n", argv[0]);
return EXIT_FAILURE;
}
/* Setup */
memset(images, 0, sizeof(images));
in_dir = argv[1];
out_dir = argv[2];
for (i = 0 ; i < 32 ; i++) {
/* Open 0.gcz, 1.gcz etc ... */
sprintf(filename, "%s/%d.gcz", in_dir, i);
f = fopen(filename, "rb");
if (f == NULL) break;
/* Read entire file */
fseek(f, 0, SEEK_END);
filesize = ftell(f);
fseek(f, 0, SEEK_SET);
fprintf(stderr, "%s: fread", filename);
lzss = malloc(filesize);
fread(lzss, filesize, 1, f);
fclose(f);
/* Decompress */
fprintf(stderr, "(OK) expand_lzss");
gc = expand_lzss(lzss, &out_sz);
free(lzss);
/* Unpack GC to 32-bit RGBA */
fprintf(stderr, "(OK) unpack_gc");
unpack_gc(&images[i], gc, out_sz);
free(gc);
fprintf(stderr, "(OK)\n");
}
/* Sanity check */
if (i == 0) {
fprintf(stderr, "No GCZ files found\n");
exit(EXIT_FAILURE);
}
/* Emit pile of TGAs */
fprintf(stderr, "split_images");
split_images(in_dir, out_dir, images, i);
fprintf(stderr, "(OK)\n\n");
return 0;
}
What is wrong with the code that could be causing this? The code is unaltered save for #define _CRT_SECURE_NO_WARNINGS being added to the code before the #include headers and having the program compiled as C.