zepete: (Default)
[personal profile] zepete

В предыдущих постах уже описал как программируется arduinoдоработка библиотеки LCD и особенности подключения потенциометра.
ИМХО, для понимания работы скетча и переноса его в arduino этого достаточно. Остальное описано на сайте arduino.ru/.

Arduino для маршрутизатора является консолью с LCD экраном и клавиатурой в виде много оборотного потенциометра. Можно использовать одно оборотный потенциометр, но тогда будет тяжело задавать больше 10 опций, хотя стоить он будет в 10 раз меньше.

Алгоритм работы.
Сразу после включения ардуино ждет строку "TPL may play <число каналов> channal.", если оно этого не дождется за 10 минут (переменная cuTimeoutOpenWRT), то начинает слать в роутер строку о необходимости сброса маршрутизатора, ибо это говорит о том, что во время работы по непонятным причинам arduino перезагрузилось.
Когда оно этой строки дождется, то начинает принимать команды с роутера.
Он понимает следующие команды:

  • Имя проигрываемой станции: "play:<номер канала>. <имя канала>".
  • Сброс arduino: "reset arduino".
  • Изменение числа доступных каналов: "TPL may play <число доступных каналов> channal.".
  • Сообщение об ошибке роутера: "error:<число>. <имя ошибки>".
  • Текущее время:"time:<текстовое сообщение о времени>".

ЖКИ поделен на 2 области.

  1. Верхняя строка, в которую выводится сведения о положении потенциометра. Сведения о положении потенциометра выводятся только когда он поворачивается, если после последнего сдвига придет сообщение с текущем временем, то сведения о положении потенциометра будут затерты строкой с текущем временем.
  2. Три нижних строки в которых индицируется имя текущего канала или ошибка проигрывателя. Они берутся из команд "play:<номер канала>. <имя канала>" и "error:<число>. <имя ошибки>", только слово "play" вырезается, а "error" нет.
Для упрощения протокола обмена между arduino и роутером имя проигрываемого канала повторяется каждую секунду, поэтому для предотвращения ежесекундного  мигания экрана, LCD переписывается только если придет строка отличная от предыдущей.

По прошествии 0.1 секунды после последнего сдвига потенциометра (переменная SPTIME), с периодичностью в 3 секунды (переменная STPLTIME) посылается в роутер команда сменить канал: "Tuner: <номер канала>".

!!!ВНИМАНИЕ!!!
1. Компилятор с++ для arduino имеет ошибку: не умеет корректно сравнивать переменные типа unsigned long, поэтому для обработки длительных временных промежутков, я сначала милисекунды преобразовывал в секунды, а потом уже их сравнивал.
2. Если непрерывно слать данные в arduino по последовательному порту, то по непонятным причинам, оно превращается в пожирателя энергии, которому БП в 1A недостаточно. Поэтому, для предотвращения перегрева, зависаний и сбоев arduino,  непрерывно слать данные ему нельзя.

#include "LCD.h"
#include <avr\wdt.h>
// initial LCD library LiquidCrystal(rs, rw, enable, d4, d5, d6, d7)
LCD lcd(6,7,8,9,10,11,12);


#define   AVR         512
#define   MIDDLE_ADC  41      // края ADC,
unsigned int    iNumOut=0;
const   char csRadioReady[]    = "TPL may play %i channal.";
const   char csTime[]          = "time:";
String  inputString="";
String  OldInputString="";

const   unsigned long STPLTIME=3000;   // время между отправками сообщений TPL
const   unsigned long SPTIME=100;   // time of stabilization state trimmer
const   unsigned int cuTimeoutOpenWRT=10*60;
const   int      ciRes330k=5;
const   int      ciRes33k=4;
const   int      ciRes560=13;
const   int      ciTunerPin=0;
static  int      iNewTuner=-1;
        boolean  fSendNumCh=false;

static  int      iNumStation;
static  int      iTuner=-1;

void(* resetFunc) (void) = 0;//declare reset function at address 0
unsigned int secondsis(void);


void setup() {
  // конфигурация входов/выходов
  pinMode(ciTunerPin,INPUT);
  pinMode(ciRes330k,INPUT);
  pinMode(ciRes33k,INPUT);
  pinMode(ciRes560,INPUT);
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  lcd.begin(20,4);
  lcd.clear();
  lcd.printf("\fWiFi radio ver. 0.0\n\rWaiting TPL...\n\r");
  unsigned int uStartTime=secondsis();
  // ожидание приветствия со стороны wifi radio с числом каналов
  do{
      while(!Serial.available())
        if(secondsis()-uStartTime>cuTimeoutOpenWRT)
          Serial.println("Arduino waiting TPL");
      char inChar = (char)Serial.read();
      if(inChar!='\n')
      {
        if(inputString.length()>80)
          inputString="";
        inputString+=inChar;
        continue;
      }
      if(!sscanf(inputString.c_str(),csRadioReady,&iNumStation))
      {
        inputString="";
        continue;
      }
      inputString="";
  } while(!iNumStation);
  // конец
  lcd.clear();
  lcd.setCursor(0,1);
  lcd.printf("Num station is %i",iNumStation);
}

void loop() {
  // чтение номера канала с усреднением
static  word  wNumRead=0;
static  unsigned long sum;
static  unsigned long dwSendTime;
static  unsigned long dwNewTime=0xffffffff;
static  unsigned long iADC=2048;
        unsigned long dwReadTime=millis();
        char v;
        int  i;
  if(!wNumRead)
    sum=0;
  sum+=analogRead(ciTunerPin);
  wNumRead+=1;
  if(wNumRead==AVR)
  {
    // номер канала считан
    wNumRead=0;
    sum=sum/AVR;
    // удаление помехи
    if(abs(iADC-sum)<=1)
      sum=iADC;
    else
      iADC=sum;
    sum=sum/2;
//  iNumOut+=1;
//  lcd.setCursor(0,2);
//  lcd.printf("numout=%u       ",iNumOut);     
   
    if(iNewTuner!=sum)
    {
      // установка подтягивающих резистров для обеспечения не большой положительной связи
      // требуется для устранения помех
      if((2*sum-(int)(sum*(long)512/(long)iNumStation)-(int)((sum+1)*(long)512/(long)(iNumStation))+1)>0)
      {
        // ближе к верхней границе диапазона, значит надо притягивать к минусу
        // проверка на размер уровня
        if(sum<MIDDLE_ADC)
        {
          // нижний диапазон
          pinMode(ciRes330k,INPUT);
          pinMode(ciRes33k,INPUT);
          pinMode(ciRes560,OUTPUT);
          digitalWrite(ciRes560,LOW);
        }
        else
        {
          if(sum<511-MIDDLE_ADC)
          {
            // средний диапазон
            pinMode(ciRes330k,OUTPUT);
            pinMode(ciRes33k,INPUT);
            pinMode(ciRes560,INPUT);
            digitalWrite(ciRes330k,LOW);
          }
          else
          {
            pinMode(ciRes330k,OUTPUT);
            pinMode(ciRes33k,INPUT);
            pinMode(ciRes560,INPUT);
            digitalWrite(ciRes33k,LOW);
          }
        }
      }
      else
      {
        // ближе к нижней границе диапазона, значит надо притягивать к плюсу
        if(sum<MIDDLE_ADC)
        {
          // нижний диапазон
          pinMode(ciRes330k,INPUT);
          pinMode(ciRes33k,OUTPUT);
          pinMode(ciRes560,INPUT);
          digitalWrite(ciRes33k,HIGH);
        }
        else
        {
          if(sum<511-MIDDLE_ADC)
          {
            // средний диапазон
            pinMode(ciRes330k,OUTPUT);
            pinMode(ciRes33k,INPUT);
            pinMode(ciRes560,INPUT);
            digitalWrite(ciRes330k,HIGH);
          }
          else
          {
            pinMode(ciRes330k,INPUT);
            pinMode(ciRes33k,INPUT);
            pinMode(ciRes560,OUTPUT);
            digitalWrite(ciRes560,HIGH);
          }
        }
      }     
      iNewTuner=sum;
      dwNewTime=dwReadTime;
      // отображение на экране этой информации   
      lcd.setCursor(0,0);
      lcd.printf("\t\t%3d ",(int)sum);
      sum=sum*(unsigned long)iNumStation;
      sum=sum/512;
      lcd.setCursor(0,0);
      lcd.printf("%3i %3i \t%3i     ",
                   (int)sum+1,
                        (int)(sum*(long)512/(long)iNumStation),
                                (int)((sum+1)*(long)512/(long)(iNumStation)));
    }
    else if(dwReadTime-dwNewTime>SPTIME)
    {
      sum=sum*(unsigned long)iNumStation;
      sum=sum/512;
      if(sum!=iTuner)
      {
        // взвод флага вывода на TPL нового номера канала
        iTuner=sum;
        fSendNumCh=true;
        dwSendTime=0xffffffff;
      }
    }
  }
  // отправка номера канала на TPL
  if(fSendNumCh)
  {
    if(dwReadTime-dwSendTime>STPLTIME)
    {
    // отправка номиера канала TPL
      Serial.print("Tuner: ");
      Serial.print(iTuner);
      Serial.print("\r");
      dwSendTime=dwReadTime;
    }
  }
//  lcd.setCursor(0,1);
//  lcd.printf("dt=%lu       ",dwReadTime-dwSendTime);
  // прием с TPL
  while (Serial.available())
  {
    // get the new byte:
    v = (char)Serial.read();
    // add it to the inputString:
    if(inputString.length()>160)
      inputString="";
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if ((v=='\n') || (v=='\r'))
    {
      // если строка с именем канала
      if(!inputString.length())
        continue;
      if(inputString!=OldInputString)
      {
        if(sscanf(inputString.c_str(),"play:%d. %*s",&i)==1)
        {
          i-=1;
          if(i==iTuner)
          {
            // тогда бросать принимать строки
            fSendNumCh=false;
            lcd.setCursor(0,1);
            for(v=60;v;v--)
              lcd.CharOut_noscroll(' ');
            // вывод полученной строки
            lcd.setCursor(0,1);
            lcd.printf_noscroll((inputString.c_str())+5);
          }
        }
        else if(!inputString.compareTo("reset arduino"))
        {
          lcd.setCursor(0,0);
          lcd.clear();
          lcd.printf("reset");
          resetFunc();
        }
        else if(sscanf(inputString.c_str(),csRadioReady,&i)==1)
        {
          iNumStation=i; // число каналов
          lcd.setCursor(0,0);
          lcd.clear();
          inputString=""; // строку с именем канала не принимали
          OldInputString="";
          fSendNumCh=true; // выводить новую строку
          lcd.clear();
          lcd.setCursor(0,1);
          lcd.printf("Num station is %i",iNumStation);
        }
        else if(sscanf(inputString.c_str(),"error:%d. %*s",&i)==1)
        {
          OldInputString=inputString;
          // очищение нижней части экрана
          lcd.setCursor(0,1);
          for(v=60;v;v--)
            lcd.CharOut_noscroll(' ');
          // вывод полученной строки
          lcd.setCursor(0,1);
          lcd.printf_noscroll(inputString.c_str());
        }
        else if(!strncmp(inputString.c_str(),csTime,sizeof(csTime)-1))
        {
          // очищение верхней строки
          lcd.setCursor(0,0);
          for(v=20;v;v--)
            lcd.CharOut_noscroll(' ');
          lcd.setCursor(0,0);
          lcd.printf_noscroll("%-20.20s",inputString.c_str()+sizeof(csTime)-1);
        }
        OldInputString=inputString;
      }
      inputString="";
      continue;
    }
    inputString+=v;
  }
}

unsigned int secondsis(void)
{
  return millis()/1000;
}



Profile

zepete: (Default)
zepete

January 2026

S M T W T F S
    1 23
4 56 78910
11121314151617
18192021222324
25262728293031

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 14th, 2026 09:07 am
Powered by Dreamwidth Studios