When I started working on this project, I had no previous experience in Arduino/ESP32 or C/C++ and because of this, everything was a learning curve.
Optimising the Digital Inputs using bit manipulation
Because I had a total of 5 switches and 10 push buttons I was using 15 bytes instead of just a couple.
In order to achieve this, I decided to allocate a single bit for every single digital input.
bool switchStatuses[15] = {
// Store the current switches
switch_1,
switch_2,
switch_3,
switch_4,
switch_5,
// Rotary Encoder - Left
left_up,
left_down,
left_left,
left_right,
left_middle,
// Rotary Encoder - Right
right_up,
right_down,
right_left,
right_right,
right_middle,
};
After reading everything, I had to extract the two bytes and add them to my payload.
...
uint16_t Tx::encodeStatusToByte(bool statuses[], int num) {
uint16_t encodedByte = 0;
for (int i = 0; i < num; i++) {
encodedByte |= (statuses[i] << i);
}
return encodedByte;
}
...
switches_state = Tx::encodeStatusToByte(switchStatuses, 15);
switches_state_1 = (switches_state & 0xFF);
switches_state_2 = ((switches_state >> 8) & 0xFF);
On the RX part, I only had to combine the bytes using this:
...
void Rx::decodeByteToStatuses(uint16_t encodedByte, bool statuses[], int num) {
for (int i = 0; i < num; i++) {
statuses[i] = (encodedByte >> i) & 0x01;
}
}
...
uint16_t combined_byte = Rx::combineBytes(
switches_state_1, switches_state_2
);
// Decode the uint16_t back into switch statuses
bool decoded_switch_statuses[15];
Rx::decodeByteToStatuses(combined_byte, decoded_switch_statuses, 15);
Optimising the payload by allocating unique channels
Using the bit manipulation I created a config that only uses 2 bytes. The first 3 bits are reserved for the sender identification while the other 13 bits are used to confirm if the channel was enabled or disabled.
/**
* TX Payload Config
* TX0 : Remote TX
* TX1 : Sensors 1
* TX2 : Sensors 2
*
* CH1 : Left Joystick Up/Down ( 1 byte )
* CH2 : Left Joystick Left/Right ( 1 byte )
* CH3 : Right Joystick Up/Down ( 1 byte )
* CH4 : Right Joystick Left/Right ( 1 byte )
* CH5 : Switches/Push Buttons ( 2 bytes )
* CH6 : Left Rotary Encoder ( 2 bytes )
* CH7 : Right Rotary Encoder ( 2 bytes )
* CH8 : Potentiometer 1 ( 1 byte )
* CH9 : Potentiometer 2 ( 1 byte )
* CH10 : Potentiometer 3 ( 1 byte )
* CH11 : Potentiometer 4 ( 1 byte )
* CH12 : Potentiometer 5 ( 1 byte )
* CH13 : Potentiometer 6 ( 1 byte )
*/
bool payload_config[16] = {
0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
After that, I only had to create a new Channel
structure and prepare the data.
typedef struct Channel {
uint8_t required_bytes;
uint8_t first_byte;
uint8_t second_byte;
};
/**
* CH1 : Left Joystick Up/Down ( 1 byte )
* CH2 : Left Joystick Left/Right ( 1 byte )
* CH3 : Right Joystick Up/Down ( 1 byte )
* CH4 : Right Joystick Left/Right ( 1 byte )
* CH5 : Switches/Push Buttons ( 2 bytes )
* CH6 : Left Rotary Encoder ( 2 bytes )
* CH7 : Right Rotary Encoder ( 2 bytes )
* CH8 : Potentiometer 1 ( 1 byte )
* CH9 : Potentiometer 2 ( 1 byte )
* CH10 : Potentiometer 3 ( 1 byte )
* CH11 : Potentiometer 4 ( 1 byte )
* CH12 : Potentiometer 5 ( 1 byte )
* CH13 : Potentiometer 6 ( 1 byte )
*/
Channel channels[13] = {
{1, joystick_default_value, 0},
{1, joystick_default_value, 0},
{1, joystick_default_value, 0},
{1, joystick_default_value, 0},
{2, 0, 0},
{2, 0, 0},
{2, 0, 0},
{1, 0, 0},
{1, 0, 0},
{1, 0, 0},
{1, 0, 0},
{1, 0, 0},
{1, 0, 0}
};
When sending the data, if the new value is not the same as the default one, I would just activate the channel by setting the bit to 1 instead of 0 and add the channel info in the current payload object.