Мазмұны:

StandardFirmata -дан асып түсу - қайта қаралды: 5 қадам
StandardFirmata -дан асып түсу - қайта қаралды: 5 қадам

Бейне: StandardFirmata -дан асып түсу - қайта қаралды: 5 қадам

Бейне: StandardFirmata -дан асып түсу - қайта қаралды: 5 қадам
Бейне: StandardFirmata Arduino 2024, Шілде
Anonim
StandardFirmata -дан асып түсу - қайта қаралды
StandardFirmata -дан асып түсу - қайта қаралды

Біраз уақыт бұрын мен pymata4 пайдаланушысы, доктор Мартын Уилермен байланысып, pymata4 кітапханасына DHT22 ылғалдылық/температура сенсорына қолдау көрсетуді қосуды ұсынды. Pymata4 кітапханасы Arduino әріптесі FirmataExpress -пен бірге пайдаланушыларға Arduino құрылғыларын қашықтан басқаруға және бақылауға мүмкіндік береді. Электрондық пошта алмасуының бірнеше раундында доктор Уилер pymata4 пен FirmataExpress -ті өзгертуде сәтті болды. Нәтижесінде DHT22 және DHT11 сенсорларына қолдау pymata4 пен FirmataExpress стандартты бөлігі болып табылады.

2014 жылдың мамырында мен Firmata -ға қосымша құрылғыларды қолдау туралы мақала жаздым. Мен осы мақаланы ой елегінен өткізе отырып, мен бұл мақалаға қағаздан қалам алғаннан бері қаншалықты өзгергенін түсіндім. Бұл мақаладан басқа, доктор Уилер өзінің күш -жігерін құжаттады және сіз оны тексергіңіз келуі мүмкін.

FirmataExpress StandardFirmata -ға негізделген және StandardFirmata каталог құрылымы дамыды. Сонымен қатар, pymata4 API 2014 жылғы түпнұсқалық PyMata API -нен біршама ерекшеленеді. Менің ойымша, бұл мақаланы қайта қарап шығуға және жаңартуға тамаша уақыт болар еді. Доктор Уилердің жұмысын негізге ала отырып, pymata4/FirmataExpress функциясын қалай кеңейтуге болатынын қарастырайық.

Біз бастамас бұрын - Arduino/Firmata туралы кейбір мәліметтер

Сонымен, Фирмата дегеніміз не? Firmata веб -бетінен үзінді келтіре отырып, «Firmata - негізгі компьютердегі бағдарламалық жасақтамадан микроконтроллерлермен байланысуға арналған жалпы хаттама».

Arduino Firmata әдетте Arduino микроконтроллері мен компьютер арасында командалық және есеп беру ақпаратын тасымалдау үшін сериялық интерфейсті қолданады, әдетте 57600 bps орнатылған сериялық/USB байланысын қолданады. Бұл сілтеме бойынша тасымалданатын деректер екілік болып табылады және хаттама клиент/сервер үлгісінде жүзеге асады.

Сервер жағы Arduino эскизі түрінде Arduino микроконтроллеріне жүктеледі. StandardFirmata эскизі Arduino IDE -мен бірге клиенттің бұйрығымен Arduino енгізу -шығару түйреуіштерін басқарады. Ол сонымен қатар кіріс түйінінің өзгеруі мен басқа есеп туралы ақпаратты клиентке қайтарады. FirmataExpress - StandardFirmata кеңейтілген нұсқасы. Ол 115200 bps сериялық байланыс жылдамдығымен жұмыс істейді.

Бұл мақалаға арналған Arduino клиенті - pymata4. Бұл компьютерде орындалатын Python қосымшасы. Ол Arduino серверіне командалар жібереді және есептерді қабылдайды. Pymata4 Python -да енгізілгендіктен, ол Windows, Linux (Raspberry Pi қоса алғанда) және macOS компьютерлерінде жұмыс істейді.

Неліктен Firmata пайдалану керек?

Arduino микроконтроллері - бұл керемет кішкентай құрылғылар, бірақ процессор мен жад ресурстары біршама шектеулі. Процессорлық немесе жады сыйымдылығы жоғары қосымшалар үшін қосымшаның табысты болуы үшін компьютерге ресурстық сұранысты жүктеуден басқа таңдау аз болады.

Бірақ бұл StandardFirmata қолданудың жалғыз себебі емес. Жеңіл салмақты Arduino қосымшаларын әзірлеу кезінде ДК Arduino микроконтроллерінде жоқ құралдар мен отладтау мүмкіндіктерін қамтамасыз ете алады. «Тұрақты» клиент пен серверді қолдану бағдарламаның күрделілігін басқаруға ыңғайлы компьютермен шектеуге көмектеседі. Қолданба жетілдірілгеннен кейін оны Arduino -ның жеке жеке нобайына аударуға болады.

Неліктен pymata4 пайдалану керек?

Оның авторы болғандықтан, әрине, мен біржақтымын. Айтуынша, бұл соңғы бірнеше жыл бойы үздіксіз қызмет көрсететін жалғыз Python негізіндегі Firmata клиенті. Ол интуитивті және қолдануға ыңғайлы API ұсынады. StandardFirmata негізіндегі эскиздерден басқа, StandardFirmataWifI эскизін пайдалану кезінде ESP-8266 сияқты құрылғылар үшін WiFi арқылы Firmata қолдайды.

Сонымен қатар, pymata4 қолданушы қазіргі уақытта StandardFirmata қолдамайтын қосымша сенсорлар мен жетектерді қолдау үшін оңай кеңейтуге арналған.

1 -қадам: Фирмата хаттамасын түсіну

Фирмата хаттамасы туралы түсінік
Фирмата хаттамасы туралы түсінік

Arduino Firmata байланыс хаттамасы MIDI протоколынан алынған, ол бір немесе бірнеше 7 биттік деректерді көрсету үшін қолданылады.

Фирмата қолданушыға кеңейетін етіп жасалған. Бұл кеңейтуді қамтамасыз ететін механизм - System Exclusive (SysEx) хабар алмасу хаттамасы.

Firmata хаттамасымен анықталатын SysEx хабарламасының форматы жоғарыдағы суретте көрсетілген. Ол он алтылық 0xF0 тіркелген мәні бар START_SYSEX байтынан басталады және одан кейін бірегей SysEx командалық байты болады. Байттың командалық мәні он алтылық 0x00-0x7F диапазонында болуы керек. Содан кейін байт пәрмені 7 биттік деректер байттарының анықталмаған санына келеді. Ақырында, хабарлама он алтылық 0xF7 тұрақты мәні бар END_SYSEX байтымен тоқтатылады.

Firmata деректерін кодтау/декодтау

SysEx хабарламасының пайдаланушы деректерінің бөлігі 7 биттік байттардан тұратындықтан, сіз 128 (0x7f) үлкен мәнді қалай көрсетеді деп ойлайсыз ба? Фирмата бұл мәндерді деректер сілтемесі бойынша маршировкаланғанға дейін бірнеше 7 биттік байтқа бөлу арқылы кодтайды. Деректер элементінің ең аз байт (LSB) алдымен жіберіледі, содан кейін конвенция бойынша деректер элементінің маңызды компоненттері. Деректер элементінің ең маңызды байты (MSB) - бұл соңғы жіберілген деректер элементі.

Бұл қалай жұмыс істейді?

SysEx хабарламасының деректер бөлігіне 525 мәнін енгізгіміз келеді делік. 525 мәні 128 мәнінен айқын үлкен болғандықтан, біз оны 7 биттік байт «бөліктерге» бөлуіміз немесе бөлшектеуіміз керек.

Міне осылай жасалады.

Ондық бөлшектегі 525 мәні 2x байт мәні болып табылатын 0x20D он алтылық мәніне барабар. LSB алу үшін біз мәнді 0x7F көмегімен ЖӘНЕ арқылы маска жасаймыз. «C» және Python бағдарламаларының екеуі де төменде көрсетілген:

// LSB оқшаулау үшін «С» енгізу

int max_distance_LSB = max_distance & 0x7f; // төменгі байтты маска # LSB max_distance_LSB = max_distance & 0x7F # оқшаулау үшін Python енгізу

Маскадан кейін max_distance_LSB құрамында 0x0d болады. 0x20D және 0x7F = 0x0D.

Әрі қарай, біз осы 2 байтты мән үшін MSB оқшаулауымыз керек. Ол үшін 0x20D мәнін оңға, 7 орынға жылжытамыз.

// 2 байт мәнді MSB оқшаулау үшін «С» енгізу

int max_distance_MSB = max_distance >> 7; // жоғары ретті байтты ауыстыру # Python іске асырылуы 2 байт мәнді изолоат MSB -ге ауыстыру max_distance_MSB = max_distance >> 7 # shift жоғары байтты алу үшін ауысымнан кейін max_distance_MSB құрамында 0x04 мәні болады.

«Бөлінген» маршированные деректер алынған кезде, оны бір мәнге қайта жинау қажет. Деректер «С» және Python -да қалай жиналады

// 2 байтты қайта жинау үшін «С» енгізу, // 7 биттік мәндер бір мәнге int max_distance = argv [0] + (argv [1] << 7); # 2 байтты, # 7 биттік мәндерді бір мәнге қайта жинау үшін # Python енгізу max_distance = data [0] + (деректер [1] << 7)

Қайта жиналғаннан кейін, мән тағы да 525 ондық немесе 0x20D он алтылыққа тең.

Бұл бөлшектеу/қайта жинау процесін клиент немесе сервер орындауы мүмкін.

2 -қадам: бастайық

Жаңа құрылғыны қолдау Arduino резиденттік серверінде де, компьютерде тұратын Python клиентінде де өзгерістерді қажет етеді. Доктор Уилердің жұмысы қажетті модификацияларды суреттеу үшін пайдаланылатын болады.

Мүмкін, ең маңызды қадам - қолданыстағы құрылғылардың кітапханасын теңдеудің Arduino жағына біріктіруді немесе өзіңіздің жеке жазбаңызды жазғыңыз келетінін шешу. Егер сізде бұрыннан бар кітапхананы таба алсаңыз, оны өз қолыңызбен жазудан гөрі пайдалану оңайырақ болады.

DHT құрылғысын қолдау үшін доктор Уилер өзінің кеңейту кодын DHTNew кітапханасына негіздеді. Доктор Уилер DHTNew кітапханасының функционалдығын Arduino мен pymata4 теңдеулерінің ардуино жағынан аз бөлуді қамтамасыз ету үшін өте ақылды түрде бөлді.

Егер DHTNew -ге қарайтын болсақ, ол келесі әрекеттерді орындайды:

  • Таңдалған істікшенің сандық шығыс режимін орнатады.
  • Ылғалдылық пен температураның соңғы мәндерін алу үшін кодталған сигнал шығады.
  • Кез келген қатені тексереді және хабарлайды.
  • Алынған бастапқы деректерден адам оқитын температура мен ылғалдылық мәндерін есептейді.

Фирма Экспресс жағынан мүмкіндігінше тиімді болу үшін доктор Уилер Arduino -дан pymata4 -ке деректерді түрлендіру процедураларын жүктеді.

3 -қадам: DHT қолдауына FirmataExpress өзгерту

FirmataExpress каталог ағашы

Төменде FirmataExpress репозиторийі бар барлық файлдар берілген. Бұл ағаш StandardFiramata -мен бірдей, тек кейбір файл атаулары репозиторий атауын көрсетеді.

Өзгертуді қажет ететін файлдар - қасында жұлдызша (*) бар файлдар.

FirmataExpress

Bo * Тақталар.h

├── мысалдар

Қосымша ақпарат FirmataExpress

Тақта

* FirmataExpress.ino

Лицензия.txt

Makefile

├── * FirmataConstants.h

F * FirmataDefines.h

├── FirmataExpress.cpp

├── FirmataExpress.h

├── FirmataMarshaller.cpp

├── FirmataMarshaller.h

├── FirmataParser.cpp

└── FirmataParser.h

Әр файлды және енгізілген өзгерістерді қарастырайық.

Тақталар.h

Бұл файлда қолдау көрсетілетін тақта түрлерінің әрқайсысы үшін пин-типті макро анықтамалары бар. Ол бірнеше құрылғыға қолдау қажет болғанда қолдау көрсетілетін құрылғылардың максималды санын анықтайды.

DHT құрылғысы үшін бір уақытта 6 құрылғы қосылуы мүмкін және бұл мән келесі түрде анықталады:

#ifndef MAX_DHTS

#dexine MAX_DHTS 6 #endif

Сондай-ақ, пин-типті макростар жаңа құрылғы үшін міндетті түрде анықталуы мүмкін, ол барлық тақта түрлері үшін немесе сізді қызықтыратындар үшін. Бұл макростар негізінен есеп беру мақсатында қолданылады және құрылғыларды басқару үшін қолданылмайды. Бұл макростар құрылғыны қолдайтын түйреуіштерді анықтайды:

#анықтаңыз IS_PIN_DHT (p) (IS_PIN_DIGITAL (p) && (p) - 2 <MAX_DHTS)

PIN-кодты түрлендіруді анықтау үшін макрос.

#PIN_TO_DHT (p) PIN_TO_DIGITAL (p) анықтау

FirmataConstants.h

Бұл файлда Arduino -ға қай нұсқаны жүктегеніңізді қадағалау үшін өзгертуге болатын микробағдарлама нұсқасының нөмірі бар. Ол сондай -ақ Firmata хабарларының мәндерін, соның ішінде Firmata SysEx хабарламаларын қамтиды.

Бұл файлда құрылғыға жаңа хабарды немесе хабарлар жинағын тағайындау қажет болады. DHT үшін екі хабарлама қосылды. Біреуі түйреуішті «DHT» түйреуіші ретінде, ал екіншісі - DHT туралы соңғы деректерді клиентке қайтару кезінде репортер хабарламасы ретінде конфигурациялайды.

статикалық const int DHT_CONFIG = 0x64;

статикалық const int DHT_DATA = 0x65;

Бұл файлда түйреуіш режимдері де көрсетілген. DHT үшін жаңа түйреуіш режимі құрылды:

статикалық const int PIN_MODE_DHT = 0x0F; // PIN DHT үшін конфигурацияланған

Жаңа түйреуіш режимін қосқанда, TOTAL_PIN_MODES реттеу керек:

статикалық const int TOTAL_PIN_MODES = 17;

FirmataDefines.h

Бұл файл FirmataConstants.h сайтына қосылған жаңа хабарларды көрсету үшін жаңартылуы керек:

#ifdef DHT_CONFIG #undef DHT_CONFIG #endif #DHT_CONFIG фирмасын анықтаңыз:: DHT_CONFIG // DHT сұранысы #ifdef DHT_DATA #undef DHT_DATA #endif #deftine DHT_DATA фирма:: DHT_DATA // DHT reply #ifdefode_ PIN_FOD_FOD_FOD_FOD_DOD_:: PIN_MODE_DHT

FirmataExpress.ino

Бұл талқылауда біз осы Arduino эскизіне енгізілген өзгерістердің «жоғары жақтарын» қарастырамыз.

FirmataExpress бір уақытта алты DHT құрылғысына қолдау көрсетуі үшін, құрылғының байланысқан пин нөмірін, WakeUpDelay мәнін және құрылғы түрін, яғни DHT22 немесе DHT11 қадағалайтын 3 массив құрылды:

// DHT сенсорлары

int numActiveDHTs = 0; // қосылған DHT саны uint8_t DHT_PinNumbers [MAX_DHTS]; uint8_t DHT_WakeUpDelay [MAX_DHTS]; uint8_t DHT_TYPE [MAX_DHTS];

Құрылғының екі түрі де оқу арасында шамамен 2 секундты қажет ететіндіктен, біз әрбір DHT-ді 2 секундтық уақыт аралығында тек бір рет оқығанымызға көз жеткізуіміз керек. DHT құрылғылары мен HC-SR04 қашықтық сенсорлары сияқты кейбір құрылғыларға мезгіл-мезгіл кіруге болады. Бұл оларға қоршаған ортамен қарым -қатынас жасауға мүмкіндік береді.

uint8_t nextDHT = 0; // келесі құрылғы оқылуы үшін dht ішіне индекстеңіз

uint8_t currentDHT = 0; // Қай сенсордың белсенді екенін қадағалайды. int dhtNumLoops = 0; // DHT int dhtLoopCounter = 0 -ге кіру b4 циклы арқылы мақсатты рет саны; // Цикл есептегіші

DHT құрылғысын конфигурациялау және оқу

DHT жұмысына арналған түйреуішті конфигурациялау үшін FirmataExpress SysEx пәрменін алған кезде, ол DHT құрылғыларының максималды санынан аспағанын тексереді. Егер жаңа DHT қолдау көрсетілсе, DHT массивтері жаңартылады. Егер DHT түрі белгісіз болса, SysEx жолдық хабарламасы құрылады және қайтадан pymata4 -ке жіберіледі

DHT_CONFIG жағдайы: int DHT_Pin = argv [0]; int DHT_type = argv [1]; if (numActiveDHTs <MAX_DHTS) {if (DHT_type == 22) {DHT_WakeUpDelay [numActiveDHTs] = 1; } else if (DHT_type == 11) {DHT_WakeUpDelay [numActiveDHTs] = 18; } else {Firmata.sendString («ҚАТЕ: БЕЛГІСІЗ СЕНЗОР ТІРІ, ЖАРАҚТЫ СЕНСОРЛАР 11, 22»); үзіліс; } // сенсорды тексеру DHT_PinNumbers [numActiveDHTs] = DHT_Pin; DHT_TYPE [numActiveDHTs] = DHT_түрі; setPinModeCallback (DHT_Pin, PIN_MODE_DHT);

Содан кейін FirmataExpress DHT құрылғысымен байланыс орнатуға тырысады. Егер қателер болса, ол қате деректері бар SysEx хабарламасын құрайды және SysEx хабарламасын pymat4 -ке қайта жібереді. _Bits айнымалысы қажет болған жағдайда pymata4 арқылы қосымша өңдеу үшін DHT құрылғысы қайтарған деректерді сақтайды.

Firmata.write (START_SYSEX);

Firmata.write (DHT_DATA); Firmata.write (DHT_Pin); Firmata.write (DHT_type); үшін (uint8_t i = 0; i> 7 & 0x7f); } Firmata.write (abs (rv)); Firmata.write (1); Firmata.write (END_SYSEX);

Егер жарамды деректер қайтарылса, белсенді DHT саны артады. Келесі DHT деректерді тексермес бұрын қанша циклді қайталауды қадағалайтын айнымалы да реттеледі. Бұл айнымалы жүйе қанша DHT қосылмаса да, олардың барлығы 2 секунд ішінде оқылатынына кепілдік береді.

int rv = readDhtSensor (numActiveDHTs);

егер (rv == DHTLIB_OK) {numActiveDHTs ++; dhtNumLoops = dhtNumLoops / numActiveDHTs; // бәрі жақсы}

Егер эскиздің циклдық функциясында бір немесе бірнеше DHT құрылғылары конфигурацияланған болса, онда келесі DHT құрылғысы оқылады. Жарамды деректер немесе оның қате күйі pysata4 -ке SysEx хабарламасы түрінде қайтарылады:

if (dhtLoopCounter ++> dhtNumLoops) {if (numActiveDHTs) {int rv = readDhtSensor (nextDHT); uint8_t current_pin = DHT_PinNumbers [nextDHT]; uint8_t current_type = DHT_TYPE [nextDHT]; dhtLoopCounter = 0; ағымдағыDHT = келесіDHT; егер (nextDHT ++> = numActiveDHTs - 1) {nextDHT = 0; } if (rv == DHTLIB_OK) {// TEST CHECKSUM uint8_t sum = _bits [0] + _bits [1] + _bits [2] + _bits [3]; егер (_bits [4]! = қосынды) {rv = -1; }} // хабарды Firmata.write (START_SYSEX) қате күйімен қайтару; Firmata.write (DHT_DATA); Firmata.write (current_pin); Firmata.write (current_type); үшін (uint8_t i = 0; i <sizeof (_bits) - 1; ++ i) {Firmata.write (_bits ); // Firmata.write (_bits ;} Firmata.write (abs (rv)); Firmata.write (0); Firmata.write (END_SYSEX);}}

DHT құрылғысымен байланысу үшін қолданылатын код тікелей DHTNew кітапханасынан алынады:

int readDhtSensor (int индексі) {

// INIT BUFFERVAR DATA uint8_t mask = 128 алу үшін; uint8_t idx = 0; // EMPTY BUFFER // memset (_bits, 0, sizeof (_bits)); for (uint8_t i = 0; i 5 BYTES for (uint8_t i = 40; i! = 0; i--) {loopCnt = DHTLIB_TIMEOUT; while (digitalRead (pin) == LOW) {if (--loopCnt == 0) DHTLIB_ERROR_TIMEOUT;} uint32_t t = micros (); loopCnt = DHTLIB_TIMEOUT; while (digitalRead (pin) == HIGH) {if (--loopCnt == 0) DHTLIB_ERROR_TIMEOUT қайтару;} if ((микросхемалар ()-) 40) {_bits [idx] | = маска;} маска >> = 1; егер (маска == 0) // келесі байт? {Mask = 128; idx ++;}} DHTLIB_OK қайтару;}

4 -қадам: DHT қолдау үшін Pymata4 өзгерту

private_constants.h

DHT қолдау үшін бізге бұл файлға жаңа пин-типті және SysEx хабарламаларын қосу қажет:

# PIN режимі INPUT = 0x00 # түйін кіріс ретінде орнатылды OUTPUT = 0x01 # түйін шығыс ретінде орнатылды ANALOG = 0x02 # аналогтық аналогтық кіріс режимі PWM = 0x03 # сандық түйін PWM шығыс режимінде SERVO = 0x04 # IvoC Servo шығыс режимінде сандық түйреуіш = 0x06 # түйреуіш I2C орнатуға қосылды STEPPER = 0x08 # қадамдық режимдегі кез келген түйреуіш SERIAL = 0x0a PULLUP = 0x0b # SONAR режиміндегі кез келген түйреуіш SONAR = 0x0c # SONAR режиміндегі кез келген түйін TONE = 0x0d # PIXY = 0x0e # дыбыс режиміндегі кез келген түйреуіш pixy камера режимі үшін резервтелген DHT = 0x0f # DHT сенсоры IGNORE = 0x7f # DHT SysEx командалық хабарлары DHT_CONFIG = 0x64 # dht config пәрмені DHT_DATA = 0x65 # dht сенсорлық жауап

Қосылған түйреуіш түрі мен SysEx пәрмендері FirmataExpress -ке қосылған FirmataConstants.h мәндеріне сәйкес келуі керек.

pymata4.py

Pymata4 кіріс фирма хабарын хабар өңдеушімен тез байланыстыру үшін Python сөздігін қолданады. Бұл сөздіктің аты report_dispatch.

Сөздікке жазудың форматы:

{MessageID: [message_handler, өңделетін деректер байттарының саны]}

Кіріс DHT хабарламаларын өңдеу үшін сөздікке жазба қосылды:

{PrivateConstants. DHT_DATA: [self._dht_read_response, 7]}

Хабардағы 7 байт деректері - Arduino цифрлық пин нөмірі, DHT құрылғысының түрі (22 немесе 11) және шикі деректердің 5 байты.

_Dht_read_response әдісі хабарланған қателерді тексереді. Егер хабарланған қателер болмаса, ылғалдылық пен температура Arduino DHTNew кітапханасынан алынған алгоритм арқылы есептеледі.

Есептелген мәндер кері байланыс әдісі арқылы жеткізіледі. Олар сонымен қатар pin_data деректер құрылымында сақталады. Есептелген соңғы мәнді dht_read әдісі арқылы pin_data сұрау арқылы еске түсіруге болады.

Жаңа DHT құрылғысын конфигурациялау

Жаңа DHT құрылғысын қосқанда, set_pin_mode_dht әдісі шақырылады. Бұл әдіс pin_data сандық түйреуіштерді жаңартады. Ол сонымен қатар DHT_CONFIG SysEx хабарламасын жасайды және FirmataExpress -ке жібереді.

5 -қадам: Жинау

Көріп отырғанымыздай, жаңа құрылғыға Firmata қолдауын қосу Arduino FirmataExpress сервер кодын және Python негізіндегі pymata4 клиент кодын өзгертуді талап етеді. FirmataExpress кодын жөндеу қиын болуы мүмкін. Жөндеуге көмектесу үшін FirmataExpress -ке printData деп аталатын әдіс қосылды. Бұл әдіс FirmataExpress -тен деректер мәндерін жіберуге мүмкіндік береді және оларды pymata4 консолінде басып шығарады.

Бұл функция таңбалар жолына көрсеткішті де, көргіңіз келетін мәнді де қажет етеді. Егер деректер мәні argc деп аталатын айнымалыда болса, келесі параметрлермен printData шақыруға болады.

printData ((char*) «argc =», argc);

Егер сізде сұрақтар туындаса, түсініктеме қалдырыңыз, мен қуана жауап беремін.

Бақытты кодтау!

Ұсынылған: