mirror of https://github.com/arendst/Tasmota.git
Refactor PCF8574 driver
This commit is contained in:
parent
6f44003cb5
commit
ccdab295e7
|
@ -1,386 +0,0 @@
|
||||||
/*
|
|
||||||
xdrv_28_pcf8574.ino - PCF8574 I2C support for Tasmota
|
|
||||||
|
|
||||||
Copyright (C) 2021 Stefan Bode
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef USE_I2C
|
|
||||||
#ifdef USE_PCF8574_V1
|
|
||||||
/*********************************************************************************************\
|
|
||||||
* PCF8574 - I2C IO Expander
|
|
||||||
*
|
|
||||||
* I2C Address: PCF8574 = 0x20 .. 0x27 (0x27 is not supported),
|
|
||||||
* PCF8574A = 0x39 .. 0x3F (0x38 is not supported)
|
|
||||||
\*********************************************************************************************/
|
|
||||||
|
|
||||||
#define XDRV_28 28
|
|
||||||
#define XI2C_02 2 // See I2CDEVICES.md
|
|
||||||
|
|
||||||
// Start address and count can be overriden in user_config_override.h to allow better
|
|
||||||
// sharing of the I2C address space. Still the covered range must remains valid.
|
|
||||||
// A count of 0 can be used totaly disable any of the 2 ranges.
|
|
||||||
// By default, the following addresses are explicitly excluded (as per the docs) :
|
|
||||||
// - 0x27 and 0x37 are reserved for USE_DISPLAY_LCD in xdsp_01_lcd.ino
|
|
||||||
// - 0X38 is reserved for other sensors
|
|
||||||
// If the respective drivers are not used, overrides allows to recover those addresses
|
|
||||||
// If defined, USE_MCP230xx_ADDR is also always excluded
|
|
||||||
|
|
||||||
// PCF8574 address range from 0x20 to 0x26
|
|
||||||
#ifndef PCF8574_ADDR1
|
|
||||||
#define PCF8574_ADDR1 0x20 // PCF8574
|
|
||||||
#endif
|
|
||||||
#ifndef PCF8574_ADDR1_COUNT
|
|
||||||
#define PCF8574_ADDR1_COUNT 7
|
|
||||||
#endif
|
|
||||||
// PCF8574A address range from 0x39 to 0x3E
|
|
||||||
#ifndef PCF8574_ADDR2
|
|
||||||
#define PCF8574_ADDR2 0x39 // PCF8574A
|
|
||||||
#endif
|
|
||||||
#ifndef PCF8574_ADDR2_COUNT
|
|
||||||
#define PCF8574_ADDR2_COUNT 6
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Consitency tests - Checked across the complete range for the PCF8574/PCF8574A to allow override
|
|
||||||
#if (PCF8574_ADDR1 < 0x20) || ((PCF8574_ADDR1 + PCF8574_ADDR1_COUNT - 1) > 0x27)
|
|
||||||
#error PCF8574_ADDR1 and/or PCF8574_ADDR1_COUNT badly overriden. Fix your user_config_override
|
|
||||||
#endif
|
|
||||||
#if (PCF8574_ADDR2 < 0x38) || ((PCF8574_ADDR2 + PCF8574_ADDR2_COUNT - 1) > 0x3F)
|
|
||||||
#error PCF8574_ADDR2 and/or PCF8574_ADDR2_COUNT badly overriden. Fix your user_config_override.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct PCF8574 {
|
|
||||||
int error;
|
|
||||||
uint8_t pin[64];
|
|
||||||
uint8_t address[MAX_PCF8574];
|
|
||||||
uint8_t pin_mask[MAX_PCF8574] = { 0 };
|
|
||||||
#ifdef USE_PCF8574_MQTTINPUT
|
|
||||||
uint8_t last_input[MAX_PCF8574] = { 0 };
|
|
||||||
#endif
|
|
||||||
uint8_t max_connected_ports = 0; // Max numbers of devices comming from PCF8574 modules
|
|
||||||
uint8_t max_devices = 0; // Max numbers of PCF8574 modules
|
|
||||||
char stype[9];
|
|
||||||
bool type = false;
|
|
||||||
} Pcf8574;
|
|
||||||
|
|
||||||
uint8_t Pcf8574Read(uint8_t idx)
|
|
||||||
{
|
|
||||||
Wire.requestFrom(Pcf8574.address[idx],(uint8_t)1);
|
|
||||||
return Wire.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t Pcf8574Write(uint8_t idx)
|
|
||||||
{
|
|
||||||
Wire.beginTransmission(Pcf8574.address[idx]);
|
|
||||||
Wire.write(Pcf8574.pin_mask[idx]);
|
|
||||||
return Wire.endTransmission();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Pcf8574SwitchRelay(void)
|
|
||||||
{
|
|
||||||
for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) {
|
|
||||||
uint8_t relay_state = bitRead(XdrvMailbox.index, i);
|
|
||||||
|
|
||||||
if (Pcf8574.max_devices > 0 && Pcf8574.pin[i] < 99) {
|
|
||||||
uint8_t board = Pcf8574.pin[i]>>3;
|
|
||||||
uint8_t pin = Pcf8574.pin[i]&0x7;
|
|
||||||
uint8_t oldpinmask = Pcf8574.pin_mask[board];
|
|
||||||
uint8_t _val = bitRead(TasmotaGlobal.rel_inverted, i) ? !relay_state : relay_state;
|
|
||||||
|
|
||||||
//AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: SwitchRelay %d=%d => PCF-%d.D%d=%d"), i, relay_state, board +1, pin, _val);
|
|
||||||
bitWrite(Pcf8574.pin_mask[board], pin, _val);
|
|
||||||
if (oldpinmask != Pcf8574.pin_mask[board]) {
|
|
||||||
Pcf8574.error = Pcf8574Write(board);
|
|
||||||
}
|
|
||||||
//else AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: SwitchRelay skipped"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Pcf8574Init(void)
|
|
||||||
{
|
|
||||||
uint8_t pcf8574_address = (PCF8574_ADDR1_COUNT > 0) ? PCF8574_ADDR1 : PCF8574_ADDR2;
|
|
||||||
while ((Pcf8574.max_devices < MAX_PCF8574) && (pcf8574_address < PCF8574_ADDR2 +PCF8574_ADDR2_COUNT)) {
|
|
||||||
|
|
||||||
#if defined(USE_MCP230xx) && defined(USE_MCP230xx_ADDR)
|
|
||||||
if (USE_MCP230xx_ADDR == pcf8574_address) {
|
|
||||||
AddLog(LOG_LEVEL_INFO, PSTR("PCF: Address 0x%02x reserved for MCP320xx skipped"), pcf8574_address);
|
|
||||||
pcf8574_address++;
|
|
||||||
if ((PCF8574_ADDR1 +PCF8574_ADDR1_COUNT) == pcf8574_address) { // See comment on allowed addresses and overrides
|
|
||||||
pcf8574_address = PCF8574_ADDR2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: Probing addr: 0x%x for PCF8574"), pcf8574_address);
|
|
||||||
|
|
||||||
if (I2cSetDevice(pcf8574_address)) {
|
|
||||||
Pcf8574.type = true;
|
|
||||||
|
|
||||||
Pcf8574.address[Pcf8574.max_devices] = pcf8574_address;
|
|
||||||
Pcf8574.max_devices++;
|
|
||||||
|
|
||||||
strcpy(Pcf8574.stype, "PCF8574");
|
|
||||||
if (pcf8574_address >= PCF8574_ADDR2) {
|
|
||||||
strcpy(Pcf8574.stype, "PCF8574A");
|
|
||||||
}
|
|
||||||
I2cSetActiveFound(pcf8574_address, Pcf8574.stype);
|
|
||||||
}
|
|
||||||
|
|
||||||
pcf8574_address++;
|
|
||||||
if ((PCF8574_ADDR1 +PCF8574_ADDR1_COUNT) == pcf8574_address) { // Support I2C addresses 0x20 to 0x26 and 0x39 to 0x3F
|
|
||||||
pcf8574_address = PCF8574_ADDR2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Pcf8574.type) {
|
|
||||||
for (uint32_t i = 0; i < sizeof(Pcf8574.pin); i++) {
|
|
||||||
Pcf8574.pin[i] = 99;
|
|
||||||
}
|
|
||||||
UpdateDevicesPresent(-Pcf8574.max_connected_ports); // reset no of devices to avoid duplicate ports on duplicate init.
|
|
||||||
|
|
||||||
Pcf8574.max_connected_ports = 0; // reset no of devices to avoid duplicate ports on duplicate init.
|
|
||||||
for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) { // suport up to 8 boards PCF8574
|
|
||||||
uint8_t gpio = Pcf8574Read(idx);
|
|
||||||
// Insure the input pins are actually writen a 1 for proper input operation
|
|
||||||
Pcf8574.pin_mask[idx] = gpio | ~Settings->pcf8574_config[idx];
|
|
||||||
Pcf8574Write(idx); // Write back to the register
|
|
||||||
#ifdef USE_PCF8574_MQTTINPUT
|
|
||||||
Pcf8574.last_input[idx] = gpio & ~Settings->pcf8574_config[idx];
|
|
||||||
#endif // #ifdef USE_PCF8574_MQTTINPUT
|
|
||||||
//AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: PCF-%d config=0x%02x, gpio=0x%02X"), idx +1, Settings->pcf8574_config[idx], gpio);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < 8; i++, gpio>>=1) {
|
|
||||||
uint8_t _result = Settings->pcf8574_config[idx] >> i &1;
|
|
||||||
//AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: I2C shift i %d: %d. Powerstate: %d, TasmotaGlobal.devices_present: %d"), i,_result, Settings->power>>i&1, TasmotaGlobal.devices_present);
|
|
||||||
if (_result > 0) {
|
|
||||||
Pcf8574.pin[TasmotaGlobal.devices_present] = i + 8 * idx;
|
|
||||||
bitWrite(TasmotaGlobal.rel_inverted, TasmotaGlobal.devices_present, Settings->flag3.pcf8574_ports_inverted); // SetOption81 - Invert all ports on PCF8574 devices
|
|
||||||
if (!Settings->flag.save_state && !Settings->flag3.no_power_feedback) { // SetOption63 - Don't scan relay power state at restart - #5594 and #5663
|
|
||||||
//AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: Set power from from chip state"));
|
|
||||||
uint8_t power_state = Settings->flag3.pcf8574_ports_inverted ? 1 & ~gpio : 1 & gpio;
|
|
||||||
bitWrite(TasmotaGlobal.power, TasmotaGlobal.devices_present, power_state);
|
|
||||||
bitWrite(Settings->power, TasmotaGlobal.devices_present, power_state);
|
|
||||||
}
|
|
||||||
//else AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: DON'T set power from chip state"));
|
|
||||||
UpdateDevicesPresent(1);
|
|
||||||
Pcf8574.max_connected_ports++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: Settings->power=0x%08X, TasmotaGlobal.power=0x%08X"), Settings->power, TasmotaGlobal.power);
|
|
||||||
AddLog(LOG_LEVEL_INFO, PSTR("PCF: Total devices %d, PCF8574 output ports %d"), Pcf8574.max_devices, Pcf8574.max_connected_ports);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************************************\
|
|
||||||
* Presentation
|
|
||||||
\*********************************************************************************************/
|
|
||||||
|
|
||||||
#ifdef USE_WEBSERVER
|
|
||||||
|
|
||||||
#define WEB_HANDLE_PCF8574 "pcf"
|
|
||||||
|
|
||||||
const char HTTP_BTN_MENU_PCF8574[] PROGMEM =
|
|
||||||
"<p><form action='" WEB_HANDLE_PCF8574 "' method='get'><button>" D_CONFIGURE_PCF8574 "</button></form></p>";
|
|
||||||
|
|
||||||
const char HTTP_FORM_I2C_PCF8574_1[] PROGMEM =
|
|
||||||
"<fieldset><legend><b> " D_PCF8574_PARAMETERS " </b></legend>"
|
|
||||||
"<form method='get' action='" WEB_HANDLE_PCF8574 "'>"
|
|
||||||
"<p><label><input id='b1' name='b1' type='checkbox'%s><b>" D_INVERT_PORTS "</b></label></p><hr/>";
|
|
||||||
|
|
||||||
const char HTTP_FORM_I2C_PCF8574_2[] PROGMEM =
|
|
||||||
"<tr><td><b>" D_DEVICE " %d " D_PORT " %d</b></td><td style='width:100px'><select id='i2cs%d' name='i2cs%d'>"
|
|
||||||
"<option%s value='0'>" D_DEVICE_INPUT "</option>"
|
|
||||||
"<option%s value='1'>" D_DEVICE_OUTPUT "</option>"
|
|
||||||
"</select></td></tr>";
|
|
||||||
|
|
||||||
const char HTTP_SNS_PCF8574_GPIO[] PROGMEM = "{s}PCF8574%c%d D%d{m}%d{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
|
||||||
|
|
||||||
|
|
||||||
void HandlePcf8574(void)
|
|
||||||
{
|
|
||||||
if (!HttpCheckPriviledgedAccess()) { return; }
|
|
||||||
|
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_PCF8574));
|
|
||||||
|
|
||||||
if (Webserver->hasArg("save")) {
|
|
||||||
Pcf8574SaveSettings();
|
|
||||||
WebRestart(1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
WSContentStart_P(D_CONFIGURE_PCF8574);
|
|
||||||
WSContentSendStyle();
|
|
||||||
WSContentSend_P(HTTP_FORM_I2C_PCF8574_1, (Settings->flag3.pcf8574_ports_inverted) ? PSTR(" checked") : ""); // SetOption81 - Invert all ports on PCF8574 devices
|
|
||||||
WSContentSend_P(HTTP_TABLE100);
|
|
||||||
for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) {
|
|
||||||
for (uint32_t idx2 = 0; idx2 < 8; idx2++) { // 8 ports on PCF8574
|
|
||||||
uint8_t helper = 1 << idx2;
|
|
||||||
WSContentSend_P(HTTP_FORM_I2C_PCF8574_2,
|
|
||||||
idx +1, idx2,
|
|
||||||
idx2 + 8*idx,
|
|
||||||
idx2 + 8*idx,
|
|
||||||
((helper & Settings->pcf8574_config[idx]) >> idx2 == 0) ? PSTR(" selected ") : " ",
|
|
||||||
((helper & Settings->pcf8574_config[idx]) >> idx2 == 1) ? PSTR(" selected ") : " "
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WSContentSend_P(PSTR("</table>"));
|
|
||||||
WSContentSend_P(HTTP_FORM_END);
|
|
||||||
WSContentSpaceButton(BUTTON_CONFIGURATION);
|
|
||||||
WSContentStop();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(USE_PCF8574_SENSOR) || defined(USE_PCF8574_DISPLAYINPUT)
|
|
||||||
void Pcf8574Show(bool json)
|
|
||||||
{
|
|
||||||
#ifdef USE_PCF8574_SENSOR
|
|
||||||
if (json) {
|
|
||||||
for (int idx = 0 ; idx < Pcf8574.max_devices ; idx++)
|
|
||||||
{
|
|
||||||
uint8_t gpio = Pcf8574Read(idx);
|
|
||||||
ResponseAppend_P(PSTR(",\"PCF8574%c%d\":{\"D0\":%i,\"D1\":%i,\"D2\":%i,\"D3\":%i,\"D4\":%i,\"D5\":%i,\"D6\":%i,\"D7\":%i}"),
|
|
||||||
IndexSeparator(), idx +1,
|
|
||||||
(gpio>>0)&1,(gpio>>1)&1,(gpio>>2)&1,(gpio>>3)&1,(gpio>>4)&1,(gpio>>5)&1,(gpio>>6)&1,(gpio>>7)&1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // #ifdef USE_PCF8574_SENSOR
|
|
||||||
#if defined(USE_WEBSERVER) && defined(USE_PCF8574_DISPLAYINPUT)
|
|
||||||
if(!json) {
|
|
||||||
for (int idx = 0 ; idx < Pcf8574.max_devices ; idx++)
|
|
||||||
{
|
|
||||||
uint8_t input_mask = ~Settings->pcf8574_config[idx]; //invert to 1 = input
|
|
||||||
uint8_t gpio = Pcf8574Read(idx);
|
|
||||||
for (int pin = 0 ; pin < 8 ; ++pin, input_mask>>=1, gpio>>=1)
|
|
||||||
{
|
|
||||||
if (input_mask & 1)
|
|
||||||
WSContentSend_P(HTTP_SNS_PCF8574_GPIO, IndexSeparator(), idx +1, pin, gpio & 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // defined(USE_WEBSERVER) && defined(USE_PCF8574_DISPLAYINPUT)
|
|
||||||
}
|
|
||||||
#endif // #if defined(USE_PCF8574_SENSOR) || defined(USE_PCF8574_DISPLAYINPUT)
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef USE_PCF8574_MQTTINPUT
|
|
||||||
void Pcf8574CheckForInputChange(void)
|
|
||||||
{
|
|
||||||
for (int idx = 0 ; idx < Pcf8574.max_devices ; idx++)
|
|
||||||
{
|
|
||||||
uint8_t input_mask = ~Settings->pcf8574_config[idx]; //invert to 1 = input
|
|
||||||
uint8_t input = Pcf8574Read(idx) & input_mask;
|
|
||||||
uint8_t last_input = Pcf8574.last_input[idx];
|
|
||||||
if (input != last_input) { // don't scan bits if no change (EVERY_50_MS !)
|
|
||||||
for (uint8_t pin = 0 ; pin < 8 ; ++pin) {
|
|
||||||
if (bitRead(input_mask,pin) && bitRead(input,pin) != bitRead(last_input,pin)) {
|
|
||||||
ResponseTime_P(PSTR(",\"PCF8574%c%d_INP\":{\"D%i\":%i}}"), IndexSeparator(), idx +1, pin, bitRead(input,pin));
|
|
||||||
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR("PCF8574_INP"));
|
|
||||||
if (Settings->flag3.hass_tele_on_power) { // SetOption59 - Send tele/%topic%/SENSOR in addition to stat/%topic%/RESULT
|
|
||||||
MqttPublishSensor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Pcf8574.last_input[idx] = input;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif //#ifdef USE_PCF8574_MQTTINPUT
|
|
||||||
|
|
||||||
void Pcf8574SaveSettings(void)
|
|
||||||
{
|
|
||||||
char stemp[7];
|
|
||||||
char tmp[100];
|
|
||||||
|
|
||||||
//AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: Start working on Save arguements: inverted:%d")), Webserver->hasArg("b1");
|
|
||||||
|
|
||||||
Settings->flag3.pcf8574_ports_inverted = Webserver->hasArg("b1"); // SetOption81 - Invert all ports on PCF8574 devices
|
|
||||||
for (byte idx = 0; idx < Pcf8574.max_devices; idx++) {
|
|
||||||
byte count=0;
|
|
||||||
byte n = Settings->pcf8574_config[idx];
|
|
||||||
while(n!=0) {
|
|
||||||
n = n&(n-1);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
if (count <= TasmotaGlobal.devices_present) {
|
|
||||||
UpdateDevicesPresent(-count);
|
|
||||||
}
|
|
||||||
for (byte i = 0; i < 8; i++) {
|
|
||||||
snprintf_P(stemp, sizeof(stemp), PSTR("i2cs%d"), i+8*idx);
|
|
||||||
WebGetArg(stemp, tmp, sizeof(tmp));
|
|
||||||
byte _value = (!strlen(tmp)) ? 0 : atoi(tmp);
|
|
||||||
if (_value) {
|
|
||||||
Settings->pcf8574_config[idx] = Settings->pcf8574_config[idx] | 1 << i;
|
|
||||||
UpdateDevicesPresent(1);
|
|
||||||
Pcf8574.max_connected_ports++;
|
|
||||||
} else {
|
|
||||||
Settings->pcf8574_config[idx] = Settings->pcf8574_config[idx] & ~(1 << i );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Settings->pcf8574_config[0] = (!strlen(webServer->arg("i2cs0").c_str())) ? 0 : atoi(webServer->arg("i2cs0").c_str());
|
|
||||||
//AddLog(LOG_LEVEL_INFO, PSTR("PCF: I2C Board: %d, Config: %2x")), idx, Settings->pcf8574_config[idx];
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // USE_WEBSERVER
|
|
||||||
|
|
||||||
/*********************************************************************************************\
|
|
||||||
* Interface
|
|
||||||
\*********************************************************************************************/
|
|
||||||
|
|
||||||
bool Xdrv28(uint32_t function)
|
|
||||||
{
|
|
||||||
if (!I2cEnabled(XI2C_02)) { return false; }
|
|
||||||
|
|
||||||
bool result = false;
|
|
||||||
|
|
||||||
if (FUNC_PRE_INIT == function) {
|
|
||||||
Pcf8574Init();
|
|
||||||
}
|
|
||||||
else if (Pcf8574.type) {
|
|
||||||
switch (function) {
|
|
||||||
case FUNC_SET_POWER:
|
|
||||||
Pcf8574SwitchRelay();
|
|
||||||
break;
|
|
||||||
#ifdef USE_PCF8574_MQTTINPUT
|
|
||||||
case FUNC_EVERY_50_MSECOND:
|
|
||||||
Pcf8574CheckForInputChange();
|
|
||||||
break;
|
|
||||||
#endif // #ifdef USE_PCF8574_MQTTINPUT
|
|
||||||
#ifdef USE_PCF8574_SENSOR
|
|
||||||
case FUNC_JSON_APPEND:
|
|
||||||
Pcf8574Show(1);
|
|
||||||
break;
|
|
||||||
#endif // #ifdef USE_PCF8574_SENSOR
|
|
||||||
#ifdef USE_WEBSERVER
|
|
||||||
case FUNC_WEB_ADD_BUTTON:
|
|
||||||
WSContentSend_P(HTTP_BTN_MENU_PCF8574);
|
|
||||||
break;
|
|
||||||
case FUNC_WEB_ADD_HANDLER:
|
|
||||||
WebServer_on(PSTR("/" WEB_HANDLE_PCF8574), HandlePcf8574);
|
|
||||||
break;
|
|
||||||
#ifdef USE_PCF8574_DISPLAYINPUT
|
|
||||||
case FUNC_WEB_SENSOR:
|
|
||||||
Pcf8574Show(0);
|
|
||||||
break;
|
|
||||||
#endif // #ifdef USE_PCF8574_DISPLAYINPUT
|
|
||||||
#endif // USE_WEBSERVER
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // USE_PCF8574
|
|
||||||
#endif // USE_I2C
|
|
|
@ -71,8 +71,6 @@
|
||||||
* {"NAME":"PCF8574 A=Ri8-1, B=B1-8","GPIO":[263,262,261,260,259,258,257,256,32,33,34,35,36,37,38,39]}
|
* {"NAME":"PCF8574 A=Ri8-1, B=B1-8","GPIO":[263,262,261,260,259,258,257,256,32,33,34,35,36,37,38,39]}
|
||||||
* B1 B2 B3 B4 Ri4 Ri3 Ri2 Ri1 B5 B6 B7 B8 Ri8 Ri7 Ri6 Ri5
|
* B1 B2 B3 B4 Ri4 Ri3 Ri2 Ri1 B5 B6 B7 B8 Ri8 Ri7 Ri6 Ri5
|
||||||
* {"NAME":"PCF8574 A=B1-4,Ri4-1, B=B5-8,Ri8-5","GPIO":[32,33,34,35,259,258,257,256,36,37,38,39,263,262,261,260]}
|
* {"NAME":"PCF8574 A=B1-4,Ri4-1, B=B5-8,Ri8-5","GPIO":[32,33,34,35,259,258,257,256,36,37,38,39,263,262,261,260]}
|
||||||
* B1 B2 B3 B4 Ri4 Ri3 Ri2 Ri1 B5 B6 B7 B8 Ri8 Ri7 Ri6 Ri5
|
|
||||||
* {"NAME":"PCF8574 A=Bi1-4,Ri4-1, B=Bi5-8,Ri8-5","GPIO":[64,65,66,67,259,258,257,256,68,69,70,71,263,262,261,260]}
|
|
||||||
\*********************************************************************************************/
|
\*********************************************************************************************/
|
||||||
|
|
||||||
#define XDRV_28 28
|
#define XDRV_28 28
|
||||||
|
@ -108,7 +106,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct PCF8574 {
|
struct PCF8574 {
|
||||||
int error;
|
|
||||||
uint32_t relay_inverted;
|
uint32_t relay_inverted;
|
||||||
uint32_t button_inverted;
|
uint32_t button_inverted;
|
||||||
uint16_t pin[PCF8574_MAX_PINS];
|
uint16_t pin[PCF8574_MAX_PINS];
|
||||||
|
@ -120,7 +117,6 @@ struct PCF8574 {
|
||||||
uint8_t max_connected_ports = 0; // Max numbers of devices comming from PCF8574 modules
|
uint8_t max_connected_ports = 0; // Max numbers of devices comming from PCF8574 modules
|
||||||
uint8_t max_devices = 0; // Max numbers of PCF8574 modules
|
uint8_t max_devices = 0; // Max numbers of PCF8574 modules
|
||||||
uint8_t mode;
|
uint8_t mode;
|
||||||
uint8_t chip;
|
|
||||||
uint8_t relay_max;
|
uint8_t relay_max;
|
||||||
uint8_t relay_offset;
|
uint8_t relay_offset;
|
||||||
uint8_t button_max;
|
uint8_t button_max;
|
||||||
|
@ -135,34 +131,21 @@ struct PCF8574 {
|
||||||
\*********************************************************************************************/
|
\*********************************************************************************************/
|
||||||
|
|
||||||
uint8_t Pcf8574Read(uint8_t idx) {
|
uint8_t Pcf8574Read(uint8_t idx) {
|
||||||
Wire.requestFrom(Pcf8574.address[idx],(uint8_t)1);
|
Wire.requestFrom(Pcf8574.address[idx], (uint8_t)1);
|
||||||
return Wire.read();
|
return Wire.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t Pcf8574Write(uint8_t idx) {
|
void Pcf8574Write(uint8_t idx) {
|
||||||
Wire.beginTransmission(Pcf8574.address[idx]);
|
Wire.beginTransmission(Pcf8574.address[idx]);
|
||||||
Wire.write(Pcf8574.pin_mask[idx]);
|
Wire.write(Pcf8574.pin_mask[idx]);
|
||||||
return Wire.endTransmission();
|
Wire.endTransmission();
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************************************************************************/
|
|
||||||
|
|
||||||
uint8_t Pcf8574ReadByte(void) {
|
|
||||||
Wire.requestFrom(Pcf8574.address[Pcf8574.chip], (uint8_t)1);
|
|
||||||
return Wire.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t Pcf8574WriteByte(uint8_t value) {
|
|
||||||
Wire.beginTransmission(Pcf8574.address[Pcf8574.chip]);
|
|
||||||
Wire.write(value);
|
|
||||||
return Wire.endTransmission();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Pcf8574DigitalRead(uint8_t pin) {
|
bool Pcf8574DigitalRead(uint8_t pin) {
|
||||||
// pin 0 - 63
|
// pin 0 - 63
|
||||||
Pcf8574.chip = pin / 8;
|
uint32_t chip = pin / 8;
|
||||||
uint8_t bit = pin % 8;
|
uint32_t bit = pin % 8;
|
||||||
uint8_t value = Pcf8574ReadByte();
|
uint32_t value = Pcf8574Read(chip);
|
||||||
return value & (1 << bit);
|
return value & (1 << bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,15 +153,17 @@ void Pcf8574DigitalWrite(uint8_t pin, bool pin_value) {
|
||||||
// pin 0 - 63
|
// pin 0 - 63
|
||||||
// INPUT or INPUT_PULLUP = Pcf8574DigitalWrite(pin, 1);
|
// INPUT or INPUT_PULLUP = Pcf8574DigitalWrite(pin, 1);
|
||||||
// OUTPUT = Pcf8574DigitalWrite(pin, 0); or Pcf8574DigitalWrite(pin, 1);
|
// OUTPUT = Pcf8574DigitalWrite(pin, 0); or Pcf8574DigitalWrite(pin, 1);
|
||||||
Pcf8574.chip = pin / 8;
|
uint32_t chip = pin / 8;
|
||||||
uint8_t bit = pin % 8;
|
uint32_t bit = pin % 8;
|
||||||
uint8_t reg_value = Pcf8574ReadByte();
|
uint32_t value = Pcf8574Read(chip);
|
||||||
if (pin_value) {
|
if (pin_value) {
|
||||||
reg_value |= 1 << bit;
|
value |= 1 << bit;
|
||||||
} else {
|
} else {
|
||||||
reg_value &= ~(1 << bit);
|
value &= ~(1 << bit);
|
||||||
}
|
}
|
||||||
Pcf8574WriteByte(reg_value);
|
Wire.beginTransmission(Pcf8574.address[chip]);
|
||||||
|
Wire.write(value);
|
||||||
|
Wire.endTransmission();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************************************************************************************\
|
/*********************************************************************************************\
|
||||||
|
@ -244,7 +229,6 @@ bool Pcf8574LoadTemplate(void) {
|
||||||
if (!root) { return false; }
|
if (!root) { return false; }
|
||||||
|
|
||||||
// rule3 on file#pcf8574.dat do {"NAME":"PCF8574 A=B1-4,Ri4-1, B=B5-8,Ri8-5","GPIO":[32,33,34,35,259,258,257,256,36,37,38,39,263,262,261,260]} endon
|
// rule3 on file#pcf8574.dat do {"NAME":"PCF8574 A=B1-4,Ri4-1, B=B5-8,Ri8-5","GPIO":[32,33,34,35,259,258,257,256,36,37,38,39,263,262,261,260]} endon
|
||||||
// rule3 on file#pcf8574.dat do {"NAME":"PCF8574 A=Bi1-4,Ri4-1, B=Bi5-8,Ri8-5","GPIO":[64,65,66,67,259,258,257,256,68,69,70,71,263,262,261,260]} endon
|
|
||||||
JsonParserToken val = root[PSTR(D_JSON_NAME)];
|
JsonParserToken val = root[PSTR(D_JSON_NAME)];
|
||||||
if (val) {
|
if (val) {
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: Template %s"), val.getStr());
|
AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: Template %s"), val.getStr());
|
||||||
|
@ -289,13 +273,13 @@ bool Pcf8574LoadTemplate(void) {
|
||||||
}
|
}
|
||||||
else if ((mpin >= AGPIO(GPIO_REL1)) && (mpin < (AGPIO(GPIO_REL1) + MAX_RELAYS_SET))) {
|
else if ((mpin >= AGPIO(GPIO_REL1)) && (mpin < (AGPIO(GPIO_REL1) + MAX_RELAYS_SET))) {
|
||||||
Pcf8574.relay_max++;
|
Pcf8574.relay_max++;
|
||||||
Pcf8574DigitalWrite(pin, 1); // OUTPUT
|
// Pcf8574DigitalWrite(pin, 1); // OUTPUT - Leave unchanged to fix restart and power on spikes (default is 1)
|
||||||
}
|
}
|
||||||
else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS_SET))) {
|
else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS_SET))) {
|
||||||
bitSet(Pcf8574.relay_inverted, mpin - AGPIO(GPIO_REL1_INV));
|
bitSet(Pcf8574.relay_inverted, mpin - AGPIO(GPIO_REL1_INV));
|
||||||
mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1));
|
mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1));
|
||||||
Pcf8574.relay_max++;
|
Pcf8574.relay_max++;
|
||||||
Pcf8574DigitalWrite(pin, 0); // OUTPUT
|
// Pcf8574DigitalWrite(pin, 1); // OUTPUT - Leave unchanged to fix restart and power on spikes (default is 1)
|
||||||
}
|
}
|
||||||
else if (mpin == AGPIO(GPIO_OUTPUT_HI)) {
|
else if (mpin == AGPIO(GPIO_OUTPUT_HI)) {
|
||||||
Pcf8574DigitalWrite(pin, 1); // OUTPUT
|
Pcf8574DigitalWrite(pin, 1); // OUTPUT
|
||||||
|
@ -315,38 +299,15 @@ bool Pcf8574LoadTemplate(void) {
|
||||||
}
|
}
|
||||||
Pcf8574.max_connected_ports = pin; // Max number of configured pins
|
Pcf8574.max_connected_ports = pin; // Max number of configured pins
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: Pins %d, pin %*_V"), Pcf8574.max_connected_ports, Pcf8574.max_connected_ports, (uint8_t*)Pcf8574.pin);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Pcf8574TemplateGpio(void) {
|
|
||||||
String pcftmplt = Pcf8574TemplateLoadFile();
|
|
||||||
uint32_t len = pcftmplt.length() +1;
|
|
||||||
if (len < 7) { return 0; } // No PcfTmplt found
|
|
||||||
|
|
||||||
JsonParser parser((char*)pcftmplt.c_str());
|
|
||||||
JsonParserObject root = parser.getRootObject();
|
|
||||||
if (!root) { return 0; }
|
|
||||||
|
|
||||||
JsonParserArray arr = root[PSTR(D_JSON_GPIO)];
|
|
||||||
if (arr.isArray()) {
|
|
||||||
return arr.size(); // Number of requested pins
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Pcf8574ServiceInput(void) {
|
void Pcf8574ServiceInput(void) {
|
||||||
Pcf8574.interrupt = false;
|
Pcf8574.interrupt = false;
|
||||||
// This works with no interrupt
|
// This works with no interrupt too
|
||||||
uint32_t pin_offset = 0;
|
uint32_t pin_offset = 0;
|
||||||
uint32_t gpio;
|
for (uint32_t chip = 0; chip < Pcf8574.max_devices; chip++) {
|
||||||
for (Pcf8574.chip = 0; Pcf8574.chip < Pcf8574.max_devices; Pcf8574.chip++) {
|
uint32_t gpio = Pcf8574Read(chip);
|
||||||
gpio = Pcf8574ReadByte();
|
|
||||||
|
|
||||||
// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PCF: Chip %d, State %02X"), Pcf8574.chip, gpio);
|
|
||||||
|
|
||||||
uint32_t mask = 1;
|
uint32_t mask = 1;
|
||||||
for (uint32_t pin = 0; pin < 8; pin++) {
|
for (uint32_t pin = 0; pin < 8; pin++) {
|
||||||
uint32_t state = ((gpio & mask) != 0);
|
uint32_t state = ((gpio & mask) != 0);
|
||||||
|
@ -367,7 +328,7 @@ void Pcf8574ServiceInput(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRAM_ATTR Pcf8574InputIsr(void) {
|
static void IRAM_ATTR Pcf8574InputIsr(void) {
|
||||||
Pcf8574.interrupt = true;
|
Pcf8574.interrupt = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,14 +342,12 @@ void Pcf8574Init(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pcf8574Power(void) {
|
void Pcf8574Power(void) {
|
||||||
|
// XdrvMailbox.index = 32-bit rpower bit mask
|
||||||
power_t rpower = XdrvMailbox.index >> Pcf8574.relay_offset;
|
power_t rpower = XdrvMailbox.index >> Pcf8574.relay_offset;
|
||||||
for (uint32_t index = 0; index < Pcf8574.relay_max; index++) {
|
for (uint32_t index = 0; index < Pcf8574.relay_max; index++) {
|
||||||
power_t state = rpower &1;
|
power_t state = rpower &1;
|
||||||
if (Pcf8574PinUsed(GPIO_REL1, index)) {
|
if (Pcf8574PinUsed(GPIO_REL1, index)) {
|
||||||
uint32_t pin = Pcf8574Pin(GPIO_REL1, index) & 0x3F; // Fix possible overflow over 63 gpios
|
uint32_t pin = Pcf8574Pin(GPIO_REL1, index) & 0x3F; // Fix possible overflow over 63 gpios
|
||||||
|
|
||||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: Power pin %d, state %d(%d)"), pin, state, bitRead(Pcf8574.relay_inverted, index));
|
|
||||||
|
|
||||||
Pcf8574DigitalWrite(pin, bitRead(Pcf8574.relay_inverted, index) ? !state : state);
|
Pcf8574DigitalWrite(pin, bitRead(Pcf8574.relay_inverted, index) ? !state : state);
|
||||||
}
|
}
|
||||||
rpower >>= 1; // Select next power
|
rpower >>= 1; // Select next power
|
||||||
|
@ -401,9 +360,6 @@ bool Pcf8574AddButton(void) {
|
||||||
uint32_t index = XdrvMailbox.index - Pcf8574.button_offset;
|
uint32_t index = XdrvMailbox.index - Pcf8574.button_offset;
|
||||||
if (index >= Pcf8574.button_max) { return false; }
|
if (index >= Pcf8574.button_max) { return false; }
|
||||||
XdrvMailbox.index = (Pcf8574DigitalRead(Pcf8574Pin(GPIO_KEY1, index)) != bitRead(Pcf8574.button_inverted, index));
|
XdrvMailbox.index = (Pcf8574DigitalRead(Pcf8574Pin(GPIO_KEY1, index)) != bitRead(Pcf8574.button_inverted, index));
|
||||||
|
|
||||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: AddButton index %d, state %d"), index, XdrvMailbox.index);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,7 +389,7 @@ void Pcf8574SwitchRelay(void) {
|
||||||
//AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: SwitchRelay %d=%d => PCF-%d.D%d=%d"), i, relay_state, board +1, pin, _val);
|
//AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: SwitchRelay %d=%d => PCF-%d.D%d=%d"), i, relay_state, board +1, pin, _val);
|
||||||
bitWrite(Pcf8574.pin_mask[board], pin, _val);
|
bitWrite(Pcf8574.pin_mask[board], pin, _val);
|
||||||
if (oldpinmask != Pcf8574.pin_mask[board]) {
|
if (oldpinmask != Pcf8574.pin_mask[board]) {
|
||||||
Pcf8574.error = Pcf8574Write(board);
|
Pcf8574Write(board);
|
||||||
}
|
}
|
||||||
//else AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: SwitchRelay skipped"));
|
//else AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: SwitchRelay skipped"));
|
||||||
}
|
}
|
||||||
|
@ -459,8 +415,8 @@ void Pcf8574ModuleInit(void) {
|
||||||
if (I2cSetDevice(pcf8574_address)) {
|
if (I2cSetDevice(pcf8574_address)) {
|
||||||
Pcf8574.mode = 1;
|
Pcf8574.mode = 1;
|
||||||
|
|
||||||
Pcf8574.address[Pcf8574.max_devices] = pcf8574_address;
|
|
||||||
Pcf8574.max_connected_ports += 8;
|
Pcf8574.max_connected_ports += 8;
|
||||||
|
Pcf8574.address[Pcf8574.max_devices] = pcf8574_address;
|
||||||
Pcf8574.max_devices++;
|
Pcf8574.max_devices++;
|
||||||
|
|
||||||
char stype[9];
|
char stype[9];
|
||||||
|
@ -469,6 +425,7 @@ void Pcf8574ModuleInit(void) {
|
||||||
strcpy(stype, "PCF8574A");
|
strcpy(stype, "PCF8574A");
|
||||||
}
|
}
|
||||||
I2cSetActiveFound(pcf8574_address, stype);
|
I2cSetActiveFound(pcf8574_address, stype);
|
||||||
|
if (Pcf8574.max_connected_ports > PCF8574_MAX_PINS -8) { break; }
|
||||||
}
|
}
|
||||||
|
|
||||||
pcf8574_address++;
|
pcf8574_address++;
|
|
@ -709,7 +709,7 @@ void MCP23xServiceInput(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRAM_ATTR MCP23xInputIsr(void) {
|
static void IRAM_ATTR MCP23xInputIsr(void) {
|
||||||
Mcp23x.interrupt = true;
|
Mcp23x.interrupt = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue