Управление rgb лентой Arduino NodeMcu через wifi на VB.net

Управление rgb лентой Arduino NodeMcu через wifi на VB.net

В данной статье хочу показать как я работал с RGB светодиодной лентой на базе Arduino NodeMcu с возможностью управления с компьютера через Wi-Fi. Я покажу как написать скетч под nodemcu, и как написать программу для управления ардуинкой на vb.net. Мой коллега собрал светильник на ардуино, собственно его видео и будет нашей отправной точкой. Я взял у него собранный им светильник, переписал скетч, добавил свои функции и разработал программу для управления через компьютер. Видео как собрать светильник можно посмотреть тут.

Что же хотим получить?

  • возможность управлять rgb лентой ардуинкой
  • возможность подключения к ардуинке с помощью wifi
  • настроить передачи пакетов по протоколу UDP
  • создать пользовательские команды для управления лентой
  • возможность сохранять настройки во внутренюю память 

Взяв за основу сборку из видео выше, я решил провести такие улучшения:

Первый апгрейт. Если на данный момент, чтобы подключиться к светильнику нужно было телефон(ПК) подключить к wifi cамого устройства, то я поставил задачу сделать возможным без переключения. Ведь это не очень удобно - ваш телефон, к примеру дома, подключен к вашему домашнему роутеру, или как обычно ваш компьютер, то манипуляции с переключением, для включения или изменения режима освещения, тратит ваше время. Я добавлю возможность подключения ардуино к вашему роутеру. Сделаю возможность сохранения соединения к домашнему вайфай.

Следующий апгрейт. Возможность изменения режима. Мне пришло в голову, сделал я светильник, нашел ему место и вот через некоторое время мне надоели режимы, которые были заданы в скетче. Что делать? Дописывать скетч новыми режимами и перепрошивать? Можно, а что если я это светильник уже высоко прикрутил и мне лень лезть и снимать его. Да, можно решить проблему перепрошивкой по wi-fi, но я решил написать универсальные функции, которые позволят создавать любой режим в программе на моем ПК.

Ну и по мелочи я добавил возможность подключения не через ip адрес, а через хост. Первоначально я думал, что просто добавлю несколько функций и все, но в результате скетч получился практически совсем иным, переписано было практически все.

 

Содержание:

  1. Как работать с NodeMcu.
  2. Алгоритм работы Arduino c rgb лентой.
  3. Как подключить arduino к Wi-Fi точке(роутеру).
  4. Как сохранить данные во внутренней памяти arduino.
  5. Как принять и распарсить принятую команду.
  6. Создаем пользовательские команды управления arduino
  7. Как задать цвет свечения rgb лентой по wi-fi.
  8. Как создать эффект свечения rgb ленты по wi-fi.
  9. Как создать пользовательский сложный эффект свечения rgb ленты.

 

Как работать с NodeMcu?

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

 

Алгоритм работы Arduino c rgb лентой.

Как же работать с rgb лентой? - довольно просто, мы будем работать с пинами. В скетче мы определим 3 пина: красный, зеленый, синий. Посредством команды analogWrite.  Есть небольшое отличие работы данной команды для ардуино и для nodemcu. Дело в том, что функция analogWrite принимает два значения - номер пина, период рабочего цикла. Так вот для arduino максимальное значение 255, а для nodemcu в нашей сборке 1024, т.е значение полного сигнала в нашем случае будет значение 1024, а не 255. Для каждого цвета мы напишем короткие функции, которые принимают значение аналоговой величины. Есть такой момент, в нашей сборке значение полного цикла будет 0, т.е чтобы выключить например красный цвет, нам нужно вызвать команду analogWrite(pin,1024), а чтоб включить analogWrite(pin,0). Т.е у нас вышла некая инверсия значений. Исходя из этого мы объявим наши пины и напишем такие функции:

#define LED_R 14 // Pin D11 --> красный
#define LED_G 12 // Pin D10 --> зеленый
#define LED_B 13 // Pin D9 --> синий

void LightRed(int s) {
  analogWrite (LED_R, abs(s - 1024));
}
void LightGreen(int s) {
  analogWrite (LED_G, abs(s - 1024));
}
void LightBlue(int s) {
  analogWrite (LED_B, abs(s - 1024));
}

Как подключить arduino к Wi-Fi точке(роутеру)

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

void ConnectWiFiRouter() {
  String client_ssid ;
  String client_pass ;

    WiFi.begin(client_ssid.c_str(), client_pass.c_str());
    //в течении 10 секунд пытаемся подключиться к wi-fi
    for (int i = 1; i < 21; i++) {
      if (WiFi.status() == WL_CONNECTED) {
        break;
      }
      delay(500);
      Serial.print(".");
    }


}

Данную функцию мы еще допишем, т.к. нам нужно сделать еще сохранение ssid и пароля. Мы сделали заготовку, для подключения nodemcu к нашей точке. Но мы также оставляем возможность подключение к ардуинке как к точке, именно по такому соединению мы будем отправлять команду для подключения к нужной нам вай фай.

Как сохранить данные во внутренней памяти Arduino nodemcu

Для данной задачи мы будем использовать несколько команд: EEPROM.read и EEPROM.write. Сохранять мы будет не только логин и пароль к вай фай, мы будем сохранять: wifi, пользовательские настройки переходов. Для этого напишем несколько функций:

  • Запись в память EEPROM
  • Чтение из памяти EEPROM
  • Очистка памяти EEPROM

Отдельно хочу сказать об очистке, она обязательно нужна и ее нужно будет вызывать каждый раз перед записью. Запись происходит по байтам, каждый байт я буду называть ячейкой. Очистка у нас будет своя, мы не будем обнулять байты, мы будем записывать в каждую ячейку свой символ "#", так нам будет проще и это будет нагляднее. Я определил три диапазона памяти, с которой мы будем работать: с 20 ячейки по 90 мы будем хранить данные для подключения к wifi, с 90-100 данные пользовательского перехода и с 100-2000 будем хранить сами пользовательские переходы, думаю нам этого хватит. После этого пишем код для наших функций:

//***************************************************
void ClearEprom(int s1, int s2) {
  EEPROM.begin(2000);
  for (int i = s1; i < s2; i++) {
    EEPROM.write(i, 35);
  }
  EEPROM.commit();
  EEPROM.end();
}

//***************************************************
void EEPROM_writeAnything(int ee, String val) {
  EEPROM.begin(2000);
  byte *text;
  int len;
  len = val.length() + 1;
  unsigned char* buf = new unsigned char[len];

  val.getBytes(buf, len, 0);

  for (int i = 0; i < len; i++) {
    int s;
    s = ee + i;
    byte sim;
    sim = byte(buf[i]);
    EEPROM.write(s, sim);
  }

  EEPROM.commit();
  EEPROM.end();
}

//***************************************************
String ReadEprom(int s, int count) {
  EEPROM.begin(2000);
  String txt;
  unsigned int i;
  for ( i = s; i < (s + count - 1); i++) {
    if (EEPROM.read(i) == 35) {
      break;
    }
    txt += char(EEPROM.read(i));
  }
  EEPROM.commit();
  EEPROM.end();
  return txt;
}

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

Как принять и распарсить принятую команду

Для этого мы напишем две функции: функцию принятие данных и функцию парсинга. Первая будет вызывать из функции цикла Loop и записывать в глобальную переменную "Str" принятые данные. Вторая будет парсить команды и вызывать соответствующую функцию. Парсить будем функцией "strtok".

void loop ()  {
  getStr();
  if (Str != "0") ParseCommand();
}
//***************************************************
void getStr() {
  int cb = udp.parsePacket();
  if (!cb) {}
  else {
    udp.read(packetBuffer, NTP_PACKET_SIZE);
    Serial.println(packetBuffer);
    String req = (packetBuffer);

    Serial.print("Request: ");
    Serial.println(req);
    Str = req;
  }
  memset(packetBuffer, 0, NTP_PACKET_SIZE);  //очищаем буфер для приема следующей команды
}
//***************************************************--ParseCommand
void ParseCommand() {
  String command = "";
  //преобразовываем строку в нужным нам тип
  Str.toCharArray(buf, 127);// Str - переменная в которой хранится пришедший пакет
  command = String( strtok (buf, " "));
  Serial.println("command: " + command + "(" + Str + ")");
  if (command == "save") {
    SaveComand(); //Сохраняем и подключаемся к Wi-Fi
  } else if (command == "LightRGB") {
    LightRGB(); //задаем цвет свечения
  } else if (command == "LightFade") {
    LightFade(); //задаем потухание/появление
  } else if (command == "LightUser") {
    FadeUser();
  }  else {
    Str = "0"; // очищаем
  }
}

Ниже я объясню, что за команды и что они будут делать

Создаем пользовательские команды управления arduino

Для необходимого решения я определил следующие команды:

  • "save" - команда сохранения wifi соединения, формат отправки "save ssid pass",  где ssid - название точки, pass - пароль wi-fi
  • "LightRGB" - команда задание цвета и яркости свечения, формат отправки "LightRGB A Red Green Blue", где A - яркость, RGB - значение цветов()
  • "LightFade" - команда перехода из одного цвета к другому, формат отправки "LightFade a_start, R_Start,G_Start,B_Start,a_end, R_End,G_End,B_End,Delay1, Inv, step,Delay2", где задается значение первоначального цвета и конечного цвет, в формате LightRGB, inv - при значении 1 переход будет переходит из первого цвета ко второму и обратно, step - шаг перехода(плавность), чем больше тем быстрее, при большей плавности шаг ставится меньше, delay1 - значение таймаута при переходе(влияет на скорость плавности), delay2 - задержка после перехода от одного цвета к другому
  • "LightUser" - данная команда дает возможность задание перехода из нескольких цветов, например если вы захотите сделать переливы из 7 цветов. Формат отправки "LightUser a_start, R_Start,G_Start,B_Start,a_end, R_End,G_End,B_End,Delay1, Inv, step,Delay2, id", все параметры как и у команды LightFade, но еще один параметр id - порядковый номер цвета

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

//***************************************************--SaveComand
void SaveComand() {

  Serial.println("SaveComand");
  String _ssid = "";
  String _pass = "";
  _ssid = String(strtok (NULL, " "));
  _pass = String(strtok (NULL, " "));
  //Serial.println("ssid: " + _ssid);
  //Serial.println("_pass: " + _pass);
  SaveConfigWifi(_ssid, _pass);
  ConnectWiFiRouter();
  Str = "0";
  Serial.println("EndSaveComand");
}

Мы парсим значение имени точки и пароля, записываем в переменные. Далее мы вызываем функцию сохранение данных в память и конектимся. Обязательно очищаем нашу глобальную переменную. Для наглядности, я пишу "0", можно обнулять просто Str="". Как говорилось выше мы дописываем функцию подключения к wi-fi. Также мы пишем функцию переподключения wifi модуляи UDP сервера.

void ConnectWiFiRouter() {
  String client_ssid ;
  String client_pass ;

  client_ssid = ReadEprom(20, 20);  //считываем из памяти точку wi-fi
  client_pass = ReadEprom(40, 20);  //считываем из памяти пароль wi-fi

  Serial.print("client_ssid:");
  Serial.println(client_ssid);
  Serial.print("pass_ssid:");
  Serial.println(client_pass);
  if (client_ssid != "" && client_pass != "") {
    Serial.print("Connect to wi-fi:");
    WiFi.begin(client_ssid.c_str(), client_pass.c_str());
    //в течении 10 секунд пытаемся подключиться к wi-fi
    for (int i = 1; i < 21; i++) {
      if (WiFi.status() == WL_CONNECTED) {
        break;
      }
      delay(500);
      Serial.print(".");
    }

    if (WiFi.status() != WL_CONNECTED) {
      ResetInitWifi();
    }
    Serial.println("");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }
}
//***************************************************
void SaveConfigWifi(String ssid, String pass) {
  ClearEprom(20, 90); //очищаем на всякий случай наши ячейки памяти
  EEPROM_writeAnything(20, ssid);
  EEPROM_writeAnything(40, pass);
}
void ResetInitWifi() {
  WiFi.mode(WIFI_OFF); //вырубаем WIFI
  WiFi.mode(WIFI_AP_STA);

  WiFi.softAP(ssid, password, 8, 0); //Стартуем точку доступа с заданными ssid и password, на 8 канале (не мешаться другим)
  IPAddress myIP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(myIP);
  Serial.println("HTTP server started");
  Serial.println("Starting UDP");
  udp.begin(localPort); // инициализируем UDP

  Serial.print("Local port: ");
  Serial.println(udp.localPort());

  pinMode( LED_R, OUTPUT );
  pinMode( LED_G, OUTPUT );
  pinMode( LED_B, OUTPUT );
  analogWrite (LED_R, 1023);
  analogWrite (LED_G, 1023);
  analogWrite (LED_B, 1023);
}

Как задать цвет свечения rgb лентой по wi-fi.

Мы определили команду и параметры приема для задания цвета ленты. Для удобства мы будем принимать значения в стандартном виде RGBA(100,255,255,255). Яркость у нас определяется значением пин, т.е. если мы дадим команду analogWrite (LED_R, 255), то у нас загорится красный цвет и яркость будет равна 25%. Зная это мы будем приводить наш стандартный вид к виду удобному arduino. Для этого нам необходимо принимаемое значение цвета(по стандарту максимум 255) умножить на 4 и умножить на процент яркости(параметр A). Отсюда получается - нам нужна функция, которая распарсит 4 параметра(A,Red,Green,Blue), приведет их в необходимый вид для ардуино и зажжет нужный цвет. Получается на стороне платы мы работаем с тремя значениями RGB, а яркость уходит в значение цвета. Потом обнуляем переменную с принимаемыми значениями.


void LightRGB() {
  Serial.println("LightRGBComand");
  int re = 0;
  int gr = 0;
  int bl = 0;
  int a = 100;
  a = String((strtok (NULL, " "))).toInt();
  re = String((strtok (NULL, " "))).toInt();
  gr = String((strtok (NULL, " "))).toInt();
  bl = String((strtok (NULL, " "))).toInt();
  if (a > 100)a = 100;
  if (a < 0)a = 0;

  if (re >= 255 || re < 0 ) re = 256;
  if (gr >= 255 || gr < 0 ) gr = 256;
  if (bl >= 255 || bl < 0 ) bl = 256;

  re = round(float(re) * 4 * float(a) / 100);
  gr = round(float(gr) * 4 * float(a) / 100);
  bl = round(float(bl) * 4 * float(a) / 100);

  Serial.println("alfa: " + String(a));
  Serial.println("LED_R: " + String(re));
  Serial.println("LED_G: " + String(gr));
  Serial.println("LED_B: " + String(bl));
  LightRed(re);
  LightGreen(gr);
  LightBlue(bl);
  Str = "0";
  Serial.println("EndLightRGBComand");
}

В начале функции и в конце я делаю вывод на порт. При отладке будет видно, что команда дошла и функция завершилась. Внутри функции также вывожу измененные значения. После отладке эти строки можно удалить.

Как задать эффект свечения rgb ленты по wi-fi.

Теперь создадим функцию для перехода от одного цвета к другому. Данная функция должна распарсить параметры двух цветов, шаг перехода, возможность обратного перехода и параметр задержки между ними. В итоге у нас 12 параметров. Немного хочу объяснить сам алгоритм эффекта перехода rgb ленты.

Мы должны создать цикл, условие выхода у него будет когда значение первого цвета будет равным значению второго цвета. Для этого перед циклом мы объявим переменную - метку, которую будем использовать в цикле. Также мы объявим переменные для текущего цвета, именно они будут изменяться внутри цикла и их будем отправлять при вызове функции задании цветов.Перед циклом присвоим им значение первоначального цвета. В цикле нам необходимо по отдельности проверять каждый цвет. При проверке нам нужно будет проверять несколько условий:

  • Нужно ли изменить значение,если текущее значение цвета равняется значению второго цвета, то в таком случае ничего не делаем.
  • Нужно определить, что делать со значением, убавлять или прибавлять, для этого мы проверяем что больше - значение первого цвета или второго. Меняем значение на значение шага.
  • Последнее условие, так как шаг может быть любым, нужна проверка, чтоб значение не перескочило значение второго цвета, в противном случае значение меняем на значение второго цвета. Это условие предотвращает бесконечный цикл и задание иного цвета от заданного.

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


//***************************************************--LightFade
void LightFade() {
  Serial.println("LightFadeComand");
  int re1 = 0;
  int gr1 = 0;
  int bl1 = 0;
  int a1 = 100;
  int re2 = 0;
  int gr2 = 0;
  int bl2 = 0;
  int a2 = 100;
  int delay1 = 0;
  int delay2 = 0;
  int inv = 1;
  int _step = 1;
  a1 = String((strtok (NULL, " "))).toInt();
  re1 = String((strtok (NULL, " "))).toInt();
  gr1 = String((strtok (NULL, " "))).toInt();
  bl1 = String((strtok (NULL, " "))).toInt();
  a2 = String((strtok (NULL, " "))).toInt();
  re2 = String((strtok (NULL, " "))).toInt();
  gr2 = String((strtok (NULL, " "))).toInt();
  bl2 = String((strtok (NULL, " "))).toInt();
  delay1 = String((strtok (NULL, " "))).toInt();
  inv = String((strtok (NULL, " "))).toInt();
  _step = String((strtok (NULL, " "))).toInt();
  delay2 = String((strtok (NULL, " "))).toInt();

  if (a1 > 100)a1 = 100;
  if (a1 < 0)a1 = 0;
  if (a2 > 100)a2 = 100;
  if (a2 < 0)a2 = 0;

  if (re1 >= 255 || re1 < 0 ) re1 = 256;
  if (gr1 >= 255 || gr1 < 0 ) gr1 = 256;
  if (bl1 >= 255 || bl1 < 0 ) bl1 = 256;
  if (re2 >= 255 || re2 < 0 ) re2 = 256;
  if (gr2 >= 255 || gr2 < 0 ) gr2 = 256;
  if (bl2 >= 255 || bl2 < 0 ) bl2 = 256;

  re1 = round(float(re1) * 4 * float(a1) / 100);
  gr1 = round(float(gr1) * 4 * float(a1) / 100);
  bl1 = round(float(bl1) * 4 * float(a1) / 100);

  re2 = round(float(re2) * 4 * float(a2) / 100);
  gr2 = round(float(gr2) * 4 * float(a2) / 100);
  bl2 = round(float(bl2) * 4 * float(a2) / 100);
  //для дебага раскомментировать
  /*Serial.println("alfa1: " + String(a1));
  Serial.println("LED_R1: " + String(re1));
  Serial.println("LED_G1: " + String(gr1));
  Serial.println("LED_B1: " + String(bl1));
  Serial.println("alfa2: " + String(a2));
  Serial.println("LED_R2: " + String(re2));
  Serial.println("LED_G2: " + String(gr2));
  Serial.println("LED_B2: " + String(bl2));
  Serial.println("delay1: " + String(delay1));
  Serial.println("_step: " + String(_step));*/

  //зажигаем начальный свет
  int re = re1;
  int gr = gr1;
  int bl = bl1;
  LightRed(re);
  LightGreen(gr);
  LightBlue(bl);
  Str = "0";

  int st = 0;//метка для выхода
  //цикл для затухания/появления
  do  {
    delay(delay1);
    if (re != re2) {
      if (re1 > re2) {
        if (re - _step >= re2) {
          re = re - _step;
        } else {
          re = re2;
        }
      } else {
        if (re + _step <= re2) {
          re = re + _step;
        } else {
          re = re2;
        }
      }
    }
    if (gr != gr2) {
      if (gr1 > gr2) {
        if (gr - _step >= gr2) {
          gr = gr - _step;
        } else {
          gr = gr2;
        }
      } else {
        if (gr + _step <= gr2) {
          gr = gr + _step;
        } else {
          gr = gr2;
        }
      }
    }
    if (bl != bl2) {
      if (bl1 > bl2) {
        if (bl - _step >= bl2) {
          bl = bl - _step;
        } else {
          bl = bl2;
        }
      } else {
        if (bl + _step <= bl2) {
          bl = bl + _step;
        } else {
          bl = bl2;
        }
      }
    }
    LightRed(re);
    LightGreen(gr);
    LightBlue(bl);
    if (re > 1024 || re < 0) {
      re = re2;
    }
    if (gr > 1024 || gr < 0) {
      gr = gr2;
    }
    if (bl > 1024 || bl < 0) {
      bl = bl2;
    }

    if (re == re2 && gr == gr2 && bl == bl2) {
      if (inv == 1) {
        re2 = re1;
        gr2 = gr1;
        bl2 = bl1;
        re1 = re;
        gr1 = gr;
        bl1 = bl;
        if (delay2 > 0) {
          delay (delay2);
        }
      } else {
        st = 1;
      }
    }
    getStr();
    if (Str != "0") {
      st = 1;
    }
  } while (st != 1);

  Serial.println("EndLightFadeComand");

}

Как создать пользовательский сложный эффект свечения rgb ленты.

Предыдущую функцию мы делаем основной. Пришедшие данные мы будем записывать в память с порядковым номером,а потом в цикле проходить по всем цветам. Для упрощения работы мы создадим пользовательский тип данных(структуру), который будет в себя включать параметры двух цветов.

struct FadeStruct {
  int a1 = 100;
  int re1 = 0;
  int gr1 = 0;
  int bl1 = 0;
  int a2 = 100;
  int re2 = 0;
  int gr2 = 0;
  int bl2 = 0;
  int delay1 = 0;
  int delay2 = 0;
  int _step = 1;
  int id;//номер эффекта
};

Мы сделаем три функции:

  • главную, в которой парсим параметры и создаем структуру;
  • функцию для записи в память и будем определять количество эффектов
  • Функция с циклом перехода эффектов

Для того чтобы правильно записать, мы должны определить какая именно команда к нам пришла - новый эффект или перезапись старого, для этого во второй функции мы сделаем условие. Считаем сколько эффектов в памяти и если в пришедшем пакете id больше, то значит новый, если меньше или равняется значит редактируем. После записываем все в память, определяя диапазон ячеек. Дальше мы сразу вызываем функцию эффекта.

 void FadeUser() {
  Serial.println("FadeUser");
  int re1 = 0;
  int gr1 = 0;
  int bl1 = 0;
  int a1 = 100;
  int re2 = 0;
  int gr2 = 0;
  int bl2 = 0;
  int a2 = 100;
  int delay1 = 0;
  int delay2 = 0;
  int _step = 1;
  int id = 1;
  a1 = String((strtok (NULL, " "))).toInt();
  re1 = String((strtok (NULL, " "))).toInt();
  gr1 = String((strtok (NULL, " "))).toInt();
  bl1 = String((strtok (NULL, " "))).toInt();
  a2 = String((strtok (NULL, " "))).toInt();
  re2 = String((strtok (NULL, " "))).toInt();
  gr2 = String((strtok (NULL, " "))).toInt();
  bl2 = String((strtok (NULL, " "))).toInt();
  delay1 = String((strtok (NULL, " "))).toInt();
  _step = String((strtok (NULL, " "))).toInt();
  delay2 = String((strtok (NULL, " "))).toInt();
  id = String((strtok (NULL, " "))).toInt();

  if (id == 0) {
    id = 1;
  }

  FadeStruct fade;
  fade.a1 = a1;
  fade.re1 = re1;
  fade.gr1 = gr1;
  fade.bl1 = bl1;
  fade.a2 = a2;
  fade.re2 = re2;
  fade.gr2 = gr2;
  fade.bl2 = bl2;
  fade.delay1 = delay1;
  fade._step = _step;
  fade.delay2 = delay2;
  fade.id = id;

  RefreshCountFade(id, fade);
  FadeUserStart();
}
void FadeUserStart() {
  int count = 0; //переменная количество эффектов
  count = ReadEprom(90, 10).toInt();
  int st2 = 0;//метка для выхода
  Str = "0";
  while (st2 != 1) {
    for (int i = 0; i < count; i++) {
      int re1 = 0;
      int gr1 = 0;
      int bl1 = 0;
      int a1 = 100;
      int re2 = 0;
      int gr2 = 0;
      int bl2 = 0;
      int a2 = 100;
      int delay1 = 0;
      int delay2 = 0;
      int _step = 1;
      int id = 1;
      int inv = 0;
      String str = "";
      str = ReadEprom(i * 64 + 100, 64);
     // Serial.println("str: " + str);
      str.toCharArray(buf, 64);

      a1 = String((strtok (buf, " "))).toInt();
      re1 = String((strtok (NULL, " "))).toInt();
      gr1 = String((strtok (NULL, " "))).toInt();
      bl1 = String((strtok (NULL, " "))).toInt();
      a2 = String((strtok (NULL, " "))).toInt();
      re2 = String((strtok (NULL, " "))).toInt();
      gr2 = String((strtok (NULL, " "))).toInt();
      bl2 = String((strtok (NULL, " "))).toInt();
      delay1 = String((strtok (NULL, " "))).toInt();
      _step = String((strtok (NULL, " "))).toInt();
      delay2 = String((strtok (NULL, " "))).toInt();

      if (a1 > 100)a1 = 100;
      if (a1 < 0)a1 = 0;
      if (a2 > 100)a2 = 100;
      if (a2 < 0)a2 = 0;

      if (re1 >= 255 || re1 < 0 ) re1 = 256;
      if (gr1 >= 255 || gr1 < 0 ) gr1 = 256;
      if (bl1 >= 255 || bl1 < 0 ) bl1 = 256;
      if (re2 >= 255 || re2 < 0 ) re2 = 256;
      if (gr2 >= 255 || gr2 < 0 ) gr2 = 256;
      if (bl2 >= 255 || bl2 < 0 ) bl2 = 256;

      re1 = round(float(re1) * 4 * float(a1) / 100);
      gr1 = round(float(gr1) * 4 * float(a1) / 100);
      bl1 = round(float(bl1) * 4 * float(a1) / 100);

      re2 = round(float(re2) * 4 * float(a2) / 100);
      gr2 = round(float(gr2) * 4 * float(a2) / 100);
      bl2 = round(float(bl2) * 4 * float(a2) / 100);
      int re = re1;
      int gr = gr1;
      int bl = bl1;
      LightRed(re);
      LightGreen(gr);
      LightBlue(bl);
      int st = 0;//метка для выхода
 
      //для дебага раскомментировать
    /* Serial.println("alfa1: " + String(a1));
      Serial.println("LED_R1: " + String(re1));
      Serial.println("LED_G1: " + String(gr1));
      Serial.println("LED_B1: " + String(bl1));
      Serial.println("alfa2: " + String(a2));
      Serial.println("LED_R2: " + String(re2));
      Serial.println("LED_G2: " + String(gr2));
      Serial.println("LED_B2: " + String(bl2));
      Serial.println("delay1: " + String(delay1));
      Serial.println("_step: " + String(_step));*/

      do  {
        delay(delay1);
        if (re != re2) {
          if (re1 > re2) {
            if (re - _step >= re2) {
              re = re - _step;
            } else {
              re = re2;
            }
          } else {
            if (re + _step <= re2) {
              re = re + _step;
            } else {
              re = re2;
            }
          }
        }
        if (gr != gr2) {
          if (gr1 > gr2) {
            if (gr - _step >= gr2) {
              gr = gr - _step;
            } else {
              gr = gr2;
            }
          } else {
            if (gr + _step <= gr2) {
              gr = gr + _step;
            } else {
              gr = gr2;
            }
          }
        }
        if (bl != bl2) {
          if (bl1 > bl2) {
            if (bl - _step >= bl2) {
              bl = bl - _step;
            } else {
              bl = bl2;
            }
          } else {
            if (bl + _step <= bl2) {
              bl = bl + _step;
            } else {
              bl = bl2;
            }
          }
        }
        LightRed(re);
        LightGreen(gr);
        LightBlue(bl);
        if (re > 1024 || re < 0) {
          re = re2;
        }
        if (gr > 1024 || gr < 0) {
          gr = gr2;
        }
        if (bl > 1024 || bl < 0) {
          bl = bl2;
        }

        if (re == re2 && gr == gr2 && bl == bl2) {
          if (inv == 1) {
            re2 = re1;
            gr2 = gr1;
            bl2 = bl1;
            re1 = re;
            gr1 = gr;
            bl1 = bl;
            if (delay2 > 0) {
              delay (delay2);
            }
          } else {
            st = 1;
          }
        }
        getStr();
        if (Str != "0") {
          st = 1;
        }
      } while (st != 1);
      delay (delay2);
    }
    getStr();
    if (Str != "0") {break;}
  }

  Serial.println("Str:=" + Str);
}
//#########################################
void RefreshCountFade(int mem_addr, struct FadeStruct fade) {
  //ячейка 90-99  для данных пользовательского эффекта
  int count = 0; //переменная для записи
  count = ReadEprom(90, 10).toInt();
  if (count < mem_addr) {
    count = count + 1;
  }
  if (count == 0) {
    count = 1;
  }
  ClearEprom(90, 100);
  EEPROM_writeAnything(90, String(count));

  count = (mem_addr - 1) * 64 + 100; // ячека с которой будем начинать запись
  String txt;
  txt = String(fade.a1) + " " + String(fade.re1) + " " + String(fade.gr1) + " " + String(fade.bl1) + " " + String(fade.a2) + " " + String(fade.re2) + " " + String(fade.gr2) + " " + String(fade.bl2) + " " + String(fade.delay1) + " " + String(fade._step) + " " + String(fade.delay2);
  Serial.println("ClearEprom" + String(count));
  ClearEprom(count, count + 64);
  EEPROM_writeAnything(count, txt);

  Serial.println(ReadEprom(100, 164));
  Serial.println("mem_count" + ReadEprom(90, 10));
}

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

Весь основной функционал у нас готов. Осталось объявить все переменные, подключить необходимые библиотеки и написать функцию Setup.

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define HOSTNAME "ufahameleon"

const char *ssid = "ufahameleon"; //ssid созданной точки wi-fi
const char *password = "1234567890";//пароль к нашей wi-fi
WiFiUDP udp;

unsigned int localPort = 8888;      // Порт, с которого принимаем UDP пакеты
const int NTP_PACKET_SIZE = 128; // Размер буфера для принимаемого пакетов
char packetBuffer[NTP_PACKET_SIZE]; //Буфер хранения пакетов

#define LED_R 14 // Pin D11 --> красный
#define LED_G 12 // Pin D10 --> зеленый
#define LED_B 13 // Pin D9 --> синий
struct FadeStruct {
  int a1 = 100;
  int re1 = 0;
  int gr1 = 0;
  int bl1 = 0;
  int a2 = 100;
  int re2 = 0;
  int gr2 = 0;
  int bl2 = 0;
  int delay1 = 0;
  int delay2 = 0;
  int _step = 1;
  int id;//номер эффекта
};


String Str = "0";//переменная для хранения команды

char buf[127];// наш буффер

HOSTNAME - эта переменная позволит нам подключатся к ардуинке по имене, в нашем случае по адресу ufahameleon.lan. Согласитесь это удобнее, нежели помнить ip адрес. Далее мы создаем данные для точки wifi, которую создает наше устройство. Остальные переменные думаю вам понятны исходя из выше описанных функций. Создаем наш Setup

void setup ()  {
  Serial.begin(115200);

  Serial.print("Configuring access point...");
  Serial.println("SSID: " + String(ssid));

  String hostname(HOSTNAME);
  WiFi.hostname(hostname); // задаем адрес host
  Serial.println("HOSTNAME: " + hostname);

  WiFi.mode(WIFI_OFF); //вырубаем WIFI
  //WiFi.mode(WIFI_AP);
  WiFi.mode(WIFI_AP_STA);//Переводим WIFI в смешанный режим

  WiFi.softAP(ssid, password, 8, 0); //Стартуем точку доступа с заданными ssid и password, на 8 канале (не мешаться другим)

  ConnectWiFiRouter(); //пытаемся подключиться по сохраненному Wi-Fi

  IPAddress myIP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(myIP);
  Serial.println("Starting UDP");
  udp.begin(localPort); // инициализируем UDP

  Serial.print("Local port: ");
  Serial.println(udp.localPort());

  ArduinoOTA.setHostname((const char *)hostname.c_str());//задаем host адрес
  ArduinoOTA.begin();

  pinMode(LED_R, OUTPUT );
  pinMode(LED_G, OUTPUT );
  pinMode(LED_B, OUTPUT );
  analogWrite(LED_R, 1024);
  analogWrite(LED_G, 1024);
  analogWrite(LED_B, 1024);
  FadeUserStart();
}

Вот наш скетч и готов! Внизу можно будет скачать его целиком.

В следующей части мы напишем простую программу для ПК, с помощью которой будем управлять светом и создавать эффекты.

5 Комментария прокомментировать

Лестницы
28 января 2022 в 06:29

Спасибо, попробую применить, если потребуется.

ответить
Andrii
9 ноября 2021 в 15:54

Ех а хотелось б продолжения)

ответить
олег
11 января 2018 в 13:11

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

ответить
БлудныйКот
17 января 2017 в 22:26

Спасибо, очень интересно!скажите а продолжение будет?

admin
17 января 2017 в 23:33

будет, программа написана, осталось саму статью написать

ответить

Добавить комментарий

Текст *

Яндекс.Метрика