MH-Z19 CO2 sensor giving diferent values using UART and PWM
char response[9]; 


On many systems including an Arduino, an unspecified char is a signed type.

int responseHigh = (int) response[2];
int responseLow = (int) response[3];
ppm = (256*responseHigh)+responseLow;


Therefore when you cast it to an int, any value value with the highest bit set will be considered negative and sign extended to a negative value in the full integer width.  So for example an 8-bit char of 0x80 becomes a 16-bit int 0xff81.  This is probably not what you want, as it will actually reduce rather than increase the value set by the upper bits.

Instead, since your values are unsigned you should either declare your array as unsigned char or cast the values to an unsigned char before you implicitly or explicitly cast them to a wider type. 

Note that even if the overall value were signed, you would probably still want to be interpreting the lower 8 bit as an unsigned modifier of the signed upper 8 bits.

However, it is not immediately apparent that this change will resolve the discrepancy in your readings - it may actually increase it.


  th = pulseIn(pwmPin, HIGH, 1004000) / 1000.0;


There does not seem to be any sound reason to use a floating point constant when dividing an integer type to produce an integer result.

You should probably print out your PWM times and see if they make sense, compared to say scope measurements.

Also ideally you would have some other reference sensor you could use to determine which value is more credible.