Часы на ESP-01(NodeMCU) + MAX7219 + WEB интерфейс

Тема в разделе "Глядите, что я сделал", создана пользователем IvanUA, 28 авг 2017.

?

Вы повторили это проект?

  1. Нет (просто ознакомился)

    8 голосов
    10,0%
  2. Да, один раз (попробовал)

    28 голосов
    35,0%
  3. Да, не однократно

    44 голосов
    55,0%
Можно выбрать сразу несколько вариантов.
  1. SergeiL

    SergeiL Гуру

    Утилита - Arduino IDE 1.8.5
    "Прошивка" своя, написана на Си.
     
  2. kolkapetkinsyn

    kolkapetkinsyn Нерд

    А посоветовать хорошую прошивку можете?
     
  3. Deniskyn

    Deniskyn Гик

    На первой странице, в первом посте ссылка на гитхаб.... =)
     
  4. OPER

    OPER Нуб

    На прошивке LED_clock_weath_v10.08.2018 часы перестали подключаться к Wi-Fi сети, перепрошивка и перезагрузка не помогает
     
  5. IvanUA

    IvanUA Гуру

    В процедуре подключения к сети есть delay - можно конечно ним поиграться. Но, я бы в первую очередь при смене прошивки, рекомендовал почистить ЕСПху. Только вчера столкнулся с глюками при заливке новой версии. Ничего не мог сделать, пока не вытер всю память.
     
    Valerchik нравится это.
  6. OPER

    OPER Нуб

    Не знаю как почистить , подскажите
     
  7. IvanUA

    IvanUA Гуру

    Я чищу так
    [​IMG][​IMG]
     
    Valerchik нравится это.
  8. Valerchik

    Valerchik Нерд

    У меня прошивке v10.08.2018 часы перестали подключаться к скрытой SSID Wi-Fi сети, (SSID имя беспроводной сети) а на v30.05.2018 подключается.
    Где в коде поправить не найду.
     
  9. IvanUA

    IvanUA Гуру

    Обновил версию на гитхабе. Там немного переделал процедуру подключения к wifi. Попробуйте, возможно все поправится)))
     
    Юра 80 и Valerchik нравится это.
  10. BenBen

    BenBen Нерд

    Собрал часики на NodeMCU 1.0, хочу поделиться опытом.

    Поставил все датчики для чистоты эксперимента – DHT22, BME280, Si7021, DS18B20, баззер пассивный.

    Версия скетча 10.08.2018

    Скомпилировать сразу не удалось конечно, версия библиотеки ArduinoJSON былa 6я по умолчанию. Откатил до 5й и часики заработали.

    Время с NTP сервером ntp.time.in.ua синхронизировалось быстро, но отличалось от моего, часовая зона другая.
    Зашел на веб сервер часов, поменял часовую зону, все сработало.

    Но обнаружил, что не работает прогноз погоды от openweathermap.org.

    Зарегистрировался на этом сервере, получил API Key. Вставил через web interface. Поставил код города.
    Но все равно, в сериал мониторе выдает ошибки как на текущую, так и на завтрашнюю погоду

    Начал разбираться с сервером погоды. Вывел в Serial.print строки обращения к серверу.
    Обнаружил, что на запрос текущей погоды сервер выдает нормальный JSON, а на запрос прогноза ругается на неправильный API Key.
    Мне это показалось странным, они при регистрации писали, что мой ключ должен работать и для прогноза.

    Посмотрел документацию api.openweathermap.org, и обнаружил, что часы обращаются к неправильному URL для прогноза!

    Правильный URL должен быть такого вида:
    http://api.openweathermap.org/data/2.5/forecast?id=2643741&units=metric&appid=хххххххх&lang=en&cnt=4

    А часы запрашивают прогноз по другому адресу:
    http://api.openweathermap.org/data/2.5/forecast/daily/?id=…

    Может у них раньше был такой адрес, но сейчас он точно не работает. Более того, вместо ошибки 404 он ругается на неправильный API Key.

    Я попробовал вообще случайные адреса, например такой:

    https://api.openweathermap.org/data/2.5/weather2kuku

    И сервер выдал свою стандартную ошибку:
    {"cod":401, "message": "Invalid API key. Please see http://openweathermap.org/faq#error401 for more info."}

    Хотя в данном случае такой страницы у них точно нет, и правильно было бы выдать ошибку 404.

    В общем, дурят они программистов, как хотят.

    Ну ладно, заменил я строку обращения к серверу для прогноза. Стал выдавать нормальный JSON. Проверил, как работает параметр cnt.
    Он задает количество погодных блоков с интервалом 3 часа. То есть, чтобы получить прогноз на 12 часов вперед, нужно задать cnt=4.
    Потом пропустить первые 3 блока при парсинге, и извлечь параметры только из 4го блока.

    Глянул я на этот JSON code от сервера, а там сам черт ногу сломит. Какие-то массивы со вложенными объектами, полный разнобой в элементах.
    Не знаю, зачем они так сделали, все тоже самое можно было передать одним обычным массивом. Наверное, чтобы программистам жизнь медом не казалась.

    Вот и Ивану, наверно, не показалась, судя по его борьбе с квадратными скобками в коде обработки :)

    Но в конце-концов есть библиотека ArduinoJSON, которая и предназначена для борьбы с таким безобразием. Раз парсинг проходит без ошибок, значит должен быть способ вытащить нужные элементы.

    Зашел я на сайт разработчика этой библиотеки, и обнаружил там очень интересную страничку:

    https://arduinojson.org/v5/assistant/

    Это автоматический генератор кода для обработки JSON. Достаточно скопипастить туда ответ ot api.openweathermap.org, и он моментально генерирует код для его парсинга! Работает мгновенно и без обращения к серверу, все сделано локально на JavaScript. Я это определил отключив WiFi у себя на компе,
    страничка продолжала работать. Пробовал грузить его JSONom с разных источников, все скушал и не поперхнулся. В общем, разработчик библиотеки код писать умеет. Непонятно только, зачем он в 6й версии библиотеки поменял названия функций и поломал совместимость с предыдущими версиями. Может, конечно, он со своей девушкой в этот день поругался…

    Код этот я использовал, изменил функции getWeatherData() и getWeatherDataz() в коде Ивана. Теперь и текущая погода и прогноз работают нормально.

    Могу выложить эти функции, если кому интересно…
     
    Юра 80 и Belyj нравится это.
  11. IvanUA

    IvanUA Гуру

    Читая такие строки, мне не вольно в голову приходят слова Сергея Третьякова - "А вот эти строки написаны мной лично".
    И действительно в этом есть смысл. В коде, очень многие строки написаны мной лично, но вот в целом проект - я все одно считаю так сказать "народным".
    Я как человек разумный (обычный) не стал изобретать велосипед от гайки до колеса, а взял разные части кода (идеи) и собрал их одну большую кучу, ну и естественно немного модифицировал.
    Одно время я даже пытался найти автора этих идей (частей кода). Но поверьте - эта задача не самая легкая.
    Сама идея такого рода часов несомненно принадлежит Марселю. По крайней мере с его кода все это безобразие началось))) Но Марсель тоже как человек разумный не стал изобретать велосипед, а почерпнул идеи из других источников...
    Единственное кого я нашел так сказать первым автором - это был РенатК. Первоначальный веб интерфейс был взят из его кода. Но сейчас в вебе остался только яваскрипт - немного модифицированный.
    Так что - в общем и целом код таки "народный"))))))
    Конечно давайте. С удовольствием добавлю это в наш проект.
    Дело в том, что я и многие здесь - используют так сказать "старые" ключи для погоды. И сервер на эти ключи исправно выдает свою собственную версию джейсон ответа. Меньше конечно повезло тем стал обладателями "новых" ключей... Тут проблема до конца не решена. Но возможно решится с вашей помощью.
     
    Юра 80 нравится это.
  12. BenBen

    BenBen Нерд

    функции getWeatherData() и getWeatherDataz() сейчас выглядят у меня так:
    =============================================================================================================================//
    // БЕРЕМО ПОГОДУ З САЙТУ api.openweathermap.org //
    //===============================================================================================================================//
    void getWeatherData() {
    if(!WIFI_connected) {
    updateForecast++;
    if(updateForecast >= 360) weatherString = tWeatrNot;
    return;
    }
    if(printCom) {
    printTime();
    Serial.println("Getting weather forecast for City " + cityID + "...");
    }
    if(ESPclient.connect(weatherHost.c_str(), 80)) {
    ESPclient.println(String("GET /data/2.5/weather?id=") + cityID + "&units=metric&appid=" + weatherKey + "&lang="+ weatherLang + "\r\n" +
    "Host: " + weatherHost + "\r\nUser-Agent: ArduinoWiFi/1.1\r\n" +
    "Connection: close\r\n\r\n");
    //iottaman 20181023 - debug
    if(printCom) {Serial.print("weather server HTTP request: ");
    Serial.println(String("GET /data/2.5/weather?id=") + cityID + "&units=metric&appid=" + weatherKey + "&lang="+ weatherLang + "\r\n" +
    "Host: " + weatherHost + "\r\nUser-Agent: ArduinoWiFi/1.1\r\n" +
    "Connection: close\r\n\r\n");
    }
    } else {
    if(printCom) Serial.println("No connection to weather server!!!");
    updateForecast++;
    if(updateForecast >= 360) weatherString = tWeatrNot;
    return;
    }
    //---------------If connection to weather server is OK and HTTP request sent, check for response (waiting for 0.5 sec x 10 times) -------
    //Якщо було підключення та запрос було відправлено, перевіряємо чи маємо відповідь (чекаємо 0,5с * 10 разів)------
    int repeatCounter = 0;
    while(!ESPclient.available() && repeatCounter < 10) {
    delay(500);
    if(printCom) {Serial.print(repeatCounter);Serial.print(" ");}
    repeatCounter++;
    }
    if (printCom) Serial.println();
    //---------------Якщо було підключення то була получена відповідь, то в змінну line2 записуємо отримані символи --
    //---------------Store server response in string variable line2 for parsing --
    String line2 = ESPclient.readStringUntil('\r');
    if(printCom) {Serial.print("Current weather JSON: ");Serial.println(line2);}
    //---------------Stop WiFi client------------------------------------------------------------------------------
    ESPclient.stop();
    //---------------Create json buffer and parse data from variable line2--------------------------------------------------
    const size_t bufferSize = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(12) + 400;
    DynamicJsonBuffer jsonBuf(bufferSize);
    //DynamicJsonBuffer jsonBuf;
    JsonObject& root=jsonBuf.parseObject(line2);
    if(!root.success()){
    if(printCom) Serial.println("Parsing current weather failed!!!");
    updateForecast++;
    if(updateForecast >= 360) weatherString = tWeatrNot;
    return;
    }

    JsonObject& weather0 = root["weather"][0];
    const char* weather0_main = weather0["main"]; // "Clouds"
    const char* weather0_description = weather0["description"]; // "broken clouds"
    weatherMain = weather0_main;
    weatherDescription = weather0_description;
    JsonObject& main = root["main"];
    temp = main["temp"];
    pressure = main["pressure"];
    humidity = main["humidity"];
    int visibility = root["visibility"]; //visibility in meters, could be added to weatherString
    windSpeed = root["wind"]["speed"];
    windDeg = root["wind"]["deg"];
    clouds = root["clouds"]["all"];
    //long dt = root["dt"]; // date/time in UNIX format
    weatherDescription.toLowerCase();
    if(lang!=5) convertWeatherDes();

    JsonObject& sys = root["sys"];
    const char* sys_country = sys["country"];
    country = sys_country;
    //long sys_sunrise = sys["sunrise"]; // sunrise and sunset times in UNIX format
    //long sys_sunset = sys["sunset"]; //also could be added to weather string
    const char* placeName = root["name"];
    cityName = placeName;
    cityId = root["id"];
    convertCity();
    String windDegString;
    if(windDeg >= 345 || windDeg <= 22) windDegString = "\211"; //"Північний";
    if(windDeg >= 23 && windDeg <= 68) windDegString = "\234"; //"Північно-східний";
    if(windDeg >= 69 && windDeg <= 114) windDegString = "\230"; //"Східний";
    if(windDeg >= 115 && windDeg <= 160) windDegString = "\235"; //"Південно-східний";
    if(windDeg >= 161 && windDeg <= 206) windDegString = "\210"; //"Південний";
    if(windDeg >= 207 && windDeg <= 252) windDegString = "\232"; //"Південно-західний";
    if(windDeg >= 253 && windDeg <= 298) windDegString = "\231"; //"Західний";
    if(windDeg >= 299 && windDeg <= 344) windDegString = "\233"; //"Північно-західний";
    weatherString = " " + cityName + ", " + tNow + ": \212 " + String(temp, 0) + ("\202") + "C";
    weatherString += " \213 " + String(humidity) + "%";
    weatherString += " \215 " + String(pressure, 0) + tPress;
    weatherString += " \214 " + windDegString + String(windSpeed, 1) + tSpeed;
    weatherString += " \216 " + String(clouds) + "% " + weatherDescription + " ";
    if(printCom) {Serial.print("WeatherString = ");Serial.println(weatherString);}
    updateForecast = 0;
    }
    // =======================================================================//
    // Беремо ПРОГНОЗ!!! погоди з сайту openweathermap.org //
    // =======================================================================//
    void getWeatherDataz() {
    if(!WIFI_connected) {
    updateForecasttomorrow++;
    if(updateForecast >= 360) weatherStringZ = "";
    return;
    }
    if(printCom) printTime();
    if(printCom) Serial.println("Getting weather forecast for tomorrow...");
    if(ESPclient.connect(weatherHost.c_str(), 80)) {
    //set cnt=4 for forecast up to 3hr x 4 = 12 hours
    ESPclient.println(String("GET /data/2.5/forecast?id=") + cityID + "&units=metric&appid=" + weatherKey + "&lang=" + weatherLang + "&cnt=4" + "\r\n" +
    "Host: " + weatherHost + "\r\nUser-Agent: ArduinoWiFi/1.1\r\n" +
    "Connection: close\r\n\r\n");
    } else {
    if(printCom) Serial.println("No server connection for tomorrow!!!");
    updateForecasttomorrow++;
    if(updateForecast >= 360) weatherStringZ = "";
    return;
    }
    int repeatCounter = 0;
    while(!ESPclient.available() && repeatCounter < 10) {
    delay(500);
    repeatCounter++;
    }
    //iottaman 20181023 get JSON line correctly like in current weather
    String line2 = ESPclient.readStringUntil('\r');
    if(printCom) {Serial.print("Weather forecast JSON: ");Serial.println(line2);}
    ESPclient.stop();
    const size_t bufferSize = 4*JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(4) + 9*JSON_OBJECT_SIZE(1) + 5*JSON_OBJECT_SIZE(2) + 5*JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + 3*JSON_OBJECT_SIZE(7) + 5*JSON_OBJECT_SIZE(8) + 1370;
    DynamicJsonBuffer jsonBuf(bufferSize);
    JsonObject& root = jsonBuf.parseObject(line2);
    if(!root.success()){
    if(printCom) Serial.println("Parsing weather forecast for tomorrow failed!!!");
    updateForecasttomorrow++;
    if(updateForecast >= 360) weatherStringZ = "";
    return;
    }
    //open array list and select 4th element for 12-hour weather forecast (each list element is 3 hours ahead)
    JsonArray& list = root["list"];
    JsonObject& list3 = list[3];
    JsonObject& list3_main = list3["main"];
    float tempZ = list3_main["temp"];
    float tempMin = list3_main["temp_min"];
    float tempMax = list3_main["temp_max"];

    JsonObject& list3_weather0 = list3["weather"][0];
    const char* list3_weather0_description = list3_weather0["description"]; // "light rain"
    weatherDescription = list3_weather0_description;
    float wSpeed = list3["wind"]["speed"];
    float wDegF = list3["wind"]["deg"];
    int wDeg = int(wDegF);
    if(printCom) {
    Serial.print("tempMin = ");Serial.println(tempMin);
    Serial.print("tempMax = ");Serial.println(tempMax);
    Serial.print("wSpeed = ");Serial.println(wSpeed);
    Serial.print("wDeg = ");Serial.println(wDeg);
    }
    weatherDescription.toLowerCase();
    if(lang!=5) convertWeatherDes();
    String wDegString;
    if(wDeg >= 345 || wDeg <= 22) wDegString = "\211"; //"Північний";
    if(wDeg >= 23 && wDeg <= 68) wDegString = "\234"; //"Північно-східний";
    if(wDeg >= 69 && wDeg <= 114) wDegString = "\230"; //"Східний";
    if(wDeg >= 115 && wDeg <= 160) wDegString = "\235"; //"Південно-східний";
    if(wDeg >= 161 && wDeg <= 206) wDegString = "\210"; //"Південний";
    if(wDeg >= 207 && wDeg <= 252) wDegString = "\232"; //"Південно-західний";
    if(wDeg >= 253 && wDeg <= 298) wDegString = "\231"; //"Західний";
    if(wDeg >= 299 && wDeg <= 344) wDegString = "\233"; //"Північно-західний";
    weatherStringZ = tTom + ": \212" + String(tempMin, 1) + "...." + String(tempMax, 1) + "\202" + "C";
    weatherStringZ = weatherStringZ + " \214 " + wDegString + String(wSpeed, 1) + tSpeed + " " + weatherDescription + " ";
    if(printCom) {Serial.println("Getting weather forecast for tomorrow is OK.");
    Serial.println(weatherStringZ);
    }
    updateForecasttomorrow = 0;
    }
     
    Юра 80 и saha74 нравится это.
  13. Valerchik

    Valerchik Нерд

    Залил прошивку v25.10.2018, опять завис на +85.0 как на v10.08.2018 до впайки резистора на 10к на кнопку будильника. Выпайка резистора, чистка или заливка пустой прошивки в флешь не принесла положительного результата. Пропал весь прогноз погоды на всех парошивках что после заливал на эти часы, мигают обе точки. На вторых часах весь прогноз работает.
     
    Последнее редактирование: 26 окт 2018
  14. IvanUA

    IvanUA Гуру

    Функция работы с кнопкой на обоих версиях одна и та же. Так что должно работать.
    Попробуйте почистить ЕСП к примеру таким же методом что я писал выше.
    Позже залейте бинарник что есть в этой версии.
     
    Valerchik нравится это.
  15. Deniskyn

    Deniskyn Гик

    Подтверждаю, все работает что с сенсорной кнопкой что с обычной тактовой...
     
  16. Valerchik

    Valerchik Нерд

    Сразу стирал в FLASH_DOWNLOAD_TOOLS_V3.6.4, как то бистро в ней стирает флешь.
    Заливал пустой банарник blank_4000KB.bin в ESP8266Flasher_64 .
    Все это много раз и пере заливкой другой прошивки.
    Почему погоду перестало показывать на всех прошивках?
     
    Последнее редактирование: 26 окт 2018
  17. IvanUA

    IvanUA Гуру

    Вы прошивку компилите или льете бинарник. Можете ответить?
     
    Valerchik нравится это.
  18. IvanUA

    IvanUA Гуру

    Ну вот собственно я залил скетч с вашим вариантом прогноза погоды.
    Сразу скажу что ИП у меня старый - который еще работает на том коде что в часах.
    Но не суть... Ответ от сервера я получаю. И скажу вам как бы... нормальный ответ
    Код (Text):
    // Заголовок джейсон ответа
    {"cod":"200",
    "message":0.0025,
    "cnt":4,
    "list":[
    // ---------- Первый блок ответа
    {"dt":1540587600,
    "main":{
        "temp":7.29,
        "temp_min":7.29,
        "temp_max":8.6,
        "pressure":984.99,
        "sea_level":1020.06,
        "grnd_level":984.99,
        "humidity":75,
        "temp_kf":-1.3},
    "weather":[{
        "id":804,
        "main":"Clouds",
        "description":"overcast clouds",
        "icon":"04n"}],
    "clouds":{"all":92},
    "wind":{"speed":5.23,"deg":261.005},
    "sys":{"pod":"n"},
    "dt_txt":"2018-10-26 21:00:00"},
    // ---------- Второй блок ответа еще +3 часа
    {"dt":1540598400,
    "main":{
        "temp":6.97,
        "temp_min":6.97,
        "temp_max":7.94,
        "pressure":985.14,
        "sea_level":1020.42,
        "grnd_level":985.14,
        "humidity":84,
        "temp_kf":-0.98},
    "weather":[{
        "id":500,
        "main":"Rain",
        "description":"light rain",
        "icon":"10n"}],
    "clouds":{"all":92},
    "wind":{"speed":4.32,"deg":269.503},
    "rain":{"3h":0.195},
    "sys":{"pod":"n"},
    "dt_txt":"2018-10-27 00:00:00"},
    // ---------- Третий блок ответа +6 чаов
    {"dt":1540609200,
    "main":{
        "temp":5.9,
        "temp_min":5.9,
        "temp_max":6.55,
        "pressure":985.37,
        "sea_level":1020.62,
        "grnd_level":985.37,
        "humidity":87,
        "temp_kf":-0.65},
    "weather":[{
        "id":500,
        "main":"Rain",
        "description":"light rain",
        "icon":"10n"}],
    "clouds":{"all":88},
    "wind":{"speed":3.96,"deg":284.004},
    "rain":{"3h":0.455},
    "sys":{"pod":"n"},
    "dt_txt":"2018-10-27 03:00:00"},
    // ---------- Четвертый блок ответа    то что просили в cnt
    {"dt":1540620000,
    "main":{
        "temp":4.9,
        "temp_min":4.9,
        "temp_max":5.23,
        "pressure":985.91,
        "sea_level":1021.34,
        "grnd_level":985.91,
        "humidity":83,
        "temp_kf":-0.33},
    "weather":[{
        "id":500,
        "main":"Rain",
        "description":"light rain",
        "icon":"10n"}],
    "clouds":{"all":76},
    "wind":{"speed":3.83,"deg":271},
    "rain":{"3h":0.015},
    "sys":{"pod":"n"},
    "dt_txt":"2018-10-27 06:00:00"}],
    // ---------- Ну и собственно хвост джейсон ответа
    "city":{
        "id":3220898,
        "name":"Landkreis Würzburg",
        "coord":{"lat":49.7731,"lon":9.9033},
        "country":"DE"}
    }
    Почему же не могу распарсить?
     
  19. BenBen

    BenBen Нерд

    Я убрал комменты и скопипастил ваш JSON в https://arduinojson.org/v5/assistant/ - он все обработал без ошибок и выдал такой же код, как и у меня был. По идее моя версия парсинга должна работать. У меня работает устойчиво уже 2 дня.
    А чем вы парсите, и что при этом происходит?
     
  20. IvanUA

    IvanUA Гуру

    А можно поподробнее что имеется ввиду чем парсю?