Помогите разобраться с кодом - Часы на ESP 8266 и матрице max 7219

Pavelkp
Offline
Зарегистрирован: 11.07.2019

Доброго времени суток! Прошу помочь разобраться  с кодом.

Нашел в инете проект часов на esp 8266 и матрицах с max 7219, повторил его, все получилось, все работает, кроме получения курса валют( в оригинале пара PLN/USD). С момента выхода проекта прошло уже прилично времени и скорей всего изменилась структура сайта с которого берется курс валют и в коде нужно указать другие ориентиры на сайте где расположена искомая информация. В общем если подскажите как правильно настроить поиск нужных символов, где и что указать что бы код заработал буду очень Вам признателен! Вот этот код, который находит на сайте курс нужной  валюты и выводит на дисплей часов:

// =======================================================================

const char* currencyHost = "cinkciarz.pl";
void getCurrencyRates()
{
  WiFiClientSecure client;
  Serial.print("connecting to "); Serial.println(currencyHost);
  if (!client.connect(currencyHost, 443)) {
    Serial.println("connection failed");
    return;
  }
  client.print(String("GET / HTTP/1.1\r\n") +
               "Host: " + currencyHost + "\r\nConnection: close\r\n\r\n");

  //Serial.print("request sent");
  int repeatCounter = 0;
  while (!client.available() && repeatCounter < 10) {
    delay(500);
    Serial.println("c.");
    repeatCounter++;
  }
  Serial.println("connected");
  while (client.connected() && client.available()) {
    String line = client.readStringUntil('\n');
    //      Serial.println(line);
    int currIdx = line.indexOf("/kantor/kursy-walut-cinkciarz-pl/usd");
    if (currIdx > 0) {
      String curr = line.substring(currIdx + 33, currIdx + 33 + 3);
      curr.toUpperCase();
      line = client.readStringUntil('\n');
      int rateIdx = line.indexOf("\">");
      if (rateIdx <= 0) {
        Serial.println("Found rate but wrong structure!");
        return;
      }
      currencyRates = "        PLN/" + curr + ": ";
      if (line[rateIdx - 1] == 'n') currencyRates += char('~'+24); else currencyRates += char('~'+23); // down/up
      currencyRates += line.substring(rateIdx + 2, rateIdx + 8) + " ";

      line = client.readStringUntil('\n');
      rateIdx = line.indexOf("\">");
      if (rateIdx <= 0) {
        Serial.println("Found rate but wrong structure!");
        return;
      }
      if (line[rateIdx - 1] == 'n') currencyRates += char('~'+24); else currencyRates += char('~'+23); // down/up
      currencyRates += line.substring(rateIdx + 2, rateIdx + 8);
      currencyRates.replace(',', '.');
      break;
    }
  }
  client.stop();
}

// =======================================================================

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Я бы Вам посоветовал брать курс валюты не с левого польского сайта, а переделать на Центробанк (если Вы в России) или на Ваш банк, в котором Вы обслуживаетесь.

Pavelkp
Offline
Зарегистрирован: 11.07.2019

Да тут собственно не в валюте дело, а хочу именно понять как находить нужную инфу на сайте. Вот закусило, хочу добиться что бы часы показывали курс валют с этого сайта(раньше это ведь работало), пока не очень понимаю как и к чему привязываться при поиске нужных символов на сайте. Вернее понятно что нужно находить какой то уникальный кусок кода, который ближе всего к искомому участку, но вот как это правильно сделать не могу додуматься.. Если знаете как подскажите, буду благодарен.

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

"Хочу добиться" и "подскажите где исправить" - две большие разницы.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Pavelkp пишет:

хочу добиться 

Так добивайтесь на сайте своего банка точно также, как на том.

Делается это просто.

1. Закачайте нужную Вам страницу в браузер
2. Включите "Просмотр кода страницы". Вы увидите текст страницы, которая Вам нужна. Именно такой текст Вы получите при скачивании страницы в ардуино.
3. Изучите текст. найдите нужную Вам информацию.
4. Выявите признаки в тексте страницы, по которым Вы сможете найти нужное (например, нужная мне информация находится в div c id=rate)
5. Только после этого пишите программу. Программа такая: скачали текст с сайта, ищите там установленные признаки и по ним извлекаете нужную информацию.

Если Вы неуверенно работаете со строками, даже не надейтесь сделать это быстро. Для начала придётся стать специалистом по обработке строк. Но всё решаемо.

Pavelkp
Offline
Зарегистрирован: 11.07.2019

текст в строке к которой можно привязаться я нахожу в общем то без проблем  если это все в одной строке:

int currIdx = line.indexOf("rate-sell");
    if (currIdx > 0) {
      String curr = line.substring(currIdx + 1, currIdx + 1 + 3);

Но как быть если сам искомый текст на следующей строке и там кроме этих цифр больше нет ничего:

<strong class="rate-sell">   (привязка для поиска)
                                3,9086    (искомый текст)                        </strong>

может как то можно эти строки объединить в одну ...или тут нужен другой метод? что нужно  сделать что бы найти эти цифры (3,9086) ?

 

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

А при чем здесь вообще строки?

Разбирайте текст по мере поступления без разбивки на строки.

Pavelkp
Offline
Зарегистрирован: 11.07.2019

Как?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Молча. Чего Вы привязались к строкам - дались они Вам? Вы ведь текст читаете не строками, а посимвольно - сплошным потоком. Перевод строки - такой же символ, как и остальные, вот и обрабатывайте его как остальные.

Pavelkp
Offline
Зарегистрирован: 11.07.2019

Я полагал что именно строками текст читается (как вижу в браузере).... не понял про перевод  строки, можете на приведенном мною коде показать как это сделать, что бы наглядно было? 

.... Вроде раздел для начинающих....

если бы я знал как это сделать не спрашивал бы..... :(

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Pavelkp пишет:

(как вижу в браузере)....

И что Вы видите в браузере? Последовательность символов. Там где в последовательности встречается символ перевода строки, там для Вашего удобства происходит переход на новую строку. Не более того. А сам по себе символ перевода строки - такой же символ, как и все остальные.

Pavelkp пишет:
Я полагал что именно строками текст читается

Это Вы его читаете строками функцией client.readStringUntil потому, что Вам так удобнее. Внутри она читает посимвольно (посмотрите в её код). И Вам никто не мешает читать посимвольно. Для этого в библиотеке есть специальный метод. Какой? Я не знаю, т.к. Вы не привели полного кода, и я понятия не имею какую библиотеку Вы используете. Но метод посимвольного чтения есть в любой.

Pavelkp
Offline
Зарегистрирован: 11.07.2019

Спасибо большое за ответ..... пробелы  - это тоже символы, их тоже нужно учитывать правильно?

Вот полный код часов:

/*
  ESP-01 pinout from top:
  
  GND    GP2 GP0 RX/GP3
  TX/GP1 CH  RST VCC

  MAX7219
  ESP-1 from rear
  Re Br Or Ye
  Gr -- -- --

  USB to Serial programming
  ESP-1 from rear, FF to GND, RR to GND before upload
  Gr FF -- Bl
  Wh -- RR Vi

  GPIO 2 - DataIn
  GPIO 1 - LOAD/CS
  GPIO 0 - CLK

  ------------------------
  NodeMCU 1.0 pinout:

  D8 - DataIn
  D7 - LOAD/CS
  D6 - CLK
  
*/


#include "Arduino.h"
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>

WiFiClient client;

String weatherMain = "";
String weatherDescription = "";
String weatherLocation = "";
String country;
int humidity;
int pressure;
float temp;
float tempMin, tempMax;
int clouds;
float windSpeed;
String date;

String currencyRates;
String weatherString;

#define NUM_MAX 6

// for ESP-01 module
//#define DIN_PIN 2 // D4
//#define CS_PIN  3 // D9/RX
//#define CLK_PIN 0 // D3

// for NodeMCU 1.0
#define DIN_PIN 15  // D8
#define CS_PIN  13  // D7
#define CLK_PIN 12  // D6

#include "max7219.h"
#include "fonts.h"

// =======================================================================
// CHANGE YOUR CONFIG HERE:
// =======================================================================
const char* ssid     = "yourssid";     // SSID of local network
const char* password = "yourpasswd";   // Password on network
String weatherKey = "yourAPIKEY";
String weatherLang = "&lang=en";
String cityID = "756135"; //Warsaw
// read OpenWeather api description for more info
// =======================================================================

void setup() 
{
  Serial.begin(115200);
  initMAX7219();
  sendCmdAll(CMD_SHUTDOWN,1);
  sendCmdAll(CMD_INTENSITY,0);
  Serial.print("Connecting WiFi ");
  WiFi.begin(ssid, password);
  printStringWithShift("Connecting",15);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected: "); Serial.println(WiFi.localIP());
}
// =======================================================================
#define MAX_DIGITS 16
byte dig[MAX_DIGITS]={0};
byte digold[MAX_DIGITS]={0};
byte digtrans[MAX_DIGITS]={0};
int updCnt = 0;
int dots = 0;
long dotTime = 0;
long clkTime = 0;
int dx=0;
int dy=0;
byte del=0;
int h,m,s;
// =======================================================================
void loop()
{
  if(updCnt<=0) { // every 10 scrolls, ~450s=7.5m
    updCnt = 10;
    Serial.println("Getting data ...");
    printStringWithShift("   Getting dat",15);
    getWeatherData();
    getCurrencyRates();
    getTime();
    Serial.println("Data loaded");
    clkTime = millis();
  }
 
  if(millis()-clkTime > 15000 && !del && dots) { // clock for 15s, then scrolls for about 30s
    printStringWithShift(date.c_str(),40);
    printStringWithShift(currencyRates.c_str(),35);
    printStringWithShift(weatherString.c_str(),40);
    updCnt--;
    clkTime = millis();
  }
  if(millis()-dotTime > 500) {
    dotTime = millis();
    dots = !dots;
  }
  updateTime();
  showAnimClock();
}

// =======================================================================

void showSimpleClock()
{
  dx=dy=0;
  clr();
  showDigit(h/10,  0, dig6x8);
  showDigit(h%10,  8, dig6x8);
  showDigit(m/10, 17, dig6x8);
  showDigit(m%10, 25, dig6x8);
  showDigit(s/10, 34, dig6x8);
  showDigit(s%10, 42, dig6x8);
  setCol(15,dots ? B00100100 : 0);
  setCol(32,dots ? B00100100 : 0);
  refreshAll();
}

// =======================================================================

void showAnimClock()
{
  byte digPos[6]={0,8,17,25,34,42};
  int digHt = 12;
  int num = 6; 
  int i;
  if(del==0) {
    del = digHt;
    for(i=0; i<num; i++) digold[i] = dig[i];
    dig[0] = h/10 ? h/10 : 10;
    dig[1] = h%10;
    dig[2] = m/10;
    dig[3] = m%10;
    dig[4] = s/10;
    dig[5] = s%10;
    for(i=0; i<num; i++)  digtrans[i] = (dig[i]==digold[i]) ? 0 : digHt;
  } else
    del--;
  
  clr();
  for(i=0; i<num; i++) {
    if(digtrans[i]==0) {
      dy=0;
      showDigit(dig[i], digPos[i], dig6x8);
    } else {
      dy = digHt-digtrans[i];
      showDigit(digold[i], digPos[i], dig6x8);
      dy = -digtrans[i];
      showDigit(dig[i], digPos[i], dig6x8);
      digtrans[i]--;
    }
  }
  dy=0;
  setCol(15,dots ? B00100100 : 0);
  setCol(32,dots ? B00100100 : 0);
  refreshAll();
  delay(30);
}

// =======================================================================

void showDigit(char ch, int col, const uint8_t *data)
{
  if(dy<-8 | dy>8) return;
  int len = pgm_read_byte(data);
  int w = pgm_read_byte(data + 1 + ch * len);
  col += dx;
  for (int i = 0; i < w; i++)
    if(col+i>=0 && col+i<8*NUM_MAX) {
      byte v = pgm_read_byte(data + 1 + ch * len + 1 + i);
      if(!dy) scr[col + i] = v; else scr[col + i] |= dy>0 ? v>>dy : v<<-dy;
    }
}

// =======================================================================

void setCol(int col, byte v)
{
  if(dy<-8 | dy>8) return;
  col += dx;
  if(col>=0 && col<8*NUM_MAX)
    if(!dy) scr[col] = v; else scr[col] |= dy>0 ? v>>dy : v<<-dy;
}

// =======================================================================

int showChar(char ch, const uint8_t *data)
{
  int len = pgm_read_byte(data);
  int i,w = pgm_read_byte(data + 1 + ch * len);
  for (i = 0; i < w; i++)
    scr[NUM_MAX*8 + i] = pgm_read_byte(data + 1 + ch * len + 1 + i);
  scr[NUM_MAX*8 + i] = 0;
  return w;
}

// =======================================================================
int dualChar = 0;

unsigned char convertPolish(unsigned char _c)
{
  unsigned char c = _c;
  if(c==196 || c==197 || c==195) {
    dualChar = c;
    return 0;
  }
  if(dualChar) {
    switch(_c) {
      case 133: c = 1+'~'; break; // 'ą'
      case 135: c = 2+'~'; break; // 'ć'
      case 153: c = 3+'~'; break; // 'ę'
      case 130: c = 4+'~'; break; // 'ł'
      case 132: c = dualChar==197 ? 5+'~' : 10+'~'; break; // 'ń' and 'Ą'
      case 179: c = 6+'~'; break; // 'ó'
      case 155: c = 7+'~'; break; // 'ś'
      case 186: c = 8+'~'; break; // 'ź'
      case 188: c = 9+'~'; break; // 'ż'
      //case 132: c = 10+'~'; break; // 'Ą'
      case 134: c = 11+'~'; break; // 'Ć'
      case 152: c = 12+'~'; break; // 'Ę'
      case 129: c = 13+'~'; break; // 'Ł'
      case 131: c = 14+'~'; break; // 'Ń'
      case 147: c = 15+'~'; break; // 'Ó'
      case 154: c = 16+'~'; break; // 'Ś'
      case 185: c = 17+'~'; break; // 'Ź'
      case 187: c = 18+'~'; break; // 'Ż'
      default:  break;
    }
    dualChar = 0;
    return c;
  }    
  switch(_c) {
    case 185: c = 1+'~'; break;
    case 230: c = 2+'~'; break;
    case 234: c = 3+'~'; break;
    case 179: c = 4+'~'; break;
    case 241: c = 5+'~'; break;
    case 243: c = 6+'~'; break;
    case 156: c = 7+'~'; break;
    case 159: c = 8+'~'; break;
    case 191: c = 9+'~'; break;
    case 165: c = 10+'~'; break;
    case 198: c = 11+'~'; break;
    case 202: c = 12+'~'; break;
    case 163: c = 13+'~'; break;
    case 209: c = 14+'~'; break;
    case 211: c = 15+'~'; break;
    case 140: c = 16+'~'; break;
    case 143: c = 17+'~'; break;
    case 175: c = 18+'~'; break;
    default:  break;
  }
  return c;
}

// =======================================================================

void printCharWithShift(unsigned char c, int shiftDelay) {
  c = convertPolish(c);
  if (c < ' ' || c > '~'+25) return;
  c -= 32;
  int w = showChar(c, font);
  for (int i=0; i<w+1; i++) {
    delay(shiftDelay);
    scrollLeft();
    refreshAll();
  }
}

// =======================================================================

void printStringWithShift(const char* s, int shiftDelay){
  while (*s) {
    printCharWithShift(*s, shiftDelay);
    s++;
  }
}

// =======================================================================

const char *weatherHost = "api.openweathermap.org";

void getWeatherData()
{
  Serial.print("connecting to "); Serial.println(weatherHost);
  if (client.connect(weatherHost, 80)) {
    client.println(String("GET /data/2.5/weather?id=") + cityID + "&units=metric&appid=" + weatherKey + weatherLang + "\r\n" +
                "Host: " + weatherHost + "\r\nUser-Agent: ArduinoWiFi/1.1\r\n" +
                "Connection: close\r\n\r\n");
  } else {
    Serial.println("connection failed");
    return;
  }
  String line;
  int repeatCounter = 0;
  while (!client.available() && repeatCounter < 10) {
    delay(500);
    Serial.println("w.");
    repeatCounter++;
  }
  while (client.connected() && client.available()) {
    char c = client.read(); 
    if (c == '[' || c == ']') c = ' ';
    line += c;
  }

  client.stop();

  DynamicJsonBuffer jsonBuf;
  JsonObject &root = jsonBuf.parseObject(line);
  if (!root.success())
  {
    Serial.println("parseObject() failed");
    return;
  }
  //weatherMain = root["weather"]["main"].as<String>();
  weatherDescription = root["weather"]["description"].as<String>();
  weatherDescription.toLowerCase();
  //  weatherLocation = root["name"].as<String>();
  //  country = root["sys"]["country"].as<String>();
  temp = root["main"]["temp"];
  humidity = root["main"]["humidity"];
  pressure = root["main"]["pressure"];
  tempMin = root["main"]["temp_min"];
  tempMax = root["main"]["temp_max"];
  windSpeed = root["wind"]["speed"];
  clouds = root["clouds"]["all"];
  String deg = String(char('~'+25));
  weatherString = "         Temp: " + String(temp,1) + deg + "C (" + String(tempMin,1) + deg + "-" + String(tempMax,1) + deg + ")  ";
  weatherString += weatherDescription;
  weatherString += "  Humidity: " + String(humidity) + "%  ";
  weatherString += "  Pressure: " + String(pressure) + "hPa  ";
  weatherString += "  Clouds: " + String(clouds) + "%  ";
  weatherString += "  Wind: " + String(windSpeed,1) + "m/s                 ";
}

// =======================================================================

const char* currencyHost = "cinkciarz.pl";
void getCurrencyRates()
{
  WiFiClientSecure client;
  Serial.print("connecting to "); Serial.println(currencyHost);
  if (!client.connect(currencyHost, 443)) {
    Serial.println("connection failed");
    return;
  }
  client.print(String("GET / HTTP/1.1\r\n") +
               "Host: " + currencyHost + "\r\nConnection: close\r\n\r\n");

  //Serial.print("request sent");
  int repeatCounter = 0;
  while (!client.available() && repeatCounter < 10) {
    delay(500);
    Serial.println("c.");
    repeatCounter++;
  }
  Serial.println("connected");
  while (client.connected() && client.available()) {
    String line = client.readStringUntil('\n');
    //      Serial.println(line);
    int currIdx = line.indexOf("/kantor/kursy-walut-cinkciarz-pl/usd");
    if (currIdx > 0) {
      String curr = line.substring(currIdx + 33, currIdx + 33 + 3);
      curr.toUpperCase();
      line = client.readStringUntil('\n');
      int rateIdx = line.indexOf("\">");
      if (rateIdx <= 0) {
        Serial.println("Found rate but wrong structure!");
        return;
      }
      currencyRates = "        PLN/" + curr + ": ";
      if (line[rateIdx - 1] == 'n') currencyRates += char('~'+24); else currencyRates += char('~'+23); // down/up
      currencyRates += line.substring(rateIdx + 2, rateIdx + 8) + " ";

      line = client.readStringUntil('\n');
      rateIdx = line.indexOf("\">");
      if (rateIdx <= 0) {
        Serial.println("Found rate but wrong structure!");
        return;
      }
      if (line[rateIdx - 1] == 'n') currencyRates += char('~'+24); else currencyRates += char('~'+23); // down/up
      currencyRates += line.substring(rateIdx + 2, rateIdx + 8);
      currencyRates.replace(',', '.');
      break;
    }
  }
  client.stop();
}

// =======================================================================

float utcOffset = 2;
long localEpoc = 0;
long localMillisAtUpdate = 0;

void getTime()
{
  WiFiClient client;
  if (!client.connect("www.google.com", 80)) {
    Serial.println("connection to google failed");
    return;
  }

  client.print(String("GET / HTTP/1.1\r\n") +
               String("Host: www.google.com\r\n") +
               String("Connection: close\r\n\r\n"));
  int repeatCounter = 0;
  while (!client.available() && repeatCounter < 10) {
    delay(500);
    //Serial.println(".");
    repeatCounter++;
  }

  String line;
  client.setNoDelay(false);
  while(client.connected() && client.available()) {
    line = client.readStringUntil('\n');
    line.toUpperCase();
    if (line.startsWith("DATE: ")) {
      date = "     "+line.substring(6, 22);
      h = line.substring(23, 25).toInt();
      m = line.substring(26, 28).toInt();
      s = line.substring(29, 31).toInt();
      localMillisAtUpdate = millis();
      localEpoc = (h * 60 * 60 + m * 60 + s);
    }
  }
  client.stop();
}

// =======================================================================

void updateTime()
{
  long curEpoch = localEpoc + ((millis() - localMillisAtUpdate) / 1000);
  long epoch = round(curEpoch + 3600 * utcOffset + 86400L) % 86400L;
  h = ((epoch  % 86400L) / 3600) % 24;
  m = (epoch % 3600) / 60;
  s = epoch % 60;
}

// =======================================================================

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, вот, смотрите, Вы используете класс WiFiClientSecure, унаследованный в конечно итоге от Stream. Значит, Вам доступны все (от слова "все") функции чтения и записи, которые есть, например, в Serial. Поэтому, если Вам нужно читать посимвольно, то и читайте себе client.read(), никто Вам не запрещает.

Я бы на Вашем месте, сначала отложил бы в сторонку большой скетч, а взял бы скачанную страницу (как текстовую константу) и научился бы выделять из неё то, что нужно в отдельной программе. А потом бы уже готовый вытаскивальщик встроил бы в большой скетч.

Pavelkp
Offline
Зарегистрирован: 11.07.2019

Понял, спасибо Вам огромное. Я в принципе так и сделал - маленький скетч, который  получает код с сайта.

Тут еще проблемка возникла - Вы советовали брать курc например с сайта сбербанка, а там он(сайт) на скриптах( на сайтах других банков картина аналогичная) и в коде я вообще ничего не смог найти по валюте, т.е на обычной странице браузера есть курс а в html коде нет ничего даже близко, я так понимаю что это типа защиты от ботов что ли, и тут видимо придется искать сайты где это можно получить данные по Api. Это как то можно обойти? я так понимаю тут вариант делать запрос как то через браузер или отсылать  запрос на сервер так, как это делает браузер?

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

Pavelkp пишет:

Это как то можно обойти? я так понимаю тут вариант делать запрос как то через браузер или отсылать  запрос на сервер так, как это делает браузер?

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

 

Pavelkp
Offline
Зарегистрирован: 11.07.2019

Понял, спасибо большое за ответ. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Pavelkp пишет:

Тут еще проблемка возникла - Вы советовали брать курc например с сайта сбербанка, а там он(сайт) на скриптах( на сайтах других банков картина аналогичная) и в коде я вообще ничего не смог найти по валюте

Ну, тут разные способы есть. Всё зависит от того, что Вам нужно. Если только курсы типа "примерно", так их можно получить через MT4 (это система для торговцев на Форексе) API и парсить ничего не надо.

Если же нужен конкретный банк, то нужно посмотреть как именно он их рисует. Если он рисует скриптами, надо смотреть на эти скрипты - возможно, там тупой json запрос, так кто Вам мешает сделать такой же? В общем, если нужен конкретный банк, то надо на него и смотреть.

Вот, например, ЦБ РФ (https://www.cbr.ru/currency_base/daily/ ) делает всё просто, посмотрите на текст - там голимая таблица прямо сидит.

Сбербанк заполняет на лету, но без защиты, т.е. расковырять можно, но уже сложнее, чем у ЦБ.

Какой банк Вас интересует?

Pavelkp
Offline
Зарегистрирован: 11.07.2019

Мне нужно получать реальный курс рубля к доллару.. ну можно еще курс евро .....получается видимо это ЦБ РФ

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, с ЦБ там всё просто - видели их страницу? Справитесь? Или помощь нужна?

Pavelkp
Offline
Зарегистрирован: 11.07.2019

Да, здесь с сайтом ЦБ получилось все без проблем, даже по сути исходный код править особо не пришлось, высчитал только расположение нужных символов и все работает.... спасибо Вам огромное за помощь!!!!!

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Не за что.