Turinys:

„Standart Firmata“- peržiūrėta: 5 žingsniai
„Standart Firmata“- peržiūrėta: 5 žingsniai

Video: „Standart Firmata“- peržiūrėta: 5 žingsniai

Video: „Standart Firmata“- peržiūrėta: 5 žingsniai
Video: Тонкости работы с монтажной пеной. То, что ты не знал! Секреты мастеров 2024, Liepa
Anonim
„Going Beyond StandardFirmata“- peržiūrėta
„Going Beyond StandardFirmata“- peržiūrėta

Prieš kurį laiką su manimi susisiekė daktaras Martynas Wheeleris, „pymata4“naudotojas, norėdamas patarti, kaip pridėti prie „pymata4“bibliotekos palaikymą DHT22 drėgmės/temperatūros jutikliui. Biblioteka „pymata4“kartu su „Arduino“atitikmeniu „FirmataExpress“leidžia vartotojams nuotoliniu būdu valdyti ir stebėti savo „Arduino“įrenginius. Per kelis keitimosi el. Laiškais raštus daktaras Wheeleris sėkmingai pakeitė ir „pymata4“, ir „FirmataExpress“. Todėl DHT22 ir DHT11 jutiklių palaikymas dabar yra standartinė pymata4 ir FirmataExpress dalis.

2014 metų gegužę parašiau straipsnį apie papildomos įrangos palaikymą „Firmata“. Pamąstydamas apie šį straipsnį, supratau, kiek pasikeitė nuo to laiko, kai šiam straipsniui paėmiau rašiklį į popierių. Be šio straipsnio, daktaras Wheeleris dokumentuojo savo pastangas ir galbūt norėsite tai patikrinti.

„FirmataExpress“yra pagrįsta „StandardFirmata“, o „StandardFirmata“katalogų struktūra pasikeitė. Be to, „pymata4“API taip pat šiek tiek skiriasi nuo pradinės 2014 m. „PyMata“API. Maniau, kad tai bus pats tinkamiausias laikas peržiūrėti ir atnaujinti šį straipsnį. Remdamiesi dr. Wheelerio darbu, panagrinėkime, kaip išplėsti „pymata4“/„FirmataExpress“funkcijas.

Prieš pradedant - šiek tiek pagrindinės informacijos apie „Arduino“/„Firmata“

Taigi, kas yra Firmata? Citata iš „Firmata“tinklalapio: „„ Firmata “yra bendras protokolas, skirtas bendrauti su mikrovaldikliais iš pagrindinio kompiuterio programinės įrangos“.

„Arduino Firmata“naudoja nuosekliąją sąsają komandų ir ataskaitų informacijai perduoti tarp „Arduino“mikrovaldiklio ir kompiuterio, paprastai naudojant 57600 bps serijos/USB nuorodą. Per šią nuorodą perduodami duomenys yra dvejetainiai, o protokolas įgyvendinamas kliento/serverio modelyje.

Serverio pusė įkeliama į „Arduino“mikrovaldiklį „Arduino“eskizo pavidalu. „StandardFirmata“eskizas, įtrauktas į „Arduino IDE“, valdo „Arduino“įvesties/išvesties kaiščius, kaip nurodė klientas. Ji taip pat praneša klientui įvesties kaiščio pakeitimus ir kitą ataskaitos informaciją. „FirmataExpress“yra išplėstinė „StandardFirmata“versija. Jis veikia 115200 bps nuoseklaus ryšio greičiu.

Šiame straipsnyje naudojamas „Arduino“klientas yra „pymata4“. Tai „Python“programa, vykdoma asmeniniame kompiuteryje. Jis siunčia komandas ir gauna ataskaitas iš „Arduino“serverio. Kadangi „pymata4“įdiegta „Python“, ji veikia „Windows“, „Linux“(įskaitant „Raspberry Pi“) ir „MacOS“kompiuteriuose.

Kodėl verta naudoti „Firmata“?

„Arduino“mikrovaldikliai yra nuostabūs maži įrenginiai, tačiau procesoriaus ir atminties ištekliai yra šiek tiek riboti. Taikant programas, kurios reikalauja daug procesoriaus ar atminties, dažnai lieka mažai pasirinkimo, kaip tik perkelti išteklių poreikį į kompiuterį, kad programa būtų sėkminga.

Tačiau tai nėra vienintelė „StandardFirmata“naudojimo priežastis. Kurdamas lengvesnes „Arduino“programas, kompiuteris gali suteikti įrankių ir derinimo galimybių, kurios nėra tiesiogiai prieinamos „Arduino“mikrovaldiklyje. Naudojant „fiksuotą“klientą ir serverį, programos sudėtingumas apsiriboja asmeniniu kompiuteriu, kuris yra lengviau valdomas. Kai programa bus ištobulinta, ją galima išversti į pasirinktinį, atskirą „Arduino“eskizą.

Kodėl verta naudoti „pymata4“?

Būdamas jo autorius, žinoma, esu šališkas. Tai yra vienintelis „Python“pagrįstas „Firmata“klientas, kuris buvo nuolat prižiūrimas per pastaruosius kelerius metus. Tai suteikia intuityvią ir lengvai naudojamą API. Be „StandardFirmata“eskizų, naudojant „StandardFirmataWifI“eskizą, ji palaiko „Firmata over WiFi“įrenginiams, tokiems kaip ESP-8266.

Be to, „pymata4“buvo sukurtas taip, kad vartotojas galėtų jį lengvai išplėsti, kad palaikytų papildomus jutiklius ir pavaras, kurių šiuo metu nepalaiko „StandardFirmata“.

1 žingsnis: „Firmata“protokolo supratimas

„Firmata“protokolo supratimas
„Firmata“protokolo supratimas

„Arduino Firmata“ryšio protokolas yra kilęs iš MIDI protokolo, kuris naudoja vieną ar daugiau 7 bitų baitų duomenims atvaizduoti.

„Firmata“buvo sukurta taip, kad ją būtų galima išplėsti vartotojui. Šis išplėtimo mechanizmas yra „System Exclusive“(„SysEx“) pranešimų protokolas.

„SysEx“pranešimo formatas, kaip apibrėžta „Firmata“protokole, parodytas aukščiau esančioje iliustracijoje. Jis prasideda START_SYSEX baitu su fiksuota šešioliktainiu 0xF0 reikšme, o po jo seka unikalus „SysEx“komandinis baitas. Komandos baito vertė turi būti šešioliktainiame 0x00-0x7F diapazone. Po komandos baito seka nenustatytas 7 bitų duomenų baitų skaičius. Galiausiai pranešimas baigiamas END_SYSEX baitu, kurio fiksuota vertė yra šešioliktainis 0xF7.

Firmata duomenų kodavimas/dekodavimas

Kadangi „SysEx“pranešimo vartotojo duomenų dalį sudaro 7 bitų baitų serija, jums gali kilti klausimas, kaip reikšmė yra didesnė nei 128 (0x7f)? „Firmata“koduoja šias vertes, išardydama jas į kelis 7 bitų baitų gabalus, kol duomenys sutvarkomi per duomenų saitą. Pirmiausia siunčiamas mažiausiai reikšmingas duomenų elemento baitas (LSB), po to - vis svarbesni duomenų elemento komponentai. Svarbiausias duomenų elemento baitas (MSB) yra paskutinis išsiųstas duomenų elementas.

Kaip tai veikia?

Tarkime, kad norime į „SysEx“pranešimo duomenų dalį įtraukti vertę 525. Kadangi 525 vertė yra akivaizdžiai didesnė už 128, turime ją padalyti arba išardyti į 7 bitų „gabaliukus“.

Štai kaip tai daroma.

Dešimtainė reikšmė 525 atitinka šešioliktainę reikšmę 0x20D, 2 baitų reikšmę. Norėdami gauti LSB, mes užmaskuojame vertę ir pažymime ją 0x7F. Tiek „C“, tiek „Python“diegimai pateikiami žemiau:

// „C“diegimas LSB izoliavimui

int max_distance_LSB = max_distance & 0x7f; // užmaskuokite apatinį baitą # „Python“diegimas, kad izoliuotumėte LSB max_distance_LSB = max_distance & 0x7F # užmaskuokite apatinį baitą

Užmaskavus, max_distance_LSB bus 0x0d. 0x20D ir 0x7F = 0x0D.

Toliau turime išskirti MSB, kad būtų gauta 2 baitų vertė. Norėdami tai padaryti, perkelsime 0x20D reikšmę į dešinę, 7 vietas.

// „C“diegimas, siekiant atskirti 2 baitų vertės MSB

int max_distance_MSB = max_distance >> 7; // perkelkite aukščiausios eilės baitą # Python diegimas į 2 baitų vertės MSB izoliaciją max_distance_MSB = max_distance >> 7 # shift, kad gautumėte viršutinį baitą Po perkėlimo max_distance_MSB bus 0x04 reikšmė.

Kai gaunami „susmulkinti“sugrupuoti duomenys, juos reikia iš naujo surinkti į vieną vertę. Štai kaip duomenys iš naujo surenkami „C“ir „Python“

// „C“diegimas, siekiant surinkti 2 baitus, // 7 bitų reikšmės į vieną reikšmę int max_distance = argv [0] + (argv [1] << 7); # „Python“diegimas, siekiant surinkti 2 baitų, # 7 bitų reikšmes į vieną vertę max_distance = data [0] + (duomenys [1] << 7)

Surinkus, vertė vėl lygi 525 dešimtainiams arba 0x20D šešioliktainiams.

Šį išardymo/surinkimo procesą gali atlikti klientas arba serveris.

2 žingsnis: pradėkime

Norint palaikyti naują įrenginį, reikia pakeisti ir „Arduino“rezidento serverį, ir kompiuterio „Python“klientą. Daktaro Wheelerio darbas bus naudojamas iliustruoti būtinas modifikacijas.

Galbūt svarbiausias žingsnis yra nuspręsti, ar norite integruoti esamą pagalbinių įrenginių biblioteką į lygties „Arduino“pusę, ar parašyti savo. Rekomenduojama, kad jei rasite esamą biblioteką, ja naudotis bus kur kas paprasčiau nei rašyti savo biblioteką nuo nulio.

Norėdami palaikyti DHT įrenginius, daktaras Wheeleris savo plėtinio kodą grindė DHTNew biblioteka. Labai sumaniai, daktaras Wheeleris padalino „DHTNew“bibliotekos funkcionalumą per lygties „Arduino“ir „pymata4“puses, kad „Arduino“pusėje būtų minimalus blokavimas.

Jei pažvelgsime į DHTNew, jis atlieka visus šiuos veiksmus:

  • Nustato pasirinktą kaiščio skaitmeninio išvesties režimą.
  • Sukuria užkoduotą signalą, kad gautų naujausias drėgmės ir temperatūros vertes.
  • Patikrina klaidas ir praneša apie jas.
  • Apskaičiuoja žmogaus skaitomas temperatūros ir drėgmės vertes pagal gautus neapdorotus duomenis.

Kad viskas būtų kuo efektyviau „FirmataExpress“pusėje, daktaras Wheeleris perkėlė duomenų konvertavimo tvarką iš „Arduino“į „pymata4“.

3 veiksmas: „FirmataExpress“modifikavimas DHT palaikymui

„FirmataExpress“katalogų medis

Žemiau yra visi failai, kuriuos sudaro „FirmataExpress“saugykla. Šis medis yra identiškas „StandardFiramata“medžiui, tik kai kurie failų pavadinimai atspindi saugyklos pavadinimą.

Failai, kuriuos reikia keisti, yra tie, prie kurių yra žvaigždutė (*).

„FirmataExpress“

├── * Lentos.h

├── pavyzdžiai

F ─── „FirmataExpress“

│ ├── boardx

F ├── * FirmataExpress.ino

IC ├── LICENCIJA.txt

Make └── Makefile

├── * FirmataConstants.h

├── * FirmataDefines.h

├── FirmataExpress.cpp

├── „FirmataExpress.h“

├── FirmataMarshaller.cpp

├── FirmataMarshaller.h

├── FirmataParser.cpp

└── FirmataParser.h

Pažvelkime į kiekvieną failą ir atliktus pakeitimus.

Lentos.h

Šiame faile yra smeigtuko tipo makrokomandų apibrėžimai kiekvienam palaikomam plokštės tipui. Jis nustato didžiausią palaikomų įrenginių skaičių, kai reikia palaikyti daugiau nei vieną įrenginį.

DHT įrenginys vienu metu gali būti prijungtas iki 6 įrenginių ir ši vertė apibrėžiama kaip:

#ifndef MAX_DHTS

#define MAX_DHTS 6 #endif

Be to, kaiščio tipo makrokomandos gali būti pasirinktinai nustatytos naujam įrenginiui, visų tipų plokštėms arba tik toms, kurios jus domina. Šios makrokomandos dažniausiai naudojamos ataskaitų teikimo tikslais ir nėra naudojamos įrenginiams valdyti. Šios makrokomandos apibrėžia abu įrenginį palaikančius kaiščius:

#define IS_PIN_DHT (p) (IS_PIN_DIGITAL (p) && (p) - 2 <MAX_DHTS)

Taip pat makrokomandą PIN kodo skaičiaus konversijai apibrėžti.

#define PIN_TO_DHT (p) PIN_TO_DIGITAL (p)

FirmataConstants.h

Šiame faile yra programinės įrangos versijos numeris, kurį galbūt norėsite pakeisti, kad galėtumėte stebėti, kurią versiją įkėlėte į „Arduino“. Jame taip pat yra „Firmata“pranešimų vertės, įskaitant „Firmata SysEx“pranešimus.

Šiame faile turėsite priskirti naują pranešimą ar pranešimų rinkinį savo įrenginiui. DHT buvo pridėti du pranešimai. Vienas konfigūruoja smeigtuką kaip „DHT“smeigtuką, o kitas - kaip reporterio pranešimą, kai klientui siunčiami naujausi DHT duomenys.

statinė konst int int DHT_CONFIG = 0x64;

statinė konst int int DHT_DATA = 0x65;

Šiame faile taip pat nurodyti smeigtukų režimai. DHT buvo sukurtas naujas kaiščio režimas:

statinė konstant int PIN_MODE_DHT = 0x0F; // smeigtukas sukonfigūruotas DHT

Pridedant naują kaiščio režimą, reikia sureguliuoti TOTAL_PIN_MODES:

static const int TOTAL_PIN_MODES = 17;

FirmataDefines.h

Šis failas turi būti atnaujintas, kad atspindėtų naujus pranešimus, pridėtus prie „FirmataConstants.h“:

#ifdef DHT_CONFIG #undef DHT_CONFIG #endif #define DHT_CONFIG firmata:: DHT_CONFIG // DHT užklausa #ifdef DHT_DATA #undef DHT_DATA #endif #define DHT_DATA firmata:: DHT_DATA // DHT atsakymas #ifdef PINDDD #:: PIN_MODE_DHT

FirmataExpress.ino

Šioje diskusijoje aptarsime šio „Arduino“eskizo pakeitimų „aukščiausius taškus“.

Kad „FirmataExpress“vienu metu palaikytų iki šešių DHT įrenginių, buvo sukurti 3 masyvai, skirti sekti kiekvieną su įrenginiu susijusį PIN kodą, jo „WakeUpDelay“vertę ir įrenginio tipą, ty DHT22 arba DHT11:

// DHT jutikliai

int numActiveDHTs = 0; // pridėtų DHT skaičius uint8_t DHT_PinNumbers [MAX_DHTS]; uint8_t DHT_WakeUpDelay [MAX_DHTS]; uint8_t DHT_TYPE [MAX_DHTS];

Kadangi abiejų tipų įrenginiams tarp nuskaitymų reikia maždaug 2 sekundžių, turime įsitikinti, kad kiekvieną DHT skaitome tik vieną kartą per 2 sekundžių laikotarpį. Kai kurie įrenginiai, pvz., DHT įrenginiai ir HC-SR04 atstumo jutikliai, pasiekiami tik periodiškai. Tai suteikia jiems laiko bendrauti su savo aplinka.

uint8_t nextDHT = 0; // indeksuoti į dht , kad būtų galima perskaityti kitą įrenginį

uint8_t currentDHT = 0; // Stebi, kuris jutiklis yra aktyvus. int dhtNumLoops = 0; // Tikslinis kartų skaičius per ciklą b4, pasiekiantis DHT int dhtLoopCounter = 0; // Kilpos skaitiklis

DHT įrenginio konfigūravimas ir skaitymas

Kai „FirmataExpress“gauna komandą „SysEx“, kad sukonfigūruotų kaištį DHT veikimui, ji patikrina, ar neviršytas maksimalus DHT įrenginių skaičius. Jei galima palaikyti naują DHT, DHT masyvai atnaujinami. Jei DHT tipas nežinomas, sukuriamas „SysEx“eilutės pranešimas ir perduodamas atgal į „pymata4“

atvejis 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 ("KLAIDA: NEŽINOMAS JUTIKLIO TIPAS, TINKAMI JUTIKLIAI ARE 11, 22"); pertrauka; } // išbandyti jutiklį DHT_PinNumbers [numActiveDHTs] = DHT_Pin; DHT_TYPE [numActiveDHTs] = DHT_type; setPinModeCallback (DHT_Pin, PIN_MODE_DHT);

Tada „FirmataExpress“bando susisiekti su DHT įrenginiu. Jei yra kokių nors klaidų, jis suformuoja „SysEx“pranešimą su klaidos duomenimis ir siunčia „SysEx“pranešimą atgal į „pymat4“. Kintamasis _bits saugo duomenis, kuriuos grąžino DHT įrenginys, jei reikia, papildomai apdoroti „pymata4“.

Firmata.write (START_SYSEX);

Firmata.write (DHT_DATA); Firmata.write (DHT_Pin); Firmata.write (DHT_type); už (uint8_t i = 0; i> 7 & 0x7f); } Firmata.write (abs (rv)); Firmata.write (1); Firmata.write (END_SYSEX);

Jei grąžinami galiojantys duomenys, aktyvių DHT skaičius padidinamas. Taip pat koreguojamas kintamasis, kuris stebi, kiek ciklo pakartojimų reikia atlikti prieš tikrinant kitą DHT. Šis kintamasis užtikrina, kad nesvarbu, kiek DHT pridedama prie sistemos, jie visi bus perskaityti per 2 sekundes.

int rv = skaitytiDhtSensor (numActiveDHTs);

if (rv == DHTLIB_OK) {numActiveDHTs ++; dhtNumLoops = dhtNumLoops / numActiveDHTs; // viskas gerai}

Jei eskizo ciklo funkcijoje sukonfigūruotas vienas ar daugiau DHT įrenginių, tada skaitomas kitas DHT įrenginys. Tinkami duomenys arba jų klaidos būsena grąžinami į „pymata4“„SysEx“pranešimo forma:

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; dabartinisDHT = kitasDHT; if (nextDHT ++> = numActiveDHTs - 1) {nextDHT = 0; } if (rv == DHTLIB_OK) {// BANDYMO PATIKRINIMO SUMA uint8_t sum = _bits [0] + _bits [1] + _bits [2] + _bits [3]; jei (_bits [4]! = suma) {rv = -1; }} // išsiųsti pranešimą su klaidos būsena Firmata.write (START_SYSEX); Firmata.write (DHT_DATA); Firmata.write (current_pin); Firmata.write (dabartinis_tipas); for (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);}}

Kodas, naudojamas bendrauti su DHT įrenginiu, yra gautas tiesiogiai iš DHTNew bibliotekos:

int readDhtSensor (int indeksas) {

// INIT BUFFERVAR GAUTI DUOMENIS 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) return DHTLIB_ERROR_TIMEOUT;} uint32_t t = micros (); loopCnt = DHTLIB_TIMEOUT; while (digitalRead (pin) == HIGH) {if (--loopCnt == 0) return DHTLIB_ERROR_TIMEOUT;} if ((micros ()-t)> 40) {_bits [idx] | = kaukė;} kaukė >> = 1; if (mask == 0) // kitas baitas? {Mask = 128; idx ++;}} grąžinti DHTLIB_OK;}

4 veiksmas: „Pymata4“modifikavimas DHT palaikymui

private_constants.h

Norėdami palaikyti DHT, prie šio failo turime pridėti ir naujų kaiščio tipo, ir „SysEx“pranešimų:

# kaiščių režimai INPUT = 0x00 # kištukas nustatytas kaip įvestis OUTPUT = 0x01 # kaištis nustatytas kaip išėjimas ANALOG = 0x02 # analoginis kaištis analoginiame Įvesties režimas PWM = 0x03 # skaitmeninis kaištis PWM išvesties režimu 0x06 # smeigtukas įtrauktas į I2C sąranką STEPPER = 0x08 # bet koks smeigtukas žingsninio režimo SERIAL = 0x0a PULLUP = 0x0b # Bet koks smeigtukas prispaudimo režimu SONAR = 0x0c # Bet koks kaištis SONAR režimu TONE = 0x0d # Bet koks kaištis tono režimu PIXY = 0x0e # rezervuotas „pixy“fotoaparato režimui

Pridėtas kaiščio tipas ir „SysEx“komandos turi atitikti „FirmataConstants.h“, pridėtos prie „FirmataExpress“, reikšmes.

pymata4.py

„Pymata4“naudoja „Python“žodyną, kad greitai susietų gaunamą „Firmata“pranešimą su pranešimų tvarkytoju. Šio žodyno pavadinimas yra report_dispatch.

Žodyno įrašo formatas yra toks:

{MessageID: [message_handler, apdorojamų duomenų baitų skaičius]}

Į žodyną buvo įtrauktas įrašas, skirtas tvarkyti gaunamus DHT pranešimus:

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

7 baitai duomenų pranešime yra „Arduino“skaitmeninio kaiščio numeris, DHT įrenginio tipas (22 arba 11) ir 5 baitai neapdorotų duomenų.

_Dht_read_response metodas patikrina, ar nėra klaidų, apie kurias pranešta. Jei pranešimų apie klaidas nėra, drėgmė ir temperatūra apskaičiuojami naudojant algoritmą, perkeltą iš „Arduino DHTNew“bibliotekos.

Apskaičiuotos vertės pateikiamos naudojant vartotojo pateiktą atgalinio ryšio metodą. Jie taip pat saugomi vidinėje pin_data duomenų struktūroje. Paskutinę vertę, apie kurią pranešta, galima atšaukti apklausiant pin_data naudojant dht_read metodą.

Naujo DHT įrenginio konfigūravimas

Pridedant naują DHT įrenginį, vadinamas set_pin_mode_dht metodas. Šis metodas atnaujina skaitmeninių kaiščių pin_data. Taip pat sukuriamas ir siunčiamas „DHT_CONFIG SysEx“pranešimas „FirmataExpress“.

5 žingsnis: Apvyniojimas

Kaip matėme, norint pridėti „Firmata“palaikymą naujam įrenginiui, reikia pakeisti „Arduino FirmataExpress“serverio kodą ir „Python“pagrįstą „pymata4“kliento kodą. „FirmataExpress“kodo derinimas gali būti sudėtingas. Prie „FirmataExpress“buvo pridėtas metodas, vadinamas printData, kad būtų lengviau derinti. Šis metodas leidžia jums siųsti duomenų reikšmes iš „FirmataExpress“ir spausdinti jas „pymata4“konsolėje.

Šiai funkcijai reikalingas žymeklis į simbolių eilutę ir reikšmė, kurią norite peržiūrėti. Jei duomenų reikšmė yra kintamajame, pavadintame argc, galite paskambinti printData su šiais parametrais.

printData ((char*) "argc =", argc);

Jei turite klausimų, tiesiog palikite komentarą ir aš mielai atsakysiu.

Laimingo kodavimo!

Rekomenduojamas: