Автор Тема: Измервателен вагон  (Прочетена 11424 пъти)

IvanC

  • Trade Count: (0)
  • Sr. Member
  • ****
  • Публикации: 274
  • Рейтинг: 198
Измервателен вагон
« -: 02 Април 2022, 22:18:06 »
Публикувам темата в този раздел, макар че не е във връзка с цифрово управление, но тъй като използвам микропроцесори и всичко е цифрово, няма как да е за раздела за аналогова техника.

Знам, че Пико имат измервателен вагон, но реших сам да си направя такъв. Вагонът използва датчик на Хол и магнити, залепени на едно от колелата за отчитане на изминатото от вагона разстояние и акселерометър за измерване на наклона на вагона. Процесорът във вагона е Ардуино Про-мини и само събира данните от датчиците и ги изпраща по серийния интерфейс към HC-05 Bluetooth модул.

Схемата:



Използвам датчик на Хол OH137, но може да се използва кой да е датчик на Хол, който работи на 5V и е достатъчно малък, за да се събере до някое от колелата на вагона. Магнитите са плоски, кръгли с диаметър 2 мм и дебелина 1 мм. Магнитите и датчикът на Хол са от eBay.

Програмата в Про-минито работи с два вида акселерометри - MMA8452Q и MPU-6050, като автоматично разпознава кой от двата е свързан. Ако при стартирането на програмата не се открие акселерометър, програмата продължава да работи без да изпраща данни за наклона. Акселерометърните модули са също от eBay.

Светодиодът D1 и баластното му съпротивление R3 са по желание на брат ми. Той е монтирал жълт светодиод на покрива на вагона и този светодиод мига с ефект на въртящ се "буркан". Аз не го използвам на моите измервателни вагони. Отделно, програмата светва периодично светодиода на Про-минито, свързан на извод 13, което също може да се използва като индикация за работа на програмата.

За захранване използвам литиево-йонна акумулаторна батерия от USB батерия, заедно с преобразувателя от USB батерията. Този преобразувател зарежда акумулатора при свързване на външно 5-волтово захранване и качва и стабилизира напрежението от акумулатора на 5V за захранване на модулите във вагона. Свързал съм ключе между USB преобразувателя и захранването на модулите във вагона. Всичко това ще покажа по-нататък нагледно със снимки.

Вагонът няма индикация. Причината за това е, че когато вагонът се използва на макет, не винаги има достъп до вагона и ако има индикация, тя няма да се вижда. Освен това, както споменах по-горе, програмата на вагона само събира необработени данни и ги изпраща към блутута без каквато и да е обработка. Идеята е да не се губи време в обработката на данните, за да не се пропускат импулси от датчика на Хол или да се допуска грешка при измерването на времето между импулсите. Обработката на данните и индицирането им стават в други устройства, което позволява по-голяма свобода при написването на програмите за тези устройства.

В момента имам три начина за обработка и индициране на данните:

1. Устройство на базата на Arduino Про-мини с 16х2 индикация.
2. Приложение за Android устройство - телефон или таблет.
3. Програма за Windows.

За тези програми - по-нататък.

Иван

IvanC

  • Trade Count: (0)
  • Sr. Member
  • ****
  • Публикации: 274
  • Рейтинг: 198
Измервателен вагон номер 1
« Отговор #1 -: 02 Април 2022, 23:11:02 »
Това са снимки от направата на първия измервателен вагон. В него няма акселерометър. Акселерометър монтирах в измервателен вагон номер 3, така че има време да стигна до него.

Монтиране на магнитите:










Подготовка и монтаж на датчика на Хол:










На този вагон се опитах да използвам захранване от релсите. За съжаление не се получи. Проблемът е от кратките прекъсвания на захранването, когато няма буфериране. Блутут модулът се рестартира и докато приемникът с индикацията се свърже наново, отново поради нестабилно токоснемане, блутутът във вагона се рестартира и връзката между двата блутута така и не може да се осъществи. Само за пробата използвах голям кондензатор:



Така схемата работеше надеждно, но ми беше ясно, че няма как да го направя за постоянно - не исках да монтирам кондензатори или акумулатор в салона, защото ще се виждат. Опитах се да сложа кондензатори в тоалетните, но не успях да събера достатъчен капацитет за надеждна работа.

Ето и (много) кратък клип от първите проби - индикаторната схема е все още събрана на breadboard:

https://youtu.be/pvOVvkhxwPQ

В крайна сметка монтирах конектор за захранване на измервателната схема от външна USB батерия. Батерията поставям в открит вагон, закачен непосредствено до измервателния вагон:




Иван


IvanC

  • Trade Count: (0)
  • Sr. Member
  • ****
  • Публикации: 274
  • Рейтинг: 198
Измервателен вагон номер 2
« Отговор #2 -: 03 Април 2022, 00:16:28 »
Измервателен вагон номер 2 е с литиево йонна акумулаторна батерия във вагона. Както писах в първото мнение в темата, използвам USB преобразувател за зареждането на акумулатора и за формиране на стабилизирано напрежение 5V за измервателната схема.

За съжаление не можах да намеря снимки от монтирането на модулите във вагона, но намерих клип, който съм качил на Тубата, където този измервателен вагон е в състава на влака за "товарната проба" на шасито за БДЖ 46 ("баба Меца") - вагонът на опашката на влака. Виждат се светлините на процесорната платка и на блутута (червени):

https://youtu.be/h9K--d2HZIs

Принципно схемата на захранването е идентична с тази на измервателен вагон номер 3, снимки на който ще пусна в следващото мнение. Монтажът на датчика на Хол пък е идентичен с този на измервателен вагон номер 1. Акселерометър няма.

USB конекторът и ключето за захранването са на дъното на вагона - както при измервателен вагон номер 3.

Иван

pach

  • Trade Count: (42)
  • Hero Member
  • *****
  • Публикации: 12256
  • Рейтинг: 1308
  • ТТ & Н0; DR & BDZ;ep.I-III(~VI)&selfmade
    • За моделизма
Re: Измервателен вагон
« Отговор #3 -: 03 Април 2022, 12:49:48 »
Страхотно! Цяла сутрин чета, гледам и препрочитам! С нетърпение очаквам третия вагон и останалите подробности!
Тази тема, ще си я направя в  поредния PDF-файл, за папката от "Иван и Владо С" на компа ми! Благодаря момчета!
 :hi: :hi: :hi:

IvanC

  • Trade Count: (0)
  • Sr. Member
  • ****
  • Публикации: 274
  • Рейтинг: 198
Измервателен вагон номер 3
« Отговор #4 -: 03 Април 2022, 23:23:05 »
Този вагон направих преди да добавя акселерометър към програмата и вагона. Мислех си, че съм направил снимки след добавянето на акселерометъра, но такива не намерих. Затова в края на мнението ще обясня накратко с думи как го монтирах. Свързването му е показано на схемата в първото мнение в темата.

Вагонът е на Пико, номер 58985. Той е много подходящ заради "сандъка" на шасито, което ще стане ясно от снимките.

И така, това са модулите, които използвам (без акселерометъра):






Направа на "дънна" платка:








Подготовка на Ардуиното:



Пиновете, които стърчат нагоре са за куплунга на датчика на Хол.


Дънната платка монтирана на вагона:



Всъщност тук е удачно да спомена, че впоследствие монтирах модула на акселерометъра залепен с тънко двустранно тиксо към дънната платка в зоната под блутут модула и го опроводих към захранването на дънната платка. На Ардуиното запоих още два пина към отворите за A4 и A5, а от към дънната платка запоих проводници с 2-пинов конектор с който да свържа SDA и SCL изводите на модула на акселерометъра към А4 и А5 изводите на Ардуиното.


Следва преработването на USB батерията:



На горната снимка се вижда, че съм махнал големия USB конектор и съм запоил контактната пластина за плюса на батерията на 90 градуса спрямо оригинала. Кутията на USB батерията е скъсена и съм изрязал малко парченце от капака, което на долните снимки се вижда вече залепено към "езичето" на капака. Така "езичето" подпира USB конектора, за да не се измества при включване на USB кабела за зареждане.



















Дънната платка и кутията на батерията са хванати към сивия детайл с малки винтчета - виждат се при увеличаване на снимките.



Магнитите, залепени на колооста:




Монтиране на датчика на хол:











Както се вижда, използвал съм конектор, залепен за шасито на вагона, в който се включва датчикът на Хол. По този начин местоположението на датчика може да бъде регулирано много лесно.




Вагонът завършен:








Кратко клипче от първите проби на вагона:

https://youtu.be/5CFYXPNedF4

Иван

IvanC

  • Trade Count: (0)
  • Sr. Member
  • ****
  • Публикации: 274
  • Рейтинг: 198
Измервателен вагон ВЦ номер 2
« Отговор #5 -: 03 Април 2022, 23:50:23 »
В предните мнения показах измервателните вагони, направени от мен. В това мнение пускам снимките на втория измервателен вагон направен от брат ми Владо (VladoC). Коментарите са също неговите и понеже са много кратки, в края ще добавя и няколко коментара от мен.

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




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






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







Владо

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

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

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

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

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

https://youtu.be/5TWFyj3nhME

Иван
« Последна редакция: 03 Април 2022, 23:53:00 от IvanC »

IvanC

  • Trade Count: (0)
  • Sr. Member
  • ****
  • Публикации: 274
  • Рейтинг: 198
Измервателен вагон номер 3 - акселерометър
« Отговор #6 -: 04 Април 2022, 04:32:53 »
Разглобих моя измервателен вагон 3, за да направя снимки на монтирания акселерометър. Бях забравил какъв кошмар е да се разглоби и после да се сглоби. При разглобяването имам чувството, че всички щипки се чупят, а при сглобяването, двете кулиси и техните пружинки изпадат при най-малкото сътресение, а две ръце не стигат да крепят двете кулиси на място и да сглобяват останалите части. Както и да е, с малко гресчица на пружинките на кулисите и много "сладки" думи казани на ум (за да не чуе жената и да се чуди защо си псувам сам...!) успях да сглобя обратно вагона успешно и без щети.







Иван

IvanC

  • Trade Count: (0)
  • Sr. Member
  • ****
  • Публикации: 274
  • Рейтинг: 198
Измервателен вагон - програма за вагона
« Отговор #7 -: 05 Април 2022, 02:27:52 »
Ето кода на програмата за Ардуиното във вагона:

#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

  • Trade Count: (0)
  • Sr. Member
  • ****
  • Публикации: 274
  • Рейтинг: 198
Използваният 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

  • Trade Count: (0)
  • Sr. Member
  • ****
  • Публикации: 274
  • Рейтинг: 198
Измервателен вагон - Bluetooth приемник с индикация
« Отговор #9 -: 07 Април 2022, 21:42:10 »
Приемникът на информацията от измервателния вагон е също базиран на Ардуино Про-мини и 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 - с или без кавички.
Няма нужда да се променя името на модула, защото модулът на индикацията няма да се свързва с компютър или Андроид устройство.

Иван

IvanC

  • Trade Count: (0)
  • Sr. Member
  • ****
  • Публикации: 274
  • Рейтинг: 198
Измервателен вагон - Bluetooth приемник с индикация
« Отговор #10 -: 08 Април 2022, 04:31:16 »












Моделите на кутийката за 3D принтер:

Основа

Капак

И двете ги печатам с лицето надолу, като на основата включвам печатането на подпори, за да се получат сравнително добри вътрешни повърхности.

Иван

IvanC

  • Trade Count: (0)
  • Sr. Member
  • ****
  • Публикации: 274
  • Рейтинг: 198
Bluetooth приемник с индикация - програма
« Отговор #11 -: 08 Април 2022, 05:05:35 »
Програмата на Ардуиното за индициране на скоростта и изминатия път:

#define verNum               "IC 2.0 "            // sketch version number                            Номер на версията на програмата
#define verDate              "07/04/21"           // and date                                         и дата на завършване

#include <LCD_I2C.h>                              // libraries used                                   Използвани библиотеки
#include <Wire.h>

unsigned long const wheelDia = 480;               // wheel running diameter in mm divided by          Диаметър на колелото в mm разделен
                                                  // number of pulses per rotation (magnets)          на броя импулси на оборот (на броя магнити)
                                                  // 480 for ROCO, 501 for PIKO

#define tarePin                  3                // zeroing button pin                               Извод за бутона за нулиране на изминатия път

#define baudRate            115200                // serial baud rate                                 Бодрейт на серийния интерфейс
#define serialTimeout         3200                // serial read timeout in ms                        Таймаут на серийния интерфейс в милисекунди
#define readStart              ' '                // serial read start symbol                         Константа за нормалната работа на програмата
#define widthDelimiter         ';'                // pulse length delimiter                           Константа за нормалната работа на програмата
#define readTerminator         '/'                // serial read terminator symbol                    Константа за нормалната работа на програмата

#define LCD_address           0x27                // LCD i2c address                                  Адрес на дисплея - използвай i2c_scanner за прочитане на адреса
#define LCD_columns             16                // LCD columns                                      Колони на дисплея
#define LCD_rows                 2                // LCD rows                                         Редици на дисплея

#define heartbeat               13                // LED pin                                          Извод на светодиода на Ардуиното

#define wheelConstKPH        11310UL * wheelDia   // wheel constant for km/h                          Константа за нормалната работа на програмата
#define wheelConstMPH         7028UL * wheelDia   // wheel constant for MPH                           Константа за нормалната работа на програмата
#define wheelConstKM         31831UL              // wheel constant for km                            Константа за нормалната работа на програмата
#define wheelConstMI         51227UL              // wheel constant for mile                          Константа за нормалната работа на програмата
#define wheelConstCM           277UL              // wheel constant for cm                            Константа за нормалната работа на програмата

int heartbeatCounter           = 0;               //                                                  Променлива за нормалната работа на програмата
unsigned long kph = 0, mph = 0, pL = 0, t = 0;    //                                                  Променливи за нормалната работа на програмата
unsigned long s, l, r, sign;                      //                                                  Променливи за нормалната работа на програмата
long nSk = 0, iSk = 0, nSm = 0, iSm = 0, vC = 0;  //                                                  Променливи за нормалната работа на програмата
unsigned long rData[] = {0, 0, 0, 0, 0, 0, 0, 0}; //                                                  Променлива за нормалната работа на програмата

LCD_I2C lcd(LCD_address, LCD_columns, LCD_rows);  // initialize display                               Дефиниране и инциализиране на дисплея

void setup() {                                    // begin setup                                      От тук до края е самият код на програмата
  pinMode(heartbeat, OUTPUT);                       // set LED pin as output
  pinMode(tarePin, INPUT_PULLUP);                   // set zero pin as input with internal pull-up
  Serial.begin (baudRate);                          // initialize serial port
  lcd.begin();                                      // initialize LCD
  lcd.backlight();                                  // turn on backlight
  lcd.clear();                                      // clear screen
  lcd.print("Speedometer");                         // display program name
  lcd.setCursor(0, 1);
  lcd.print(String(verNum) + String(verDate));      // display version and date
  delay(1200);
}                                                 // end of setup

void loop() {                                     // begin main cycle
  int lF = 0, sTimedout;
  do {
    if (Serial.available()) {                         // read serial
      t = millis();                                     // read current timestamp for serial timeout
      char c = Serial.read();
      if (c == readStart) {r = 0; vC = 0; sign = 1;}
      else if (c == widthDelimiter) {rData[vC] = r * sign; r = 0; vC++; sign = 1;}
      else if (c == '-') sign = -1;
      else if (c != readTerminator) r = r * 10 + String(c).toInt();
      else if (c == readTerminator) {
        rData[vC] = r * sign; lF = true; s = rData[0]; l = rData[1];
      } // if (c == readTerminator)
    }  // if (Serial.available())
    sTimedout = ((millis() - t) > serialTimeout);
  } while ((lF == 0) && (sTimedout == 0));
  if ((lF) && (s)) {                                // data received
    nSk = wheelConstKPH / s;                          // calculate new speed
    iSk = nSk - kph;                                  // calculate speed increment
    if (nSk >  2) iSk /= 2; if (nSk >   8) iSk /= 2;  // adjust speed increment
    if (nSk > 30) iSk /= 2; if (nSk > 120) iSk /= 2;  // based on new speed
    if (nSk > 480) iSk /= 2;
    if (abs(nSk - kph) < abs(iSk) * 3 / 2) kph = nSk; else kph += iSk;
    nSm = wheelConstMPH / s;                          // do the same as above for mph
    iSm = nSm - mph;
    if (nSm >  2) iSm /= 2; if (nSm >   8) iSm /= 2;
    if (nSm > 30) iSm /= 2; if (nSm > 120) iSm /= 2;
    if (nSm > 480) iSm /= 2;
    if (abs(nSm - mph) < abs(iSm) * 3 / 2) mph = nSm; else mph += iSm;
    displaySpeed(0, 0, kph % 1000, " km/h ");         // display speed in km/h
    displaySpeed(0, 1, mph % 1000, " mph ");          // display speed in mph
    unsigned long km = ((l - pL) * wheelDia / wheelConstKM) % 10000UL;    // calculate distance in km
    unsigned long cm = ((l - pL) * wheelDia / wheelConstCM) % 1000000UL;  // calculate distance in miles or cm
    int i = 9; if ((km < 1000) && (cm < 100000)) i--; // figure out from where to display distance
    displayDistance(i, 0, km, " km ");                // display distance in km
    displayDistancm(i - 1, 1, cm, " cm ");            // display distance in cm
    heartbeatCounter = (heartbeatCounter + 1) % 17;   // heartbeat logic
    digitalWrite(heartbeat, !heartbeatCounter);       // flash heartbeat
  }                                                 // end of calculating received data
  if (!digitalRead(tarePin)) pL = l;                // zero button pressed - get zero distance counter value
  if (sTimedout) {lcd.clear();lcd.print("   NO SIGNAL!");}  // serial timed out - display "NO SIGNAL!"
  t = millis();                                     // read current timestamp for serial timeout
  if (t % 16 == 0) {
    if (abs(nSk - kph) < abs(iSk) * 3 / 2) kph = nSk; else kph += iSk;
    if (abs(nSm - mph) < abs(iSm) * 3 / 2) mph = nSm; else mph += iSm;
  }
}                                                 // end of main cycle

void displaySpeed( int c, int r, long n, String s) {
  lcd.setCursor(c, r);
  if (n < 100) lcd.print(" ");
  if (n <  10) lcd.print(" ");
  lcd.print(String(n) + s);
}

void displayDistance( int c, int r, long n, String s) {
  lcd.setCursor(c, r);
  if (n < 1000) lcd.print(" ");
  if (n <  100) lcd.print(" ");
  lcd.print(String(n / 10) + "." + String(n % 10) + s);
}

void displayDistancm( int c, int r, long n, String s) {
  lcd.setCursor(c, r);
  if (n < 100000) lcd.print(" ");
  if (n <  10000) lcd.print(" ");
  if (n <   1000) lcd.print(" ");
  if (n <    100) lcd.print(" ");
  if (n <     10) lcd.print(" ");
  lcd.print(String(n) + s);
}

В наалото на кода има един ред, който задава константата на колелото и датчика на Хол:

unsigned long const wheelDia = 480;               // Диаметър на колелото в mm разделен
                                                                         // на броя импулси на оборот (на броя магнити)
                                                                         // обикновено 480 за ROCO, 501 за PIKO

Тази константа е равна на диаметъра на колелото на оригинала в милиметри, разделен на броя магнити. Под "оригинала" имам предвид колелото на вагон в мащаб 1:1.

От опит установих, че диаметърът на колоосите на вагоните на Роко отговаря на диаметър на оригинала 960 мм, а на Пико на 1002 мм. Тъй като използвам два магнита на колело, съответната константа е 480 за колоос на Роко и 501 за Пико, което съм написал и в коментара на програмата.

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

Друга константа, която може да се наложи да променим е адресът на дисплея, зададен на реда

#define LCD_address           0x27                // Адрес на дисплея - използвай i2c_scanner за прочитане на адреса

Този адрес е или 0x27 или 0x3f, в зависимост от това кой I2C чип е монтиран на втората платка на дисплея (I2C адаптера). Ако чипът е PCF8574(T), адресът е 0x27, ако чипът е PCF8574А(Т), адресът е 0x3f.

Програмата използва две библиотеки. Едната е "Wire", която идва заедно с Ардуино средата, втората е "LCD_I2C". Втората може да е моя разработка, затова пускам препратка за изтегляне:

Библиотека LCD_I2C

В ZIP файла ма една папка, която трябва да се копира в папката "libraries" в работната папка на Ардуино средата - където са по подразбиране програмите за Ардуино.


Иван

IvanC

  • Trade Count: (0)
  • Sr. Member
  • ****
  • Публикации: 274
  • Рейтинг: 198
Измервателен вагон - Bluetooth приемник с индикация
« Отговор #12 -: 08 Април 2022, 05:24:50 »
При включване на захранването на скоростомера, на екрана за кратко се изписва информация за програмата:




Ако скоростомерът не намери измервателен вагон с парола/пин, отговарящ на неговия, на екрана се изписва, че няма сигнал от измервателен вагон:




При осъществена връзка с измервателния вагон, на екрана се изписват моментната скорост в км/ч (km/h) и мили в час (mph) и изминатият път на "оригинала" в километри (km) и на модела в сантиметри (cm). Скоростта е винаги на оригинала, т.е. мащабната скорост.



Константите в програмата са за HO, т.е. мащаб 1:87, поради което мащабната скорост и изминат път в километри са за този мащаб. Ако мащабът е различен от HO, някои от константите в програмата трябва да се променят.

По поръчка на Владо Цанков, програмата следи извод 3 на Ардуиното (tarePin). Между този извод и маса може да се свърже бутон. При натискането на бутона, показанието за изминатия път се нулира. Това нулиране е само в скоростомера. Вагонът продължава да изпраща броя регистрирани импулси от момента на подаване на захранването му. Ако изключим скоростомера и го включим отново, показанието за изминатия път ще е отново от включване на захранването на вагона. Разбира се може да нулираме показанието на индикацията по всяко време и повече от един път.

Пропуснах да спомена в предно мнение, че на платката на модула на индикацията има едно тримерче (врътка за отвертка - на снимките в две мнения преди това  е синичко с бяла врътка) - с него се настройва контрастът на индикацията. Ако след включване на захранването, на екрана не се вижда нищо или се виждат само черните точки на матрицата, трябва да завъртим това тримерче в едната или другата посока, за да си нагласим желания контраст на текста.

Иван
« Последна редакция: 08 Април 2022, 05:32:11 от IvanC »

IvanC

  • Trade Count: (0)
  • Sr. Member
  • ****
  • Публикации: 274
  • Рейтинг: 198
Както писах в първото мнение в тази тема, вторият метод за индициране на данните от измервателния вагон е с Android устройство. Проложението иска сравнително бързо устройство иначе показва информацията с голямо закъснение. Това се получи след като добавих акселерометърът (ъгломерът) към вагона. Ако вагонът няма акселерометър, може да се използва и по-стар/бавен таблет или телефон.

Приложението може да се свали от тук:

Скоростомер плюс за Android устройство

Приложението не е в магазина на Гугъл по лично мои съображения. Поради това, за да се инсталира, трябва в телефона/таблета да е разрешено инсталиране на приложения, които не са в гугълския магазин. Това е доста специфично за всяко устройство, така че ако не знаете как да го разрешите, търсете някой познат, който е навътре с това. Иначе операционната система няма да позволи инсталирането на приложението и дори може да изтрие файла.

Приложението не иска никакви разрешения от операционната система и няма никакви реклами (една от причините да не го публикувам в магазина на Гугъл). Приложението не иска никакви активирания или заплащания (друга причина да не е в магазина).

При стартиране на приложението, на екрана се показва главният екран:



Всички съобщения са на английски. Не съм ги превеждал на български, защото всичко е елементарно за работа и се подразбира от мерните единици, които са показани.

Когато няма свързан измервателен вагон, приложението е в демонстрационен режим - както е показано по-горе. За да се свърже измервателен вагон, първо трябва да се направи "съешването" от Bluetooth настройките на телефона. Това е идентично с процеса с всяко друго Bluetooth устройство. Да припомня, че паролата/пинът е този който сме прочели или променили за HC-05 модула във вагона - няколко мнения по-горе. Тук е особено полезно ако сме променили името на модула с нещо "по-говорящо".

След като вагонът е вече свързан с телефона/таблета, трябва да се избере в приложението. Това става, като цъкнем на бутона "Bluetooth Devices" (Bluetooth устройства). Изписва се списък с всички свързани към телефона/таблета устройства:



Горната картинка е за моя телефон. Понеже съм правил експерименти с много HC-05 модули, списъкът е доста дълъг и пак понеже на повечето не съм променял имената (защото е само за тест), става голяма обърквация кой модул къде се намира. Та затова силно препоръчах (и все още препоръчвам) да се промени името на модула в измервателния вагон с нещо по-смислено.

И така, намираме измервателния вагон и цъкаме на името му. Ако захранването на вагона е включено, ще се покаже информацията от него, ако не, операционната система ще каже, че не намира избраното устройство и приложението ще се върне в демонстрационен режим.


Настройки на приложението

За да променим настройките на приложението, цъкаме на бутончето с трите хоризонтални чертички в горния десен ъгъл.



Настройките са:

1. Диаметър на колелото на вагона (Wheel Diameter) в милиметри. Това е диаметърът на колелото на оригинала, разделен на броя магнити - също, както в програмата за индикацията с Ардуино.

2. Мерна единица (Unit of Measure) - километри (km) или мили (mi) (сухоземни, а не морски/въздушни). Бутонът показва текущо активната мерна единица. Мерната единица се използва за изминат път (мащабен на "оригинала") и мащабна скорост, съответно в километри в час (km/h) или мили в час (mph).

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

4. Вид на индикацията на скоростта (Display Type) - аналогова (Analog) или цифрова (Digital). Цъкаме на бутона за да превключим между двата типа. И тук, както при избор на мерна единица, бутонът показва текущо активния вид на индикацията. На следващите картинки е показана цифрова индикациа, а на първата в това мнение е показана аналоговата.

5. Обхват на индикацията на скоростта - максималната скорост на циферблата на аналоговата индикация. Не е активно при избрана цифрова индикация.

Като сме свършили с настройките, цъкаме на бутончето със стрелката в горния ляв ъгъл, за да се върнем на основния екран.

Ако приложението се е свързало с измервателен вагон, основният екран изглежда така:



Вижда се "цифровото" индициране на скоростта, за разлика от "аналоговото", което е поазано на първата картинка в мнението.

Ако скоростта е под 20 км/ч, тя се индицира с точност до една десета в "цифров" режим:



Искам да обърна внимание, че ако вагонът има акселерометър, на екрана се показват наклоните на вагона по надлъжната ос X и по напречната ос Y в едно на хиляда (промили) с точност до една десета. Показва се и бутон за калибриране на датчика. Ако вагонът няма акселерометър, даните за наклона не се показват.

И така, на основния екран се показват:

1. Мащабната скорост в км/ч или мили в час (mph)
2. Изминатият мащабен път в км или мили
3. Избраният мащаб
4. Изминатият моделен път в сантиметри (cm)
5. Наклонът на вагона по дължината му (X) и страничният му наклон (Y) в промили (едно на хиляда)

Бутонът "Reset" нулира показанията за изминатия път. Това става в приложението. Ако затворим приложението и го стартираме отново, то ще покаже изминатия път от вагона от включване на захранването му.

Приложението запомня последното Bluetooth устройство, с което е работило последно и при следващо стартиране автоматично го търси, за да се свърже с него. Ако не го открие (или ако Bluetooth на Андроид устройството е изключен), приложението автоматично минава в демонстрационен режим.


Калибриране на датчика за наклона (акселерометъра)

1. Поставяме вагона на релси, не на ребордите. Не е задължително повърхността, на която са релсите да е хоризонтална. Раздвижваме вагона леко напред-назад, за да улегнат колоосите в гнездата им (където лагеруват).

2. Изчакваме показанията на наклона да се успокоят.

3. Цъкаме на бутона "Calibrate". На екрана се появяват едни точки, които изчезват една по една:



а бутонът "Calibrate" става недостъпен. По време на тази стъпка е важно вагонът да не се разклаща.

4. Когато приложението завърши този етап от калибрирането, на екрана се изписва съобшение да завъртим вагона на 180° (Rotate Car 180°) и бутонът "Calibrate" отново става достъпен:



5. Вдигаме вагона от релсите, завъртаме го на 180° и го поставяме отново на релсите, на същото място. Отново раздвижваме вагона леко напред-назад.

6. Пак изчакваме показанията на наклона да се успокоят.

7. Пак цъкаме бутона "Calibrate". На екрана отново се показват точките, които постепенно изчезват, бутонът е недостъпен и пак внимаваме да не се разклати вагонът.

8. Когато завърши и вторият етап от калибрирането, приложението се връща в изходно положение.

Калибрирането на датчика за наклона (акселерометъра) става в приложението, а не във вагона. Затова е важно калибрирането да се направи за всяко използвано приложение (в различни Андроид устройства). Така направеното калибриране е също специфично за вагона, с който е направено. Ако трябва да използваме друг измервателен вагон с акселерометър, трябва наново да калибрираме и предишното калибриране се губи.

Иван
« Последна редакция: 12 Април 2022, 05:52:28 от IvanC »

Ivo Nedkov

  • Trade Count: (16)
  • Hero Member
  • *****
  • Публикации: 1163
  • Рейтинг: 177
  • HO, София
Re: Измервателен вагон
« Отговор #14 -: 13 Април 2022, 07:31:16 »
 :hi: Свалям ти шапка за този проект.
Много оригинална идея и реализация: вагон + скоростомер + мобилно приложение.
Благодаря за подробното описание и многото снимки.
За да реализираш подобен проект трябва да имаш доста познания в различни области:
моделиране, електроника, програмиране и др.
Поздравявам те за успешната работа!