The first step when beginning this project was dealing with the limited IO pins on the ESP8266 modules. The ESP-07 and ESP-12 modules break out all of the useful pins. A number of pins needs to be pulled high or low during power on so it boots off the SPI flash memory (or bootloader).
GPIO0 - 10k pullup for boot mode - Bootload/Program button pulls this to ground on powerup/reset to enable update over serial. Doubles as a i2c clock pin
GPIO2 - 10k pullup for boot mode - also doubles as i2c data pin
If you gotta pull the pins up for boot select, might as well put anything needing a pullup on those pins ;) i2c is broken out to a header on this for future use
GPIO4 - 10k pullup - user switch
GPIO5 - user LED output
GPIO12/13/14/15 - hardware SPI pins for Data In/Out, Clock, and Chip Select
Note: GPIO15 has to be pulled low for both SPI flash/Serial boot modes. A 10k pulldown is fine for this as it is only the SPI clock line.
GPIO16 - unconnected by default - footprint for resistor connection to reset for sleep mode reset (not needed for this project).
Microchips MCP23S08 provides 8 GPIO pins over the SPI bus. Up to 4 of these can be connected to the same bus with a common chip select line. Each chip can be set to one of 4 addresses. So a total of 32 GPIOs is possible. Even better, the bigger brother MCP23S17 provides 16 GPIOs and can have up to 8 devices on one SPI bus (128 GPIOs!).
Some driver code for the MCP23Sxx chips is available on my Github - including support for multiple devices per SPI bus: https://github.com/MetalPhreak
So in summary, you can have 2 on-module GPIOs, (software) i2c and SPI with no issues. GPIO expanders provide plenty of addition IOs as required. The 2 on-module GPIOs can be used with the Microchip's interupt-on-change output pin to provide equivalent functionality to on-module GPIOs. Using the hardware SPI module for this is much better than the i2c versions, as you can buffer the data in the SPI module (which sends it at 10MHz), while you continue to do work with the main cpu (@80MHz). Software i2c ties up the whole device while it sends at sub 1MHz speeds.