An IR "Brute Force" code blaster that I just can't get working

I have been working on this for a couple of weeks now and have just been going around in circles. I can see what is going wrong but all my attempts to correct it have failed. I am sure that it is staring me in the face but I now have a complete mental block. I have tried to put the output into the serial monitor to help with diagnostics. Any pointers to my idiocy would be gratefully received.

This sketch is "brute force" scanning potentially 65k codes to test for any reaction from a piece of equipment.
The code issued would (normally) be an address up to &FF and a command up to &FF. However to cut it down for test, I have removed the address scan and am using &F1 as a fixed address for now. Future enhancements are to use the rotary encoder to select ranges and address/code for manual sending. Not much point until the core is working of course.

Flow:

Run the FOR loop to get the list of Commands.
Combine that value with the ADDRESS, currently &F1 for test.
Run the array scan and compare the combined value with the array contents.
If no match, pass the combined value to the transmitter routine for sending.
If it matches, skip that combined value, carry on and flag MATCH on the display.

Issues noted:
The transmitted value seems to be transmitted twice except for the matches..
Only the first match, suppresses the code transmission. It only seems to transmit once in that situation too.

I have used delay because of my inexperience but intended to revisit that, unless you tell me that is why I can't sort the problem out! Some of the delays are only for fault finding use.


int Kodes = 15;
int i = 0;
int J = 0;
int match = 0;
int cl2 = 0;

// This section for later use...
#define CLK_PIN 2
#define DT_PIN 3
#define SW_PIN 4
#define DIRECTION_CW 0   // clockwise direction
#define DIRECTION_CCW 1  // counter-clockwise direction
int counter = 0;
int direction = DIRECTION_CW;
int CLK_state;
int prev_CLK_state;

// the library to use for SW pin
// Not currently required.
// #include <ezButton.h>
// ezButton (buttonSW_PIN);  // create ezButton object that attach to pin 4

#include <Wire.h>
#include <hd44780.h>                        // main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h>  // i2c expander i/o class header
hd44780_I2Cexp lcd;                         // declare lcd object: auto locate & auto config expander chip
const int LCD_COLS = 16;
const int LCD_ROWS = 2;

#define IR_SEND_PIN 9
#define USE_NO_SEND_PWM  // Use no carrier PWM, just simulate an active low receiver signal. Overrides SEND_PWM_BY_TIMER definition
#include <IRremote.hpp>  //Ken Shirriff's IRremote

unsigned int command = 0;
byte al, cl;               // the address and command byte
unsigned int total = 0;    // 
const byte buttonPin = 7;  // Just a push button for resets when testing.

unsigned long Exclude[] = { 0xF104, 0xF10D, 0xF10A, 0xF111, 0xF11B, 0xF121, 0xF129, 0xF149, 0xF159, 0xF171, 0xF1A1, 0xF1A9, 0xF1B1, 0xF1C9, 0xF1ED };

void setup() {
  Serial.begin(9600);
  pinMode(7, INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP);

  // For the display...
  int status;
  status = lcd.begin(LCD_COLS, LCD_ROWS);
  if (status)  // non zero status means it was unsuccesful
  { hd44780::fatalError(status); }

  IrSender.begin();  // The IR blaster init. call...

  /*
  // For later use with rotary encoder...
  pinMode(CLK_PIN, INPUT);
  pinMode(DT_PIN, INPUT);
  //button.setDebounceTime(50);  // set debounce time to 50 milliseconds
  // read the initial state of the rotary encoder's CLK pin
  prev_CLK_state = digitalRead(CLK_PIN);

*/
}
void loop() {

  for (cl = 0x0; cl < 255; cl++) {  //  We first loop through the commands

    int JVCSend = (Exclude[J]);  //Look up the next code from the array 'Exclude'.
    for (int J = 0; J < Kodes; J++) {

      total = combineint(cl, 0xF1); // &F1 is the test, single address.

      if ((total) == (Exclude[J])) {  // Compare code to be sent with the Exclude list.
        cl++; // Increment FOR loop
        match = 1;

        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("MATCH");  // This is working for the first Exclude code only.
        Serial.print("  MATCH  ");
        IrSender.sendJVC((total + 1), 16, 0);
        delay(1000);
      } else {
        match = 0;
        IrSender.sendJVC((total), 16, 0);
      }
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Total ");
      lcd.setCursor(10, 0);
      lcd.print(total, HEX);
      lcd.setCursor(0, 1);
      lcd.print("Exclude: ");
      lcd.setCursor(10, 1);
      lcd.print(Exclude[J], HEX);
      Serial.println("    ");
      Serial.print(" Transmitted: ");
      Serial.print(total, HEX);
      //      Serial.print("   Excluded: "); Serial.print(Exclude[J], HEX);

      int buttonState = digitalRead(buttonPin);
      if ((buttonState) == LOW) { ReStart(); }
    }
    buttonSelect();  // Slows down the issuing of control codes via IR.
  }
}

//makes a long from two integers
unsigned long combineint(unsigned int right, unsigned int left) {
  unsigned long value2 = 0;
  value2 = left << 8;
  value2 = value2 | (right);
  return value2;
}

void buttonSelect() {  // Delay timer select.
  int buttonstate10 = digitalRead(10);
  int buttonstate11 = digitalRead(11);
  if (buttonstate10 == LOW) {
    delay(5000);
  }
  if (buttonstate11 == LOW) {
    delay(2000);
  } else {
    delay(50);
  }
}

//Restart
void ReStart() {
  void (*resetFunc)(void) = 0;  //declare reset function at address 0
  resetFunc();  //call reset
}

—-

I strongly recommend against modifying the for control variable inside the for loop it is an invitation to create/hide hard to find bugs.

—-

Try this:

int excludeIndex = 0;
for (cl = 0; cl <= 0xFF; cl++) {
  // when testing is complete change the following to 
  //for (int addr = 0; addr <= 0xFF; addr++) {
   for (int addr = 0xF1; addr <= 0xF1; addr++) {
   unsigned int total = combineint(cl,addr);
    if(excludeIndex<Kodes && total==Exclude[excludeIndex]) {
    // exclude this value 
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("MATCH");
      Serial.print("  MATCH - No value sent: ");
      Serial.println(total, HEX);
      excludeIndex++;

    } else {
       // no exclude match: send value

      IrSender.sendJVC(total, 16, 0);
      Serial.print(" Transmitted: ");
      Serial.println(total, HEX);
    }
    // delay(500);
  }
}

Notes:

  1. The above code expects the Exclude[] array to be sorted in ascending order. You have to switch the second and third elements.
  2. I have not compiled or tested this code (working off my cell)

Thank you kindly for that. It needed a little tweaking to get it running and I can see your totally different approach to the issue, one I hadn't considered. It is late here in England and I may turn into a pumpkin if I stay up much later, so I will play with it again tomorrow afternoon. I am most grateful to you.

A quick test shows that it works as expected the first time around the commands list and then the Exclude value goes to zero rather than the value from the Exclude array.

int Kodes = 15;
int i = 0;
int J = 0;
int match = 0;
int cl2 = 0;

// This section for later use...
#define CLK_PIN 2
#define DT_PIN 3
#define SW_PIN 4
#define DIRECTION_CW 0   // clockwise direction
#define DIRECTION_CCW 1  // counter-clockwise direction
int counter = 0;
int direction = DIRECTION_CW;
int CLK_state;
int prev_CLK_state;

// the library to use for SW pin
// Not currently required.
// #include <ezButton.h>
// ezButton (buttonSW_PIN);  // create ezButton object that attach to pin 4

#include <Wire.h>
#include <hd44780.h>                        // main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h>  // i2c expander i/o class header
hd44780_I2Cexp lcd;                         // declare lcd object: auto locate & auto config expander chip
const int LCD_COLS = 16;
const int LCD_ROWS = 2;

#define IR_SEND_PIN 9
#define USE_NO_SEND_PWM  // Use no carrier PWM, just simulate an active low receiver signal. Overrides SEND_PWM_BY_TIMER definition
#include <IRremote.hpp>  //Ken Shirriff's IRremote

unsigned int command = 0;
byte al, cl;               // the address and command byte
unsigned int total = 0;    // 32 bits although we only need 16.  Don't really understand the "0L".
const byte buttonPin = 7;  // Just a push button for later use.
//unsigned int combineint = 0;
int excludeIndex = 0;

unsigned long Exclude[] = { 0xF104, 0xF10A, 0xF10D, 0xF111, 0xF11B, 0xF121, 0xF129, 0xF149, 0xF159, 0xF171, 0xF1A1, 0xF1A9, 0xF1B1, 0xF1C9, 0xF1ED };

void setup() {
  Serial.begin(9600);
  pinMode(7, INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP);

  // For the display...
  int status;
  status = lcd.begin(LCD_COLS, LCD_ROWS);
  if (status)  // non zero status means it was unsuccesful
  { hd44780::fatalError(status); }

  IrSender.begin();  // The IR blaster init. call...

  /*
  // For later use with rotary encoder...
  pinMode(CLK_PIN, INPUT);
  pinMode(DT_PIN, INPUT);
  //button.setDebounceTime(50);  // set debounce time to 50 milliseconds
  // read the initial state of the rotary encoder's CLK pin
  prev_CLK_state = digitalRead(CLK_PIN);

*/
}
void loop() {

  for (cl = 0; cl <= 0xFF; cl++) {
    // when testing is complete change the following to
    //for (int addr = 0; addr <= 0xFF; addr++) {
    for (int addr = 0xF1; addr <= 0xF1; addr++) {

      unsigned int total = combineint(cl, addr);

      if (excludeIndex < Kodes && total == Exclude[excludeIndex]) {
        // exclude this value
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("MATCH");
        Serial.print("  MATCH - No value sent: ");
        Serial.println(total, HEX);
        excludeIndex++;

      } else {
        // no exclude match: send value

        IrSender.sendJVC(total, 16, 0);
        Serial.print(" Transmitted: ");
        Serial.println(total, HEX);
        // delay(500);

        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Total ");
        lcd.setCursor(10, 0);
        lcd.print(total, HEX);
        lcd.setCursor(0, 1);
        lcd.print("Exclude: ");
        lcd.setCursor(10, 1);
        lcd.print(Exclude[excludeIndex], HEX);
        Serial.println("    ");
        Serial.print(" Transmitted: ");
        Serial.print(total, HEX);
        buttonSelect();  // Slows down the issuing of control codes via IR.

        int buttonState = digitalRead(buttonPin);
        if ((buttonState) == LOW) {
          ReStart();
        }
      }
    }
  }
}

//makes a long from two integers
unsigned int combineint(unsigned int right, unsigned int left) {
  unsigned int value2 = 0;
  value2 = left << 8;
  value2 = value2 | (right);
  return value2;
}

void buttonSelect() {  // Delay timer select.
  int buttonstate10 = digitalRead(10);
  int buttonstate11 = digitalRead(11);
  if (buttonstate10 == LOW) {
    delay(5000);
  }
  if (buttonstate11 == LOW) {
    delay(2000);
  } else {
    delay(50);
  }
}

//Restart
void ReStart() {
  void (*resetFunc)(void) = 0;  //declare reset function at address 0
  resetFunc();                  //call reset
}

Yes! You have to move this to the top inside loop() so it starts with 0 every loop iteration

void loop() {
  int excludeIndex =0;

…

}

I moved that line into place in a very quick test this morning, but the exclusion value still drops to 0 at the end of the first cycle. I will have a look later today to analyse it, two minutes here and there are not conducive to efficiency!

I am enormously impressed that you wrote it on a 'phone too. :wink:

Change this

to

int al, cl; // although I don’t know what “al” is for

Using byte here messes with the for loop making it an endless loop

Thank you. "al" is a carry over from the 32 bit sketch written by someone else, you have changed that variable for "addr". I just tweaked it for the 16 bit JVC protocol. I will be adding the 32 bit NEC scan back in once I understand what I am doing a bit better.

This is where the 'bones' came from...

That has made a difference. The Exclude value goes to zero immediately after the final match for the rest of the sequence. The change is that once the sequence restarts, it correctly restarts the loop.

It seems that I have some thinking about what we have done here, to do. I really appreciate your input, it has at the very least given me a basic scan that I can use against the device for the &F1 range.

The NEC protocol is a bit unusual, it still has an address and a command. However what they do is to put the address first, then its inverse, next comes the command and finishing with its inverse to give 32 bits for transmission. The JVC protocol just sends address, command 3 times for redundancy.