Мега в качестве агрегатора с разных серверов

inspiritus
Offline
Зарегистрирован: 17.12.2012

Здравствуйте уважаемые коллеги.

ввиду невозможности редактирования стартового сообщения продолжу далее

inspiritus
Offline
Зарегистрирован: 17.12.2012

У меня есть 1-2-3-много серверов , поднятых на esp8266, которые отдают состояние датчиков в csv строке каждый. 


Неужели для каждого из них надо поднимать
char server1[] = "192.168.35.1";   
char server2[] = "192.168.35.2";  
char server3[] = "192.168.35.3";  
 
 
EthernetClient client1;
EthernetClient client2;
EthernetClient client3;......
И потом
if (client1.connect(server1, 80)).....
if (client2.connect(server2, 80)).....
if (client3.connect(server3, 80)).....
 
И еще
 
  if (client1.available()) .....
  if (client2.available()) .....
  if (client3.available()) .....
 
 
Оно конечно в таком варианте у меня заработало для опроса двух серверов, новсе это выглядит как то криво.
 
По общей структуре у меня агрегатор на меге в режиме сервера формирует и выдает web-страницу состояния оборудования.
 
И им же в режиме клиента я планировал опрашивать сервера на ESP8266 ( пытался сделать ссылку, но выстреливает на ардуино.ру так что просто адрес 77.37.208.83:8080).
 
 
Вот только неужели плодить простыню ?
Может быть найдется подсказка, как это сделать компактно?
Далее собственно кривой код для опроса двух серверов на базе стандартного примера.
 
#include <SPI.h>
#include <Ethernet.h>

#define W5100_RESET_PIN  3 // порт аппаратного сбоса сетевой платы
#define W5100_CS        10 // порт управления сетевой платой
#define SD_CS            4 // порт управления SD картой

//***************************var for ethernet**************************
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address

//****************************настройки для дома************************
byte ip[] = { 192, 168, 88, 233 }; // fixed IP addr in LAN
byte gateway[] = { 192, 168, 88, 254 }; // internet access via router
byte subnet[] = { 255, 255, 248, 0 }; //subnet mask
byte dnServer[] = {77,37,255,30};

char server1[] = "192.168.88.244";    // name address for Google (using DNS)
char server2[] = "192.168.88.245";    // name address for Google (using DNS)

// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
EthernetClient client1;
EthernetClient client2;
void resetLAN();                            //сброс и инициализация сетевой платы


unsigned long lastConnectionTime = 0;             // last time you connected to the server, in milliseconds
const unsigned long postingInterval = 10L * 1000L; // delay between updates, in milliseconds
// the "L" is needed to use long type numbers

void setup() {
  // start serial port:
  Serial.begin(115200);

  // give the ethernet module time to boot up:
  delay(1000);
    Serial.println("MyAjax_chiller_4_2");
// ********************** инициализация портов сетевой платы и SD карты **************  
  pinMode(W5100_CS, OUTPUT);       digitalWrite(W5100_CS,HIGH);
  pinMode(W5100_RESET_PIN, OUTPUT);digitalWrite(W5100_RESET_PIN,LOW);
  pinMode(SD_CS, OUTPUT);          digitalWrite(SD_CS,HIGH); 
//********************* запуск сетевых устройств и сервисов **************************
  resetLAN();

}

void loop() {

  if (client1.available()) {
    char c = client1.read();
    Serial.write(c);
  }

    if (client2.available()) {
    char d = client2.read();
    Serial.write(d);
  }


  if (millis() - lastConnectionTime > postingInterval) {
    httpRequest();
  }

}

// this method makes a HTTP connection to the server:
void httpRequest() {
  // close any connection before send a new request.
  // This will free the socket on the WiFi shield
  client1.stop();  client2.stop();

  // if there's a successful connection:
  if (client1.connect(server1, 80)) {
    Serial.println();
    Serial.println("connecting...");
    // send the HTTP PUT request:
    client1.println("GET /csv HTTP/1.1");
    client1.println("Connection: close");
    client1.println();

    // note the time that the connection was made:
    lastConnectionTime = millis();
  }
    else {
    // if you couldn't make a connection:
    Serial.println();
    Serial.println("connection client 1 failed");
  }
  if (client2.connect(server2, 80)) {
    Serial.println();
    Serial.println("connecting...");
    // send the HTTP PUT request:
    client2.println("GET /csv HTTP/1.1");
    client2.println("Connection: close");
    client2.println();

    // note the time that the connection was made:
    lastConnectionTime = millis();
  }
  else {
    // if you couldn't make a connection:
    Serial.println();
    Serial.println("connection client 2 failed");
  }
}

///////////////////////сброс и инициализация сетевой платы////////////////////////////////////
void resetLAN()
{
digitalWrite(W5100_RESET_PIN, LOW);  Serial.println("reset lock");
delay(100);
digitalWrite(W5100_RESET_PIN, HIGH);Serial.println("reset release");
delay(1000);
Ethernet.begin(mac,ip,dnServer,gateway,subnet);
delay(500);
  Serial.print("My IP adress: "); Serial.println(Ethernet.localIP());
  Serial.print("My gateway: "); Serial.println(Ethernet.gatewayIP());
  Serial.print("My subnet mask: "); Serial.println(Ethernet.subnetMask());
  Serial.print("My dns Server: "); Serial.println(Ethernet.dnsServerIP());
}

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Плодить объекты client не надо. Достаточно одному подсовывать в connect() разные адреса.

b707
Offline
Зарегистрирован: 26.05.2017

inspiritus пишет:
Вот только неужели плодить простыню ? Может быть найдется подсказка, как это сделать компактно?

два ключевых слова - массивы и циклв

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

А почему не наоборот? ESP собрала данные и скидывает на один единственный сервер, как в народном мониторинге например

inspiritus
Offline
Зарегистрирован: 17.12.2012

А у Вас нет ли примера , как подсовывать?

я конечно же попробовал по-очереди кидать запрос в один объект. Отрабатывает только первый, что закономерно, так как закинутый запрос потом некоторое время ждет отклика. 

Поразмышлял не у копьютера, получилась такая вот логика...

Видимо надо кидать запрос к певому серверу , выставлять флаг и ждать ответа от него, крутясь в луупе, по приходу обрабатывать и сбрасывать флаг. Так как у меня все запускается псевдопроцессами изитаймера, то раз в секунду например в псевдопроцессе анализировать, был ли обработан отклик и соответственно запрашивать следующий или перезапрашивать текущий.

ua6em- мне нужна возможность посмотреть каждую ESP по-отдельности.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

inspiritus пишет:

ua6em- мне нужна возможность посмотреть каждую ESP по-отдельности.

всё равно не понял, каждая ESP имеет свой ID в базе, по нему и смотреть можно

inspiritus
Offline
Зарегистрирован: 17.12.2012

Возможность зайти на нее браузером напрямую, без агрегатора необходима.

b707
Offline
Зарегистрирован: 26.05.2017

inspiritus пишет:

Возможность зайти на нее браузером напрямую, без агрегатора необходима.

и в чем противоречие?

sadman41
Offline
Зарегистрирован: 19.10.2016

Если вам нужны одновременные коннекты, то, конечно, без массива объектов client не обойтись - это так. Только можно поизящней их загнать в массив и при открытии нового соединения смотреть, какой из них свободен. Но тут уже не 10 строчек - более замороченная логика выходить.

 

inspiritus
Offline
Зарегистрирован: 17.12.2012

нуууу... пока как то так, пока без обработки таймаутов, но уже с перебором серверов 

наверно надо городить очередь статусов с учетом таймаутов в массиве структур.

#include <SPI.h>
#include <Ethernet.h>

#define W5100_RESET_PIN  3 // порт аппаратного сбоса сетевой платы
#define W5100_CS        10 // порт управления сетевой платой
#define SD_CS            4 // порт управления SD картой

//***************************var for ethernet**************************
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address

//****************************настройки для дома************************
byte ip[] = { 192, 168, 88, 233 }; // fixed IP addr in LAN
byte gateway[] = { 192, 168, 88, 254 }; // internet access via router
byte subnet[] = { 255, 255, 248, 0 }; //subnet mask
byte dnServer[] = {77,37,255,30};

char* server[2] = {"192.168.88.244","192.168.88.245"};
 

EthernetClient client;

struct condition
{
  uint32_t counter;
  float duration;
  float average; 
};
condition tpa_status[54];
boolean   flag_serv = LOW;
byte      line_counter = 0;

      unsigned long lastConnectionTime = 0;         // last time you connected to the server, in milliseconds
const unsigned long postingInterval = 4000;         // delay between updates, in milliseconds
//********************предОбъявление функций*******************
void resetLAN();                            //сброс и инициализация сетевой платы
void httpRequest();                         // создание коннекта к серверу
//---------------------------------------------------------------------------------------------------------------------------------------------------------------------
void setup() {

  Serial.begin(115200);
  delay(1000);
  Serial.println("AGregator multi client");
// ********************** инициализация портов сетевой платы и SD карты **************  
  pinMode(W5100_CS, OUTPUT);       digitalWrite(W5100_CS,HIGH);
  pinMode(W5100_RESET_PIN, OUTPUT);digitalWrite(W5100_RESET_PIN,LOW);
  pinMode(SD_CS, OUTPUT);          digitalWrite(SD_CS,HIGH); 
//********************* запуск сетевых устройств и сервисов **************************
  resetLAN();
//********************очистка таблицы статусов *********************************************** 

for (int i =0; i<54; i++){
  tpa_status[i].counter  = 0;
  tpa_status[i].duration = 0;
  tpa_status[i].average  = 0;
}
  httpRequest();
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------------------

void loop() {
  if (client.available()) {
    Serial.print("available ");Serial.println(server[line_counter]);
    flag_serv = LOW;
  while (client.available()){
    char c = client.read();
    Serial.write(c);
  }

  }


   if (!flag_serv) {
    if (millis() - lastConnectionTime > postingInterval)   {
        client.stop();
      ++line_counter;
      if (line_counter==2) line_counter=0;
      httpRequest();
    }
  }

}

///////////////////////посылка запроса к  очередному серверу////////////////////////////////////

void httpRequest() {

  if (client.connect(server[line_counter], 80)) {
    Serial.println();
    Serial.println("connecting...");
    // send the HTTP PUT request:
    client.println("GET /csv HTTP/1.1");
    client.println("Connection: close");
    client.println();
    flag_serv = HIGH;

    // note the time that the connection was made:
    lastConnectionTime = millis();
  }
    else {
    // if you couldn't make a connection:
    Serial.println();
         Serial.print("Connection ");Serial.print(server[line_counter]);Serial.println(" not available :(");
  }

}

///////////////////////сброс и инициализация сетевой платы////////////////////////////////////
void resetLAN()
{
digitalWrite(W5100_RESET_PIN, LOW);  Serial.println("reset lock");
delay(100);
digitalWrite(W5100_RESET_PIN, HIGH);Serial.println("reset release");
delay(1000);
Ethernet.begin(mac,ip,dnServer,gateway,subnet);
delay(500);
  Serial.print("My IP adress: "); Serial.println(Ethernet.localIP());
  Serial.print("My gateway: "); Serial.println(Ethernet.gatewayIP());
  Serial.print("My subnet mask: "); Serial.println(Ethernet.subnetMask());
  Serial.print("My dns Server: "); Serial.println(Ethernet.dnsServerIP());
}

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Можно так. Или сразу массив IPAddress завести. println() его умеет печатать. Али в массиве структур держать с дополнительными параметрами соединения - портом, логином, паролем...

const uint8_t servers[][4] = {
        {192, 168, 88, 244}, 
        {192, 168, 88, 245}
};


void loop() {
  
  IPAddress server;
....
  for (uint8_t = 0; i< 2; i++) {
     server = IPAddress(server[i]);
     if (client.connect(server, 80)) {
         ....
     }
  }
...

}

 

inspiritus
Offline
Зарегистрирован: 17.12.2012

Да, можно. Спасибо. Еоот массив ip пока для пробы. Но там вся подсетка моя и последняя триада совпадает с номером машины и счетчиком вызова. Потому массива дя ip не будет. Он станет вычисляться в порядке очередности вызовов. Пока придумываю кольцевой массив статусов и способы его перебора, обновления и использования. И для полноценного тестирования (раскопал еще две esp8266-12) спаяю еще два модуля. Четыре это уже можно отлаживать.

inspiritus
Offline
Зарегистрирован: 17.12.2012

Кстати вопрос немного не по теме, но рядом. Глюк кодировки передаваемой иде при компилляции.

Слетает кодировка вывода в браузер. В преамбуле html стоит utf-8. Однако после открытия программы и компилляции прут крякозябры. Но, если всю программу закинуть в notepad++ и прогнать в другую кодировки и обратнов utf, копипастнуть обратно в иде, то до следующего открытия в иде любой программы все отображается правильно.

может быть это где то в настройках лечится?

inspiritus
Offline
Зарегистрирован: 17.12.2012

Уважаемые коллеги! Извитеее что к Вам абращаюсь. Отзовитесь плз по кодировкам.  Оно конечно криво-обходится, но может быть это только я такой "криворуко-косоглазый" ? 

Лабел 1: Еще раз... загружаю исходник

компиллирую

сервер отдает в веб крякозябры. преамбула такая между start и end  впихивую что то непринципиальное

const String header_start =  "<!DOCTYPE html>"
                             "<html lang=\"ru\">"
                             "<head><title>HEGEL AGregator 1.0.2</title>"
                             "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">";
const String header_end = "</head><body>";                             

const String footer = "</body></html>\n";

копипастю весь код в нотепад ++ там перекодирую в анси и снова в ютф 

копипастю в иде

работаю нормально с правильным отображением в (как мимнимум) в хроме, компиллирую много раз ...

новая загрузка любого кода из файла в новое окно и снова на Лабел 1 :(

sadman41
Offline
Зарегистрирован: 19.10.2016

Я бы ответил, но русские символы не сую в прошивки, так что не в курсе.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

inspiritus пишет:

Лабел 1: Еще раз... загружаю исходник

компиллирую

сервер отдает в веб крякозябры. преамбула такая между start и end  впихивую что то непринципиальное

копипастю весь код в нотепад ++ там перекодирую в анси и снова в ютф 

копипастю в иде

работаю нормально с правильным отображением в (как мимнимум) в хроме, компиллирую много раз ...

А самому посмотреть, чем второй файл отличается от первого, религия не позволяет?

inspiritus
Offline
Зарегистрирован: 17.12.2012

в общем опрос как минимум 4-х модулей происходит стабильно.

данные с модулей отдаются в csv из трех показателей, на агрегаторе пасятся и распихиваются в массив структур.

#include <SPI.h>
#include <Ethernet.h>

#define W5100_RESET_PIN  3 // порт аппаратного сбоса сетевой платы
#define W5100_CS        10 // порт управления сетевой платой
#define SD_CS            4 // порт управления SD картой

#define REQ_BUF_SZ   80
//***************************var for ethernet**************************
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address

//****************************настройки для дома************************
byte ip[] = { 192, 168, 88, 233 }; // fixed IP addr in LAN
byte gateway[] = { 192, 168, 88, 254 }; // internet access via router
byte subnet[] = { 255, 255, 248, 0 }; //subnet mask
byte dnServer[] = {78,55,256,38};
//***************************настройки для клиента**********************
char* server[4] = {"192.168.88.244","192.168.88.245","192.168.88.246","192.168.88.247"};
 
char req_index = 0; 
char HTTP_req[REQ_BUF_SZ] = {0};
String req;

EthernetClient client;

struct condition
{
  uint32_t counter;
  float duration;
  float average;
  uint16_t avail; 
};
condition tpa_status[54];
boolean   flag_serv = LOW;
byte      line_counter = 0;

byte commaIndex =0;
byte secondCommaIndex = 0;
String firstValue;
String secondValue;
String thirdValue;

      unsigned long lastConnectionTime = 0;         // last time you connected to the server, in milliseconds
const unsigned long postingInterval = 3500;         // delay between updates, in milliseconds
//********************предОбъявление функций*******************
void resetLAN();                            //сброс и инициализация сетевой платы
void httpRequest();                         // создание коннекта к серверу
void StrClear(char *str, char length);
char StrContains(char *str, char *sfind);
//---------------------------------------------------------------------------------------------------------------------------------------------------------------------
void setup() {

  Serial.begin(115200);
  delay(1000);
  Serial.println("AGregator multi client");
// ********************** инициализация портов сетевой платы и SD карты **************  
  pinMode(W5100_CS, OUTPUT);       digitalWrite(W5100_CS,HIGH);
  pinMode(W5100_RESET_PIN, OUTPUT);digitalWrite(W5100_RESET_PIN,LOW);
  pinMode(SD_CS, OUTPUT);          digitalWrite(SD_CS,HIGH); 
//********************* запуск сетевых устройств и сервисов **************************
  resetLAN();
//********************очистка таблицы статусов *********************************************** 

for (int i =0; i<54; i++){
  tpa_status[i].counter  = 0;
  tpa_status[i].duration = 0;
  tpa_status[i].average  = 0;
  tpa_status[i].avail    = 0;
}
  httpRequest();
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------------------

void loop() {
  if (client.available()) {
    flag_serv = LOW;
  while (client.available()){
    char c = client.read();
                if (req_index < (REQ_BUF_SZ - 1)) {
                    HTTP_req[req_index] = c;     
                    req_index++;
                           }
  }
          if (StrContains(HTTP_req, "Content-Type")) {
                    req_index = 0;
                    StrClear(HTTP_req, REQ_BUF_SZ);
          } 
          else {
                    req = String(HTTP_req);
                    commaIndex = req.indexOf(',');
                    secondCommaIndex = req.indexOf(',', commaIndex+1);
                    firstValue = req.substring(0,commaIndex);
                    secondValue = req.substring(commaIndex+1 ,secondCommaIndex );
                    thirdValue = req.substring(secondCommaIndex+1 );
                    tpa_status[line_counter].counter  = firstValue.toInt();   
                    tpa_status[line_counter].duration = secondValue.toFloat();
                    tpa_status[line_counter].average  = thirdValue.toFloat();                
                    Serial.print(server[line_counter]);Serial.print(" ");
                    Serial.print(tpa_status[line_counter].counter);Serial.print(" ");
                    Serial.print(tpa_status[line_counter].duration);Serial.print(" ");
                    Serial.print(tpa_status[line_counter].average);Serial.print(" ");
                    Serial.print(tpa_status[line_counter].avail);Serial.println();
                    req_index = 0;
                    StrClear(HTTP_req, REQ_BUF_SZ); 
          }
  }


   if (!flag_serv) {
    if (millis() - lastConnectionTime > postingInterval)   {
      ++line_counter;
      if (line_counter==4) line_counter=0;
      client.stop();
      httpRequest();
    }
  }

}

///////////////////////посылка запроса к  очередному серверу////////////////////////////////////

void httpRequest() {

  if (client.connect(server[line_counter], 80)) {
    Serial.println();
//    Serial.println("connecting...");
    // send the HTTP PUT request:
    client.println("GET /csv HTTP/1.1");
    client.println("Connection: close");
    client.println();
    flag_serv = HIGH;
    // note the time that the connection was made:
    lastConnectionTime = millis();
  }
    else {
    // if you couldn't make a connection:
    Serial.println();
    Serial.print("Connection ");Serial.print(server[line_counter]);Serial.println(" not available :(");
    tpa_status[line_counter].avail++;
  }

}

///////////////////////сброс и инициализация сетевой платы////////////////////////////////////
void resetLAN()
{
digitalWrite(W5100_RESET_PIN, LOW);  Serial.println("reset lock");
delay(100);
digitalWrite(W5100_RESET_PIN, HIGH);Serial.println("reset release");
delay(1000);
Ethernet.begin(mac,ip,dnServer,gateway,subnet);
delay(500);
  Serial.print("My IP adress: "); Serial.println(Ethernet.localIP());
  Serial.print("My gateway: "); Serial.println(Ethernet.gatewayIP());
  Serial.print("My subnet mask: "); Serial.println(Ethernet.subnetMask());
  Serial.print("My dns Server: "); Serial.println(Ethernet.dnsServerIP());
}
/////////////////////очистка строки/////////////////////////////////////////////////////////

void StrClear(char *str, char length)
{
    for (int i = 0; i < length; i++) {
        str[i] = 0;
    }
}

////////////////поиск в строке подстроки///////////////////////////////////////////////////
// returns 1 if string found
// returns 0 if string not found
char StrContains(char *str, char *sfind)
{
    char found = 0;
    char index = 0;
    char len;

    len = strlen(str);
    
    if (strlen(sfind) > len) {
        return 0;
    }
    while (index < len) {
        if (str[index] == sfind[found]) {
            found++;
            if (strlen(sfind) == found) {
                return 1;
            }
        }
        else {
            found = 0;
        }
        index++;
    }

    return 0;
}



Также получилось на одной меге поднять сервер, отдающий страницу агрегированных данных с модулей из этого массива, клиента NTP, запись в лог на SF, выгрузку лога и его обнуление, но это уже другая история.

По кодировке выяснилось, что IDE периодически меняет кодировку на  UTF-9 (BOM), перекодирование в ноте++ этот глюк исправляет. 

и еще одна особенность. если в программе используется обращение по UDP к ntp серверам, то перед запросом необходимо гасить клиент принудительно       client.stop();