Working 433mhz sensor receiver (wh2 fineoffset)

I think your problem relates to the variables.
I will test later, but I think they need to be declared ‘volatile’ so that they can be accessed both by the main code and by the ISR.

Somehow it worked. I don’t know why, but int to float conversion doesn’t work. I changed to hum = humidity * 1.0;, it made a trick. Probably not the best solution :slight_smile:

wr2_rx.h:

volatile byte have_data_string = 0;
std::string my_data;
volatile int have_data_sensor = 0;
volatile float temp = 0.0;
volatile float hum = 0.0;

static void ICACHE_RAM_ATTR ext_int_1()
{	
  static unsigned long edgeTimeStamp[3] = {0, };  // Timestamp of edges
  static bool skip = true;
  
  // Filter out too short pulses. This method works as a low pass filter.  (borroved from new remote reciever)
  edgeTimeStamp[1] = edgeTimeStamp[2];
  edgeTimeStamp[2] = micros();

  if (skip) {
    skip = false;
    return;
  }

  if (edgeTimeStamp[2]-edgeTimeStamp[1] < 200) {
    // Last edge was too short.
    // Skip this edge, and the next too.
    skip = true;
    return;
  }

  unsigned int pulse = edgeTimeStamp[1] - edgeTimeStamp[0];
  edgeTimeStamp[0] = edgeTimeStamp[1];
	
//--------------------------------------------------------
    // 1 is indicated by 500uS pulse
    // wh2_accept from 2 = 400us to 3 = 600us
#define IS_HI_PULSE(interval)   (interval >= 250 && interval <= 750)
    // 0 is indicated by ~1500us pulse
    // wh2_accept from 7 = 1400us to 8 = 1600us
#define IS_LOW_PULSE(interval)  (interval >= 1200 && interval <= 1750)
    // worst case packet length
    // 6 bytes x 8 bits =48
#define IDLE_HAS_TIMED_OUT(interval) (interval > 1199)
    // our expected pulse should arrive after 1ms
    // we'll wh2_accept it if it arrives after
    // 4 x 200us = 800us
#define IDLE_PERIOD_DONE(interval) (interval >= 751)
#define GOT_PULSE 0x01
#define LOGIC_HI  0x02

      static byte wh2_flags = 0x00;
      static bool wh2_accept_flag = false;
      static byte wh2_packet_state = 0;
      static byte wh2_packet[5];
      static bool wh2_valid = false;
      static byte sampling_state = 0;
      static byte packet_no = 0; 
      static byte bit_no = 0; 
      static byte history = 0x01;

      switch (sampling_state) {
        case 0: // waiting

          if (IS_HI_PULSE(pulse)) {
            wh2_flags = GOT_PULSE | LOGIC_HI;
            sampling_state = 1;

          } else if (IS_LOW_PULSE(pulse)) {
            wh2_flags = GOT_PULSE; // logic low

          } else {
            sampling_state = 0;
          }
          break;
        case 1: // observe 1ms of idle time

          if (IDLE_HAS_TIMED_OUT(pulse)) {
            sampling_state = 0;
            wh2_packet_state = 1;

          } else if (IDLE_PERIOD_DONE(pulse)) {
            sampling_state = 0;
          }
          else
          {
            sampling_state = 0;
            wh2_packet_state = 1;
          }
          break;
      }


      if (wh2_flags) {


        // acquire preamble
        if (wh2_packet_state == 1) {
          // shift history right and store new value
          history <<= 1;
          // store a 1 if required (right shift along will store a 0)
          if (wh2_flags & LOGIC_HI) {
            history |= 0x01;
          }

          // check if we have a valid start of frame
          // xxxxx110
          if ((history & B00000111) == B00000110) {
            // need to clear packet, and pulseers
            packet_no = 0;
            // start at 1 becuase only need to acquire 7 bits for first packet byte.
            bit_no = 1;
            wh2_packet[0] = wh2_packet[1] = wh2_packet[2] = wh2_packet[3] = wh2_packet[4] = 0;
            // we've acquired the preamble
            wh2_packet_state = 2;
            history = 0xFF;
          }
          wh2_accept_flag = false;
        }
        // acquire packet
        else if (wh2_packet_state == 2) {
          wh2_packet[packet_no] <<= 1;
          if (wh2_flags & LOGIC_HI) {
            wh2_packet[packet_no] |= 0x01;
          }
          bit_no ++;
          if (bit_no > 7) {
            bit_no = 0;
            packet_no ++;
          }
          if (packet_no > 4) {
            // start the sampling process from scratch
            wh2_packet_state = 1;

            wh2_accept_flag = true;
          }
        }
        else
        {
          wh2_accept_flag = false;
        }


        if (wh2_accept_flag) {

          int Sensor_ID = (wh2_packet[0] << 4) + (wh2_packet[1] >> 4);
          int humidity = wh2_packet[3];
          int temperature = ((wh2_packet[1] & B00000111) << 8) + wh2_packet[2];
          // make negative
          if (wh2_packet[1] & B00001000) {
            temperature = -temperature;
          }
          uint8_t crc = 0;
          uint8_t len = 4;
          uint8_t addr = 0;
          // Indicated changes are from reference CRC-8 function in OneWire library
          while (len--) {
            uint8_t inbyte = wh2_packet[addr++];
            for (uint8_t i = 8; i; i--) {
              uint8_t mix = (crc ^ inbyte) & 0x80; // changed from & 0x01
              crc <<= 1; // changed from right shift
              if (mix) crc ^= 0x31;// changed from 0x8C;
              inbyte <<= 1; // changed from right shift
            }
          }
          if (crc == wh2_packet[4])
          {
            wh2_valid = true;
          }
          else
          {
            wh2_valid = false;
          }
          extern volatile int have_data_sensor;
          if (wh2_valid == true && have_data_sensor==0) //avoid change sensor data during update...
          {            
			extern volatile float temp;
			extern volatile float hum;
            temp = temperature / 10.0;            
            hum = humidity * 1.0;
			have_data_sensor = Sensor_ID;
          }

	extern volatile byte have_data_string;	  
if (have_data_string==0) //avoid change sensor data during update.
{
          extern std::string my_data;
          my_data = "| Sensor ID: ";
          my_data += to_string(Sensor_ID);
          my_data += " | humidity: ";
          my_data += to_string(humidity);
          my_data += " | temperature: ";
          my_data += to_string(temperature / 10.0);
          my_data += "C | ";
          my_data += (wh2_valid ? "OK " : "BAD" );
          have_data_string = 1;
}

          wh2_accept_flag = false;
        }
        wh2_flags = 0x00;
      }
      //--------------------------------------------------------


}

class MyCustomSensor : public PollingComponent {
  public:

    Sensor *sensor1 = new Sensor();
    Sensor *sensor2 = new Sensor();
		

	
MyCustomSensor() : PollingComponent(100) { }

    void setup() override 
	{
	Serial.end();
	//For ESP8266,if you are using rx pin for reciever set to 3 below
	//For Sonoff Bridge with direct HW patch use pin 4
	attachInterrupt(23, ext_int_1, CHANGE);			  	  
    }


    void update() override {
      // This is the actual sensor reading logic.
      extern volatile int have_data_sensor;
      extern volatile float temp;
      extern volatile float hum;


      if (have_data_sensor==1136)
      {     
        sensor1->publish_state(temp);
        sensor2->publish_state(hum);
        have_data_sensor = 0;
      }


      else if (have_data_sensor)
      {
        have_data_sensor = 0;
      }

    }
};



class Last_sensor_data : public PollingComponent, public TextSensor {
  public:

    // constructor
    Last_sensor_data() : PollingComponent(500) {}

    void setup() override {
      // This will be called by App.setup()
    }
    void update() override {
      extern std::string my_data;
      extern volatile byte have_data_string;
      if (have_data_string == 1)
      {
        publish_state(my_data);
        have_data_string = 0;
      }

      // This will be called every "update_interval" milliseconds.
      // Publish state

    }
};

Hi KST,

Great to hear that you have it working. Good luck with the rest.

With regard to the float issue, the following may be useful to you.

As a general comment, I think the ISR is way too long. ESP32 have a lot going on, especially when implemented for ESPHOME. Usually, the ISR will capture the main info, return, and allow time for other routines (WiFi, Timers, etc). The loop/update code will then handle that interrupt information as an interruptible process.
In your case, you could store the timestamps in a common array, then the main update code can access and process those timestamps. If you store and process the logic level of the interrupt, you will probably get more reliable information.

e.g.

static void ICACHE_RAM_ATTR ext_int_1()
{	
  static unsigned long edgeTimeStamp[3] = {0, };  // Timestamp of edges
  static bool skip = true;
  
  // Filter out too short pulses. This method works as a low pass filter.  (borroved from new remote reciever)
  edgeTimeStamp[1] = edgeTimeStamp[2];
  edgeTimeStamp[2] = micros();

  if (skip) {
    skip = false;
    return;
  }

  if (edgeTimeStamp[2]-edgeTimeStamp[1] < 200) {
    // Last edge was too short.
    // Skip this edge, and the next too.
    skip = true;
    return;
  }

  unsigned int pulse = edgeTimeStamp[1] - edgeTimeStamp[0];
  edgeTimeStamp[0] = edgeTimeStamp[1];
}
...

    void update() override {
...

//--------------------------------------------------------
    // 1 is indicated by 500uS pulse
    // wh2_accept from 2 = 400us to 3 = 600us
#define IS_HI_PULSE(interval)   (interval >= 250 && interval <= 750)
    // 0 is indicated by ~1500us pulse
    // wh2_accept from 7 = 1400us to 8 = 1600us
#define IS_LOW_PULSE(interval)  (interval >= 1200 && interval <= 1750)
    // worst case packet length
    // 6 bytes x 8 bits =48
#define IDLE_HAS_TIMED_OUT(interval) (interval > 1199)
    // our expected pulse should arrive after 1ms
    // we'll wh2_accept it if it arrives after
    // 4 x 200us = 800us
#define IDLE_PERIOD_DONE(interval) (interval >= 751)
#define GOT_PULSE 0x01
#define LOGIC_HI  0x02

      static byte wh2_flags = 0x00;
      static bool wh2_accept_flag = false;
      static byte wh2_packet_state = 0;
      static byte wh2_packet[5];
      static bool wh2_valid = false;
      static byte sampling_state = 0;
      static byte packet_no = 0; 
      static byte bit_no = 0; 
      static byte history = 0x01;

      switch (sampling_state) {
        case 0: // waiting

 ... etc...

...

}

Hi John,
Thank you very much.
Moved float and string operations out from ISR. Should be ok :slight_smile:

volatile byte have_data_string = 0;
volatile int have_data_sensor = 0;
volatile int temp;
volatile int hum;
volatile int str_Sensor_ID;
volatile int str_humidity;
volatile int str_temperature;
volatile bool str_wh2_valid;

static void ICACHE_RAM_ATTR ext_int_1()
{	
  static unsigned long edgeTimeStamp[3] = {0, };  // Timestamp of edges
  static bool skip = true;
  
  // Filter out too short pulses. This method works as a low pass filter.  (borroved from new remote reciever)
  edgeTimeStamp[1] = edgeTimeStamp[2];
  edgeTimeStamp[2] = micros();

  if (skip) {
    skip = false;
    return;
  }

  if (edgeTimeStamp[2]-edgeTimeStamp[1] < 200) {
    // Last edge was too short.
    // Skip this edge, and the next too.
    skip = true;
    return;
  }

  unsigned int pulse = edgeTimeStamp[1] - edgeTimeStamp[0];
  edgeTimeStamp[0] = edgeTimeStamp[1];
	
//--------------------------------------------------------
    // 1 is indicated by 500uS pulse
    // wh2_accept from 2 = 400us to 3 = 600us
#define IS_HI_PULSE(interval)   (interval >= 250 && interval <= 750)
    // 0 is indicated by ~1500us pulse
    // wh2_accept from 7 = 1400us to 8 = 1600us
#define IS_LOW_PULSE(interval)  (interval >= 1200 && interval <= 1750)
    // worst case packet length
    // 6 bytes x 8 bits =48
#define IDLE_HAS_TIMED_OUT(interval) (interval > 1199)
    // our expected pulse should arrive after 1ms
    // we'll wh2_accept it if it arrives after
    // 4 x 200us = 800us
#define IDLE_PERIOD_DONE(interval) (interval >= 751)
#define GOT_PULSE 0x01
#define LOGIC_HI  0x02

      static byte wh2_flags = 0x00;
      static bool wh2_accept_flag = false;
      static byte wh2_packet_state = 0;
      static byte wh2_packet[5];
      static bool wh2_valid = false;
      static byte sampling_state = 0;
      static byte packet_no = 0; 
      static byte bit_no = 0; 
      static byte history = 0x01;

      switch (sampling_state) {
        case 0: // waiting

          if (IS_HI_PULSE(pulse)) {
            wh2_flags = GOT_PULSE | LOGIC_HI;
            sampling_state = 1;

          } else if (IS_LOW_PULSE(pulse)) {
            wh2_flags = GOT_PULSE; // logic low

          } else {
            sampling_state = 0;
          }
          break;
        case 1: // observe 1ms of idle time

          if (IDLE_HAS_TIMED_OUT(pulse)) {
            sampling_state = 0;
            wh2_packet_state = 1;

          } else if (IDLE_PERIOD_DONE(pulse)) {
            sampling_state = 0;
          }
          else
          {
            sampling_state = 0;
            wh2_packet_state = 1;
          }
          break;
      }


      if (wh2_flags) {


        // acquire preamble
        if (wh2_packet_state == 1) {
          // shift history right and store new value
          history <<= 1;
          // store a 1 if required (right shift along will store a 0)
          if (wh2_flags & LOGIC_HI) {
            history |= 0x01;
          }

          // check if we have a valid start of frame
          // xxxxx110
          if ((history & B00000111) == B00000110) {
            // need to clear packet, and pulseers
            packet_no = 0;
            // start at 1 becuase only need to acquire 7 bits for first packet byte.
            bit_no = 1;
            wh2_packet[0] = wh2_packet[1] = wh2_packet[2] = wh2_packet[3] = wh2_packet[4] = 0;
            // we've acquired the preamble
            wh2_packet_state = 2;
            history = 0xFF;
          }
          wh2_accept_flag = false;
        }
        // acquire packet
        else if (wh2_packet_state == 2) {
          wh2_packet[packet_no] <<= 1;
          if (wh2_flags & LOGIC_HI) {
            wh2_packet[packet_no] |= 0x01;
          }
          bit_no ++;
          if (bit_no > 7) {
            bit_no = 0;
            packet_no ++;
          }
          if (packet_no > 4) {
            // start the sampling process from scratch
            wh2_packet_state = 1;

            wh2_accept_flag = true;
          }
        }
        else
        {
          wh2_accept_flag = false;
        }


        if (wh2_accept_flag) {

          int Sensor_ID = (wh2_packet[0] << 4) + (wh2_packet[1] >> 4);
          int humidity = wh2_packet[3];
          int temperature = ((wh2_packet[1] & B00000111) << 8) + wh2_packet[2];
          // make negative
          if (wh2_packet[1] & B00001000) {
            temperature = -temperature;
          }
          uint8_t crc = 0;
          uint8_t len = 4;
          uint8_t addr = 0;
          // Indicated changes are from reference CRC-8 function in OneWire library
          while (len--) {
            uint8_t inbyte = wh2_packet[addr++];
            for (uint8_t i = 8; i; i--) {
              uint8_t mix = (crc ^ inbyte) & 0x80; // changed from & 0x01
              crc <<= 1; // changed from right shift
              if (mix) crc ^= 0x31;// changed from 0x8C;
              inbyte <<= 1; // changed from right shift
            }
          }
          if (crc == wh2_packet[4])
          {
            wh2_valid = true;
          }
          else
          {
            wh2_valid = false;
          }
          extern volatile int have_data_sensor;
          if (wh2_valid == true && have_data_sensor==0) //avoid change sensor data during update...
          {            
			extern volatile int temp;
			extern volatile int hum;
            temp = temperature;            
            hum = humidity;
			have_data_sensor = Sensor_ID;
          }

	extern volatile byte have_data_string;	  
if (have_data_string==0) //avoid change sensor data during update.
{
          str_Sensor_ID = Sensor_ID;
		  str_humidity = humidity;
		  str_temperature = temperature;
		  str_wh2_valid = wh2_valid;
          have_data_string = 1;
}

          wh2_accept_flag = false;
        }
        wh2_flags = 0x00;
      }
      //--------------------------------------------------------


}

class MyCustomSensor : public PollingComponent {
  public:

    Sensor *sensor1 = new Sensor();
    Sensor *sensor2 = new Sensor();
		

	
MyCustomSensor() : PollingComponent(100) { }

    void setup() override 
	{
	//Serial.end();
	//For ESP8266,if you are using rx pin for reciever set to 3 below
	//For Sonoff Bridge with direct HW patch use pin 4
	attachInterrupt(23, ext_int_1, CHANGE);			  	  
    }


    void update() override {
      // This is the actual sensor reading logic.
      extern volatile int have_data_sensor;
      extern volatile int temp;
      extern volatile int hum;
	  
      if (have_data_sensor==1136)
      {     
        sensor1->publish_state(temp / 10.0);
        sensor2->publish_state(float(hum));
	    have_data_sensor = 0;
      }

      else if (have_data_sensor)
      {
        have_data_sensor = 0;
      }

    }
};



class Last_sensor_data : public PollingComponent, public TextSensor {
  public:

    // constructor
    Last_sensor_data() : PollingComponent(500) {}

    void setup() override {
      // This will be called by App.setup()
    }
    void update() override {
      extern volatile byte have_data_string;
	  extern volatile int str_Sensor_ID;
	  extern volatile int str_humidity;
	  extern volatile int str_temperature;
	  extern volatile bool str_wh2_valid;
	  
      if (have_data_string == 1)
      {
		std::string my_data;
        my_data = "| Sensor ID: ";
        my_data += to_string(str_Sensor_ID);
        my_data += " | humidity: ";
        my_data += to_string(str_humidity);
        my_data += " | temperature: ";
        my_data += to_string(str_temperature / 10.0);
        my_data += "C | ";
        my_data += (str_wh2_valid ? "OK " : "BAD" );
		
        publish_state(my_data);
        have_data_string = 0;
      }

      // This will be called every "update_interval" milliseconds.
      // Publish state

    }
};
1 Like

Hi, You define Sensor_ID as 32 and 68. How I get my id of sensor first before compile it.

and second question: How concrete define Auriol sensor:

  • sync bit ~9 msec,
  • 1 data bit ~4 msec,
  • 0 data bit ~2 msec.

(http://www.tfd.hu/tfdhu/files/wsprotocol/auriol_protocol_v20.pdf)

Trying to compile the program without any changes I get many errors like this and I don’t know how to solve

C:\Users\Vincenzo\AppData\Local\Temp\ccXMeQi4.s:1794: Error: junk at end of line, first unrecognized character valued 0x9