The serial protocol aims to have these characteristics:
- Reduced message size
- Correctness of message (length and CRC check)
- Message version system
A single payload is limited to 255 bytes (length FF).
Program has to hardcode which data are being sent and where.
Only 16 message types can exist, and they must be hardcoded in the program.
The first byte is a simply start header: 0xAA.
The second byte is the length of the payload (so from the first byte of the payload to the last). The CRC is excluded.
The third byte is XY, where X is the length of the payload, from the first byte of the payload to the last, without CRC field; Y is the message type (from 0 to F).
Examples of this byte can be 0x80, 0x81, ...,0x8F.
CRC8 selfwritten library is used as checksum, including the header and the length.
// checksum calculator
uint8_t checksumCalculator(uint8_t *data, uint8_t length){
uint8_t curr_crc = 0x0000;
uint8_t sum1 = (uint8_t) curr_crc;
uint8_t sum2 = (uint8_t) (curr_crc >> 8);
int index;
for(index = 0; index < length; index = index+1)
{
sum1 = (sum1 + data[index]) % 255;
sum2 = (sum2 + sum1) % 255;
}
return (sum2 << 8) | sum1;
}
- Start string (AA)
- Length (number of fields = 8)
- Message type (0)
- Payload
- temperature
- humidity
- raw_data high byte
- raw_data low byte
- End string (FF)
- CRC8 value
//Answer to AA1FFF MESSAGE
void getMsg0(int temp, int humidity, int raw_data){
uint8_t buffer[8];
buffer[0] = 0xAA;
buffer[1] = 0x80;
buffer[2] = (uint8_t) temp;
buffer[3] = (uint8_t) humidity;
buffer[4] = highByte(raw_data);
buffer[5] = lowByte(raw_data);
buffer[6] = 0xFF;
uint8_t message[4];
message[0] = (uint8_t) 0xAA80;
message[1] = (uint8_t) temp;
message[2] = (uint8_t) humidity;
message[3] = (uint8_t) raw_data;
uint8_t crc = checksumCalculator(message,4);
buffer[7] = crc;
ble.write(buffer,8);
}
- Start string (AA)
- Length (number of fields = 8)
- Message type (1)
- Payload
- temperature
- humidity
- co2 high byte
- co2 low byte
- End string (FF)
- CRC8 value
//Answer to AA1EFF MESSAGE
void getMsg1(int temp, int humidity, int co2) {
uint8_t buffer[8];
uint8_t message[4];
message[0] = (uint8_t) 0xAA81;
message[1] = (uint8_t) temp;
message[2] = (uint8_t) humidity;
message[3] = (uint8_t) co2;
buffer[0] = 0xAA;
buffer[1] = 0x81;
buffer[2] = (uint8_t) temp;
buffer[3] = (uint8_t) humidity;
buffer[4] = highByte(co2);
buffer[5] = lowByte(co2);
buffer[6] = 0xFF;
uint8_t crc = checksumCalculator(message,4);
buffer[7] = crc;
ble.write(buffer,8);
}
- Start string (AA)
- Length (number of fields = 8)
- Message type (3)
- Payload
- model
- version
- serial number first byte
- serial number second byte
- serial number third byte
- serial number fourth byte
- battery
- End string (FF)
- CRC8 value
//Answer to AA1CFF MESSAGE
void getMsg3(){
uint8_t battery = 100;
uint8_t buffer[11];
buffer[0] = 0xAA;
buffer[1] = 0x83;
buffer[2] = (uint8_t) MODEL;
buffer[3] = (uint8_t) VERSION;
buffer[4] = (uint8_t) ((serialNumber) >> 24); //primo byte
buffer[5] = (uint8_t) (((serialNumber) & 0x00ff0000) >> 16); //secondo
buffer[6] = (uint8_t) (((serialNumber) & 0x0000ff00) >> 8); //terzo
buffer[7] = (uint8_t) (((serialNumber) & 0x000000ff)); //quarto
buffer[8] = battery;
buffer[9] = 0xFF;
uint8_t message[9];
message[0] = (uint8_t) 0xAA;
message[1] = (uint8_t) 0x83;
message[2] = (uint8_t) MODEL;
message[3] = (uint8_t) VERSION;
message[4] = (uint8_t) ((serialNumber) >> 24); //primo byte
message[5] = (uint8_t) (((serialNumber) & 0x00ff0000) >> 16); //secondo
message[6] = (uint8_t) (((serialNumber) & 0x0000ff00) >> 8); //terzo
message[7] = (uint8_t) (((serialNumber) & 0x000000ff)); //quarto
message[8] = (uint8_t) battery;
uint8_t crc = checksumCalculator(message,9);
buffer[10] = crc;
ble.write(buffer,11);
}
- Start string (AA)
- Length (number of fields = 9)
- Message type (4)
- Payload
- temperature
- humidity
- co2 high byte
- co2 low byte
- feedback value (positive=1, neutral=2, negative=3);
- End string (FF)
- CRC8 value
//Answer to AA1BFF MESSAGE
void getMsg4(int temp, int humidity, int co2, uint8_t feedback) {
uint8_t buffer[9];
buffer[0] = 0xAA;
buffer[1] = 0x84;
buffer[2] = (uint8_t) temp;
buffer[3] = (uint8_t) humidity;
buffer[4] = highByte(co2);
buffer[5] = lowByte(co2);
buffer[6] = feedback;
buffer[7] = 0xFF;
uint8_t message[5];
message[0] = (uint8_t) 0xAA81;
message[1] = (uint8_t) temp;
message[2] = (uint8_t) humidity;
message[3] = (uint8_t) co2;
message[4] = (uint8_t) feedback;
uint8_t crc = checksumCalculator(message,5);
buffer[8] = crc;
ble.write(buffer,9);
}
uint8_t = 8 bits
Index | Description | Data | Notes |
---|---|---|---|
0 | Raw info message | Temp(int), hum(int),raw(int) | It sends raw data for co2 sensor calibration (proto1: raw info from co2 sensor; proto2: co2 sensor temp) |
1 | co2 only message | Temp(int), hum(int),co2(int) | |
2 | extended data message | Temp (int), hum(int), co2(int)... | Reserved for future use |
3 | Startup information | Model(int),Version(int),battery(int) | battery can be 0 if sensor not present |
4 | feedback message | Temp(int), hum(int),co2(int), feedback(uint8_t) | Feedback message and debug mode toggle |
F | Request for message 0 | AA1F and empty payload | |
E | Request for message 1 | AA1E and empty payload | |
D | Request for message 2 | AA1D and empty payload | |
C | Request for message 3 | AA1C and empty payload | |
B | Request for message 4 | AA1B and empty payload |
The sensor should send a 1-type message every 10 seconds.
By sending request B, proto2 enters debug mode: sending a first message 4 with feedback set to 123 and changing the frequency of sending messages 1 from 10 seconds to 1 second.