The example I see on that URL uses:
uint16_t light;
std::vector<uint8_t> tx_data;
...
// get the latest light sample and send it to the gateway
light = lux.getData();
tx_data.push_back((light >> 8) & 0xFF);
tx_data.push_back(light & 0xFF);
logInfo("light: %lu [0x%04X]", light, light);
send_data(tx_data);
The above sends 2 bytes for an unsigned 16 bits integer.
For CO2, I see this part of your struct:
uint16_t co2m; /**< High order 16 bit word of CO2 */
uint16_t co2l; /**< Low order 16 bit word of CO2 */
uint32_t co2i; /**< 32 bit int of CO2 */
float co2f; /**< float of CO2 concentration */
This is populated using the following in your measurement code:
_i2c.read(SCD30_I2C_ADDR | 1, i2cBuff, 18, false);
uint16_t stat = (i2cBuff[0] << 8) | i2cBuff[1];
scdSTR.co2m = stat;
uint8_t dat = scd30::checkCrc2b(stat, i2cBuff[2]);
if(dat == SCDcrcERROR) return SCDcrcERRORv1;
stat = (i2cBuff[3] << 8) | i2cBuff[4];
scdSTR.co2l = stat;
dat = scd30::checkCrc2b(stat, i2cBuff[5]);
if(dat == SCDcrcERROR) return SCDcrcERRORv2;
...
scdSTR.co2i = (scdSTR.co2m << 16) | scdSTR.co2l ;
...
scdSTR.co2f = *(float*)&scdSTR.co2i;
So, I’d say that only the floats such as co2f
hold usable values. And I assume that the CO2 reading is within the range of 0 thru 6553.5, hence fits into a 16 bits unsigned integer when first multiplying by 10 like explained in Working with bytes. For temperature, I’d expect it to be within -327.68 thru 327.67, so fits in a 16 bits signed integer when first multiplying by 100 to keep two decimals (which probably is far beyond the sensor’s accuracy). Finally, humidity probably does not need any decimal, so ranges from 0 through 100, fitting in a single unsigned byte of 0 thru 255.
With the above, I’d assume the following might do to send the unsigned CO2 value in 2 bytes:
// Unsigned; multiply float by 10 to keep one decimal, 0 thru 6553.5
uint16_t co2 = scd.scdSTR.co2f * 10;
// MSB, most significant byte first
tx_data.push_back((co2 >> 8) & 0xFF);
tx_data.push_back(co2 & 0xFF);
Even more, with std::vector<uint8_t> tx_data
the vector can only hold bytes (uint8_t
being unsigned 8 bits), so I’d assume that pushing something larger (say: 16 bits) will simply ignore whatever does not fit. So, I’d guess this would even suffice:
tx_data.push_back(co2 >> 8);
tx_data.push_back(co2);
Temperature should allow for negative values:
// Signed, multiply float by 100 to keep two decimals, -327.68 thru 327.67
int16_t temp = scd.scdSTR.tempf * 100;
tx_data.push_back(temp >> 8);
tx_data.push_back(temp);
// Unsigned, ignore any decimals, 0 thru 255:
uint8_t hum = scd.scdSTR.humf;
tx_data.push_back(hum);
// send the data to the gateway
if ((res = dot->send(data)) != mDot::MDOT_OK) {
logError("failed to send", res, mDot::getReturnCodeString(res).c_str());
} else {
logInfo("successfully sent data to gateway: %s", data_str);
}
So, the above sends two bytes for CO2, two more for temperature, and one for humidity.
In TTN Console, this would then need:
function Decoder(bytes, port) {
// MSB, unsigned
var co2 = (bytes[0]<<8 | bytes[1]) * 10;
// Sign-extend to 32 bits to support negative values, by shifting 24
// bits to the left (16 too far), followed by shifting 16 bits to the
// right, to effectively shift 8 bits:
var temp = (bytes[2]<<24>>16 | bytes[3]) * 100;
var hum = bytes[4];
return {
co2: co2,
temperature: temp,
humidity: hum
};
}
All untested…