Step 5 - last step: Receive, Transcode and Send
In this step, Send and receive code are merged.
Here is the new strategy:
- Wait for data
- Verify packet length
- Read data
- Decode manchester
- Verifiy manchester validity
- Verify crc
- Verify that ID is one of the wheel on car
- Transcode data
- Generate CRC8
- Encode Manchester
- Shift data
- Send data twice
The full process is 53 ms for recognised ID, one sending/receive is 9.6ms
The code in loop is very long with a lot a printings at each step but this is very useful to understand if each encoding/decoding behave properly.
Data are no fully decoded before transcoding: this is not usefull because only ID, unknown and CRC need to be overwrited.
I need to send data twice because CC1101 do not set Sync Mode to 0 the first time. I do not know why: a CC1101 bug or I didn't understand the CC1101 data sheet. Maybe a CC1101 specialist could explain. Maybe a ELECHOUSE_cc1101.S.reset could solve this.
Here is the full code:
#include <ELECHOUSE_CC1101_SRC_DRV.h>
#define CC1101_FREQUENCY 433.92 //433.92 // Frequency for TPMS
#define PacketLength 18 //receive
uint32_t IDfrom[] = { 0x102e87, 0x0fd7bb }; //left,right
uint32_t IDto[] = { 0x101bb3, 0x102647 }; //left,right
int count = 0;
byte buffer[] = { 0xA5, 0x55, 0x55, 0x55, 0x5A, 0x69, 0x95, 0x6A, 0x59, 0xA9, 0x56, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x59 }; //for test
byte decoded[9];
byte encodedData[18]; // Manchester encoded data (2x the size)
byte shiftedData[23]; // shifted+preamble Manchester encoded data (Manchester size+5)
void setup() {
Serial.begin(115200);
Serial.println("Initializing CC1101...");
if (ELECHOUSE_cc1101.getCC1101()) { // Check the CC1101 Spi connection.
Serial.println("Connection OK !!!!!!!!!!!!!!!!!!!!!!!!!!!!");
} else {
Serial.println("Connection Error ?????????????????????????");
}
ELECHOUSE_cc1101.Init(); // must be set to initialize the cc1101!
ELECHOUSE_cc1101.setCCMode(1); // set config for internal transmission mode.
ELECHOUSE_cc1101.setModulation(0); // set modulation mode. 0 = 2-FSK, 1 = GFSK, 2 = ASK/OOK, 3 = 4-FSK, 4 = MSK.
ELECHOUSE_cc1101.setMHZ(CC1101_FREQUENCY); // Here you can set your basic frequency. The lib calculates the frequency automatically (default = 433.92).The cc1101 can: 300-348 MHZ, 387-464MHZ and 779-928MHZ. Read More info from datasheet.
ELECHOUSE_cc1101.setDeviation(35); //47.60// Set the Frequency deviation in kHz. Value from 1.58 to 380.85. Default is 47.60 kHz.
ELECHOUSE_cc1101.setChannel(0); // Set the Channelnumber from 0 to 255. Default is cahnnel 0.
ELECHOUSE_cc1101.setChsp(199.95); // The channel spacing is multiplied by the channel number CHAN and added to the base frequency in kHz. Value from 25.39 to 405.45. Default is 199.95 kHz.
ELECHOUSE_cc1101.setRxBW(812.50); // Set the Receive Bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50 kHz.
ELECHOUSE_cc1101.setDRate(19.23); //18.5 // Set the Data Rate in kBaud. Value from 0.02 to 1621.83. Default is 99.97 kBaud!
ELECHOUSE_cc1101.setPA(10); // Set TxPower. The following settings are possible depending on the frequency band. (-30 -20 -15 -10 -6 0 5 7 10 11 12) Default is max!
ELECHOUSE_cc1101.setSyncWord(0x55, 0x56); // Set sync word. Must be the same for the transmitter and receiver. (Syncword high, Syncword low)
ELECHOUSE_cc1101.setAdrChk(0); // Controls address check configuration of received packages. 0 = No address check. 1 = Address check, no broadcast. 2 = Address check and 0 (0x00) broadcast. 3 = Address check and 0 (0x00) and 255 (0xFF) broadcast.
ELECHOUSE_cc1101.setAddr(0); // Address used for packet filtration. Optional broadcast addresses are 0 (0x00) and 255 (0xFF).
ELECHOUSE_cc1101.setWhiteData(0); // Turn data whitening on / off. 0 = Whitening off. 1 = Whitening on.
ELECHOUSE_cc1101.setPktFormat(0); // Format of RX and TX data. 0 = Normal mode, use FIFOs for RX and TX. 1 = Synchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins. 2 = Random TX mode; sends random data using PN9 generator. Used for test. Works as normal mode, setting 0 (00), in RX. 3 = Asynchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins.
ELECHOUSE_cc1101.setLengthConfig(0); //!!! // 0 = Fixed packet length mode. 1 = Variable packet length mode. 2 = Infinite packet length mode. 3 = Reserved
ELECHOUSE_cc1101.setCrc(0); // 1 = CRC calculation in TX and CRC check in RX enabled. 0 = CRC disabled for TX and RX.
ELECHOUSE_cc1101.setCRC_AF(0); // Enable automatic flush of RX FIFO when CRC is not OK. This requires that only one packet is in the RXIFIFO and that packet length is limited to the RX FIFO size.
ELECHOUSE_cc1101.setDcFilterOff(0); // Disable digital DC blocking filter before demodulator. Only for data rates ≤ 250 kBaud The recommended IF frequency changes when the DC blocking is disabled. 1 = Disable (current optimized). 0 = Enable (better sensitivity).
ELECHOUSE_cc1101.setManchester(0); // Enables Manchester encoding/decoding. 0 = Disable. 1 = Enable.
ELECHOUSE_cc1101.setFEC(0); // Enable Forward Error Correction (FEC) with interleaving for packet payload (Only supported for fixed packet length mode. 0 = Disable. 1 = Enable.
ELECHOUSE_cc1101.setPRE(0); // Sets the minimum number of preamble bytes to be transmitted. Values: 0 : 2, 1 : 3, 2 : 4, 3 : 6, 4 : 8, 5 : 12, 6 : 16, 7 : 24
ELECHOUSE_cc1101.setPQT(0); // Preamble quality estimator threshold. The preamble quality estimator increases an internal counter by one each time a bit is received that is different from the previous bit, and decreases the counter by 8 each time a bit is received that is the same as the last bit. A threshold of 4∙PQT for this counter is used to gate sync word detection. When PQT=0 a sync word is always accepted.
ELECHOUSE_cc1101.setAppendStatus(0); // When enabled, two status bytes will be appended to the payload of the packet. The status bytes contain RSSI and LQI values, as well as CRC OK.
Serial.println("CC1101 initialized.");
//Start RX
ELECHOUSE_cc1101.setPacketLength(18);
ELECHOUSE_cc1101.setSyncMode(1); //sync on 0x5556
ELECHOUSE_cc1101.SpiStrobe(CC1101_SFRX); //flush fifo
ELECHOUSE_cc1101.SpiStrobe(CC1101_SRX); //enable rx
Serial.println("Rx Mode");
}
void loop() {
byte len = ELECHOUSE_cc1101.SpiReadStatus(CC1101_RXBYTES); //!!!!!!!!!!!!!!!!!
if (len > 0) {
long t = millis();
long t2;
do {
len = ELECHOUSE_cc1101.SpiReadStatus(CC1101_RXBYTES);
t2 = millis();
} while (len < PacketLength & ((t2 - t) < 20)); //20ms timeout
Serial.println();
Serial.print("len :");
Serial.print(len);
Serial.print(" temps : ");
Serial.print(t2 - t);
Serial.print(" ms");
if (len == PacketLength) {
ELECHOUSE_cc1101.SpiReadBurstReg(CC1101_RXFIFO, buffer, PacketLength); //read rx FIFO !!!!!!!!!!!!!!!!!!!!!
//Print received in bytes format.
Serial.print(" Data: ");
for (int i = 0; i < PacketLength; i++) {
if (buffer[i] < 16) Serial.print("0");
Serial.print(buffer[i], HEX);
Serial.print(" ");
}
Serial.println();
int nbdecoded = manchesterDecode(buffer, decoded, PacketLength);
Serial.print("Nb decoded :");
Serial.print(nbdecoded);
if (nbdecoded == 9) { //manchester valide
//Print decoded in bytes format.
Serial.print(" Decoded: ");
for (int i = 0; i < nbdecoded; i++) {
if (decoded[i] < 16) Serial.print("0");
Serial.print(decoded[i], HEX);
Serial.print(" ");
}
byte crc = crc8(decoded, 8, 0x07, 0x00);
Serial.print(" CRC: ");
Serial.print(crc, HEX);
if (crc == decoded[8]) {
//Print readings
Serial.println(" OK !!!!!!"); //crc ok
Serial.print(" FLAG : ");
Serial.print(decoded[0] >> 2, HEX);
float pressure_raw = (decoded[0] & 0x0003) << 8 | (decoded[1] & 0x00ff);
float pressure_kpa = pressure_raw * 0.75;
Serial.print(" PRESSURE : ");
Serial.print(pressure_kpa);
Serial.print(" TEMP : ");
Serial.print(decoded[2] - 30);
Serial.print(" ID : ");
if (decoded[5] < 16) Serial.print("0");
Serial.print(decoded[5], HEX);
if (decoded[4] < 16) Serial.print("0");
Serial.print(decoded[4], HEX);
if (decoded[3] < 16) Serial.print("0");
Serial.print(decoded[3], HEX);
Serial.print(" CODE : ");
if (decoded[7] < 16) Serial.print("0");
Serial.print(decoded[7], HEX);
if (decoded[6] < 16) Serial.print("0");
Serial.print(decoded[6], HEX);
//Verify ID
byte j = 127;
byte ID3 = IDfrom[0] & 0xFF;
byte ID4 = (IDfrom[0] >> 8) & 0xFF;
byte ID5 = (IDfrom[0] >> 16) & 0xFF;
if ((decoded[3] == ID3) && (decoded[4] == ID4) && (decoded[5] == ID5)) j = 0;
ID3 = IDfrom[1] & 0xFF;
ID4 = (IDfrom[1] >> 8) & 0xFF;
ID5 = (IDfrom[1] >> 16) & 0xFF;
if ((decoded[3] == ID3) && (decoded[4] == ID4) && (decoded[5] == ID5)) j = 1;
Serial.print("Index : ");
Serial.println(j);
if (j < 2) { //valid ID from
//Send data -----------------------------------------------
//transcode data
decoded[3] = IDto[j] & 0xFF;
decoded[4] = (IDto[j] >> 8) & 0xFF;
decoded[5] = (IDto[j] >> 16) & 0xFF;
decoded[6] = 0x09; //0x09;
decoded[7] = 0x94; //0x94 degonfle:8C ;
decoded[8] = crc8(decoded, 8, 0x07, 0x00); // Calculate CRC8 checksum with polynomial 0x07 and initial value 0x00=CRC-8-ATM (CRC-8-CCITT)
// Re-Encode data using Manchester encoding
encodeManchester(encodedData, decoded, 9);
//shift tpms data
shift(shiftedData, encodedData, 18);
// Print data
Serial.println("Transcode DATA:");
for (int k = 0; k < 9; k++) {
Serial.print(decoded[k], HEX);
Serial.print(" ");
}
Serial.println();
// Print data
Serial.println("ENCODED TPMS DATA:");
for (int i = 0; i < 18; i++) {
Serial.print(encodedData[i], HEX);
Serial.print(" ");
}
Serial.println();
// Print data
Serial.println("SHIFTED TPMS DATA:");
for (int i = 0; i < 23; i++) {
char s[80];
snprintf(s, sizeof(s), "%02x ", shiftedData[i]);
Serial.write(s);
}
Serial.println();
// Send TPMS data
ELECHOUSE_cc1101.setSyncMode(0);
ELECHOUSE_cc1101.setPacketLength(23); // Indicates the packet length when fixed packet length mode is enabled. If variable packet length mode is used, this value indicates the maximum packet length allowed.
ELECHOUSE_cc1101.SpiStrobe(CC1101_SIDLE);
ELECHOUSE_cc1101.SpiStrobe(CC1101_SFTX); //flush TXfifo
ELECHOUSE_cc1101.SpiWriteBurstReg(CC1101_TXFIFO, shiftedData, 23); //write data to send
ELECHOUSE_cc1101.SpiStrobe(CC1101_STX); //start send
Serial.print("TX bytes:");
txbytes(); //wait for the end of transmission
//send again because first send do not set syncmode=0
//ELECHOUSE_cc1101.S.reset could be tested as an quickest alternative
ELECHOUSE_cc1101.SpiStrobe(CC1101_SFTX); //flush TXfifo
ELECHOUSE_cc1101.SpiWriteBurstReg(CC1101_TXFIFO, shiftedData, 23); //write data to send
ELECHOUSE_cc1101.SpiStrobe(CC1101_STX); //start send
Serial.print("TX bytes:");
txbytes(); //wait for the end of transmission
count++;
Serial.println("TPMS sent count: " + (String)count);
//End send data---------------------------------------------
} else { //no valide ID from
Serial.println("No valid ID !!!!!!!!!!!!!");
}
} else {
Serial.println(" CRC erreur!!!");
}
} else { //nbdecoded <> 9
Serial.println(" erreur de decodage Manchester");
}
} else { //len <> PacketLength
Serial.println(" len <> PacketLength");
}
ELECHOUSE_cc1101.setSyncWord(0x55, 0x56);
ELECHOUSE_cc1101.setSyncMode(1); //sync on 0x5556, 1 = 16 sync word bits detected.
ELECHOUSE_cc1101.setPacketLength(18);
ELECHOUSE_cc1101.SpiStrobe(CC1101_SFRX); //flush rx fifo
ELECHOUSE_cc1101.SpiStrobe(CC1101_SIDLE);
ELECHOUSE_cc1101.SpiStrobe(CC1101_SRX); //enable rx
Serial.println("Rx Mode: " + (String)(millis() - t) + " ms");
} //end if (len > 0)
}
void encodeManchester(uint8_t *encoded, const uint8_t *data, size_t len) {
size_t bitIndex = 0;
for (size_t i = 0; i < len; i++) {
for (int bit = 7; bit >= 0; bit--) {
if (data[i] & (1 << bit)) {
// Bit is 1, Manchester encoding is 10
encoded[bitIndex / 8] |= (1 << (7 - (bitIndex % 8)));
bitIndex++;
encoded[bitIndex / 8] &= ~(1 << (7 - (bitIndex % 8)));
} else {
// Bit is 0, Manchester encoding is 01
encoded[bitIndex / 8] &= ~(1 << (7 - (bitIndex % 8)));
bitIndex++;
encoded[bitIndex / 8] |= (1 << (7 - (bitIndex % 8)));
}
bitIndex++;
}
}
}
byte manchesterDecode(const byte *manchesterData, byte *decodedData, size_t length) {
size_t bitCount = length * 8; // Chaque byte contient 8 bits
if (bitCount % 2 != 0) return 255; // Vérifie si la longueur est valide
byte bitIndex = 0;
byte byteIndex = 0;
decodedData[byteIndex] = 0;
for (size_t i = 0; i < bitCount; i += 2) {
byte first = (manchesterData[i / 8] >> (7 - (i % 8))) & 0x01;
byte second = (manchesterData[(i + 1) / 8] >> (7 - ((i + 1) % 8))) & 0x01;
if (first == 0 && second == 1) {
decodedData[byteIndex] <<= 1;
decodedData[byteIndex] |= 0; // 0 logique
} else if (first == 1 && second == 0) {
decodedData[byteIndex] <<= 1;
decodedData[byteIndex] |= 1; // 1 logique
} else {
return 254; // Séquence invalide
}
bitIndex++;
if (bitIndex == 8) {
bitIndex = 0;
byteIndex++;
if (byteIndex < length / 2) { // Évite de dépasser la taille du tableau
decodedData[byteIndex] = 0;
}
}
}
return byteIndex; // Nombre d'octets décodés
}
void shift(uint8_t *shifted, const uint8_t *data, size_t len) { //add preamble 05 55 55 6. , shift data 4 bits right and add 0 at end
shifted[0] = 0x05;
shifted[1] = 0x55;
shifted[2] = 0x55;
shifted[3] = 0x55;
shifted[4] = 0x60 | (data[0] & 0xf0) >> 4;
for (int i = 1; i < len; i++) { //shit 4 bits
shifted[i + 4] = (data[i - 1] & 0x0f) << 4 | (data[i] & 0xf0) >> 4;
}
shifted[len + 4] = (data[len - 1] & 0x0f) << 4; //last hex and 0 at end
}
uint8_t crc8(const uint8_t *data, size_t len, uint8_t polynomial, uint8_t initial) {
uint8_t crc = initial;
while (len--) {
crc ^= *data++;
for (uint8_t i = 0; i < 8; i++) {
if (crc & 0x80)
crc = (crc << 1) ^ polynomial;
else
crc <<= 1;
}
}
return crc;
}
void state() { //voir Main Radio Control State Machine State sur cc1101.pdf p93 & p50
int state;
do {
state = 0x0f & ELECHOUSE_cc1101.SpiReadStatus(CC1101_MARCSTATE);
Serial.print(state);
Serial.print(" ");
} while (state != 1);
Serial.println();
}
void txbytes() { //voir status sur cc1101.pdf p94
int txbytes;
do {
txbytes = ELECHOUSE_cc1101.SpiReadStatus(CC1101_TXBYTES);
Serial.print(txbytes);
Serial.print(" ");
} while (txbytes > 0);
Serial.println();
}
The code can certainly be optimised.
A further step would be a code able to transcode any TPMS but that's a huge job.
I hope that my posts will help you to decode/encode/transcode your own TPMS: only one step could be usefull to you if your project is for example:
- Fake TPMS sender
- Show TPMS data on LCD
- TPMS alarm buzzer