Forum: Mikrocontroller und Digitale Elektronik ESP8266 NodeMCU 1.0 and PCF8574 - Call PCF Object by Reference


von Ed S. (ed_s)


Angehängte Dateien:

Lesenswert?

Hallo zusammen, ich arbeite schon seit einiger Zeit an einem Projekt und 
komme nun an dieser Stelle garnicht weiter... Ich habe schon ein paar 
Posts in anderen Foren (englisch sprachig) gemacht, jedoch bei diesen 
kaum Beachtung geschenkt bekommen. Meinen originalen Post seht ihr unter 
diesem Vermerk auf englisch, ich hoffe ihr habt kein Problem damit. Ich 
wäre euch so dankbar wenn ihr mir weiter helfen könntet und freue mich 
auf jede Antwort! Hier der Post:

PS. Angehängte Datei nicht beachten, da ist was schief gelaufen :/
--------------------------------------

Hello everyone,

so I've been working on this project for a while now and stumbled upon a 
problem which I haven't been able to solve so far. For this project I'm 
using an ESP8266 NodeMCU 1.0 with industrial buttons (see link below) 
connected to the PCF8574 port expander (I also integrated ws2812 smd's 
into the buttons). For the buttons I use a modified header file 
(input_pcf.h) which was initially written by a friend of mine and for 
the ws2812 I am also using a header file (neo.h) written by the same 
friend (both of them are working fine). As for the port expander, I use 
the PCF8574 library written by xreef on github 
https://github.com/xreef/PCF8574_library . To sum everything up, I 
connected the buttons to (GND and) the PCF8574, which is connected to 
the Microcontroller via I2C. The data bus of the smd's are also 
connected to the ESP.

industrial buttons: 
https://de.aliexpress.com/item/KEINE-NC-START-STOP-DPST-LED-Momentary-Drucktastenschalter-AC-220-V-F-r-Starter-Sch-tz/32841013279.html?spm=a2g0s.11045068.rcmd404.3.7af356a4BSADBP&pvid=39e487c7-cbee-4b92-91dc-703921392acb&gps-id=detail404&scm=1007.16891.96945.0&scm-url=1007.16891.96945.0&scm_id=1007.16891.96945.0

(Almost) everything has been working fine so far, until I modified the 
input.h file and connected the buttons to the port expander. You 
initialize the input object with a pin, which is connected to a button. 
You can connect the industrial button two ways: on the closing side and 
on the opening side, I am using the opening side against GND. As I 
cannot just initialize the input object with a pin from the port 
expander, I just modified the input.h to input_pcf.h where I also have 
to pass a reference of the PCF8574 object to it, so I can directly read 
the pin state in my input_pcf.h. Unfortunately, this is not working at 
all. The button presses are not detected at all and nothing is reacting 
to it. Now I am trying to resolve the problem...

...did I do something wrong when passing the object by reference?
...is there a fallacy in my logic to it?

I have checked all connections many times, so I am pretty sure the 
connections are fine. When examining the button press with the esp, 
everything is working fine, and the smd's light up as intended. I tested 
the port expander as well with a simple code where I light up an LED and 
there were no problems with it. I hope some of you could help me to 
solve this problem! The code of all relevant files used are below. Thank 
you in advance!

NOTE: I know the code needs some refactoring and the names of some 
functions might be somewhat ambiguous (like setup() or loop() in neo.h). 
Please try to ignore these issues when answering

simplePCFInputTest.ino
1
#include "PCF8574.h"
2
#include "input_pcf.h"
3
#include "neo.h"
4
5
// neopixel
6
const int neo_data = D3;
7
const int btn = 0;
8
neo neo(2, neo_data);
9
10
//input button(btn);
11
PCF8574 pcf(0x20, D2, D1);
12
input_pcf button(btn, pcf);
13
14
bool pressed = false;
15
bool debug_once = true;
16
unsigned long timer = 0;
17
18
void setup() {
19
  // put your setup code here, to run once:
20
  delay(500);
21
  // put your setup code here, to run once:
22
  Serial.begin(115200);
23
  Serial.println("\nSerial Init OK");
24
25
  //pcf
26
  pcf.pinMode(btn, INPUT);
27
  pcf.digitalWrite(btn, HIGH);
28
  pcf.begin();
29
  Serial.println("PCF Init OK");
30
31
 
32
  pinMode(neo_data, OUTPUT);
33
  digitalWrite(neo_data, LOW);
34
35
  neo.Setup();
36
 
37
  Serial.println("Init OK\n");
38
39
}
40
41
void loop() {
42
  // put your main code here, to run repeatedly:
43
  if(debug_once){
44
    Serial.println("Entering Loop\n");
45
  }
46
  timer = millis();
47
48
  if(button.pressed){
49
    Serial.println("Button pressed");
50
    //delay(200);
51
    if(pressed){
52
      neo.led.setPixelColor(0, BLACK);
53
      neo.led.setPixelColor(1, BLACK);
54
      pressed = false;
55
    } else {
56
      neo.led.setPixelColor(0, PURPLE);
57
      neo.led.setPixelColor(1, WHITE);
58
      pressed = true;
59
    }
60
 
61
  }
62
63
  neo.Loop();
64
65
  button.Update();
66
  //if(millis() - timer >= 3) Serial.println(millis() - timer);
67
68
  debug_once = false;
69
70
}

input_pcf.h
1
//@(#) input_pcf.h
2
3
#ifndef INPUT_PCF_H
4
#define INPUT_PCF_H
5
6
#include "PCF8574.h"
7
8
9
class input_pcf
10
{
11
  private:
12
13
    PCF8574& pcf;
14
    const uint16_t pin;
15
    unsigned long timer = 0;
16
    boolean state = false;
17
    boolean state_long = false;
18
19
20
  public:
21
 
22
    boolean pressed_long = false;
23
    boolean pressed = false;
24
25
26
    input_pcf(const uint16_t pin, PCF8574& pcf_ref) : pin(pin), pcf(pcf_ref){
27
    }
28
   
29
    void Update(){ //button input debouncing and press duration detection
30
   
31
   
32
      pressed = false;
33
      pressed_long = false;
34
35
      //button pressed
36
      if(!pcf.digitalRead(pin) && !state && millis() - timer >= 50){
37
        state = true;
38
        timer = millis();
39
      }
40
      //button pressed long
41
      else if(!pcf.digitalRead(pin) && state && !state_long && millis() - timer >= 1000){
42
        state_long = true;
43
        pressed_long = true;
44
      }
45
      //button pressed short
46
      else if(pcf.digitalRead(pin) && state && millis() - timer >= 50){
47
        if(millis() - timer < 1000) pressed = true;
48
        state_long = false;
49
        state = false;
50
        timer = millis();
51
      }
52
    }
53
   
54
};
55
56
#endif

neo.h
1
//@(#) neo.h
2
3
#ifndef NEO_H
4
#define NEO_H
5
6
#include <Adafruit_NeoPixel.h>          //WS2812 library
7
#include "WS2812_Definitions.h"
8
9
10
class neo
11
{
12
     
13
  private:
14
15
    unsigned long neo_show_timer = 0;
16
    const uint16_t neo_show_delay = 75;
17
18
   
19
    const uint16_t led_count;
20
    const uint16_t led_pin;
21
22
23
24
25
  public:
26
27
    Adafruit_NeoPixel led;
28
29
    //constructor
30
    neo(const uint16_t &count,const uint16_t &pin) : led_count(count), led_pin(pin), led(Adafruit_NeoPixel(this->led_count, this->led_pin, NEO_GRB + NEO_KHZ800)){
31
     
32
      //this->led = new Adafruit_NeoPixel(this->led_count, this->led_pin, NEO_GRB + NEO_KHZ800);
33
    }
34
35
36
37
    //Sets all LEDs to off, but DOES NOT update the display;
38
    void clearLEDs(){
39
      for (uint16_t i=0; i<led.numPixels(); i++){
40
        led.setPixelColor(i, 0);
41
      }
42
    }
43
44
45
    //setup
46
    void Setup(){
47
      led.begin();
48
      clearLEDs();
49
      led.show();
50
    }
51
   
52
    //mainloop   
53
    void Loop(){
54
   
55
      //update rgb LED ring
56
      if(millis() - neo_show_timer >= neo_show_delay){   
57
        led.show(); 
58
        neo_show_timer = millis();
59
      }
60
    }
61
};
62
63
64
//leds.setPixelColor(i, color);
65
//leds.setPixelColor(i, red, green, blue);
66
67
#endif

: Verschoben durch Moderator
von Stefan F. (Gast)


Lesenswert?

I think that nobody was able to help ypu because you did not provide the 
schematics.

> Unfortunately, this is not working at
> all. The button presses are not detected

This is no real proper problem description.

1) You should reduce your program to the smalles possible version which 
still has the problem.

2) You should measure the operating voltage of the microcontroller as 
well as the voltages on the related I/O pins.

3) Since you have issues to read data from the PCF8574, check if you can 
successfully use the oppsite direction (e.g. switch LEDs on and off).

4) Since you are using I²C communication, it is very important to 
measure the voltate on the I²C bus before and during the error. I would 
also high recommend to use an logic analysator (costs only 10€) to find 
out what's going on with the two signal lines.

von Stefan F. (Gast)


Lesenswert?

By the way: The implementation of methods belong into *.cpp files, not 
*.h.

von Ed S. (ed_s)


Angehängte Dateien:

Lesenswert?

Hello,

Stefanus F. schrieb:
> I think that nobody was able to help ypu because you did not
> provide the
> schematics.
>
>> Unfortunately, this is not working at
>> all. The button presses are not detected
>
> This is no real proper problem description.
>
> 1) You should reduce your program to the smalles possible version which
> still has the problem.
>
> 2) You should measure the operating voltage of the microcontroller as
> well as the voltages on the related I/O pins.
>
> 3) Since you have issues to read data from the PCF8574, check if you can
> successfully use the oppsite direction (e.g. switch LEDs on and off).
>
> 4) Since you are using I²C communication, it is very important to
> measure the voltate on the I²C bus before and during the error. I would
> also high recommend to use an logic analysator (costs only 10€) to find
> out what's going on with the two signal lines.

thank you very much for your suggestions Stefanus. It has been a while 
since I started working on my project again. I have just created the 
schematics of my project (see attachments). Also, I am going to measure 
the voltage on each pin today and post an update later.

Best regards!

von Ed S. (ed_s)


Lesenswert?

Stefanus F. schrieb:
> 3) Since you have issues to read data from the PCF8574, check if you can
> successfully use the oppsite direction (e.g. switch LEDs on and off).

I have already tried this out a few days prior to my post and it worked 
fine, thats why I got so confused. I am pretty sure there is some issue 
with passing the PCF-Object as reference... I will post an update pretty 
soon!

Best regards!

von W.A. (Gast)


Lesenswert?

Ed S. schrieb:
> I have just created the schematics of my project (see attachments).

Pse check your attachment. You appended a fancy picture but not the 
schematic for your circuit.

von Ed S. (ed_s)


Angehängte Dateien:

Lesenswert?

Hello,

W.A. schrieb:
> Pse check your attachment. You appended a fancy picture but not the
> schematic for your circuit.

thanks for clearing this out! I hope the picture in this attachment is 
sufficient enough. I will also provide you guys with the fritzing file 
if needed.

Best regards.

von Stefan F. (Gast)


Lesenswert?

I see two possible issues.

10kΩ ist too much for the I²C Pull-Up resistors. Better use 2,2kΩ or 
1,8kΩ.

The WS2812 LED might require 5V power supply and you should put a 
resistor (220 - 470Ω) in the signal line. I'm not sure why this is 
needed but it was suggested very often by other people.

von Ed S. (ed_s)


Lesenswert?

Stefanus F. schrieb:
> 10kΩ ist too much for the I²C Pull-Up resistors. Better use 2,2kΩ or
> 1,8kΩ.

Ok, I will try it out.

Stefanus F. schrieb:
> The WS2812 LED might require 5V power supply and you should put a
> resistor (220 - 470Ω) in the signal line. I'm not sure why this is
> needed but it was suggested very often by other people.

I have read these suggestions as well. Another thread suggested (can't 
find it right now) that you can operate WS2812 smds on 3.3 V if you use 
3.3V for your data lines as well. Using this voltage hasn't caused me 
any trouble so far (tried it out in prior projects or tests). That's why 
I omitted the resistor in my circuit.

Best regards.

von W.A. (Gast)


Lesenswert?

Ed S. schrieb:
> simplePCFInputTestSCHEMATICS.PNG

You should provide a power supply for the LEDs (VDD on WS2812).

Filters on VCC line are missing at WS2812 (100nF capacitor and 150Ω 
serial resistor, cf. datasheet "Typical application circuit").

Regards

von Ed S. (ed_s)


Lesenswert?

Hello everyone,

so I have had some time to measure the voltage on all (necessary) pins. 
And basically everything seems to work properly, only the P0 pin of the 
PCF module doesn't seem to react (although I set the pin HIGH from the 
very beginning). You can see my measurements below.

Without any button presses (standby):

//ESP8266
ESP(D1) = 3.24 V    //I2C SCL line
ESP(D2) = 3,24 V    //I2C SDA line
ESP(D3) = 0 V       //Data line of WS2812 (WS1) chip

//PCF8574
PCF(SCL) = 3,25 V
PCF(SDA) = 3,24 V
PCF(VCC) = 3,29 V
PCF(A0) = 0 V       //address pins
PCF(A1) = 0 V
PCF(A2) = 0 V
PCF(P0) = 0 V       //?! should be high

//WS2812
WS1(VCC) = 3,29 V   //first WS2812 chip
WS1(DIN) = 0 V
WS1(DOUT) = 0 V

WS2(VCC) = 3,28 V   //second WS2812 chip
WS2(DIN) = 0 V

//Button
S1(IN) = 0 V        //Connected to P0 pin of PCF
S1(OUT) = 0 V       //Connected to GND

When the button is pressed:

//PCF8574
PCF(P0) = 0 V       //...

//Button
S1(IN) = 0 V
S1(OUT) = 0 V

Everything else just behaved the same.


I have also done another Test where the button press is detected by the 
ESP8266 and the P0 pin of the PCF module just sets an LED high or low 
accordingly. The code is almost the same (see below), except I don't 
pass any references of the PCF Object to another class. Everything works 
as intended in this test. The LED as well as the WS2812 SMDs light up 
when the button is pressed once, and get off when the button is pressed 
a second time (just tell me if you need the schematics of the second 
test below).

simplePCFTest.ino
1
#include "PCF8574.h"
2
#include "input.h"
3
#include "neo.h"
4
5
// neopixel
6
const int neo_data = D3;
7
const int btn = D4;
8
const int pcf_led = 0;
9
neo neo(2, neo_data);
10
11
//input button(btn);
12
PCF8574 pcf(0x20, D2, D1);
13
input button(btn);
14
15
//bool btn_on = false;
16
bool air = false;
17
bool debug_once = true;
18
unsigned long timer = 0;
19
20
void setup() {
21
  // put your setup code here, to run once:
22
  delay(500);
23
  // put your setup code here, to run once:
24
  Serial.begin(115200);
25
  Serial.println("\nSerial Init OK");
26
27
  //pcf
28
  pcf.pinMode(pcf_led, OUTPUT);
29
  pcf.digitalWrite(pcf_led, LOW);
30
  pcf.begin();
31
  Serial.println("PCF Init OK");
32
33
  
34
  pinMode(neo_data, OUTPUT);
35
  digitalWrite(neo_data, LOW);
36
  pinMode(btn, INPUT);
37
  digitalWrite(btn, HIGH);
38
39
  neo.Setup();
40
  
41
  Serial.println("Init OK\n");
42
43
}
44
45
void loop() {
46
  // put your main code here, to run repeatedly:
47
  if(debug_once){
48
    Serial.println("Entering Loop\n");
49
  }
50
  timer = millis();
51
52
  if(button.pressed){
53
    Serial.println("Button pressed");
54
    //delay(200);
55
    if(air){
56
      neo.led.setPixelColor(0, BLACK);
57
      neo.led.setPixelColor(1, BLACK);
58
      pcf.digitalWrite(pcf_led, LOW);
59
      air = false;
60
    } else {
61
      neo.led.setPixelColor(0, PURPLE);
62
      neo.led.setPixelColor(1, WHITE);
63
      pcf.digitalWrite(pcf_led, HIGH);
64
      air = true;
65
    }
66
    //neo.led.show();
67
  
68
  }
69
70
  neo.Loop();
71
72
  button.Update();
73
  //if(millis() - timer >= 3) Serial.println(millis() - timer);
74
75
  debug_once = false;
76
77
}

PS: I use a different header file for the button in this one called 
"input.h" which works the same as "input_pcf.h" but only takes a pin as 
an argument.

Best regards!

von Ed S. (ed_s)


Lesenswert?

Stefanus F. schrieb:
> 10kΩ ist too much for the I²C Pull-Up resistors. Better use 2,2kΩ or
> 1,8kΩ.

Oh, and I switched to 3,3k resistors (I only had these or higher :/) but 
nothing really changed.

von Ed S. (ed_s)


Lesenswert?

W.A. schrieb:
> You should provide a power supply for the LEDs (VDD on WS2812).
Is that really necessary? The SMDs seem to work fine even without the 
VDD power supply (light up in all colors) and I have already soldered 
and integrated them into the industrial buttons :d

W.A. schrieb:
> Filters on VCC line are missing at WS2812 (100nF capacitor and 150Ω
> serial resistor, cf. datasheet "Typical application circuit").

Ok, I will connect them to the circuit and post an update (most likely 
tomorrow).

Best regards

von Stefan F. (Gast)


Lesenswert?

Ed S. schrieb:
> Oh, and I switched to 3,3k resistors (I only had these or higher :/) but
> nothing really changed.

Keep them anyway to improve stability. When you attach an oscilloskope 
then you will see that 10kΩ are very sensitive to the RF signal of the 
WiFi antenna and the logic signal changes very slowly from LOW to HIGH.

The I/O Pin on the PCF should be high. It seems that the chip is not 
initialized properly - maybe due to broken communication. Can you check 
the communication with a logic anaylzer (Saleae or similar)?

von Ed S. (ed_s)


Lesenswert?

Stefanus F. schrieb:
> The I/O Pin on the PCF should be high. It seems that the chip is not
> initialized properly - maybe due to broken communication. Can you check
> the communication with a logic anaylzer (Saleae or similar)?

Hello,
well, I have to buy one which will take a couple of days. I'm not sure 
if it will help me in this particular scenario but I will give it a try!

Best regards!

von Stefan F. (Gast)


Lesenswert?

Yes give it a try. A cheap device for 10€ should be sufficient.

von Günther S. (Gast)


Lesenswert?

First of all your call by reference is fine. However you are facing 
several problems here.

Let's start with the library you are using. Have a look at the source 
code and you'll see it is pretending to implement all the functions 
you'd typically expect from Arduino's digitalWrite() and pinMode() 
methods rather than actually implementing them in the same way.

For example the begin() method of the lib initializes the Wire library 
for you so calling
1
pcf.digitalWrite(btn, HIGH)

before calling
1
pcf.begin()

will issue an I²C transmission to an uninitialized Wire object. Not the 
best idea really and more likely to crash your µC than anything else.

The PCF library also inhibits any write commands to your output pins as 
soon as you declare them as inputs. That's why you don't see any HIGH 
output on pin 0.
1
pcf.pinMode(btn, INPUT);
2
  pcf.digitalWrite(btn, HIGH);
3
  pcf.begin();

This can't work as stated above because the digitalWrite has no effect 
on the pin. You'd rather need to do the following:
1
pcf.begin();
2
  pcf.pinMode(btn, OUTPUT);
3
  pcf.digitalWrite(btn, HIGH);
4
  pcf.pinMode(btn, INPUT);

This is very counter intuitive and a clear sign to maybe use another 
library ;)

The second thing you have to consider is timing. You can't just poll the 
I²C bus within your main loop because you will run into timing issues 
and effectively block the bus and receive garbage data.
Have a delay between each call to pcf.digitalRead() or 
pcf.digitalWrite()
of at least 10ms. This becomes very clear when you look at your Update() 
method in input_pcf.h
1
//button pressed
2
      if(!pcf.digitalRead(pin) && !state && millis() - timer >= 50){
3
        state = true;
4
        timer = millis();
5
      }
6
      //button pressed long
7
      else if(!pcf.digitalRead(pin) && state && !state_long && millis() - timer >= 1000){
8
        state_long = true;
9
        pressed_long = true;
10
      }
11
      //button pressed short
12
      else if(pcf.digitalRead(pin) && state && millis() - timer >= 50){
13
        if(millis() - timer < 1000) pressed = true;
14
        state_long = false;
15
        state = false;
16
        timer = millis();
17
      }
Worst case: every line of your if clause is executed which results in 3 
consecutive I²C requests without any delay between them for the bus to 
process the requests. You will need to completely rewrite that part 
using additional timers to compensate for this.

von Ed S. (ed_s)


Lesenswert?

I haven't logged in for a while now and forgot to reply to the last 
response. Sorry for that.

First of all I wanted to thank all of you for trying to help, especially 
Günther S. who solved my problem with his response! I basically switched 
to another pcf-library and solved my timing issues regarding the I2C 
bus. So, thank you very much Günther S. for taking the time to help ;)

I might post an update very soon to the project.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.