Innehållsförteckning:

Going Beyond Standard Firmata - Revisited: 5 Steg
Going Beyond Standard Firmata - Revisited: 5 Steg

Video: Going Beyond Standard Firmata - Revisited: 5 Steg

Video: Going Beyond Standard Firmata - Revisited: 5 Steg
Video: 🚀 RUTUBE и ТОЧКА. Разработка РУТУБА за 5 часов *БЕЗ ВОДЫ* [Next / React / Nest / PostgreSQL / Redux] 2024, November
Anonim
Going Beyond StandardFirmata - Revisited
Going Beyond StandardFirmata - Revisited

För en liten stund sedan kontaktades jag av Dr. Martyn Wheeler, en pymata4 -användare, för vägledning för att lägga till stöd för DHT22 fukt-/temperatursensor i pymata4 -biblioteket. Pymata4 -biblioteket, tillsammans med sin Arduino -motsvarighet, FirmataExpress, tillåter användare att fjärrstyra och övervaka sina Arduino -enheter. Inom några rundor av e -postutbyten lyckades Dr. Wheeler modifiera både pymata4 och FirmataExpress. Som ett resultat är stödet för sensorerna DHT22 och DHT11 nu en standarddel av pymata4 och FirmataExpress.

I maj 2014 skrev jag en artikel om att lägga till stöd för Firmata för ytterligare enheter. När jag reflekterade över den här artikeln insåg jag hur mycket som har förändrats sedan jag tog penna på papper för den artikeln. Utöver den här artikeln dokumenterade Dr Wheeler sina ansträngningar, och du kanske också vill kolla in det.

FirmataExpress är baserat på StandardFirmata, och StandardFirmata -katalogstrukturen har utvecklats. Dessutom är pymata4 API också en hel del annorlunda än det ursprungliga PyMata API för 2014. Jag trodde att det här skulle vara den perfekta tiden att besöka och uppdatera den artikeln. Låt oss utforska hur vi kan utöka funktionerna i pymata4/FirmataExpress med hjälp av Dr Wheelers arbete som grund.

Innan vi börjar - lite bakgrundsinformation om Arduino/Firmata

Så vad är Firmata? Citerar från Firmata -webbsidan, "Firmata är ett generiskt protokoll för kommunikation med mikrokontroller från programvara på en värddator."

Arduino Firmata använder ett seriellt gränssnitt för att transportera både kommando- och rapportinformation mellan en Arduino -mikrokontroller och en dator, vanligtvis med hjälp av en seriell/USB -länk till 57600 bps. Data som överförs via denna länk är binär och protokollet implementeras i en klient/server -modell.

Serversidan laddas upp till en Arduino -mikrokontroller i form av en Arduino -skiss. StandardFirmata -skissen, som ingår i Arduino IDE, styr Arduino I/O -stiften, enligt kommando från klienten. Det rapporterar också ändringar av inmatningsstift och annan rapportinformation tillbaka till klienten. FirmataExpress är en utökad version av StandardFirmata. Den körs med en seriell länkhastighet på 115200 bps.

Arduino -klienten som används för den här artikeln är pymata4. Det är ett Python -program som körs på en dator. Den både skickar kommandon till och tar emot rapporter från Arduino -servern. Eftersom pymata4 är implementerat i Python körs det på Windows, Linux (inklusive Raspberry Pi) och macOS -datorer.

Varför använda Firmata?

Arduino mikrokontroller är underbara små enheter, men processor- och minnesresurser är något begränsade. För applikationer som är processor- eller minneintensiva finns det ofta lite annat val än att ladda resursbehovet till en dator för att programmet ska lyckas.

Men det är inte den enda anledningen till att använda StandardFirmata. Vid utveckling av lättare Arduino -applikationer kan en dator tillhandahålla verktyg och felsökningsfunktioner som inte är direkt tillgängliga på en Arduino -mikrokontroller. Att använda en "fast" klient och server hjälper till att begränsa applikationens komplexitet till en dator, som är lättare att hantera. När applikationen är perfekt kan den översättas till en anpassad, fristående Arduino -skiss.

Varför använda pymata4?

Som författare är jag naturligtvis partisk. Med detta sagt är det den enda Python-baserade Firmata-klienten som kontinuerligt har underhållits under de senaste åren. Det ger ett intuitivt och lättanvänt API. Förutom StandardFirmata-baserade skisser, stöder den Firmata över WiFi för enheter som ESP-8266 när du använder StandardFirmataWifI-skissen.

Dessutom var pymata4 utformad för att enkelt kunna utökas av en användare för att stödja ytterligare sensorer och ställdon som för närvarande inte stöds av StandardFirmata.

Steg 1: Förstå Firmata -protokollet

Förstå Firmata -protokollet
Förstå Firmata -protokollet

Arduino Firmata-kommunikationsprotokollet härleds från MIDI-protokollet, som använder en eller flera 7-bitars byte för att representera data.

Firmata har utformats för att kunna utökas av användare. Mekanismen som ger denna utökningsbarhet är meddelandeprotokollet System Exclusive (SysEx).

Formatet för ett SysEx -meddelande, enligt definitionen i Firmata -protokollet, visas i illustrationen ovan. Den börjar med en START_SYSEX -byte med ett fast värde på hexadecimal 0xF0 och följs av en unik SysEx -kommandobyte. Värdet på kommandobyten måste vara inom området hexadecimalt 0x00-0x7F. Kommandobyten följs sedan av ett ospecificerat antal 7-bitars databyte. Slutligen avslutas meddelandet med en END_SYSEX -byte, med ett fast värde på hexadecimal 0xF7.

Firmata Data Encoding/Decoding

Eftersom användardatadelen av ett SysEx-meddelande består av en serie 7-bitars byte kan du undra hur man representerar ett värde större än 128 (0x7f)? Firmata kodar dessa värden genom att demontera dem i flera 7-bitars byte-bitar innan data marscheras över datalänken. Den minst signifikanta byten (LSB) för ett dataobjekt skickas först, följt av allt viktigare komponenter i dataposten enligt konvention. Den viktigaste byten (MSB) för dataposten är den senaste dataposten som skickades.

Hur fungerar detta?

Låt oss säga att vi vill införliva ett värde 525 i datadelen av ett SysEx -meddelande. Eftersom ett värde på 525 är klart större än värdet 128, måste vi dela upp eller isär det i 7-bitars "bitar".

Så här går det till.

Värdet 525 i decimal motsvarar det hexadecimala värdet 0x20D, ett 2-byte-värde. För att få LSB maskerar vi värdet genom att AND'a det med 0x7F. Både "C" och Python -implementeringar visas nedan:

// "C" -implementering för att isolera LSB

int max_distance_LSB = max_distance & 0x7f; // maskera den lägre byten # Python -implementering för att isolera LSB max_distance_LSB = max_distance & 0x7F # maskera den lägre byten

Efter maskering innehåller max_distance_LSB 0x0d. 0x20D & 0x7F = 0x0D.

Därefter måste vi isolera MSB för detta 2-byte-värde. För att göra detta kommer vi att flytta värdet 0x20D åt höger, 7 platser.

// "C" -implementering för att isolera MSB med 2 byte

int max_distance_MSB = max_distance >> 7; // skifta byten med hög ordning # Python -implementering för att isolera MSB med 2 byte värde max_distance_MSB = max_distance >> 7 # shift för att få den övre byten Efter skiftning kommer max_distance_MSB att innehålla ett värde på 0x04.

När "chunkified" marshaled data tas emot måste den sättas ihop till ett enda värde. Så här samlas data om i både "C" och Python

// "C" -implementering för att sätta ihop 2 byte, // 7 bitars värden till ett enda värde int max_distance = argv [0] + (argv [1] << 7); # Python -implementering för att återmontera 2 byte, # 7 bitvärden till ett enda värde max_distance = data [0] + (data [1] << 7)

Efter återmontering är värdet återigen lika med 525 decimaler eller 0x20D hexadecimalt.

Denna demonterings-/återmonteringsprocess kan utföras av antingen klienten eller servern.

Steg 2: Låt oss komma igång

Stöd för en ny enhet kräver ändringar av både Arduino resident -servern och PC -resident Python -klienten. Dr Wheelers arbete kommer att användas för att illustrera de nödvändiga modifieringarna.

Det kanske viktigaste steget är att bestämma om du vill integrera ett befintligt bibliotek för stödjande enheter i Arduino -sidan av ekvationen eller skriva ditt eget. Det rekommenderas att om du kan hitta ett befintligt bibliotek är det mycket enklare att använda det än att skriva ditt eget från grunden.

För DHT -enhetsstöd baserade Dr. Wheeler sin tilläggskod på DHTNew -biblioteket. Mycket smart delade Dr. Wheeler funktionaliteten i DHTNew -biblioteket över Arduino- och pymata4 -sidorna av ekvationen för att ge minimal blockering på Arduino -sidan.

Om vi tittar på DHTNew utför den allt av följande:

  • Ställer in det valda digitala utgångsläget.
  • Klockar ut en kodad signal för att hämta de senaste fukt- och temperaturvärdena.
  • Kontrollerar och rapporterar eventuella fel.
  • Beräknar människoläsbara temperatur- och luftfuktighetsvärden från rådata.

För att hålla sakerna så effektiva som möjligt på FirmataExpress -sidan laddade Dr. Wheeler ner datakonverteringsrutinerna från Arduino till pymata4.

Steg 3: Ändra FirmataExpress för DHT -stöd

FirmataExpress -katalogträdet

Nedan visas alla filer som innehåller FirmataExpress -förvaret. Det här trädet är identiskt med det för StandardFiramata, bara att några av filnamnen återspeglar lagringsnamnet.

Filerna som behöver ändras är de som har en asterisk (*) bredvid sig.

FirmataExpress

├── * Boards.h

├── exempel

│ └── FirmataExpress

│ ├── boardx

│ ├── * FirmataExpress.ino

│ ├── LICENS.txt

│ └── Makefile

├── * FirmataConstants.h

├── * FirmataDefines.h

├── FirmataExpress.cpp

├── FirmataExpress.h

├── FirmataMarshaller.cpp

├── FirmataMarshaller.h

├── FirmataParser.cpp

└── FirmataParser.h

Låt oss titta på var och en av filerna och de ändringar som gjordes.

Boards.h

Den här filen innehåller makrodefinitioner av stifttyp för var och en av de korttyper som stöds. Den definierar det maximala antalet enheter som stöds när mer än en enhet behöver stödjas.

För DHT -enheten kan upp till 6 enheter anslutas åt gången och detta värde definieras som:

#ifndef MAX_DHTS

#define MAX_DHTS 6 #endif

Dessutom kan stiftmakron definieras valfritt för den nya enheten, antingen för alla korttyper eller bara de som är av intresse för dig. Dessa makron används mest för rapporteringsändamål och används inte för att styra enheterna. Dessa makron definierar båda stiften som stöder enheten:

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

Samt ett makro för att definiera en pin-nummer-konvertering.

#define PIN_TO_DHT (p) PIN_TO_DIGITAL (p)

FirmataConstants.h

Denna fil innehåller versionsnummer för firmware, som du kanske vill ändra för att hålla reda på vilken version du har laddat till din Arduino. Den innehåller också Firmata -meddelandevärden, inklusive Firmata SysEx -meddelanden.

Du måste tilldela ett nytt meddelande eller en uppsättning meddelanden för din enhet i den här filen. För DHT har två meddelanden lagts till. Den ena konfigurerar en nål som en "DHT" -nål, och den andra, som ett reportermeddelande, när de senaste DHT -data skickas tillbaka till klienten.

statisk const int DHT_CONFIG = 0x64;

statisk const int DHT_DATA = 0x65;

Pin -lägen anges också i den här filen. För DHT skapades ett nytt pin -läge:

statisk const int PIN_MODE_DHT = 0x0F; // pin konfigurerad för DHT

När du lägger till ett nytt pin -läge måste TOTAL_PIN_MODES justeras:

statisk const int TOTAL_PIN_MODES = 17;

FirmataDefines.h

Den här filen måste uppdateras för att återspegla de nya meddelanden som läggs till i FirmataConstants.h:

#ifdef DHT_CONFIG # undef DHT_CONFIG #endif #define DHT_CONFIG firmata:: DHT_CONFIG // DHT begäran #ifdef DHT_DATA #undef DHT_DATA #endif #define DHT_DATA firmata:: DHT_DATA // DHT svara #ifdef PIN_MODE_DHT #undef PIN_MODE_DHT #endif #define PIN_MODE_DHT firmata:: PIN_MODE_DHT

FirmataExpress.ino

I denna diskussion kommer vi att täcka "höjdpunkterna" i de ändringar som gjorts i denna Arduino-skiss.

För att FirmataExpress skulle kunna stödja upp till sex DHT -enheter samtidigt skapades tre matriser för att hålla reda på var och en av enhetens tillhörande pin -nummer, dess WakeUpDelay -värde och enhetstypen, det vill säga DHT22 eller DHT11:

// DHT -sensorer

int numActiveDHTs = 0; // antal bifogade DHTs uint8_t DHT_PinNumbers [MAX_DHTS]; uint8_t DHT_WakeUpDelay [MAX_DHTS]; uint8_t DHT_TYPE [MAX_DHTS];

Eftersom båda enhetstyperna kräver cirka 2 sekunder mellan läsningarna måste vi se till att vi bara läser varje DHT en gång i 2-sekunders tidsram. Vissa enheter, till exempel DHT-enheter och avståndssensorer HC-SR04, är endast åtkomliga med jämna mellanrum. Detta ger dem tid att interagera med sina miljöer.

uint8_t nextDHT = 0; // indexera i dht för nästa enhet som ska läsas

uint8_t currentDHT = 0; // Håller koll på vilken sensor som är aktiv. int dhtNumLoops = 0; // Mål antal gånger genom loop b4 som får åtkomst till en DHT int dhtLoopCounter = 0; // Loop -räknare

Konfigurera och läsa DHT -enheten

När FirmataExpress tar emot ett SysEx -kommando för att konfigurera en pin för DHT -drift, verifierar det att det maximala antalet DHT -enheter inte har överskridits. Om den nya DHT kan stödjas uppdateras DHT -matriserna. Om DHT -typen är okänd skapas ett SysEx -strängmeddelande och skickas tillbaka till pymata4

fall DHT_CONFIG: int DHT_Pin = argv [0]; int DHT_type = argv [1]; if (numActiveDHTs <MAX_DHTS) {if (DHT_type == 22) {DHT_WakeUpDelay [numActiveDHTs] = 1; } annars om (DHT_type == 11) {DHT_WakeUpDelay [numActiveDHTs] = 18; } annat {Firmata.sendString ("ERROR: Okänd givartyp, giltiga givare är 11, 22"); ha sönder; } // testa sensorn DHT_PinNumbers [numActiveDHTs] = DHT_Pin; DHT_TYPE [numActiveDHTs] = DHT_typ; setPinModeCallback (DHT_Pin, PIN_MODE_DHT);

FirmataExpress försöker sedan kommunicera med DHT -enheten. Om det finns några fel, bildar det ett SysEx -meddelande med feldata och skickar SysEx -meddelandet tillbaka till pymat4. Variabeln _bits innehåller data som returneras av DHT -enheten för ytterligare behandling av pymata4 om så önskas.

Firmata.write (START_SYSEX);

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

Om giltig data returneras ökas antalet aktiva DHT: er. En variabel som håller reda på hur många loop -iterationer som ska slutföras innan du kontrollerar nästa DHT för data justeras också. Denna variabel säkerställer att oavsett hur många DHT: er som läggs till systemet kommer de alla att läsas inom en period på 2 sekunder.

int rv = readDhtSensor (numActiveDHTs);

if (rv == DHTLIB_OK) {numActiveDHTs ++; dhtNumLoops = dhtNumLoops / numActiveDHTs; // allt väl }

Om en eller flera DHT -enheter har konfigurerats i skissens loop -funktion läses nästa DHT -enhet. Antingen returneras giltiga data eller dess felstatus till pymata4 i form av ett SysEx -meddelande:

if (dhtLoopCounter ++> dhtNumLoops) {if (numActiveDHTs) {int rv = readDhtSensor (nextDHT); uint8_t current_pin = DHT_PinNumbers [nästaDHT]; uint8_t current_type = DHT_TYPE [nästaDHT]; dhtLoopCounter = 0; currentDHT = nästaDHT; if (nextDHT ++> = numActiveDHTs - 1) {nextDHT = 0; } if (rv == DHTLIB_OK) {// TEST CHECKSUM uint8_t sum = _bits [0] + _bits [1] + _bits [2] + _bits [3]; om (_bits [4]! = summa) {rv = -1; }}} // skicka tillbaka meddelandet med en felstatus Firmata.write (START_SYSEX); Firmata.write (DHT_DATA); Firmata.write (current_pin); Firmata.write (aktuell_typ); för (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);}}

Koden som används för att kommunicera med DHT -enheten härleds direkt från DHTNew -biblioteket:

int readDhtSensor (int index) {

// INIT BUFFERVAR FÖR ATT MOTTA DATA uint8_t mask = 128; uint8_t idx = 0; // EMPTY BUFFER // memset (_bits, 0, sizeof (_bits)); för (uint8_t i = 0; i 5 BYTES för (uint8_t i = 40; i! = 0; i--) {loopCnt = DHTLIB_TIMEOUT; while (digitalRead (pin) == LOW) {if (--loopCnt == 0) returnera DHTLIB_ERROR_TIMEOUT;} uint32_t t = micros (); loopCnt = DHTLIB_TIMEOUT; medan (digitalRead (pin) == HIGH) {if (--loopCnt == 0) return DHTLIB_ERROR_TIMEOUT;} if ((micros ()-t)> 40) {_bits [idx] | = mask;} mask >> = 1; if (mask == 0) // nästa byte? {Mask = 128; idx ++;}} returnera DHTLIB_OK;}

Steg 4: Ändra Pymata4 för DHT -stöd

private_constants.h

För att stödja DHT måste vi lägga till både den nya PIN-typen och SysEx-meddelanden till den här filen:

# stiftlägen INPUT = 0x00 # stift som ingång OUTPUT = 0x01 # stift som utgång ANALOG = 0x02 # analog stift i analogt ingångsläge PWM = 0x03 # digital stift i PWM -utgångsläge SERVO = 0x04 # digitalt stift i servoutgångsläge I2C = 0x06 # stift ingår i I2C -inställning STEPPER = 0x08 # valfri stift i stegläge SERIAL = 0x0a PULLUP = 0x0b # Alla stift i pullup -läge SONAR = 0x0c # Alla stift i SONAR -läge TONE = 0x0d # Alla stift i tonläge PIXY = 0x0e # reserverat för pixy -kameraläge DHT = 0x0f # DHT -sensor IGNORERA = 0x7f # DHT SysEx -kommandomeddelanden DHT_CONFIG = 0x64 # dht config -kommando DHT_DATA = 0x65 # dht sensor svar

Den tillagda stifttypen och SysEx -kommandon måste matcha värdena i FirmataConstants.h som läggs till i FirmataExpress.

pymata4.py

Pymata4 använder en Python -ordlista för att snabbt associera ett inkommande Firmata -meddelande med en meddelandehanterare. Namnet på denna ordbok är report_dispatch.

Formatet för en ordbokspost är:

{MessageID: [message_handler, antal databyte som ska bearbetas]}

En post har lagts till i ordlistan för att hantera inkommande DHT -meddelanden:

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

De 7 byte med data i meddelandet är Arduino digitala pin -nummer, typen av DHT -enhet (22 eller 11) och de 5 byte av rådata.

Metoden _dht_read_response söker efter rapporterade fel. Om det inte finns några rapporterade fel beräknas luftfuktigheten och temperaturen med hjälp av algoritmen som överförs från Arduino DHTNew -biblioteket.

De beräknade värdena rapporteras via en återuppringningsmetod som användaren tillhandahåller. De lagras också i den interna pin_data -datastrukturen. Det senaste värdet som kan rapporteras kan återkallas genom att undersöka pin_data med dht_read -metoden.

Konfigurera en ny DHT -enhet

När du lägger till en ny DHT -enhet kallas metoden set_pin_mode_dht. Denna metod uppdaterar pin_data för digitala pins. Det skapar och skickar också ett DHT_CONFIG SysEx -meddelande till FirmataExpress.

Steg 5: Förpackning

Som vi har sett måste du ändra Arduino FirmataExpress-serverkoden och den Python-baserade pymata4-klientkoden för att lägga till Firmata-stöd för en ny enhet. FirmataExpress -kod kan vara utmanande att felsöka. En metod som kallas printData har lagts till i FirmataExpress för att hjälpa till med felsökning. Denna metod låter dig skicka datavärden från FirmataExpress och skriver ut dem på pymata4 -konsolen.

Denna funktion kräver både en pekare till en teckensträng och det värde du vill visa. Om datavärdet finns i en variabel som heter argc kan du ringa printData med följande parametrar.

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

Om du har några frågor är det bara att lämna en kommentar, så svarar jag gärna.

Glad kodning!

Rekommenderad: