Електроника и Електротехника | Electronics and Electrical Engineering > Цифрово / дигитално управление | Digital Command Control

Измервателен вагон

<< < (2/8) > >>

IvanC:
В предните мнения показах измервателните вагони, направени от мен. В това мнение пускам снимките на втория измервателен вагон направен от брат ми Владо (VladoC). Коментарите са също неговите и понеже са много кратки, в края ще добавя и няколко коментара от мен.

Вагонът е на Piko, от хоби серията, кат. номер 58781:




Колоос и датчик на Хол:






Акумулаторът е LiIon 14500, 800 mAh, USB зарядно за една клетка и стабилизатор на напрежение за 5 V:







Владо

---------------------------------------------------------------------------------------

Виждам, че Владо не е използвал преобразувателна платка от USB батерия, а два отделни модула. Освен това неговият акумулатор не е от USB батерия. Най-вероятно затова не е използвал платка от USB батерия.

Вижда се по-различният подход при монтажа на елементите, датчика на Хол и магнитите, което е интересно и дава повече идеи.

На снимките не виждам обаче модула на акселерометъра. Виждам проводници, запоени към A4 и A5 на Ардуиното, така че предполагам има акселерометър, може би в средата под батерията и 4-те проводика с конектора до батерията са на този модул.

На снимките се вижда светодиодът, монтиран на покрива на вагона, който имитира въртящ се буркан и за който писах в първото мнение в темата. Ето и кратък клип от работата на вагона по време на настройване на декодера в Людмилата:

https://youtu.be/5TWFyj3nhME

Иван

IvanC:
Разглобих моя измервателен вагон 3, за да направя снимки на монтирания акселерометър. Бях забравил какъв кошмар е да се разглоби и после да се сглоби. При разглобяването имам чувството, че всички щипки се чупят, а при сглобяването, двете кулиси и техните пружинки изпадат при най-малкото сътресение, а две ръце не стигат да крепят двете кулиси на място и да сглобяват останалите части. Както и да е, с малко гресчица на пружинките на кулисите и много "сладки" думи казани на ум (за да не чуе жената и да се чуди защо си псувам сам...!) успях да сглобя обратно вагона успешно и без щети.







Иван

IvanC:
Ето кода на програмата за Ардуиното във вагона:


--- Код: ---#include <Wire.h>                                 // i2c library                                      Използвани библиотеки

#define baudRate               115200             // serial baud rate
#define sensPin                    17             // sensor pin (initially 3)
#define heartbeat                  13             // LED pin
#define heartbeatPeriod            19             // heartbeat period
#define beaconPin                  11             // beacon pin; only 3, 5, 6, 9, 10 and 11 on ATmega328P
#define beaconMaxPwm              160             // beacon max PWM
#define beaconCycle                30             // beacon cycle time in 0.1s between 1 and 255
#define debounceTime             8500             // debounce time for mechanical contact in µs
#define sendPeriod             300000             // time to wait before sending another value in µs
#define sendStart                 ' '             // serial send start symbol
#define sendDelimiter             ';'             // serial send delimiter
#define sendTerminator            '/'             // serial send terminator symbol
#define beaconS max(1, 43 / beaconCycle)
#define beaconM beaconS * beaconCycle / 2.56

//                             MMA8452Q, MPU-6050
const byte accelAddress[]   = {  0x1C  ,   0x68  };   // accelerometer I2C address
const byte accelID[]        = {  0x2A  ,   0x68  };   // accelerometer ID
const byte accelStatusReg[] = {  0x00  ,   0x3A  };   // accelerometer status register
const byte accelXReg[]      = {  0x01  ,   0x3B  };   // accelerometer X-axis register
const byte accelYReg[]      = {  0x03  ,   0x3D  };   // accelerometer Y-axis register
const byte accelZReg[]      = {  0x05  ,   0x3F  };   // accelerometer Z-axis register
const byte accelWhoAmIReg[] = {  0x0D  ,   0x75  };   // accelerometer device ID register
const byte accelCtrl1Reg[]  = {  0x2A  ,   0x6B  };   // accelerometer control register
const byte accelActive[]    = {  0x01  ,   0x00  };   // accelerometer activate
const byte accelXSeq[]      = {     0  ,      2  };   // accelerometer X-axis sequence
const byte accelYSeq[]      = {     2  ,      0  };   // accelerometer Y-axis sequence
const byte accelZSeq[]      = {     4  ,      4  };   // accelerometer Z-axis sequence
const long maxSamples           = 610;            // accelerometer number of samples

unsigned long const infinity    = 1e9;            // "infinity" constant
unsigned long const sensTimeout = 6e6;            // pulse timeout
unsigned long timeToSend   = infinity;            // elapsed time between two pulses to send out
unsigned long currentTime         = 0;            // current time
unsigned long lastTime            = 0;            // last read time value
unsigned long pulseCounter        = 0;            // counter of total number of pulses
bool sensFlag                 = false;            // last sensor state
unsigned long previousTrans       = 0;
int heartbeatCounter              = 0;
unsigned long beaconMillis  = beaconM;
int beaconCounter                 = 0;
bool accelFound               = false;
long accelX                       = 0;
long accelY                       = 0;
long accelZ                       = 0;
long tX                           = 0;
long tY                           = 0;
long tZ                           = 0;
long samples                      = 0;
long accelSamples                 = 0;
int  accel                        = 0;

void setup() {                                    // setup
  pinMode(sensPin, INPUT_PULLUP);                   // set sensor pin as input with internal pull-ups
  pinMode(heartbeat, OUTPUT);                       // set LED pin as output
  Serial.begin (baudRate);                          // initialize serial port
  Wire.begin();                                     // join i2c bus as master
  for (accel = 0; accel < sizeof(accelAddress) / sizeof(byte); accel++) {
    accelFound = (readRegister(accelAddress[accel], accelWhoAmIReg[accel]) == accelID[accel]);  // check if accelerometer is present
    if (accelFound) break;
  } // for (accel)
  writeRegister(accelAddress[accel], accelCtrl1Reg[accel], accelActive[accel]);
} // setup()

void loop() {                                     // main cycle
  if (digitalRead(sensPin) == LOW) {                // sensor activated
    currentTime = micros();                           // read current time
    if (sensFlag && (currentTime - lastTime > debounceTime)) {  // debounce done
      timeToSend = currentTime - lastTime;                        // load new pulse time
      lastTime = currentTime;                                     // remember current time
      pulseCounter++;                                             // increment pulse counter
      sensFlag = false;                                           // wait for next transition to LOW
    } // if (sensFlag && (currentTime - lastTime > debounceTime))
  } // if (digitalRead(sensPin) == LOW)
  else if (!sensFlag && (micros() - currentTime > debounceTime)) sensFlag = true;  // ignore bounce at transition to HIGH and prepare for debounce
  if (micros() - lastTime > sensTimeout) timeToSend = infinity;                    // load infinity if pulse timed out
  if ((micros() < previousTrans) || (micros() - previousTrans > sendPeriod)) {     // send everything
    previousTrans = micros();                         // remember current time of transmission
    Serial.print(sendStart);                          // send start symbol
    Serial.print(timeToSend);                         // send pulse length
    Serial.print(sendDelimiter);                      // send pulse length delimiter
    Serial.print(pulseCounter);                       // send pulse counter
    if (accelFound) {                                 // send accelerometer data if accelerometer is present
      Serial.print(sendDelimiter);                      // send delimiter
      Serial.print(samples);                            // send number of samples acquired
      Serial.print(sendDelimiter);                      // send pulse length delimiter
      Serial.print(accelX);                             // send X-axis acceleration
      Serial.print(sendDelimiter);                      // send delimiter
      Serial.print(accelY);                             // send Y-axis acceleration
      Serial.print(sendDelimiter);                      // send delimiter
      Serial.print(accelZ);                             // send Z-axis acceleration
    } // if (accelFound)
    Serial.println(sendTerminator);                   // send transmission terminator
    digitalWrite(heartbeat, LOW);                     // begin heartbeat logic
    heartbeatCounter++;
    if (heartbeatCounter > heartbeatPeriod) {         // flash heartbeat
      heartbeatCounter = 0;
      digitalWrite(heartbeat, HIGH);
    }                                                 // end of heartbeat logic
  }                                                 // end of send everything
  byte f = beaconCounter << 2;                      // beacon PWM is activated in quarters of the cycle
  if (beaconCounter & 64) f = ~f;                   // invert PWM if fading out
  if (millis() - beaconMillis > expo(beaconM, f)) {  // check if beacon time has expired
    beaconMillis = millis();                          // get current time for next checks
    beaconCounter += beaconS;                         // increment beacon cycle counter
    if (beaconCounter & 128) analogWrite(beaconPin, 0); // turn off beacon in 3rd and last quarters
    else {                                            // 1st and 2nd quarters of beacon cycle
      f = beaconCounter << 2;                           // calculate beacon PWM
      if (beaconCounter & 64) f = ~f;                   // invert beacon PWM in 2nd quarter
      f = highByte((unsigned int)f * (unsigned int)beaconMaxPwm);  // apply beacon max PWM
      analogWrite(beaconPin, f);                        // send PWM to beacon pin
    }                                                 // done with 1st and 2nd beacon cycle quarters
  }                                                 // done with beacon
  if (accelFound) {                                 // read accelerometer values if accelerometer is present and accumulate for averaging
    byte rawData[6];                                  // initialize buffer
    readRegisters(accelAddress[accel], accelXReg[accel], rawData, sizeof(rawData)); // read accelerometer raw data into buffer
    tX += (int)(rawData[accelXSeq[accel]] << 8 | rawData[accelXSeq[accel]+1]);  // accumulate accelerometer raw X
    tY += (int)(rawData[accelYSeq[accel]] << 8 | rawData[accelYSeq[accel]+1]);  // accumulate accelerometer raw Y
    tZ += (int)(rawData[accelZSeq[accel]] << 8 | rawData[accelZSeq[accel]+1]);  // accumulate accelerometer raw Z
    accelSamples++;                                   // increment sample counter
    if (accelSamples >= maxSamples) {                  // desired number of samples are collected:
      accelX = tX;                                    // average and convert accelerometer raw X to ten thousands slope
      accelY = tY;                                    // average and convert accelerometer raw Y to ten thousands slope
      accelZ = tZ;                                    // average and convert accelerometer raw Z to ten thousands slope
      samples = accelSamples;                         // get number of samples to be sent
      tX = 0;                                         // zero raw accumulators
      tY = 0;
      tZ = 0;
      accelSamples = 0;                               // zero sample counter
    } // if (accelSapmples > maxSamples)
  } // if (accelFound)
} // loop()

unsigned long expo(unsigned long m, byte c) {
  if (c < 51)  return (m * 2);
  if (c < 102) return (m * 1.290);
  if (c < 153) return (m * 0.831);
  if (c < 240) return (m * 0.535);
  return (m * 0.345);
} // expo

byte readRegister(byte addr, int reg) {           // read single byte from accelerometer
  Wire.beginTransmission(addr);                     // initialize transmission
  Wire.write(reg);                                  // send register address
  Wire.endTransmission(false);                      // send above and keep connection open
  Wire.requestFrom(addr, (byte)1);                  // request 1 byte, bus is released when done by default
  if (Wire.available()) {                           // wait for data to arrive
    return Wire.read();                               // return byte
  } else {
    return 0;
  } // if (Wire.available())
} // byte readRegister(byte addr, int reg)

void readRegisters(byte addr, int reg, byte *buffer, byte len) { // read len bytes starting from reg; store bytes in buffer
  Wire.beginTransmission(addr);                     // initialize transmission
  Wire.write(reg);                                  // send register address
  Wire.endTransmission(false);                      // send above and keep connection open
  Wire.requestFrom(addr, (byte)len);                // request len bytes, bus is released when done by default
  if (Wire.available()) {                           // wait for data to arrive
    for (int i = 0; i < len; i++) buffer[i] = Wire.read();  // read len bytes into buffer
  } // if (Wire.available())
} // void readRegisters(byte addr, int reg, byte *buffer, byte len)

void writeRegister(byte addr, byte reg, byte data) {  // write single byte to accelerometer
  Wire.beginTransmission(addr);                     // initialize transmission
  Wire.write(reg);                                  // send register address
  Wire.write(data);                                 // send data
  Wire.endTransmission();                           // send above and close connection
} // void writeRegister(byte reg, byte data)

--- Край на кода ---

Иван

IvanC:
Използваният Bluetooth модул е HC-05. Фабричните му настройки са за скорост на обмен по серийния интерфейс (бодрейт) 9600 бита в секунда, което е прекалено бавно за нуждите на измервателния вагон. Програмата в Ардуиното използва 115200 бита в секунда, заради което настройките на HC-05 трябва да се променят.

Това става в т.нар. "AT" режим, по подобие на едновремешните модеми. Влизането в "AT" режим е малко особено - при изключено захранване на модула, трябва да се натисне и задържи бутонът на модула и да се подаде захранването. След около секунда - две бутонът може да се отпусне и светодиодът на модула трябва да мига бавно, което показва, че модулът е в "AT" режим. Ако светодиодът мига бързо, модулът не е успял да влезе в "AT" режим и трябва да повторим процедурата наново.

След като модулът е влязъл в "AT" режим, стартираме серийния монитор на Ардуино средата. Разбира се предварително трябва да сме избрали верният COM порт за USB-Serial кабела/преобразувателя. Всъщност серийният монитор на Ардуино средата може да се стартира и преди да сме вкарали HC-05 модула в "AT" режима.

Важно е да настроим серийния монитор правилно. Това става, като изберем "38400 baud" долу вдясно на екрана на монитора и "Both NL & CR" отляво на скоростта. Сега за да тестваме връзката с HC-05 модула, цъкаме с мишката горе на екрана на серийния монитор, вляво от бутона "Send", за да се появи там мигащ курсор и написваме с ГЛАВНИ БУКВИ НА ЛАТИНИЦА (всичко, което пишем като команди на HC-05 модула е с главни латински букви):

AT

и натискаме бутона "Enter" на клавиатурата или бутона "Send" на серийния монитор. Въобще след всяка въведена команда, трябва да натиснем един от тези два бутона, за да бъде изпратена към модула.

Ако всичко работи както трябва, на екрана трябва да се изпише отговорът от модула

OK

Ако искаме да видим текущата настройка на скоростта на обмен на серийния интерфейс, написваме

AT+UART

и натискаме "Enter" на клавиатурата или "Send" на прозореца на серийния монитор, на което модулът ще отговори например с

AT+UART:9600,0,0

Отговорите от модула може да пропуснат "AT" преди плюса, в зависимост от техния фърмуер, така че това да не ни хвърля в оркестъра. Горният отговор показва, че скоростта на обмен на модула е 9600 бита в секунда (бода).

За да променим скоростта на 115200 бита в секунда, написваме

AT+UART=115200,0,0

и изпращаме тази команда към модула с натискане на един от двата бутона, както съм написал по-горе. Модулът трябва да отговори

OK

за да потвърди, че е възприел и изпълнил командата. Ако искаме може да изпратим пак командата

AT+UART

на която модулът трябва да отговори с

AT+UART:115200,0,0

което означава, че настройката е направена.

Ако модулът е вече използван, трябва да му проверим паролата, която ще ни трябва при свързване с модула от някое от устройствата/програмите за индициране на параметрите, измервани от вагона. Това става с командата

AT+PSWD

на което модулът ще отговори примерно

AT+PIN:"1234"

или

AT+PIN:1234

Важното тук е да обърнем внимание дали в отговора паролата (пинът) е заграден в кавички или не. Ако искаме да променим паролата, командата е

AT+PSWD="9876"

или

AT+PSWD=9876

като вместо 9876 въведем 4-цифрен пин по наше желание, който обаче трябва да запомним или още по-добре да си го запишем. Важното е да въведем новия пин в кавички или без такива в зависимост от това как модулът ни е отговорил преди това на комадната AT+PSWD - със или без кавички. Ако отговорът е с кавички, пишем ги и ние, ако е без - въвеждаме новия пин без кавички.

Ако модулът е възприел новата команда, трябва да ни отговори с "OK". Препоръчвам отново да запитаме модула за паролата му, за да сме напълно убедени, че е възприета правилно.

Друго, което може да искаме да променим е името на модула. Фабричната стойност (име) е "HC-05". След време, ако използвате повече модули в различни устройства, всичките ще се появяват на компютъра или на GSM-а/таблета с HC-05 и ще е трудно да ги разграничим. Проверката за името става с командата

AT+NAME

отговорът от модула ще е

AT+NAME:HC-05

За да променим името на модула изпращаме команда

AT+NAME=MESSWAGEN-1

като вместо MESSWAGEN-1 слагаме името, което желаем.

Друга важна настройка е за ролята на модула. Този в измервателния вагон трябва да е "SLAVE", т.е. подчинен. Заводската настройка е за "SLAVE", но е желателно да го проверим, а ако модулът вече е използван за други нужди и да го променим, ако трябва. Командата за проверка е

AT+ROLE

на което модулът трябва да отговори с

AT+ROLE:0 ако е "SLAVE"

или

AT+ROLE:1 ако е "MASTER"

Ние искаме първото. Ако модулът е отговорил с второто, изпращаме команда

AT+ROLE=0

При изпращане на команда за промяна на AT+ROLE, модулът ще излезе от "AT" режима и затова обикновено това е последната команда, която изпращаме. Ако искаме отново да вкараме модула в "AT" режим, изключваме му захранването и повтаряме процедурата, която съм описал в началото на мнението.

Това е по настройките на HC-05 модула. В следващо мнение ще спомена и за настройките на HC-05 за устройството за индициране на скоростта и изминатия път, които се различават само при AT+ROLE командата. Това е ако някой реши да си направи това устройство вместо да използва приложението за Андроид устройство или програмата за Windows. Аз лично предпочитам да използвам самоделната индикация.

Иван

IvanC:
Приемникът на информацията от измервателния вагон е също базиран на Ардуино Про-мини и HC-05 Bluetooth модул. За показване на измерените от вагона и изчислени параметри използвам течнокристална индикация с 16х2 символа, т.е. с 2 реда с 16 символа на ред. Индикацията има I2C към паралелен преобразувател. Така връзките към процесора се свеждат до минимум (4 проводника - маса, +5 V, SDA и SCL).

Ето схемата на приемника с индикацията:



Захранването е идентично на това в измервателните вагони 2 и 3 - литиево-йонен акумулатор и зарядно/преобразувател до 5 V от USB батерия и ключе за захранването.

Настройките на HC-05 Bluetooth модула са почти идентични с тези на модула във вагона. Единствената разлика е настройката за MASTER:

AT+ROLE=1

Важно е паролата/пинът на модула в индикацията да е еднакъв с този на модула във вагона, така че преди да направим настройката с AT+ROLE, трябва да сменим паролата/пина с тази, която сме записали в HC-05 модула на вагона.

Ето всички настройки за модула на индикацията:

AT+UART=115200,0,0
AT+PSWD="същата_парола_като_на_вагона"
AT+ROLE=1

Да припомня, че паролата се въвежда с или без кавички в зависимост от това как модулът е отговорил на запитването AT+PSWD - с или без кавички.
Няма нужда да се променя името на модула, защото модулът на индикацията няма да се свързва с компютър или Андроид устройство.

Иван

Навигация

[0] Списък на темите

[#] Следваща страница

[*] Предходна страница

Премини на пълна версия