Questions with regards to ESP8266 Basic and hardware interfacing and control via Basic commands

Moderator: Mmiscool

User avatar
By forlotto
#51778 In my case I am using the PCF8574 GPIO expansion board located HERE

First off some things I do know the PCF8574 is a GPIO expander with some limitations vs a typical esp8266 digital pin.


I am not entirely sure but I do not think you can control the PWM of a pin this is an assumption just based off of reading so I could be wrong here but just figured it was worth the mention there may be other limitations as well.
It may be possible that we can or can't read the last status of the pins or read them while operating as an output. I have not read enough to determine this but it should be possible to somehow track the information in our code. Pin status is huge knowing the last status of your pins enables you to know if your GPIO is in the On or OFF state everyone I know and see post values this information and with good reason. And as a side note there is really no good way that I personally know of tracking your own variable in esp basic v3.0 all attempts for me seem to fail to manually track a pin. There is io(laststat,D5) this works great for tracking the status of the esp's GPIO however a method of tracking the value of your own variables so that you can track the state of the pin seems to prove a little more difficult in basic 3.x. Just a fair warning does not like to track beyond the first attempt at tracking it seems. There likely is a way to do it but I am too dense to know it currently interestingly enough I was able to do it in early versions of esp basic so dense or not something has changed which deleted a bit of my understanding of basic. But enough on this moving along LETS GATHER SOME IMPORTANT INFO!!!!

I am completely blank when it comes to basic and the I2C protocol so I must first gather an understanding and do some research sorry for not knowing all this stuff folks but I will admit I am dead in the water when it comes to a lot of the hardware interfacing with the esp8266 and basic.

Questions to answer when gathering info:

Q: What is the I2C code in basic?
A: The I2C code in basic is as follows:
Code: Select all else if (fname.startsWith(F("i2c.")) )      // block I2C functions; this reduces the number of compares
    fname = fname.substring(4); // skip the term i2c.
    if ( fname == F("begin") && num_args == 1 ) {
      // function i2c.begin(address)
      // set return value
      *value_str  =  String(F(""));
      return PARSER_STRING;
    else if ( fname == F("write") && num_args == 1 ) {
      // function i2c.write(byte to be written)
      // set return value
      *value_str  =  String(Wire.write((byte)args[0]));
      return PARSER_STRING;
    else if ( fname == F("end") && num_args == 0 ) {
      // function i2c.end()
      // set return value
      *value_str  =  String(Wire.endTransmission());
      return PARSER_STRING;
    else if ( fname == F("requestfrom") && num_args > 0 ) {
      // function i2c.requestfrom(address, qty)
      // set return value
      *value_str  =  String(Wire.requestFrom((byte)args[0], (byte)args[1]));
      return PARSER_STRING;

    else if ( fname == F("available") && num_args == 0 ) {
      // function i2c.available()
      // set return value
      *value_str  =  String(Wire.available());
      return PARSER_STRING;

    else if ( fname == F("read") && num_args == 0 ) {
      // function
      // set return value
      *value_str  =  String(;
      return PARSER_STRING;

Q: Knowing that D3 and D4 of nodemcu is I2C for basic which line is clock D3 or D4? Then the other line is data when using basic.
A: ????? Dunno Yet...

Q: What parameters are available in basic to communicate with a device using I2C?
A: Looking at the espbasic v3.0 documentation here is what it says:
Code: Select allFUNCTIONS I2C
i2c functions:

For more information on usage look at this example.


Will begin transmission to the I2C device with desired address.

i2c.begin({value or var for device address})


Will write a single value (1 character) to the i2c device.

i2c.write({value or var for data})


Will terminate the i2c contamination with a particular device.



Will request a quantity of bytes from  device.

i2c.requestfrom({value or var for device id},{value or var for number of bytes to request})

Returns the number of bytes available for retrieval with


Will return a single character as an integer. Character returned will be next out of buffer.

Side Note:
For reasons really unknown to me personally Raintime believes it is not possible to properly use the I2C functions built into basic to handle the PCF8574 fully but if I understand him right we will need to still use it to address the PCF8574? But he states there may likely be a bitbang type routine to handle the states of the ports of the PCF8574 and this will be potentially a lot slower due to everything being interpreted?

Q: After knowing this would it be best to have basic support in the forum of a library and some code possibly for the PCF8574 and have a build of basic that would support this would this solve speed issues as well?
A: IDK ??? After looking at this page to see what libs are available for esp8266 HERE I have determined that under other libs there is a library available for the esp8266 which can be found HERE
Q: Okay that's nice but how do we add these libs and add support for it?
A: IDK for sure but HERE IS A START

Q: How do we address the PCF8574 ?

A: I2C Address table for PCF8574 and PCF8574A below
I2C Addressing PCF8574 and PCF8574A.jpg

Q: Where is a good datasheet of the PCF8574?
A: This seems to be fairly in depth coverage:
EDIT RAINTIME added this datasheet as a better alternative both hosted on the same website may be possible we have the same thing just different links as I wanted people to be able to click on technical documents etc as well.
Anyhow just keep in mind both links may and should apply.

Q: Are there any good examples of putting any of this information to use with ELI5 (Explain Like I'm 5 years old) explanations
A: ???? Dunno Yet...

Q: Does the hardware allow for tracking last status of the pins?
A: Don't Know ????

Q: If the hardware does not allow for tracking last status of pins can basic do it if so what is a good way of doing this in v3.0 where the variable is constantly tracked without fail through the entire operation of the code? Often I find tracking the status of a variable the status somehow gets lost in translation so would be nice to see a solid example of this?
A: ??? Dunno LIkely But How IDK???

Q: Is it possible to bitbang this device to toggle GPIO on/off?
A: I don't know ??? I really not even have researched this possibility at all!

Interested goat head chime in and share some info :P
woops didn't mean to call you a goat head I meant to say go ahead!


Conclusion of all of this I now have a working example it works very similar to a shift register no bit bang required just math functions getting it to work how it works I cannot really explain I just kind of took the code that was provided by TrackerJ and modified it until it worked. His code would cause all GPIO expansion ports to be on on first button push and his code also only allowed you to turn port on at a time...

So after messing around I arrived at a program I now know and understand. If you wish to understand open a calculator in programmer's mode and you will see the bits going high and low as you run the xor routines.

But here you go folks if you like it then donate to me some dogecoin :P

Code: Select alllet address = 32 'PCF8574 I2C Address
'below we sync the chip all on or all off your choice for all on use ss = ss xor 255
'instead of ss = 0 xor 255
ss = 0 xor 255 ' this is the line that syncs the chip
button "1", [1]
button "2", [2]
button "3", [3]
button "4", [4]
button "5", [5]
button "6", [6]
button "7", [7]
button "8", [8]
button "OFF", [9]

ss = 0 xor 255
ss = ss xor 1

ss = ss xor 2

ss = ss xor 4

ss = ss xor 8

ss = ss xor 16

ss = ss xor 32

ss = ss xor 64

ss = ss xor 128

You do not have the required permissions to view the files attached to this post.
Last edited by forlotto on Sun Jul 31, 2016 11:28 pm, edited 10 times in total.
User avatar
By trackerj
#51785 PCF8574 8Bit I/O port is a quasi-bidirectional I/O Port.

A quasi-bidirectional I/O is an input or output port without using a direction control register.
Whenever the master reads the register, the value returned to master depends on the
actual voltage or status of the pin. At power on, all the ports are HIGH with a weak 100 uA
internal pull-up to VDD, but can be driven LOW by an internal transistor, or an external
signal. The I/O ports are entirely independent of each other, but each I/O octal is controlled by the same read or write data byte.

Advantages of the quasi-bidirectional I/O over totem pole I/O include:

- Better for driving LEDs since the p-channel (transistor to VDD) is small, which saves die size and therefore cost. LED drive only requires an internal transistor to ground, while the LED is connected to VDD through a current-limiting resistor. Totem pole I/O have both n-channel and p-channel transistors, which allow solid HIGH and LOW output levels without a pull-up resistor — good for logic levels.

- Simpler architecture — only a single register and the I/O can be both input and output at the same time. Totem pole I/O have a direction register that specifies the port pin direction and it is always in that configuration unless the direction is explicitlychanged.

- Does not require a command byte. The simplicity of one register (no need for the pointer register or, technically, the command byte) is an advantage in some embedded systems where every byte counts because of memory or bandwidth limitations.

There is only ONE register to control four possibilities of the port pin: Input HIGH, input
LOW, output HIGH, or output LOW. No PWM at all.

- Input HIGH: The master needs to write 1 to the register to set the port as an input mode if the device is not in the default power-on condition. The master reads the register to check the input status. If the external source pulls the port pin up to VDD or drives logic 1, then the master will read the value of 1.

- Input LOW: The master needs to write 1 to the register to set the port to input mode if the device is not in the default power-on condition. The master reads the register to check the input status. If the external source pulls the port pin down to VSS or drives logic 0, which sinks the weak 100uA current source, then the master will read the value of 0.

- Output HIGH: The master writes 1 to the register. There is an additional ‘accelerator’ or strong pull-up current when the master sets the port HIGH. The additional strong pull-up is only active during the HIGH time of the acknowledge clock cycle. This accelerator current helps the port’s 100uA current source make a faster rising edge into a heavily loaded output, but only at the start of the acknowledge clock cycle to avoid bus contention if an external signal is pulling the port LOW to VSS/driving the port with logic 0 at the same time. After the half clock cycle there is only the 100uA current source to hold the port HIGH.

- Output LOW: The master writes 0 to the register. There is a strong current sink transistor that holds the port pin LOW. A large current may flow into the port, which could potentially damage the part if the master writes a 0 to the register and an external source is pulling the port HIGH at the same time.

For even more details about PCF8574 and driver example implementation it might be a good idea to drop again a deeper look on the nEXT EVO AN-1 Extention Board related articles and software examples.
User avatar
By raintime
#51788 Forlotto, the datasheet you want is at

All the information you are asking about can be answered by a careful reading of Section 8.3 Feature Description (Starting on page 12) and Section 8.4 Device Functional Modes. The ESP chip is the Master, the PC8574 is the Slave.

After a start condition is created ("a high-to-low transition on the SDA I/O while the SCL input is high") which is presumably part of what happens when issuing an I2C.begin (var) command - the first byte to send out specifies the address of the desired chip (which you determine by how you wire pins A0, A1 nd A2 on the chip - for example, if you ground them all, these bits would be 0,0,0).

Looking at the first line of the table below 8.3.2 Interface Description on page 12, we see that bit 7 (MSB) of this var in the I2C.begin command must have a value of Low or zero. Bit 6 must be High or one. Bits 5 and 4 must both be 0. Bits 3,2 and 1 are your address bits, which, if you ground A3, A2, and A1, would all need to be zeros. Finally, the LSB of this first byte you send to the chip is either a 1 for READ or a zero for WRITE.

You can get a nice look at this first byte at the left sides of Figures 16 and 17 on the datasheet. For READ, assuming you are using 0,0,0 for A2, A1 & A0, the value of var above would be: 0100 0001, or 0x41, or decimal 65. (This equals For WRITE, the value would be 0100 0000, or 0x40, or decimal 64.)

How do we know which pin is SDA and which is SCL? Looking at a post by martinayotte on the forum Re: Default SDA and SCL pins on NodeMCU V1.0, he says,

"The default pins are defined in variants/nodemcu/pins_arduino.h as SDA=4 and SCL=5, but those are not pins number but GPIO number, so since the pins are D1=5 and D2=4.

Anyway, you can also choose the pins yourself using the I2C constructor Wire.begin(int sda, int scl);"

So we don't know for sure which pins to use. We know which are the defaults for arduino, on which ESP Basic is based. But the commands might be using other pins. To make sure, it will need a look into the source code of the I2C commands.

You will probably not be able to output PWM at a very fast rate, since ESP BASIC is interpreted, not compiled, and once you have sent the WRITE address to the PCF8574 and the address has been ACKnowledged, it looks like you can send a stream of integral multiples of two bytes, where each data byte is ACKed during a ninth clock pulse. Frankly, it is quite unlikely that the built-in I2C commands in ESP BASIC are set up to handle this type of protocol, so you will need to bit bang it, which will slow things down even more!

Anyway, enough rambling for now! Hopefully, this can help a little bit. You have bit off quite a chunk!
User avatar
By forlotto
#51789 @trackerj
Totem Pole "I ASSUME" = 595 shift register I believe for those wondering but I would not discount these 595 shift registers either as they do work rather well but you basically toggle them on or toggle them off I like the idea of the pcf8574 being able to control on or off state rather than just toggling on or off but either one is a workable solution I just feel we should have a good reference to both.

Thus the quest for beginning how to interface this thing and get it working with espbasic. TrackerJ are you able to get this going with espbasic instead of just LUA if so do you have an example of how to do so?

@ raintime
Lots of good information thank you very much for participating any more information or a code example would be great or even an example of how you think it should work without testing would be a nice framework to build off of . Anyhow thanks and feel free to reply more at any time!

Note I will update the datasheet as well!

In my case I am using the PCF8574 GPIO expansion board located HERE

On this board all 3 pins come default as grounded with jumpers but can be set high with the jumper however I believe the low will work fine for my application and I am unaware of the safety issues with setting them high or if these are just used as flags etc but for my testing it will be 0,0,0 ...