123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- /*
- * The MySensors Arduino library handles the wireless radio link and protocol
- * between your home built sensors/actuators and HA controller of choice.
- * The sensors forms a self healing radio network with optional repeaters. Each
- * repeater and gateway builds a routing tables in EEPROM which keeps track of the
- * network topology allowing messages to be routed to nodes.
- *
- * Created by Tomas Hozza <thozza@gmail.com>
- * Copyright (C) 2015 Tomas Hozza
- * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
- *
- * Documentation: http://www.mysensors.org
- * Support Forum: http://forum.mysensors.org
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- */
-
- #include "MyGatewayTransport.h"
-
- // global variables
- extern MyMessage _msgTmp;
-
- // housekeeping, remove for 3.0.0
- #ifdef MY_ESP8266_SSID
- #warning MY_ESP8266_SSID is deprecated, use MY_WIFI_SSID instead!
- #define MY_WIFI_SSID MY_ESP8266_SSID
- #undef MY_ESP8266_SSID // cleanup
- #endif
-
- #ifdef MY_ESP8266_PASSWORD
- #warning MY_ESP8266_PASSWORD is deprecated, use MY_WIFI_PASSWORD instead!
- #define MY_WIFI_PASSWORD MY_ESP8266_PASSWORD
- #undef MY_ESP8266_PASSWORD // cleanup
- #endif
-
- #ifdef MY_ESP8266_BSSID
- #warning MY_ESP8266_BSSID is deprecated, use MY_WIFI_BSSID instead!
- #define MY_WIFI_BSSID MY_ESP8266_BSSID
- #undef MY_ESP8266_BSSID // cleanup
- #endif
-
- #ifdef MY_ESP8266_HOSTNAME
- #warning MY_ESP8266_HOSTNAME is deprecated, use MY_HOSTNAME instead!
- #define MY_HOSTNAME MY_ESP8266_HOSTNAME
- #undef MY_ESP8266_HOSTNAME // cleanup
- #endif
-
- #ifndef MY_WIFI_BSSID
- #define MY_WIFI_BSSID NULL
- #endif
-
- #if defined(MY_CONTROLLER_IP_ADDRESS)
- IPAddress _ethernetControllerIP(MY_CONTROLLER_IP_ADDRESS);
- #endif
-
- #if defined(MY_IP_ADDRESS)
- IPAddress _ethernetGatewayIP(MY_IP_ADDRESS);
- #if defined(MY_IP_GATEWAY_ADDRESS)
- IPAddress _gatewayIp(MY_IP_GATEWAY_ADDRESS);
- #elif defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32)
- // Assume the gateway will be the machine on the same network as the local IP
- // but with last octet being '1'
- IPAddress _gatewayIp(_ethernetGatewayIP[0], _ethernetGatewayIP[1], _ethernetGatewayIP[2], 1);
- #endif /* End of MY_IP_GATEWAY_ADDRESS */
- #if defined(MY_IP_SUBNET_ADDRESS)
- IPAddress _subnetIp(MY_IP_SUBNET_ADDRESS);
- #elif defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32)
- IPAddress _subnetIp(255, 255, 255, 0);
- #endif /* End of MY_IP_SUBNET_ADDRESS */
- #endif /* End of MY_IP_ADDRESS */
-
- uint8_t _ethernetGatewayMAC[] = { MY_MAC_ADDRESS };
- uint16_t _ethernetGatewayPort = MY_PORT;
- MyMessage _ethernetMsg;
-
- #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
-
- typedef struct {
- // Suppress the warning about unused members in this struct because it is used through a complex
- // set of preprocessor directives
- // cppcheck-suppress unusedStructMember
- char string[MY_GATEWAY_MAX_RECEIVE_LENGTH];
- // cppcheck-suppress unusedStructMember
- uint8_t idx;
- } inputBuffer;
-
- #if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32)
- // Some re-defines to make code more readable below
- #define EthernetServer WiFiServer
- #define EthernetClient WiFiClient
- #define EthernetUDP WiFiUDP
- #endif
-
- #if defined(MY_GATEWAY_CLIENT_MODE)
- #if defined(MY_USE_UDP)
- EthernetUDP _ethernetServer;
- #endif /* End of MY_USE_UDP */
- #elif defined(MY_GATEWAY_LINUX) /* Elif part of MY_GATEWAY_CLIENT_MODE */
- EthernetServer _ethernetServer(_ethernetGatewayPort, MY_GATEWAY_MAX_CLIENTS);
- #else /* Else part of MY_GATEWAY_CLIENT_MODE */
- EthernetServer _ethernetServer(_ethernetGatewayPort);
- #endif /* End of MY_GATEWAY_CLIENT_MODE */
-
- #if defined(MY_GATEWAY_CLIENT_MODE)
- static inputBuffer inputString;
- #if defined(MY_USE_UDP)
- // Nothing to do here
- #else
- static EthernetClient client = EthernetClient();
- #endif /* End of MY_USE_UDP */
- #elif defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) || defined(MY_GATEWAY_LINUX)
- static EthernetClient clients[MY_GATEWAY_MAX_CLIENTS];
- static bool clientsConnected[MY_GATEWAY_MAX_CLIENTS];
- static inputBuffer inputString[MY_GATEWAY_MAX_CLIENTS];
- #else /* Else part of MY_GATEWAY_CLIENT_MODE */
- static EthernetClient client = EthernetClient();
- static inputBuffer inputString;
- #endif /* End of MY_GATEWAY_CLIENT_MODE */
-
- #ifndef MY_IP_ADDRESS
- void gatewayTransportRenewIP();
- #endif
-
- // On W5100 boards with SPI_EN exposed we can use the real SPI bus together with radio
- // (if we enable it during usage)
- #if defined(MY_W5100_SPI_EN)
- void _w5100_spi_en(bool enable)
- {
- if (enable) {
- // Pull up pin
- hwPinMode(MY_W5100_SPI_EN, INPUT);
- hwDigitalWrite(MY_W5100_SPI_EN, HIGH);
- } else {
- // Ground pin
- hwPinMode(MY_W5100_SPI_EN, OUTPUT);
- hwDigitalWrite(MY_W5100_SPI_EN, LOW);
- }
- }
- #else
- #define _w5100_spi_en(x)
- #endif
-
- bool gatewayTransportInit(void)
- {
- _w5100_spi_en(true);
-
- #if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32)
- #if defined(MY_WIFI_SSID)
- // Turn off access point
- WiFi.mode(WIFI_STA);
- #if defined(MY_HOSTNAME)
- #if defined(MY_GATEWAY_ESP8266)
- WiFi.hostname(MY_HOSTNAME);
- #elif defined(MY_GATEWAY_ESP32)
- WiFi.setHostname(MY_HOSTNAME);
- #endif
- #endif
- #ifdef MY_IP_ADDRESS
- WiFi.config(_ethernetGatewayIP, _gatewayIp, _subnetIp);
- #endif
- (void)WiFi.begin(MY_WIFI_SSID, MY_WIFI_PASSWORD, 0, MY_WIFI_BSSID);
- while (WiFi.status() != WL_CONNECTED) {
- wait(500);
- GATEWAY_DEBUG(PSTR("GWT:TIN:CONNECTING...\n"));
- }
- GATEWAY_DEBUG(PSTR("GWT:TIN:IP: %s\n"), WiFi.localIP().toString().c_str());
- #endif
- #elif defined(MY_GATEWAY_LINUX)
- // Nothing to do here
- #else
- #if defined(MY_IP_GATEWAY_ADDRESS) && defined(MY_IP_SUBNET_ADDRESS)
- // DNS server set to gateway ip
- Ethernet.begin(_ethernetGatewayMAC, _ethernetGatewayIP, _gatewayIp, _gatewayIp, _subnetIp);
- #elif defined(MY_IP_ADDRESS)
- Ethernet.begin(_ethernetGatewayMAC, _ethernetGatewayIP);
- #else /* Else part of MY_IP_GATEWAY_ADDRESS && MY_IP_SUBNET_ADDRESS */
- // Get IP address from DHCP
- if (!Ethernet.begin(_ethernetGatewayMAC)) {
- GATEWAY_DEBUG(PSTR("!GWT:TIN:DHCP FAIL\n"));
- _w5100_spi_en(false);
- return false;
- }
- #endif /* End of MY_IP_GATEWAY_ADDRESS && MY_IP_SUBNET_ADDRESS */
- GATEWAY_DEBUG(PSTR("GWT:TIN:IP=%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 "\n"),
- Ethernet.localIP()[0],
- Ethernet.localIP()[1], Ethernet.localIP()[2], Ethernet.localIP()[3]);
- // give the Ethernet interface a second to initialize
- delay(1000);
- #endif /* MY_GATEWAY_ESP8266 / MY_GATEWAY_ESP32 */
-
- #if defined(MY_GATEWAY_CLIENT_MODE)
- #if defined(MY_USE_UDP)
- _ethernetServer.begin(_ethernetGatewayPort);
- #else /* Else part of MY_USE_UDP */
- #if defined(MY_GATEWAY_LINUX) && defined(MY_IP_ADDRESS)
- client.bind(_ethernetGatewayIP);
- #endif /* End of MY_GATEWAY_LINUX && MY_IP_ADDRESS */
- #if defined(MY_CONTROLLER_URL_ADDRESS)
- if (client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
- #else
- if (client.connect(_ethernetControllerIP, MY_PORT)) {
- #endif /* End of MY_CONTROLLER_URL_ADDRESS */
- GATEWAY_DEBUG(PSTR("GWT:TIN:ETH OK\n"));
- _w5100_spi_en(false);
- gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE));
- _w5100_spi_en(true);
- presentNode();
- } else {
- client.stop();
- GATEWAY_DEBUG(PSTR("!GWT:TIN:ETH FAIL\n"));
- }
- #endif /* End of MY_USE_UDP */
- #else /* Else part of MY_GATEWAY_CLIENT_MODE */
- #if defined(MY_GATEWAY_LINUX) && defined(MY_IP_ADDRESS)
- _ethernetServer.begin(_ethernetGatewayIP);
- #else
- // we have to use pointers due to the constructor of EthernetServer
- _ethernetServer.begin();
- #endif /* End of MY_GATEWAY_LINUX && MY_IP_ADDRESS */
- #endif /* End of MY_GATEWAY_CLIENT_MODE */
-
- _w5100_spi_en(false);
- return true;
- }
-
- bool gatewayTransportSend(MyMessage &message)
- {
- int nbytes = 0;
- char *_ethernetMsg = protocolFormat(message);
-
- setIndication(INDICATION_GW_TX);
-
- _w5100_spi_en(true);
- #if defined(MY_GATEWAY_CLIENT_MODE)
- #if defined(MY_USE_UDP)
- #if defined(MY_CONTROLLER_URL_ADDRESS)
- _ethernetServer.beginPacket(MY_CONTROLLER_URL_ADDRESS, MY_PORT);
- #else
- _ethernetServer.beginPacket(_ethernetControllerIP, MY_PORT);
- #endif /* End of MY_CONTROLLER_URL_ADDRESS */
- _ethernetServer.write(_ethernetMsg, strlen(_ethernetMsg));
- // returns 1 if the packet was sent successfully
- nbytes = _ethernetServer.endPacket();
- #else /* Else part of MY_USE_UDP */
- if (!client.connected()) {
- client.stop();
- #if defined(MY_CONTROLLER_URL_ADDRESS)
- if (client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
- #else
- if (client.connect(_ethernetControllerIP, MY_PORT)) {
- #endif /* End of MY_CONTROLLER_URL_ADDRESS */
- GATEWAY_DEBUG(PSTR("GWT:TPS:ETH OK\n"));
- _w5100_spi_en(false);
- gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE));
- _w5100_spi_en(true);
- presentNode();
- } else {
- // connecting to the server failed!
- GATEWAY_DEBUG(PSTR("!GWT:TPS:ETH FAIL\n"));
- _w5100_spi_en(false);
- return false;
- }
- }
- nbytes = client.write((const uint8_t*)_ethernetMsg, strlen(_ethernetMsg));
- #endif /* End of MY_USE_UDP */
- #else /* Else part of MY_GATEWAY_CLIENT_MODE */
- // Send message to connected clients
- #if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32)
- for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
- if (clients[i] && clients[i].connected()) {
- nbytes += clients[i].write((uint8_t*)_ethernetMsg, strlen(_ethernetMsg));
- }
- }
- #else /* Else part of MY_GATEWAY_ESP8266 */
- nbytes = _ethernetServer.write(_ethernetMsg);
- #endif /* End of MY_GATEWAY_ESP8266 */
- #endif /* End of MY_GATEWAY_CLIENT_MODE */
- _w5100_spi_en(false);
- return (nbytes > 0);
- }
-
- #if defined(MY_USE_UDP)
- // Nothing to do here
- #else
- #if (defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) || defined(MY_GATEWAY_LINUX)) && !defined(MY_GATEWAY_CLIENT_MODE)
- bool _readFromClient(uint8_t i)
- {
- while (clients[i].connected() && clients[i].available()) {
- const char inChar = clients[i].read();
- if (inputString[i].idx < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) {
- // if newline then command is complete
- if (inChar == '\n' || inChar == '\r') {
- // Add string terminator and prepare for the next message
- inputString[i].string[inputString[i].idx] = 0;
- GATEWAY_DEBUG(PSTR("GWT:RFC:C=%" PRIu8 ",MSG=%s\n"), i, inputString[i].string);
- inputString[i].idx = 0;
- if (protocolParse(_ethernetMsg, inputString[i].string)) {
- return true;
- }
-
- } else {
- // add it to the inputString:
- inputString[i].string[inputString[i].idx++] = inChar;
- }
- } else {
- // Incoming message too long. Throw away
- GATEWAY_DEBUG(PSTR("!GWT:RFC:C=%" PRIu8 ",MSG TOO LONG\n"), i);
- inputString[i].idx = 0;
- // Finished with this client's message. Next loop() we'll see if there's more to read.
- break;
- }
- }
- return false;
- }
- #else /* Else part of MY_GATEWAY_ESP8266 || MY_GATEWAY_LINUX || !MY_GATEWAY_CLIENT_MODE */
- bool _readFromClient(void)
- {
- while (client.connected() && client.available()) {
- const char inChar = client.read();
- if (inputString.idx < MY_GATEWAY_MAX_RECEIVE_LENGTH - 1) {
- // if newline then command is complete
- if (inChar == '\n' || inChar == '\r') {
- // Add string terminator and prepare for the next message
- inputString.string[inputString.idx] = 0;
- GATEWAY_DEBUG(PSTR("GWT:RFC:MSG=%s\n"), inputString.string);
- inputString.idx = 0;
- if (protocolParse(_ethernetMsg, inputString.string)) {
- return true;
- }
-
- } else {
- // add it to the inputString:
- inputString.string[inputString.idx++] = inChar;
- }
- } else {
- // Incoming message too long. Throw away
- GATEWAY_DEBUG(PSTR("!GWT:RFC:MSG TOO LONG\n"));
- inputString.idx = 0;
- // Finished with this client's message. Next loop() we'll see if there's more to read.
- break;
- }
- }
- return false;
- }
- #endif /* End of MY_GATEWAY_ESP8266 || MY_GATEWAY_LINUX || !MY_GATEWAY_CLIENT_MODE */
- #endif /* End of MY_USE_UDP */
-
- bool gatewayTransportAvailable(void)
- {
- _w5100_spi_en(true);
- #if !defined(MY_IP_ADDRESS) && defined(MY_GATEWAY_W5100)
- // renew IP address using DHCP
- gatewayTransportRenewIP();
- #endif
-
- #if defined(MY_GATEWAY_CLIENT_MODE)
- #if defined(MY_USE_UDP)
- int packet_size = _ethernetServer.parsePacket();
-
- if (packet_size) {
- //GATEWAY_DEBUG(PSTR("UDP packet available. Size:%" PRIu8 "\n"), packet_size);
- _ethernetServer.read(inputString.string, MY_GATEWAY_MAX_RECEIVE_LENGTH);
- inputString.string[packet_size] = 0;
- GATEWAY_DEBUG(PSTR("GWT:TSA:UDP MSG=%s\n"), inputString.string);
- _w5100_spi_en(false);
- const bool ok = protocolParse(_ethernetMsg, inputString.string);
- if (ok) {
- setIndication(INDICATION_GW_RX);
- }
- return ok;
- }
- #else /* Else part of MY_USE_UDP */
- if (!client.connected()) {
- client.stop();
- #if defined(MY_CONTROLLER_URL_ADDRESS)
- if (client.connect(MY_CONTROLLER_URL_ADDRESS, MY_PORT)) {
- #else
- if (client.connect(_ethernetControllerIP, MY_PORT)) {
- #endif /* End of MY_CONTROLLER_URL_ADDRESS */
- GATEWAY_DEBUG(PSTR("GWT:TSA:ETH OK\n"));
- _w5100_spi_en(false);
- gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE));
- _w5100_spi_en(true);
- presentNode();
- } else {
- GATEWAY_DEBUG(PSTR("!GWT:TSA:ETH FAIL\n"));
- _w5100_spi_en(false);
- return false;
- }
- }
- if (_readFromClient()) {
- setIndication(INDICATION_GW_RX);
- _w5100_spi_en(false);
- return true;
- }
- #endif /* End of MY_USE_UDP */
- #else /* Else part of MY_GATEWAY_CLIENT_MODE */
- #if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) || defined(MY_GATEWAY_LINUX)
- // ESP8266: Go over list of clients and stop any that are no longer connected.
- // If the server has a new client connection it will be assigned to a free slot.
- bool allSlotsOccupied = true;
- for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
- if (!clients[i].connected()) {
- if (clientsConnected[i]) {
- GATEWAY_DEBUG(PSTR("GWT:TSA:C=%" PRIu8 ",DISCONNECTED\n"), i);
- clients[i].stop();
- }
- //check if there are any new clients
- if (_ethernetServer.hasClient()) {
- clients[i] = _ethernetServer.available();
- inputString[i].idx = 0;
- GATEWAY_DEBUG(PSTR("GWT:TSA:C=%" PRIu8 ",CONNECTED\n"), i);
- gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE));
- // Send presentation of locally attached sensors (and node if applicable)
- presentNode();
- }
- }
- bool connected = clients[i].connected();
- clientsConnected[i] = connected;
- allSlotsOccupied &= connected;
- }
- if (allSlotsOccupied && _ethernetServer.hasClient()) {
- //no free/disconnected spot so reject
- GATEWAY_DEBUG(PSTR("!GWT:TSA:NO FREE SLOT\n"));
- EthernetClient c = _ethernetServer.available();
- c.stop();
- }
- // Loop over clients connect and read available data
- for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++) {
- if (_readFromClient(i)) {
- setIndication(INDICATION_GW_RX);
- _w5100_spi_en(false);
- return true;
- }
- }
- #else /* Else part of MY_GATEWAY_ESP8266 || MY_GATEWAY_LINUX */
- // W5100/ENC module does not have hasClient-method. We can only serve one client at the time.
- EthernetClient newclient = _ethernetServer.available();
- // if a new client connects make sure to dispose any previous existing sockets
- if (newclient) {
- if (client != newclient) {
- client.stop();
- client = newclient;
- GATEWAY_DEBUG(PSTR("GWT:TSA:ETH OK\n"));
- _w5100_spi_en(false);
- gatewayTransportSend(buildGw(_msgTmp, I_GATEWAY_READY).set(MSG_GW_STARTUP_COMPLETE));
- _w5100_spi_en(true);
- presentNode();
- }
- }
- if (client) {
- if (!client.connected()) {
- GATEWAY_DEBUG(PSTR("!GWT:TSA:ETH FAIL\n"));
- client.stop();
- } else {
- if (_readFromClient()) {
- setIndication(INDICATION_GW_RX);
- _w5100_spi_en(false);
- return true;
- }
- }
- }
- #endif /* End of MY_GATEWAY_ESP8266 || MY_GATEWAY_LINUX */
- #endif /* End of MY_GATEWAY_CLIENT_MODE */
- _w5100_spi_en(false);
- return false;
- }
-
- MyMessage& gatewayTransportReceive(void)
- {
- // Return the last parsed message
- return _ethernetMsg;
- }
-
- #if !defined(MY_IP_ADDRESS) && !defined(MY_GATEWAY_ESP8266) && !defined(MY_GATEWAY_ESP32) && !defined(MY_GATEWAY_LINUX)
- void gatewayTransportRenewIP(void)
- {
- /* renew/rebind IP address
- 0 - nothing happened
- 1 - renew failed
- 2 - renew success
- 3 - rebind failed
- 4 - rebind success
- */
- static unsigned long next_time = hwMillis() + MY_IP_RENEWAL_INTERVAL_MS;
- unsigned long now = hwMillis();
-
- // http://playground.arduino.cc/Code/TimingRollover
- if ((long)(now - next_time) < 0) {
- return;
- }
- if (Ethernet.maintain() & ~(0x06)) {
- GATEWAY_DEBUG(PSTR("!GWT:TRC:IP RENEW FAIL\n"));
- /* Error occurred -> IP was not renewed */
- return;
- }
- _w5100_spi_en(false);
- next_time = now + MY_IP_RENEWAL_INTERVAL_MS;
- }
- #endif
|