WS2812 + Apds-9960

Q-Tuzoff
Q-Tuzoff аватар
Offline
Зарегистрирован: 22.02.2018

Всем привет.

Хочу сделать себе светодиодное освещение из 300 светодиодов (WS2812) с переключением эффектов жестами (с помощью датчика Apds-9960).

Сами эффекты беру тут: https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDEffects

Сам тоже писал немного, поосваивал, так сказать, программирование WS2812.

Пока испытываю на 16 светодиодах это дело (ну, жесты, то есть), дабы потом без проблем залить скетч под 300 штук. Начал с простого: с пары эффектов (влево - один эффект, вправо - другой, вниз и вверх - выключить ленту).

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

Сам код:

#include <Adafruit_NeoPixel.h>
#include <Wire.h>                                               // Для работы с шиной I2C
#include <SparkFun_APDS9960.h>                                  // Для работы с датчиком APDS-9960

#define PIN 6
#define NUM_LEDS 16
#define APDS9960_INT 2  // Needs to be an interrupt pin. We should be able to use the Arduino's pin 3 as well.

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);
SparkFun_APDS9960 apds = SparkFun_APDS9960(); // Initialize a SparkFun_APDS9960 object. This does all the magic incl. i2c communication with the sensor.
volatile int isr_flag = 0;                             // interrupt flag, triggered when a gesture has been dectected. Used to detect gesture detection in the interrupt handler

void setup()
{
  // Инициализация RGB
  pinMode(PIN, OUTPUT);
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'

  // APDS Initialization code
  pinMode(APDS9960_INT, INPUT);   // Set interrupt pin as input. @@Note: this should be handled my the library. But it isn't
  attachInterrupt(0, interruptRoutine, FALLING);   // Initialize interrupt service routine. Basicly it'll call our interrupt routine

  if (apds.init()) // Initialize APDS-9960 (configure I2C and initial values)
  {
    // @@NOTE: original value is two. But it looks like the modern gesture sensor or more sensitive. 1 does it for me
    apds.setGestureGain( 1 );
  }
  
  if (apds.enableGestureSensor(true)) // Start running the APDS-9960 gesture sensor engine. Sensor support more functions than gesture detection only.
  { 
    //strip.begin();
    strip.show();
  }
}

void loop()
{
  if(isr_flag == 1)
  {
    detachInterrupt(0);
    handleGesture();
    isr_flag = 0;
    attachInterrupt(0, interruptRoutine, FALLING);
  }
}

void interruptRoutine()
{
  isr_flag = 1;
}

void handleGesture()
{
  if (apds.isGestureAvailable())
  {
    switch (apds.readGesture())
    {
      case DIR_LEFT: RGBLoop(); break;
      case DIR_RIGHT: CylonBounce(0, 0xff, 0, 4, 10, 50); break;
      case DIR_UP: strip.clear(); strip.show(); break;
      case DIR_DOWN: strip.show(); strip.clear(); break;
    }
  }
}
// ---> here we define the effect function <---
// *** REPLACE TO HERE ***

void RGBLoop()
{
  for(;;)
  {
    for(int j = 0; j < 3; j++ )
    {
      // Fade IN
      for(int k = 0; k < 256; k++)
      { 
        switch(j)
        { 
          case 0: setAll(k,0,0); break;
          case 1: setAll(0,k,0); break;
          case 2: setAll(0,0,k); break;
        }
        showStrip();
        //delay(3);
      }
      // Fade OUT
      for(int k = 255; k >= 0; k--)
      {
        switch(j)
        { 
          case 0: setAll(k,0,0); break;
          case 1: setAll(0,k,0); break;
          case 2: setAll(0,0,k); break;
        }
        showStrip();
        //delay(3); 
      }
    }
    //break;
  }
}

void CylonBounce(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay)
{
  for(;;)
  {
    for(int i = 0; i < NUM_LEDS-EyeSize-2; i++)
    {
      setAll(0,0,0);
      setPixel(i, red/10, green/10, blue/10);
      for(int j = 1; j <= EyeSize; j++) 
      {
        setPixel(i+j, red, green, blue); 
      }
      setPixel(i+EyeSize+1, red/10, green/10, blue/10);
      showStrip();
      delay(SpeedDelay);
    }
    delay(ReturnDelay);

    for(int i = NUM_LEDS-EyeSize-2; i > 0; i--)
    {
      setAll(0,0,0);
      setPixel(i, red/10, green/10, blue/10);
      for(int j = 1; j <= EyeSize; j++)
      {
        setPixel(i+j, red, green, blue); 
      }
      setPixel(i+EyeSize+1, red/10, green/10, blue/10);
      showStrip();
      delay(SpeedDelay);
    }
    delay(ReturnDelay);
    //break;
  }
}

void showStrip()
{
  #ifdef ADAFRUIT_NEOPIXEL_H 
    // NeoPixel
    strip.show();
  #endif
  #ifndef ADAFRUIT_NEOPIXEL_H
    // FastLED
    FastLED.show();
  #endif
}

void setPixel(int Pixel, byte red, byte green, byte blue)
{
  #ifdef ADAFRUIT_NEOPIXEL_H 
    // NeoPixel
    strip.setPixelColor(Pixel, strip.Color(red, green, blue));
  #endif
  #ifndef ADAFRUIT_NEOPIXEL_H 
    // FastLED
    leds[Pixel].r = red;
    leds[Pixel].g = green;
    leds[Pixel].b = blue;
  #endif
}

void setAll(byte red, byte green, byte blue)
{
  for(int i = 0; i < NUM_LEDS; i++ )
  {
    setPixel(i, red, green, blue); 
  }
  showStrip();
}

Где нужно ставить isr_flag = 0? Что только не перепробовал... Примеров внятных нет по внешним прерываниям циклов for или while. Всё что находил - мигание светодиодом (в библиотеке apds тоже есть примеры с прерываниями, но так же не цикличны), поэтому мне сложно понять как работает прерывание.

Если рассуждать логически, то нужно сначала зациклить в бесконечность какой-нибудь эффект, а когда сработал жест, выйти из цикла и начать следующее действие.

И, да, я сознательно закомментировал delay в эффекте RGBLoop, потому что эта задержка, возможно, мешает чтению жестов. Как делать с millis() тоже пока не разобрался, как им заменить delay, но это ладно, потом уже. Сейчас главное читать датчик во время выполнения эффекта.

Q-Tuzoff
Q-Tuzoff аватар
Offline
Зарегистрирован: 22.02.2018

Заменял ещё for(;;) на while(!apds.readGesture()) в 71 и 106 строках. Но работает по прошествии всего цикла, а не в любой момент. Есть долгоработающие эффекты и ждать прошествия цикла ну как-то не вариант.

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

Если отвечать на поставленный вами вопрос - как выйти из цикла по прерыванию, то можно, например, сказать, что нужно проверять isr_flag внутри самого "глубокого" цикла (например в районе строк 78, 90 и тп.) и делать return, если он равен ненулевой.

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

Что для вас проще - я не знаю.

Q-Tuzoff
Q-Tuzoff аватар
Offline
Зарегистрирован: 22.02.2018

sadman41 пишет:

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

В смысле без циклов, одними условиями if-else?

А не подскажете ещё по millis()? Где можно подробнее почитать, кроме примеров со светодиодом "мигаем blink без delay"? Этот пример мало что даёт, например, не объясняет как использовать его в глубинах циклов, при чтении каких-либо датчиков...

Q-Tuzoff
Q-Tuzoff аватар
Offline
Зарегистрирован: 22.02.2018

И ещё интересно... Подключал до apds модуль радиоканала. Управлял с пульта. Самое интересное, что там никакая пропитка выходами из цикла не нужна, переключение работает и так. Но тут другая проблема, после первого чтения команды с пульта, последующие переключения работают плохо, пульт нужно подносить очень близко, срабатывает не сразу. Может кто что посоветовать/объяснить как это исправить? Из-за задержек, насколько я понимаю? Привожу код.

#include <Adafruit_NeoPixel.h>
#include <RCSwitch.h>
#define PIN 6
#define NUM_LEDS 16
// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);
RCSwitch mySwitch = RCSwitch();

void setup() {
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  mySwitch.enableReceive(0);  // Receiver on interrupt 0 => that is pin #2
}

// *** REPLACE FROM HERE ***
void loop() {
  // ---> here we call the effect function <---
  readpult();
}
// Функция чтения пульта
void readpult()
{
  if(mySwitch.getReceivedValue() == 12932400 || mySwitch.getReceivedValue() == 11591234)
  {
    RGBLoop();
  }

  if(mySwitch.getReceivedValue() == 12932364)
  {
    CylonBounce(0xff, 0, 0, 4, 10, 50);
  }
}

// ---> here we define the effect function <---
// *** REPLACE TO HERE ***

void RGBLoop(){
for(;;)
{
  for(int j = 0; j < 3; j++ ) { 
    // Fade IN
    for(int k = 0; k < 256; k++) { 
      switch(j) { 
        case 0: setAll(k,0,0); break;
        case 1: setAll(0,k,0); break;
        case 2: setAll(0,0,k); break;
      }
      showStrip();
      delay(3);
    }
    // Fade OUT
    for(int k = 255; k >= 0; k--) { 
      switch(j) { 
        case 0: setAll(k,0,0); break;
        case 1: setAll(0,k,0); break;
        case 2: setAll(0,0,k); break;
      }
      showStrip();
      delay(3);
    }
  }
  break;
}
}

void CylonBounce(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay){
for(;;)
{
  for(int i = 0; i < NUM_LEDS-EyeSize-2; i++) {
    setAll(0,0,0);
    setPixel(i, red/10, green/10, blue/10);
    for(int j = 1; j <= EyeSize; j++) {
      setPixel(i+j, red, green, blue); 
    }
    setPixel(i+EyeSize+1, red/10, green/10, blue/10);
    showStrip();
    delay(SpeedDelay);
  }

  delay(ReturnDelay);

  for(int i = NUM_LEDS-EyeSize-2; i > 0; i--) {
    setAll(0,0,0);
    setPixel(i, red/10, green/10, blue/10);
    for(int j = 1; j <= EyeSize; j++) {
      setPixel(i+j, red, green, blue); 
    }
    setPixel(i+EyeSize+1, red/10, green/10, blue/10);
    showStrip();
    delay(SpeedDelay);
  }
  
  delay(ReturnDelay);
  break;
}
}

void showStrip() {
 #ifdef ADAFRUIT_NEOPIXEL_H 
   // NeoPixel
   strip.show();
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H
   // FastLED
   FastLED.show();
 #endif
}

void setPixel(int Pixel, byte red, byte green, byte blue) {
 #ifdef ADAFRUIT_NEOPIXEL_H 
   // NeoPixel
   strip.setPixelColor(Pixel, strip.Color(red, green, blue));
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H 
   // FastLED
   leds[Pixel].r = red;
   leds[Pixel].g = green;
   leds[Pixel].b = blue;
 #endif
}

void setAll(byte red, byte green, byte blue) {
  for(int i = 0; i < NUM_LEDS; i++ ) {
    setPixel(i, red, green, blue); 
  }
  showStrip();
}

 

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

Q-Tuzoff пишет:

В смысле без циклов, одними условиями if-else?

Без циклов вы наврядли обойдетесь, так как цикл - суть жизни ;)
Однако, вы можете сделать так, чтобы при вызове функции выполнялся ровно один шаг вашего эффекта. Например - снижение яркости на 3%. Тогда между таковыми шагами (вызове функции "эффект1") вы в loop() вполне можете проверять состояние датчика и, при необходимости, начать вызывать такую же "шагающую" функцию "эффект2".

 

Q-Tuzoff пишет:

А не подскажете ещё по millis()? Где можно подробнее почитать, кроме примеров со светодиодом "мигаем blink без delay"? Этот пример мало что даёт, например, не объясняет как использовать его в глубинах циклов, при чтении каких-либо датчиков...

Слишком абстрактный вопрос задаете. Наврядли ответ на него вам даст понимание. Приведите простой случай  который вам близок. Могу на вареном яйце объяснить, к примеру.

 

Q-Tuzoff
Q-Tuzoff аватар
Offline
Зарегистрирован: 22.02.2018

sadman41 пишет:

Слишком абстрактный вопрос задаете. Наврядли ответ на него вам даст понимание. Приведите простой случай  который вам близок. Могу на вареном яйце объяснить, к примеру.

Ну вот более простой пример, написал такую вот мигалку:

#include <Adafruit_NeoPixel.h>
#define PIN 6 // номер порта к которому подключен модуль
#define count_led 16 // количество светодиодов 
  int8_t i;
  int8_t j;
  for (i = 0, j = count_led / 2; i < count_led / 2, j < count_led; i++, j++)
  {
    pixels.setPixelColor(i, 0,0,10);
    pixels.setPixelColor(j, 0,10,0);
    delay(35); // здесь
    pixels.show();
  }
  for (j = 0, i = count_led / 2; j < count_led / 2, i < count_led; j++, i++)
  {
    pixels.setPixelColor(i, 0,0,10);
    pixels.setPixelColor(j, 0,10,0);
    delay(35); // и здесь
    pixels.show();
  }

delay здесь явно лишние для чтения каких-либо датчиков... Заменить на millis вообще не понял как.

Поправьте, что не так делаю?

#include <Adafruit_NeoPixel.h>
#define PIN 6 // номер порта к которому подключен модуль
#define count_led 16 // количество светодиодов 

unsigned long timing; // переменная для хранения времени

  int8_t i;
  int8_t j;

  for (i = 0, j = count_led / 2; i < count_led / 2, j < count_led; i++, j++)
  {
    if(millis() - timing > 3500)
    {
      pixels.setPixelColor(i, 0,0,10);
      pixels.setPixelColor(j, 0,10,0);
      //delay(35);
      pixels.show();
    }
  }
  for (j = 0, i = count_led / 2; j < count_led / 2, i < count_led; j++, i++)
  {
    if(millis() - timing > 3500)
    {
      pixels.setPixelColor(i, 0,0,10);
      pixels.setPixelColor(j, 0,10,0);
      //delay(35);
      pixels.show();
    }
  }

 

sadman41
Offline
Зарегистрирован: 19.10.2016
Я еще больше упростил ваш пример, чтобы было всё предельно ясно. Не компилировал, не гонял на лампочках, просто идею проиллюстрировал.
 
void loop() {

    static uint8_t pos = 0, dir;
    static uint32_t timing = 0, color; 

    // тут можем читать датчик, проверять кнопки и пр.

    // каждые 35 ms делаем очередной "шаг"
    if(millis() - timing > 35)
    {
      timing = millis();
      // Если стоим в начале страйпа, то начинаем двигаться вперед и заливать все цветом 0xFF00FF
      if (0 >= pos) {
         color = 0xFF00FF;
         dir = 1;
      } 
      // Если дошли/стоим в конце страйпа, то начинаем двигаться назад и заливать все цветом 0x00FF00
      if (count_led <= pos) {
         color = 0x00FF00;
         dir = -1;
      }
      // На этом шаге красим только один пиксель (остальные уже покрашены) и выводим массив pixels на страйп
      pos += dir;
      pixels.setPixelColor(pos, color);
      pixels.show();
    }
}

 

 

Q-Tuzoff
Q-Tuzoff аватар
Offline
Зарегистрирован: 22.02.2018

То есть всё-таки рекомендуете заменить циклы и заменять готовые эффекты на подобное чтобы получилось как надо с millis? Эххх... Очень всё непросто выходит. Спасибо, буду пробовать что-нибудь.

А по прерываниям на радиоканале что посоветуете? Почему пульт плохо срабатывает когда нужно выйти из подпрограммы?

Q-Tuzoff
Q-Tuzoff аватар
Offline
Зарегистрирован: 22.02.2018

Мм... Проблема всё же в прерываниях. Убрал все attachInterrupt из Setup и Loop - работает всё так же до конца цикла. Не работают прерывания, в общем. Может кто показать как их применить в моём примере?

Теперь более понятно почему с пульта работает почти как надо. В библиотеке RCSwitch есть такая замечательная функция mySwitch.enableReceive(0);  // Receiver on interrupt 0 => that is pin #2

С ней прерывания работают почти как надо (выходит из цикла for(;;) в любой момент по нажатию кнопки), за исключением того что во время цикла эффекта почему-то плохо работает пульт (но это, вероятно, из-за задержек (?)).

А на APDS-9960 без понятия как использовать прерывания... Нужны ли флаги (isr_flag)? Пытался сделать как здесь: http://mysensors.ru/build/gesture-controller/ взяв пример DimmableLight_v2.ino. Не работают прерывания если делать как здесь, заменяя функции на свои.

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

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

Например, известно, что для вывода на WS2811/2812/etc библиотеки, как правило, запрещают обработку прерываний. Так что всё, что прилетает на ардуину извне  имеет ненулевой шанс быть упущенным.

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

Q-Tuzoff
Q-Tuzoff аватар
Offline
Зарегистрирован: 22.02.2018

sadman41 пишет:

Например, известно, что для вывода на WS2811/2812/etc библиотеки, как правило, запрещают обработку прерываний. Так что всё, что прилетает на ардуину извне  имеет ненулевой шанс быть упущенным

Ну как-то же делают наверняка такое. Те же китайцы прилагают готовые запрограммированные пульты к таким лентам. Ардуина не справляется разве с таким?

sadman41 пишет:

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

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

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

Q-Tuzoff пишет:

Ну как-то же делают наверняка такое. Те же китайцы прилагают готовые запрограммированные пульты к таким лентам. Ардуина не справляется разве с таким?

Я понятия не имею, что китайцы напихивают в "свои пульты" - их, полагаю, не один десяток разновидностей. 
 
Поэтому ответить на вопрос о том, может ли абстрактная ардуина работать наравне с неким абстрактным контроллером, за тыщу рэ - я не имею никакой возможности.
 
Если вас интересует мое мнение, то я думаю, что если затратить некоторое время на оптимизацию процессов и алгоритмов, то можно и на ардуине сотворить чудеса. Но тут уже не обойтись чтением документации по FastLed, нужно копать глубже.
Q-Tuzoff
Q-Tuzoff аватар
Offline
Зарегистрирован: 22.02.2018

В общем, что хотел сделать - пожалуй, удалось.) Поделюсь, может, кому пригодится. Взял за основу готовые эффекты AlexGyver'а.

https://www.youtube.com/watch?v=_h-Vj8Z6hqs

Прикрутил модули радиоканала и bluetooth'а. Дописал вот такой код:

#include <RCSwitch.h>
#include <SoftwareSerial.h>

#define PIN_BTRX 15
#define PIN_BTTX 14

RCSwitch mySwitch = RCSwitch();
SoftwareSerial bluetooth(PIN_BTTX, PIN_BTRX);

//функция для запуска синезубого
void setupBluetooth()
{
  bluetooth.begin(115200);  // The Bluetooth Mate defaults to 115200bps
  bluetooth.print("$$$");  // Enter command mode
  delay(100);  // Short delay, wait for the Mate to send back CMD
  bluetooth.println("U,9600,N");  // Temporarily Change the baudrate to 9600, no parity
  // 115200 can be too fast at times for NewSoftSerial to relay the data reliably
  bluetooth.begin(9600);  // Start bluetooth serial at 9600
}

... // Далее код Alex'а

void setup()
{
  mySwitch.enableReceive(0);  // Receiver on interrupt 0 => that is pin #2
  setupBluetooth();
  ... // далее код Alex'а
}

void loop()
{
  // Здесь просто две кнопки переключения между эффектами
  if (mySwitch.available())
  {
    if(mySwitch.getReceivedValue() == 12932400) // команды пульта пишем свои
    {
      ledMode++;
    }

    if(mySwitch.getReceivedValue() == 12932364) // здесь тоже команда
    {
      ledMode--;
    }
    // сброс счётчика эффектов, если число больше или меньше диапазона
    if(ledMode < 2)
    {
      ledMode = 41;
    }
    if(ledMode > 41)
    {
      ledMode = 2;
    }
  mySwitch.resetAvailable();
  }
  // чтение синезуба
  if (bluetooth.available())
  {
    ledMode = bluetooth.parseInt();
  }
  ... // далее код Alex'а
}

...

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

Синезубый приятель же работает без отказов - идеально. Программу для управления рекомендую Bluetooth Electronics. В ней настраиваются не только сами кнопки, но и их количество, а ещё можно приделать всякие слайдеры и переключатели.

Из личного минуса - не работает айфон с этими модулями (HR-05) bluetooth, но это уже "спасибо" эпплу с его политикой:

ios does not support most bluetooth profiles, because apple wants a slice of peripheral profits through its MFi program. If you can't dump ios, you will have to use BLE (which is unrestricted and not part of MFi).

Товарищ андройд - наше всё.)

duino
Offline
Зарегистрирован: 13.03.2018

Все что Вы искали, уже выложено в открытом доступе https://youtu.be/C4QELMzOpiA

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

duino пишет:

Все что Вы искали, уже выложено в открытом доступе https://youtu.be/C4QELMzOpiA

реклама? "Все что вы искали" - кому это? - тем более что ТС ничего и не искал

Ссылку не открывал, что там - не знаю, но уж очень агрессивно подается. На уровне "самые красивые сиськи - в нашем видео"

 

duino
Offline
Зарегистрирован: 13.03.2018

А Вы полностью прочитали сообщение топикстартера?

Цитирую:

"Собственно, столкнулся с проблемой прерываний. Если зациклить какой-нибудь эффект, то из него уже не выйти, а если сделать конечным, то он просто пройдет один раз и остановится. Сами эффекты находятся в циклах for(). Как реализовать выход из циклов по событию (при смене жеста)?"

duino
Offline
Зарегистрирован: 13.03.2018

b707 пишет:

Ссылку не открывал, что там - не знаю, но уж очень агрессивно подается. На уровне "самые красивые сиськи - в нашем видео"

А Вам , то что от этого плохо стало или другие мотивы?

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

duino пишет:

А Вы полностью прочитали сообщение топикстартера?

А вы всю ветку читали? - Автору было предложено порядка пяти разных способов решения его проблемы...

Не говоря уж о том, что проблема эта обсуждается каждым вторым новичком и решения для нее приведены в сотнях источников в гугле.

Вы уверены. что ваша ссылка спустя год хоть что-то добавляет к обсуждению? :)))

duino
Offline
Зарегистрирован: 13.03.2018

Я уверен на 100%, что мое решение избавит от всех проблем, присущих этому датчику. Вы его скорее всего не использовали никогда и не в курсе о его нестабильной работе