Place to put your Basic demos and examples

Moderator: Mmiscool

User avatar
By Ecoli-557
#53170 Running out of I/O on the ESP is frustrating. You can use a 16-bit port expander for I/O and several examples are shown in the code below:
Code: Select all'Demo showing how to configure and use a MCP23S17 SPI port expander for the ESP8266
spi.setup(1000000) 'clk on gpio14(d5) mosi on gpio13(d7)
CS = 4 ' CS on gpio4(d2)
'equate registers to vars for later use
let Wrt = "40"            'Write hexcode
let Rd = "41"            'Read hexcode
'Registers for Bank=0 configuration as done with IOCON
let IODIRA = "00"         'Direction for PORTA; IN or OUT.  In=1, Out=0
let IODIRB = "01"         'Direction for PORTB; IN or OUT.  In=1, Out=0
let IPOLA = "02"         'Input polarity for PORTA
let IPOLB = "03"         'Input polarity for PORTB
let GPINTENA = "04"         'Interrupt on change for PORTA
let GPINTENB = "05"         'Interrupt on change for PORTA
let DEFVALA = "06"         'Default value for interrupt on change for PORTA
let DEFVALB = "07"         'Default value for interrupt on change for PORTB
let INTCONA = "08"         'Interrupt control for PORTA
let INTCONB = "09"         'Interrupt control for PORTB
let IOCON = "0A"         'Main configuration for the chip
let GPPUA = "0C"         'Pull-Up resisters for PORTA
let GPPUB = "0D"         'Pull-Up resisters for PORTB
let INTFA = "0E"         'Interrupt flags for PORTA
let INTFB = "0F"         'Interrupt flags for PORTB
let INTCAPA = "10"         'Interrupt capture for PORTA
let INTCAPB = "11"         'Interrupt capture for PORTB
let InPrtA = "12"         'GPIO for PORTA, used to READ, can be used to write
let InPrtB = "13"         'GPIO for PORTB, used to READ, can be used to write
let OutA = "14"            'Output latches for PORTA - I prefer this
let OutB = "15"            'Output latches for PORTB - I prefer this

'Now setup the 23S17 for use in this demo
io(po,CS,0)
spi.hex(Wrt & IOCON & "22",3) 'Sets up the IOCON register
io(po,CS,1) 

io(po,CS,0)
spi.hex(Wrt & IODIRB & "FF",3) 'All PORTB are INPUTS
io(po,CS,1)

io(po,CS,0)
spi.hex(Wrt & GPPUB & "FF",3) 'All PORTB are PULLED HIGH internally
io(po,CS,1)

io(po,CS,0)
spi.hex(Wrt & IODIRA & "00",3) 'All PORTA are OUTPUTS
io(po,CS,1)
'------------------------------------------------------------------------------------
[Start]
'Performs a 'Cylon' LED movement, works but at a 'brute' force tactic.  Need more elegance.
'Shift through PORT bits 1-8
for Pin = 1 to 8               'Cycle through the bit positions on the PORT
    if Pin = 1 then ThisPin =  1    'Get the physical bit position to the PORT - not elegant
    if Pin = 2 then ThisPin =  2   'Use decimal port equivilent due to no port/bit function
    if Pin = 3 then ThisPin =  4
    if Pin = 4 then ThisPin =  8
    if Pin = 5 then ThisPin =  16
    if Pin = 6 then ThisPin =  32
    if Pin = 7 then ThisPin =  64
    if Pin = 8 then ThisPin =  128
    'LEDs are active LOW
    '    OutPin = hex(ThisPin xor 255) 'this should function the same as below, but doesn't
    OutPin = ThisPin xor 255      'Need the INVERSE as the LEDs are active LOW
    OutPin = hex(OutPin)         'Need to convert to Hex for the port expander
    io(po,CS,0)
    spi.hex(Wrt & OutA & OutPin,3)  'Write data
    io(po,CS,1)
    delay 50                  'Wait a bit
next Pin   
'Then for next going from 8 to 1
for Pin = 8 to 1 step -1         'Cycle through the bit positions on the PORT
    if Pin = 1 then ThisPin =  1    'Get the physical bit position to the PORT - not elegant
    if Pin = 2 then ThisPin =  2   'Use decimal port equivilent due to no port/bit function
    if Pin = 3 then ThisPin =  4
    if Pin = 4 then ThisPin =  8
    if Pin = 5 then ThisPin =  16
    if Pin = 6 then ThisPin =  32
    if Pin = 7 then ThisPin =  64
    if Pin = 8 then ThisPin =  128
    'LEDs are active LOW
    '    OutPin = hex(ThisPin xor 255) 'this should function the same as below, but doesn't
    OutPin = ThisPin xor 255         'Need the INVERSE as the LEDs are active LOW
    OutPin = hex(OutPin)            'Need to convert to Hex for the port expander
    io(po,CS,0)
    spi.hex(Wrt & OutA & OutPin,3)     'Write data
    io(po,CS,1)
    delay 50                     'Wait a bit
next Pin   
goto [Start]                     'Do it endlessly
'------------------------------------------------------------------------------------
'This will write a port at once.  My LEDs are active LOW.  ONLY the 4th bit counting from 1 is lit
'io(po,CS,0)
'spi.hex(Wrt & OutA & "F7",3)
'io(po,CS,1)
'------------------------------------------------------------------------------------
'read a pushbutton on PORTB and display on PORTA
'io(po,CS,0)
'buttons = spi.hex(Rd & InPrtB & "00",3) 'READ PORTB for keypresses, note '00' as a pad char
'io(po,CS,1)
'pressed = mid(buttons,5,2)
'io(po,CS,0)
'spi.hex(Wrt & OutA & pressed,3) 'writes whatever is pressed to PORTA
'io(po,CS,1)
'-----------------------------------------------------------------------------------
end

I strongly suggest you download the datasheet (48 pages) to get a more in depth feel on how I am controlling this chip in order to work it for your own use.

As is, it will turn on LEDs connected to PORTA through a 220 ohm resistor to 3.3v - they are active LOW. It will start with LED on GPA0 ON, then OFF, move to GPA1 ON, then OFF, and so on until GPA7 then it runs back down like the Cylons 'eye' did or Kit in Knight Rider.

There are 2 other programs which are commented and you can comment the Cylon portion and selectively use the other 2; one is for a byte-wide push to an entire port at once. The other illustrates how to read an input on PORTB (pulled HIGH) and display on PORTA which is active LOW.

The Cylon routine is brute force as I am still quite new to this and I don't know a more elegant or simple way to do what I needed to do as we do not have more granularity for SPI control - but it does work and it is illustrative. I tried to upload a MP4 of the Cylon demo portion, but MP4s are not allowed on this site.

Any comments to make it work more efficiently would be appreciated.

Enjoy and Regards to All.
User avatar
By martinayotte
#53174 BTW, just to let people know :
There is a I2C variant of that chip, the MCP23017 (zero instead of S), it has all the same functionalities, except the interface is only 2 wires and I2C bus can hold 8 of those chips since there is 3 address pins, so 128 GPIOs with only 2 ESP GPIOs.
User avatar
By trackerj
#53198 For I2C you have also the PCF8574 for a 8 bit port extension, or for 16 bit, the bigger brother, PCF8575.

Both deadly simple to use with ESP Basic, see full code examples in the above articles.

Code: Select alllet address = 32 'PCF8574 I2C Address

i2c.begin(address)
ss = 0 xor 255 'XOR - Bit masking for the desired I/O pins
i2c.write(ss)
i2c.end()
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]
wait

[9]
i2c.begin(address)
ss = 0 xor 255 'XOR - Bit masking for the desired I/O pins
i2c.write(ss)
i2c.end()
wait

[1]
i2c.begin(address)
ss = ss xor 1   'XOR - Bit masking for the desired I/O pins
i2c.write(ss)
i2c.end()
wait

[2]
i2c.begin(address)
ss = ss xor 2
i2c.write(ss)
i2c.end()
wait

[3]
i2c.begin(address)
ss = ss xor 4
i2c.write(ss)
i2c.end()
wait

[4]
i2c.begin(address)
ss = ss xor 8
i2c.write(ss)
i2c.end()
wait

[5]
i2c.begin(address)
ss = ss xor 16
i2c.write(ss)
i2c.end()
wait

[6]
i2c.begin(address)
ss = ss xor 32
i2c.write(ss)
i2c.end()
wait

[7]
i2c.begin(address)
ss = ss xor 64
i2c.write(ss)
i2c.end()
wait

[8]
i2c.begin(address)
ss = ss xor 128
i2c.write(ss)
i2c.end()
wait
User avatar
By bugs
#53237
Ecoli-557 wrote:Any comments to make it work more efficiently would be appreciated.


Don't know about efficient - but I would do all the calculations at the begining of the program and put the results in an array to be used during the cycling.
Note that the code is untried as I don't have the hardware but it gets rid of the "ifs"...

Code: Select all
[begin]
dim portpin$(7)

for Pin = 0 to 7
    ThisPin  = 2^Pin                    'The bit positions are powers of 2.
    ThisPin  = ThisPin xor 255      'Need the INVERSE as the LEDs are active LOW
    OutPin = hex(ThisPin)         'Need to convert to Hex for the port expander
    portpin$(Pin) = OutPin
next Pin

[Start]
for Pin = 0 to 7               'Cycle through the bit positions on the PORT
    OutPin= portpin$(Pin)
    gosub [setpin]
next Pin
   
'Then for next going from 8 to 1
' might be better just going 7 to 2 otherwise get double time on 1 and 8
' so use for Pin = 6 to 1 step -1
for Pin = 7 to 0 step -1         'Cycle through the bit positions on the PORT
    OutPin= portpin$(Pin)
    gosub [setpin]
next Pin
goto  [Start]

[setpin]
    io(po,CS,0)
    spi.hex(Wrt & OutA & OutPin,3)  'Write data
    io(po,CS,1)
    delay 50                  'Wait a bit
return