Nicola Tesla Had it Right. Making the Bit Protocol Decoder Work.
Nicola Tesla Had it Right. Making the Bit Protocol Decoder Work.
If you have ever studied Nicola Tesla, one thing was his constant tiny incremental improvements to inventions. The second was how he realized he could visually build, modify and repair an electric motor just inside his own mind. Cool.
- Small tiny steps built up.
- Visualization.
In the last article we looked at an auto-classifier that could decode a varying frame where the baud rate is constantly changing we explored various hashes to do this.
But in reality it is simply too big a jump and we will fall back and just make a simple classifier:
Starting with some samples from the chip as it received remote control signals would show us:
I
GPIO Dif 9001 pin 1
GPIO Dif 4536 pin 0
GPIO Dif 616 pin 1
GPIO Dif 516 pin 0
GPIO Dif 584 pin 1
GPIO Dif 549 pin 0
GPIO Dif 580 pin 1
GPIO Dif 549 pin 0
GPIO Dif 536 pin 1
GPIO Dif 596 pin 0
GPIO Dif 558 pin 1
We ended up manually building the following classifications:
ASCII Code | Pin High | Min Value | Max Value |
---|---|---|---|
A | 0 | 500 | 640 |
B | 1 | 500 | 640 |
C | 0 | 1600 | 1700 |
D | 1 | 1600 | 1700 |
E | 0 | 2100 | 2300 |
F | 1 | 2100 | 2300 |
G | 0 | 4400 | 4600 |
H | 1 | 4400 | 4600 |
I | 0 | 8900 | 9100 |
J | 1 | 8900 | 9100 |
So effectively is defined as 'If the pulse duration elapsed in milliseconds is between 500 and 640 and the current state of the pin is '1' - meaning it came off a 0, that is a 'A'
#include <stdio.h>
#include <math.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#define max_pulse 14000
#define max_char_array 80
uint32_t time_a = 0;
int hash_a = 0;
int hash_b = 0;
int hash_c = 0;
int hash_d = 0;
int cpos = 0;
char key_set[max_char_array] = {'0'};
void gpio_callback(uint gpio, uint32_t events)
{
uint32_t rnow = time_us_32();
uint32_t dif = rnow - time_a;
if (dif > max_pulse)
{
time_a = rnow; // reset the offset.
if (cpos < max_char_array)
{
for (int t = 0; t < max_char_array; t++)
{
if (key_set[t] != '0')
{
printf("%c",key_set[t]);
key_set[t] = '0';
}
}
printf("\n");
return; // Dump and run
}
}
int cpin = gpio_get(gpio);
key_set[cpos] = '0';
int done = 0;
if (dif < max_pulse)
{
if ((dif >= 500) && (dif <= 640))
{
if (cpin == 0)
key_set[cpos] = 'A';
if (cpin == 1)
key_set[cpos] = 'B';
done = 1;
cpos += 1;
}
if ((dif >= 1600) && (dif <= 1700))
{
if (cpin == 0)
key_set[cpos] = 'C';
if (cpin == 1)
key_set[cpos] = 'D';
done = 1;
cpos += 1;
}
if ((dif >= 2100) && (dif <= 2300))
{
if (cpin == 0)
key_set[cpos] = 'E';
if (cpin == 1)
key_set[cpos] = 'F';
done = 1;
cpos += 1;
}
if ((dif >= 4400) && (dif <= 4600))
{
if (cpin == 0)
key_set[cpos] = 'G';
if (cpin == 1)
key_set[cpos] = 'H';
done = 1;
cpos += 1;
}
if ((dif >= 8800) && (dif <= 9100))
{
if (cpin == 0)
key_set[cpos] = 'I';
if (cpin == 1)
key_set[cpos] = 'J';
done = 1;
cpos += 1;
}
if ((dif >= 9400) && (dif <= 9600))
{
if (cpin == 0)
key_set[cpos] = 'K';
if (cpin == 1)
key_set[cpos] = 'L';
done = 1;
cpos += 1;
}
if (cpos >= max_char_array)
{
printf("Overflow cpos: %d Output: %s\n", cpos, key_set);
for (int t = 0; t < max_char_array; t++)
key_set[t] = '0';
cpos = 0;
return; // Dump and run
}
}
if (done == 0)
{
printf("Missed value: %d pin: %d\n", dif, cpin);
}
}
int main()
{
stdio_init_all();
printf("Startup!\n");
const uint LED_PIN = PICO_DEFAULT_LED_PIN;
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
gpio_init(0);
gpio_set_dir(0, GPIO_IN);
gpio_init(1);
gpio_set_dir(1, GPIO_IN);
gpio_set_irq_enabled_with_callback(0, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
//gpio_set_irq_enabled(1, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
//gpio_set_irq_enabled_with_callback(1, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
while (true)
{
sleep_ms(100);
//printf("Test push\n");
//printf("Test push\n");
}
}
- It should be noted what is really good is the interrupt still works even though the chip is constantly being put to sleep:
sleep_ms(100);
Right away when we run this we could see some missed values as in:
Missed value: 8815 pin: 0
Missed value: 8938 pin: 1
Missed value: 9546 pin: 0
Missed value: 9771 pin: 1
Missed value: 9342 pin: 1
Missed value: 9469 pin: 1
Missed value: 9834 pin: 0
Missed value: 9947 pin: 1
Missed value: 9062 pin: 1
So adjusting thresholds for the 8800-9000 and adding a fresh range for 9400 - 9600, a second fresh range for 9800-10200 and setting overflow at 12000 our fresh decode table will now become:
ASCII Code | Pin High | Min Value | Max Value |
---|---|---|---|
A | 0 | 500 | 640 |
B | 1 | 500 | 640 |
C | 0 | 1600 | 1700 |
D | 1 | 1600 | 1700 |
E | 0 | 2100 | 2300 |
F | 1 | 2100 | 2300 |
G | 0 | 4400 | 4600 |
H | 1 | 4400 | 4600 |
I | 0 | 8900 | 9100 |
J | 1 | 8900 | 9100 |
K | 0 | 9400 | 9600 |
L | 1 | 9400 | 9600 |
How did it work out? It still needs improvement. But here is some of the results:
Missed value: 7849 pin: 0
Missed value: 8396 pin: 1
Missed value: 10108 pin: 0
Missed value: 10655 pin: 1
Missed value: 11235 pin: 0
Missed value: 11808 pin: 1
Missed value: 12366 pin: 0
Missed value: 12911 pin: 1
BEGIL
Missed value: 2779 pin: 1
Missed value: 5034 pin: 1
Missed value: 5593 pin: 0
Missed value: 6141 pin: 1
Missed value: 7825 pin: 0
Missed value: 8396 pin: 1
Clearly there can be a larger range of pins so it would be better to encode as hexadecimal.
Next we build a manual histogram which shows some surprising results:
- There is the potential that the same pulses even with a very large slew may repeat very frequently.
- The next option is to walk the frequency domain and see what has the highest percentage of close to even division against the set of measured slews potentially this will give us another frequency all together.