Soru:
Ev ışıklarını / anahtarları kontrol etmek için birden fazla Arduino'yu bir Rpi ile birbirine bağlama
Damiano Verzulli
2015-09-05 04:37:32 UTC
view on stackexchange narkive permalink

Yeni evimin (hala yapım aşamasında) yıldırım altyapısını (duvar anahtarları ve ışıkları) planlarken "otomatik rota" yolunu seçtim ve geçmişim nedeniyle ("yaşlıyım") programlama becerileri ve bol miktarda Açık Kaynak "tutkusu" ve "savunuculuğu" olan sistem / ağ yöneticisi) Cidden bunu üç Arduino ve bir RPi2 ile uygulamaya çalışıyorum.

Duvar butonlarının ve ışıkların sayısı / konumu nedeniyle, hem duvar butonları hem de surrouding odaların ışıklarını birbirine bağlayan üç MEGA kullanmak istiyorum. Ayrıca, bir RPi2 (veya benzeri), gerektiğinde MEGA'ları düzgün bir şekilde programlamak ve diğer bazı ekipmanlara (dokunmatik ekran; wifi tablet / akıllı telefon; uzaktan kumandalar; vb.) IP üzerinden arayüz oluşturmak için bir "kontrolör" olarak kullanılacaktır. ağ.

Uygulamaya çalıştığım şema şuna benzer:

Draft Schema gördüğünüz yerde:

  • Her biri kendi özel rölesi (R1 ila R9) tarafından kontrol edilen 9 ışık (L1 ila L9);
  • Açmak / açmak için kullanılacak 7 duvar düğmesi (WPB1 ila WPB7) bir veya daha fazla ışık kapalı;
  • Duvar butonları ve ışıkları arabirimine sahip 3 MEGA;
  • "gözetmen" ve "İnternet / Ethernet ağ geçidi" olarak işlev gören 1 RPi2.

Ana mimari sorunum RPI2 ve MEGA'lar arasındaki ara bağlantıyla ilgili. Her cihaz birbirinden onlarca metre uzakta bulunacağından, iki tek seçenekle sonlandırdım (lütfen yanılıyorsam düzeltin):

  1. Ethernet
  2. RS485

( BTW: Tüm elektrik borularını "uyumlu" bir şekilde yerleştirdiğim için "kablosuz bağlantıları" hariç tutuyorum. Başka bir deyişle: "Kontrol eden ağ" içindeki kablosuz teknolojilerden kaçınmak istiyorum )

Ethernet'e gelince, hem biraz daha yüksek maliyet hem de karmaşıklık nedeniyle bunu 2. seçenek olarak seçeceğim ( bir anahtarın açılması gerekir; ek kablolama sorunları vb.).

" RS485 veri yolu " nu kapsamlı bir şekilde araştırdım ve onu birden çok bağlantı noktalı yapılandırmada iki kabloyla .

Ne yazık ki, "uygulama açısından", yalnızca "yarı çift yönlü" iletişimi desteklediğinden ve daha da kötüsü, hiçbir iletişim sırasında "çarpışmalardan" kaçınmak için hazırlık (muhtemelen bu nedenle, tipik olarak RS485 veriyolunda kullanılan MODBUS protokolü bir "tek master; çoklu slave" senaryosu sağlar).

Daha önce sorular, başka bir sınırlama eklemem gerekiyor: Altyapının, özellikle BUS ve / veya "kontrolör" (RPI2) ile ilgili sorunlara karşı "olabildiğince hataya dayanıklı" olmasını istiyorum. Başka bir deyişle:

  • Her MEGA, kendi butonlarından biri bunu gerektirdiğinde kendi ışıklarını açmaya izin vermelidir. Örneğin:
    • WPB1, L2 ve L3'ü kontrol ediyorsa, BUS bozuk veya RPI2 kapalı olsa bile çalışıyor olması gerekir;
    • WPB3, L4 ve L9'u kontrol ediyorsa, o zaman Bus / RPI2'de sorunlar var, WPB3'e basıldığında yalnızca L4 açılacak;

Yukarıdakilerin hepsinden sonra, işte sorularım:

  1. benim senaryom için uygun bir seçim olan iki telli RS485 çoklu bağlantı yolu mu?

  2. değilse, hangisi (muhtemelen ucuz ve basit) diğer çözümler araştırabilir miyim?

  3. Evetse, MEGA'larda uygulamam gereken mantık şu:

    • a) diğer MEGA'lara bağlı butonla komut verilen ışıkları açarken veya RP2 bazı ışıkları açmaya karar verdiğinde (örneğin, uzaktan / İnternet erişimi nedeniyle) RPI2'ye göre "bağımlı" olarak hareket etmeleri gerekir ;
    • b) düğmelerinden birine basıldığında "ana" olarak hareket etmeleri gerekir ve ... bunun RPI2'ye iletilmesi gerekir, böylece diğer MEGA'lara komutlar gönderilir, böylece açılır "barındırılan" ışıklar;
  4. 3b'de olduğu gibi), bir WPB'ye basıldığında MEGA'ların ana görevi görmesi nedeniyle, RPI2'de bir "sık sorgulama" mantığı uygulayabilir miyim? Evetse, bu tür bir anket için hangisi makul bir değerdir (saniyede 1 anket? Saniyede 5 anket? Çok fazla mı? Çok düşük?)

Bunun çok geniş bir soru, ancak gerçekten çok araştırma yaptım ve çok sayıda, çok ve çok sayıda çevrimiçi dokümantasyona rağmen, bu sorulara cevap bulamadım.


1. Güncelleme

  • Toplam sayılar açısından, 4 farklı şekilde aşağı yukarı eşit olarak dağıtılmış 46 duvar butonu ile kontrol edilecek 31 ışığım olacak panolar;
  • MEGA (UNO'ya karşı) seçimine gelince, daha fazla sayıda G / Ç PIN'i olması nedeniyle MEGA'yı seçtim. Yalnızca maksimum PIN sayısına sahip panoyu seçtim;
  • RPI2'ye gelince, uygun bir "bilgisayar" (ek bir mikro denetleyiciye karşı) kullanmayı seçtim, çünkü "fiziksel kontrol ağı" ile "Kullanıcı Yönetimi Arayüzü" arasında bir tür "ayrıştırma" istiyorum. Başka bir deyişle, fiziksel düğmelerin / rölenin yönetiminin PLC benzeri cihaz tarafından yapılmasını istiyorum (Arduino: çok hızlı güç açık; bir tür gerçek zamanlı performans; çok güvenilir; gecikmelere neden olan küçük / harici "hesaplama" faktörleri yok / sorunlar); aynı zamanda, tüm kullanıcı arayüzü, Arduino ile pek uyumlu olmayan teknolojileri ve dilleri kullanarak, güçlü HTTP web servislerini ve / veya gerçekten karmaşık "mantıkları" kolayca yazabildiğim gerçek bir bilgisayar tarafından yönetilebilir. (daha sonra --çok daha sonra--, evin her yerine, "güçlü" olması gerekenlere yönelik kablosuz "istemciler" olarak hareket edecek birkaç 150 $ full-HD 10.1 "android tablet eklemeyi planlıyorum. bilgi işlem kapasiteleri) web sunucusu; Ayrıca, trend oluşturma / arşivleme nedenleriyle verilerinin depolanması gereken çeşitli sensörler [sıcaklık; nem; kontaklar; güç ölçerler; vb.] eklemeyi planlıyorum. Bu nedenle, RPi2 hakkında düşünebileceğimden çok gerektiğinde daha güçlü bir şeyle kolayca değiştirilebilir.
Muhtemelen RPi'ye bağlı, RS-485'te 9 bit iletişim yapabilen bir şey isteyeceksiniz. Linux, 9-bit UART'leri desteklemez, bu yüzden onsuz eşlik biti ile çirkin numaralar oynamanız gerekir.
What if I'll connect, via RS485, only ARDUINOs? I mean, the problem you're mentioning is strictly introduced by connecting RPI to RS485, right? I'm asking, 'cause based also on AiliokehunCMT answer below, I'll probably terminate the RS485 bus on ad additional Arduino, and having the interconnection between RPI and Arduino using Ethernet (and not RS485). In such a case, RPI will not need to speak RS485 and problem shoud disappear, right?
I just wanted to note that the chance of packet collisions is rather low. You only send packets when a switch is pressed, or when a light needs to turn on. Unless you live inside a disco, this only happens a few times a day per light/switch.
@Gerben: iki küçük oğlumla (2,5 ve 5,5), sizi temin ederim ki böyle bir olasılık "normal" koşullar altında beklediğiniz kadar düşük DEĞİL :-). Şakalar bir yana, her şeyin yolunda gitmesi durumunda, "veri yolu trafiği" (sıcaklık / nem sensörleri [oda başına bir]; kapılar / pencereler için kontakları aç / kapat [en azından her pencere için iki tane]; elektrik-güç-sayaçları ve su-kullanım-sayaçları muhtemelen bir şekilde toplanacak "darbeler" üretiyor. Yani yine "çarpışma" olduğunda MEGA'larım "hayatta kalacak mı"? yanacak mısın?
Dört yanıtlar:
Nick Gammon
2015-09-05 06:00:09 UTC
view on stackexchange narkive permalink

RS485 hakkında uzun bir gönderi yaptım.


İlk olarak, Megas kullanımınız, eğer zaten elinizde yoksa, abartılı görünüyor. Bir Uno veya daha küçük form faktörü panolarından biri, birkaç anahtarı izlemek ve birkaç ışığı açmak için mükemmel şekilde yeterli görünebilir.

Rpi bile gereksiz görünüyor. Başka bir Uno, RS485 hatlarınızı kolayca izleyebilir ve Ethernet (bir Ethernet kalkanı) yoluyla evinizin geri kalanına veya yaptığınız her ne yapıyorsanız bağlanabilir.


... provizyon yok iletişim kurarken "çarpışmalardan" kaçınmak için ...

Pekala, onu inşa edersiniz. Her Mega'ya bir adres verirsiniz (örn. EEPROM'da kayıtlı) ve sonra istiyorum ve sonra bir yanıt bekleyin. Örneğin, yukarıdaki sayfamdaki kodda:

Ana

  #include "RS485_protocol .h "#include <SoftwareSerial.h>const baytı ENABLE_PIN = 4; const bayt LED_PIN = 13; SoftwareSerial rs485 (2, 3); // pin al, pin gönder // geri çağırma rutinleri void fWrite (const byte ne) {rs485.write (ne); } int fAvailable () {return rs485.available (); } int fRead () {dönüş rs485.read (); } geçersiz kurulum () {rs485.begin (28800); pinMode (ENABLE_PIN, OUTPUT); // sürücü çıkışı etkinleştirin pinMode (LED_PIN, OUTPUT); // yerleşik LED} // kurulum baytı sonu old_level = 0; void loop () {// potansiyometre bayt seviyesi oku = analogRead (0) / 4; // değişiklik yok? (level == old_level) dönerse unutun; // mesaj baytını birleştirin msg [] = {1, // cihaz 1 2, // ışığı hangi seviyede açın // hangi seviyeye getirin}; // slave digitalWrite'a gönder (ENABLE_PIN, HIGH); // sendMsg göndermeyi etkinleştir (fWrite, msg, sizeof msg); digitalWrite (ENABLE_PIN, LOW); // göndermeyi devre dışı bırak // yanıt baytı al buf [10]; alınan bayt = recvMsg (fAvailable, fRead, buf, sizeof buf); digitalWrite (LED_PIN, alınan == 0); // hata durumunda LED'i aç
// (alındı) old_level = level;} // döngünün sonu  

RS485 alıcı-vericisini göndermek veya almak için yapılandırırsanız // başarılı değişiklik başına yalnızca bir kez gönderin. Normalde alma modundadır ve bir veri "paketi" göndermek için gönderme moduna geçersiniz. (Yukarıdaki "göndermeyi etkinleştir" bölümüne bakın).


Adreslenen cihaz artık alıcı vericisini "gönderme" moduna ve yanıtlara dönüştürüyor. Yazdığım kütüphanedeki kodun bir zaman aşımı var, bu nedenle o belirli köle ölmüşse, alma zaman aşımına uğradı. Bunu usta sonunda hatırlayabilir ve onunla daha az iletişim kurmaya çalışabilirsiniz. Ya da zaman aşımının kısa olması umrunda olmayabilir.

Slave

  #include <SoftwareSerial.h> # include "RS485_protocol.h" SoftwareSerial rs485 (2, 3); // pin al, pinconst baytını ilet ENABLE_PIN = 4; void fWrite (const bayt ne) {rs485.write (ne); } int fAvailable () {return rs485.available (); } int fRead () {dönüş rs485.read (); } geçersiz kurulum () {rs485.begin (28800); pinMode (ENABLE_PIN, OUTPUT); // sürücü çıktısı etkinleştirme} void döngü () {bayt buf [10]; alınan bayt = recvMsg (fAvailable, fRead, buf, sizeof (buf)); eğer (alındı) {if (buf [0]! = 1) iade; // benim cihazım değil if (buf [1]! = 2) return; // bilinmeyen komut baytı msg [] = {0, // cihaz 0 (ana) 3, // alınan komuta ışığı aç}; gecikme (1); // yöneticiye digitalWrite (ENABLE_PIN, HIGH) 'ı almaya hazırlanmak için bir dakika verin; // sendMsg göndermeyi etkinleştir (fWrite, msg, sizeof msg); digitalWrite (ENABLE_PIN, LOW); // analogWrite (11, buf [2]) göndermeyi devre dışı bırakın; // ışık düzeyini ayarla} // bir şey alınırsa bitir} // döngünün sonu  

Not : Bağlı sayfamdaki örnek kod adresi okumuyor EEPROM'dan, ancak bunun uygulanması önemsizdir.


Köleleri neden bu kadar sık ​​sorgulayamadığınızla ilgili belirli bir neden göremiyorum. Usta başka ne yapacaktı? Ana sunucuyu bir HTTP sunucusu olarak da kurabilirsiniz, böylece onunla dizüstü bilgisayarınızdan veya evin başka bir yerinden konuşabilirsiniz.


Kablolarım:

RS485 wiring


Fikri göstermek için, yaklaşık 8 m çan teli ile bağlanan iki Uno kurdum (bükülmüş çift bile değil ve kesinlikle korumalı).

RS485 demo

Bir Uno standart ASCII tablo taslağını çalıştırıyordu (9600 baud'da), Tx pini LTC1480'e girdi, ve sonra A / B pinleri çan kablosuna gitti.

Diğer Uno bir USB arayüzü olarak bağlandı (toprağa sıfırlandı) ve Tx pinine gelen her şeyi USB'ye yansıtıyordu.

Görebildiğim kadarıyla mükemmel çalıştı.


Yaklaşımınızda bir " tek ana / çoklu bağımlı "bağlamı ... ancak" hareketli "bir ana birim ile. Bu doğru mu?

Yukarıdaki cevabım kölelerin başarısız olma olasılığının efendiden daha yüksek olduğunu varsaydı (bunun neden olacağını tam olarak düşünemiyorum, ama belki de efendilerden daha fazla köle ve efendi ışıklar gibi şeyleri kontrol etmez).

Arduino'larımı basit şeyler yaparken son derece güvenilir buluyorum (RFID kartı sunulduğunda bir kapının kilidini açmak gibi).

Muhtemelen kölelere karşı bir geri çekilme pozisyonu oluşturabilirsiniz. Sonuçta, eğer her saniye yoklanırlarsa ve sonra hiçbir anket gelmezse, çatışmaları önlemek için, muhtemelen artan cihaz numarası sırasına göre ana olarak devralmaya çalışabilirler. "Yeni usta" tarafından yapılan bu anketin bir kısmı, görevine devam etmeye hazır olup olmadığını görmek için "orijinal ustayı" kontrol etmek olabilir.

Bağlantılı sayfamda anlattığım kitaplıkta hata denetimi yerleşiktir; paketteki bir CRC'yi kontrol ederek bir paketin yarısına gelmemenizi ve içindeki verileri yanlış yorumlamanızı sağlamanızdır. .

Ayrıca, iki köle aynı anda efendi olmaya çalıştıysa, bir çıkmaza son vermek için bazı rasgele anket süreleri de oluşturabilirsiniz. Bir köle başarısız olursa, başka bir köleye bunu yapma şansı vermek için tekrar denemeden önce rastgele (ve artan) bir süre bekleyebilirdi.


Sadece şansı not etmek istedim paket çarpışmalarının oranı oldukça düşüktür. Paketleri yalnızca bir anahtara basıldığında veya bir ışığın yanması gerektiğinde gönderirsiniz.

Gerben haklı, ancak bir anahtarla ilgili bir bildirimin fark edilmemiş olmasından endişelenirdim. Buradaki olası bir çözüm, bağımlıların bir sorguya durum değişiklikleri ile yanıt vermesinden ziyade, mevcut durumla yanıt vermeleridir. Öyleyse şöyle olabilir:

  Master: Bağımlı 3, durumunuz nedir? Bağımlı 3: Işıklar 1 ve 4 açık, ışıklar 2 ve 3 söner.  

Herhangi bir ankete ihtiyacım olmayacak, çünkü sizin yaklaşımınızla "tek-ana / çok-köle" bağlamı uygulayabilirim ... ancak "hareketli" bir usta. Doğru mu?

Bunu biraz düşünüyordum ve bence artık temelde ustasız bir sistem yapabilirsiniz. Şu şekilde çalışabilir:

  • Her cihazın EEPROM'dan (veya DIP anahtarlarından) aldığı kendi adresi vardır. Örneğin. 1, 2, 3, 4, 5 ...

  • Kullanacağınız bir adres aralığı seçersiniz (örn. Maksimum 10)

  • Cihaz açıldığında, ilk olarak veri yolunda "konuşan" diğer cihazları dinler. Umarım en az bir tane daha duyacaktır (değilse, aşağıya bakınız).

  • Sabit bir "mesaj paketi", örneğin adres, CRC vb. Dahil 50 baytlık bir karar veriyoruz. . 9600 baud'da gönderilmesi 52 ms sürer.

  • Her cihaz bir "zaman aralığı" alır ve otobüsle konuşma sırasının gelmesini bekler.

  • Zaman dilimi geldiğinde çıkış moduna geçer kendi adresini içeren paketini yayınlar. Bu nedenle, diğer tüm cihazlar artık durumunu okuyabilir (ve gerekirse buna göre hareket edebilir). Örneğin. cihaz 1, anahtar 3'ün kapalı olduğunu bildirebilir, bu da cihaz 2'nin ışığı yakması gerektiği anlamına gelir.

  • İdeal olarak, zaman aralığınızın geldiğini biliyorsunuz çünkü cihaz adresiniz az önce dinlediğiniz paket. Örneğin. Cihaz 3'sünüz. Cihaz 2'nin durumunu duyurduğunu duydunuz. Şimdi senin sıran. Elbette maksimum sayıya ulaşırsınız, bu nedenle 10. cihazdan sonra 1. cihaza geri dönersiniz.

  • Bir cihaz eksikse ve yanıt vermiyorsa, onu verirsiniz ( Diyelim ki) yarım zaman dilimi yanıt vermek için ve sonra öldüğünü varsayın ve veriyolu üzerindeki her aygıt şimdi bir sonraki zaman diliminin başladığını varsayar. (örn. Cihaz 2'yi duydunuz, cihaz 3 yanıt vermelidir, 25 ms'lik hareketsizlikten sonra cihaz 4 artık yanıt verebilir). Bu kural, bir aygıta yanıt vermesi için 25 ms verir; bu, bir kesintiye veya benzeri bir şeye hizmet ediyor olsa bile çok olmalıdır.

  • Sırayla birden fazla cihaz eksikse, bir Sıra size gelene kadar her kayıp cihaz için 25 ms'lik boşluk.

  • En az bir yanıt aldığınızda, zamanlama yeniden senkronize edilir, böylece saatlerdeki herhangi bir sapma iptal edilir.

Buradaki tek zorluk, ilk çalıştırmada (binanın gücü kesilirse ve ardından eski haline gelirse eşzamanlı olarak gerçekleşebilir) şu anda durumunu yayınlayan hiçbir cihazın olmamasıdır. ve dolayısıyla senkronize edilecek hiçbir şey yok.

Bu durumda:

  • Tüm cihazların duyulması için yeterince uzun süre dinledikten sonra (ör. 250 ms) ve hiçbir şey duymadığında, cihaz geçici olarak ilk olduğunu varsayar ve bir yayın yapar. Ancak muhtemelen iki cihaz bunu aynı anda yapacak ve dolayısıyla birbirlerini asla duymayacaktır.

  • Bir cihaz başka bir cihazdan haber almadıysa, yayınlar arasındaki zamanı rasgele sıralar (belki de tüm cihazların yayınları "rastgele" aynı miktarda şaşırtmasını önlemek için rastgele sayı oluşturucuyu cihaz numarasından başlatır. ).

  • Ek süre ile bu rastgele sersemletme önemli olmayacak, çünkü zaten dinleyen kimse yok.

  • Er ya da geç bir cihaz, veri yolunun özel kullanımına sahip olacak ve diğerleri daha sonra normal şekilde onunla senkronize edilebilecek.

İletişim girişimleri arasındaki bu rastgele boşluk birden fazla cihaz tek bir koaksiyel kabloyu paylaştığında Ethernet'in yaptığına benzer.


Ana içermeyen sistem demosu

Bu ilginç bir zorluktu, bu yüzden bir araya getirdim Yukarıda açıklandığı gibi, belirli bir ana makineye sahip olmadan bunu yapmanın bir demosu.

Öncelikle EEPROM'da mevcut cihaz adresini ve cihaz sayısını ayarlamanız gerekir, bu nedenle bu çizimi çalıştırın ve myAddress her biri için ch Arduino:

  #include <EEPROM.h>const byte myAddress = 3; const byte numberOfDevices = 4; void setup () {if (EEPROM.read ( 0)! = MyAddress) EEPROM.write (0, myAddress); eğer (EEPROM.read (1)! = numberOfDevices) EEPROM.write (1, numberOfDevices); } // setupvoid döngüsünün sonu () {}  

Şimdi bunu her cihaza yükleyin:

  / * Multi-drop RS485 cihaz kontrol demosu. Nick Gammon tarafından tasarlanmış ve yazılmıştır. Tarih: 7 Eylül 2015 Sürüm: 1.0 Lisans: Genel kullanım için yayınlandı. RS485_non_blocking kitaplığı için bkz: http://www.gammon.com.au/forum/?id=11428 JKISS32 için bkz: http://forum.arduino.cc/index.php?topic=263849.0*/#include <RS485_non_blocking. h> # include <SoftwareSerial.h> # include <EEPROM.h> // birbirimize yayınladığımız veriler devicestruct {bayt adresi; bayt anahtarları [10];
int durumu; } mesaj; const unsigned long BAUD_RATE = 9600; const float TIME_PER_BYTE = 1.0 / (BAUD_RATE / 10.0); // bir baytlık işaretsiz uzun gönderme başına saniye PACKET_LENGTH = ((sizeof (mesaj) * 2) + 6); // Yük baytı başına 2 bayt artı STX / ETC / CRCconst işaretsiz uzun PACKET_TIME = TIME_PER_BYTE * PACKET_LENGTH * 1000000; // mikrosaniye // yazılım seri pinsconst bayt RX_PIN = 2; const bayt TX_PIN = 3; // etkinleştirme baytını ilet XMIT_ENABLE_PIN = 4; // hata ayıklama pinsconst baytı OK_PIN = 6; const bayt TIMEOUT_PIN = 7; sabit bayt SEND_PIN = 8; const bayt SEARCHING_PIN = 9; const bayt ERROR_PIN = 10; // eylem pimleri (demo) sabit bayt LED_PIN = 13; const bayt SWITCH_PIN = A0; // kez mikrosaniye cinsinden işaretsiz uzun TIME_BETWEEN_MESSAGES = 3000; işaretsiz uzun noMessagesTimeout; byte nextAddress; unsigned long lastMessageTime; unsigned long lastCommsTime; unsigned long randomTime; SoftwareSerial rs485 (RX_PIN, TX_PIN); // pin al, pini ilet // hangi durumdayız biz inenum {STATE_NO_DEVICES, STATE_RECENT_RESPONSE, STATE_TIMED_OUT,} durum; // engellemeyen RS485 librarysize_t fWrite (const byte what) {rs485.write (ne); } int fAvailable () {return rs485.available (); } int fRead () {lastCommsTime = micros (); rs485.read () döndür; } // RS485 kütüphanesi örneğiRS485 myChannel (fRead, fAvailable, fWrite, 20); // EEPROMbyte myAddress'den; // numberOfDevices olarak kim olduğumuzu; // veriyolundaki maksimum aygıtlar // JKISS32static unsigned long için ilk tohum x = 123456789, y = 234567891, z = 345678912, w = 456789123, c = 0; // Simple Random Number Generatorunsigned long JKISS32 () {long t; y ^ = y << 5; y ^ = y >> 7; y ^ = y << 22; t = z + w + c; z = w; c = t < 0; w = t & 2147483647; x + = 1411392427; dönüş x + y + w;
} // JKISS32'nin sonu void Seed_JKISS32 (const unsigned long newseed) {if (newseed! = 0) {x = 123456789; y = yeni tohum; z = 345678912; w = 456789123; c = 0; }} // Seed_JKISS32'nin sonu void setup () {// hata ayıklama, Serial.begin (115200) yazdırır; // diğer cihazlarla konuşmak için yazılım serisi rs485.begin (BAUD_RATE); // RS485 kitaplığını başlatın myChannel.begin (); // hata ayıklama yazdırır Serial.println (); Serial.println (F ("Başlıyor")); myAddress = EEPROM.read (0); Serial.print (F ("Adresim")); Serial.println (int (myAddress)); numberOfDevices = EEPROM.read (1); Serial.print (F ("Azami adres")); Serial.println (int (numberOfDevices)); if (myAddress > = numberOfDevices) Serial.print (F ("** UYARI ** - cihaz numarası aralık dışı, algılanmayacak.")); Serial.print (F ("Paket uzunluğu =")); Seri.baskı (PACKET_LENGTH); Serial.println (F ("bayt")); Serial.print (F ("Paket süresi =")); Serial.print (PACKET_TIME); Serial.println (F ("mikrosaniye")); // hiçbir şeyin yanıt vermediğini ne kadar süre varsayacağını hesapla noMessagesTimeout = (PACKET_TIME + TIME_BETWEEN_MESSAGES) * numberOfDevices * 2; Serial.print (F ("Mesaj yokken zaman aşımı =")); Serial.print (noMessagesTimeout); Serial.println (F ("mikrosaniye")); // çeşitli pinler kurun pinMode (XMIT_ENABLE_PIN, OUTPUT); // demo eylem pinleri pinMode (SWITCH_PIN, INPUT_PULLUP); pinMode (LED_PIN, OUTPUT); // pinlerde hata ayıklama pinMode (OK_PIN, OUTPUT); pinMode (TIMEOUT_PIN, OUTPUT); pinMode (SEND_PIN, OUTPUT); pinMode (SEARCHING_PIN, OUTPUT); pinMode (ERROR_PIN, OUTPUT); // PRNG Seed_JKISS32'yi (myAddress + 1000) tohumlayın; durum = STATE_NO_DEVICES; nextAddress = 0; randomTime = JKISS32 ()% 500000; // mikrosaniye} // kurulumun sonu // bir sonraki beklenen adresi ayarlayın, maximumvoid setNextAddress (sabit bayt akımı) {nextAddress = current; eğer (nextAddress > = numberOfDevices)
nextAddress = 0; } // setNextAddress'in sonu // Gelen bir mesajı işlemek için burada processMessage () {// kendimizden bir mesaj alamayız // eğer (message.address == myAddress) {digitalWrite ( ERROR_PIN, HIGH); while (true) {} // vazgeç} // digitalWrite adresimizi alamıyoruz (OK_PIN, HIGH); // Kimden geldiğine ve içindeki verilere bağlı olarak gelen mesajı işleyin // LED'imizi önceki cihazın anahtarıyla sırayla eşleştirin (mesaj.adresi == (myAddress - 1)) digitalWrite (LED_PIN, mesaj . anahtarlar [0]); digitalWrite (OK_PIN, DÜŞÜK); } // processMessage'ın sonu // Burada kendi mesajımızı göndermek için void sendMessage () {digitalWrite (SEND_PIN, HIGH); memset (&message, 0, sizeof mesaj); message.address = myAddress; // diğer bilgileri buraya doldurun (örn. anahtar konumları, analog okumalar, vb.) message.switches [0] = digitalRead (SWITCH_PIN); // şimdi ona digitalWrite (XMIT_ENABLE_PIN, HIGH) gönderin; // myChannel.sendMsg ((bayt *) &message, sizeof message) göndermeyi etkinleştir; digitalWrite (XMIT_ENABLE_PIN, DÜŞÜK); // setNextAddress (myAddress + 1) göndermeyi devre dışı bırakın; digitalWrite (SEND_PIN, DÜŞÜK); lastCommsTime = micros (); // kendi gönderimizi randomTime etkinliği olarak sayıyoruz = JKISS32 ()% 500000; // mikrosaniye} // sendMessage void döngüsünün sonu () {// gelen mesaj? if (myChannel.update ()) {memset (&message, 0, sizeof mesaj); int len ​​= myChannel.getLength (); eğer (len > sizeof mesaj) len = sizeof mesaj; memcpy (&message, myChannel.getData (), len); lastMessageTime = micros (); setNextAddress (mesaj.adresi + 1); processMessage (); durum = STATE_RECENT_RESPONSE; } // mesajın sonu tamamen alındı ​​// mesajlar arasında çok uzun bir boşluk olup olmadığını belirtir if (micros () - lastMessageTime > noMessagesTimeout)
durum = STATE_NO_DEVICES; else if (micros () - lastCommsTime > PACKET_TIME) state = STATE_TIMED_OUT; switch (state) {// uzun süredir hiçbir şey duyulmadı mı? STATE_NO_DEVICES durumunu devralacağız: if (micros () - lastCommsTime > = (noMessagesTimeout + randomTime)) {Serial.println (F ("Cihaz yok.")); digitalWrite (SEARCHING_PIN, HIGH); mesaj gönder (); digitalWrite (SEARCHING_PIN, DÜŞÜK); } mola; // yakın zamanda başka bir cihazdan haber aldık // sıra bizde ise yanıt vakası STATE_RECENT_RESPONSE: // küçük bir boşluğa izin veriyoruz ve sıra bizde ise mesajımızı gönderiyoruz if (micros () - lastCommsTime > = TIME_BETWEEN_MESSAGES && myAddress == nextAddress) sendMessage (); kırmak; // bir aygıt yuva zamanında yanıt vermedi, bir sonraki duruma geç STATE_TIMED_OUT: digitalWrite (TIMEOUT_PIN, HIGH); setNextAddress (nextAddress + 1); lastCommsTime + = PACKET_TIME; digitalWrite (TIMEOUT_PIN, LOW); durum = STATE_RECENT_RESPONSE; // eksik yanıt aralığını bulduğumuzu varsayalım; } // açma durumunun sonu} // döngünün sonu 

Şu anda olduğu gibi, A0 üzerindeki bir anahtarı kapatırsanız (toprağa kısa devre) bir LED'i (pim 13) sırayla bir sonraki en yüksek cihazda. Bu, cihazların birbirleriyle konuştuğunu kanıtlıyor. Elbette pratikte, yayınlanmakta olan pakette daha karmaşık bir şeye sahip olacaksınız.

Testlerde LED'in anında açılıp kapandığını gördüm.

Tüm cihazların bağlantısı kesildiğinde ilk bağlanan cihaz diğer cihazları "arar". Eğer benim yaptığım gibi bağlı hata ayıklama LED'leri varsa, paketini rastgele değişen aralıklarla yayınlarken "arama" LED'inin rastgele aralıklarla yandığını görebilirsiniz. İkinci bir bağlantı kurduğunuzda, yerleşirler ve sadece bilgi alışverişinde bulunurlar. Aynı anda üç bağlantılı test yaptım.

Muhtemelen HardwareSerial ile daha güvenilir olurdu - Hata ayıklamaya yardımcı olması için SoftwareSerial'ı kullandım. Birkaç küçük değişiklik bunu başarabilir.


Değiştirilmiş şematik

RS485 moving master schematic


Ekran -kodun eylem halindeki fotoğrafları

Bu resimler, kodun çalıştığını gösterir. İlk olarak, yalnızca bir cihaz bağlıyken:

RS485 - one device

Oradaki darbelerden, cihazın verilerini rastgele değişen oranlarda yayınladığını görebilirsiniz. aynı anda çalışan başka bir cihazla çatışmaya devam etmekten kaçınmak için aralıklarla.


RS485 - two devices

Artık bloklar görüyoruz Ortada aşağı yukarı aynı boyutta boşluklar bulunan iki cihazdan gelen veriler. Dört cihaz için yapılandırdım, ancak yalnızca ikisi mevcut, bu nedenle iki veri bloğu ve iki boşluk görüyoruz.


RS485 - three devices

Artık çevrimiçi üç cihazla, eksik cihaz atlanırken üç veri bloğu ve bir boşluk görüyoruz.


Rakamları kontrol ediyorsanız, bunlar baud hızı bir test olarak iki katına çıktı ve 19200 baud oldu.


Kablo çalıştırma testi

Uygun bir donanım testi için cihazları şirket içi UTP'ime bağladım kablolama. Çeşitli odalardan merkezi bir prize giden cat-5 kablom var. Evin bir ucundan diğerine giderken (makul bir uzunlukta koşu) hala iyi çalışıyor. Başlangıç ​​için Arduino ile duvar prizi arasında 5 m kablo bulunmaktadır. Artı diğer ucunda 5 m kablo daha. Ardından, odalardan anahtar odasına yaklaşık 2 x 15 m'lik geçişler var ve bunların içinde bunları birbirine bağlamak için kısa bir köprü kablosu var.

Bu, hala 19200 baud'da çalışacak şekilde programlanmış anakartlarla oldu.

1) Cevabınız ve RS-485 eğitiminiz için ÇOK teşekkürler. Kesinlikle kullanacağım; 2.) MEGA ve RPI'lerin katılımına gelince, OP'yi güncelledim. Lütfen ayrıntılar için ona bakın; 3) "_ Köleleri çok sık sorgulayamamanızın belirli bir nedenini göremiyorum_" ise saniyede 9600 baud hızında 10 anket, makul bir seçim mi? Ve son olarak: eğer doğru anladıysam, herhangi bir ankete ihtiyacım olmayacak **, çünkü sizin yaklaşımınızla "tek ana / çoklu köle" bağlamı uygulayabilirim ... ama "hareketli" bir ana ile. Bu doğru mu?
See updated answer.
See amended answer which demonstrates a moving master concept.
`` 9600 baud hızında saniyede 10 anket mi, makul bir seçim mi? '' - mevcut testimde (ekran görüntülerine bakın) 19200 baud'da 75 ms'de 4 cihazı tekrar tekrar test ediyorum, yani saniyede 13 durum raporu . Temel olarak bu, bir cihazın başka bir cihazdan bir durum değişikliğine (örneğin, bir anahtarın kapanması) saniyenin 1 / 10'u içinde tepki vermesi gerektiği anlamına gelir. İlk sorunuz 4 cihazdan bahsetti, bu nedenle benzer sonuçlar alabilmelisiniz.
Kırmızı arkaplanlı beyaz tel klipsi nereden aldığınızı söyler misiniz lütfen (klips kablo demetine takılıdır)
Ebay: https://www.ebay.com/itm/400846299509. "10x 2p Yaylı Bağlantı teli kaynak yok vida yok" ifadesini arayın.
Gil
2015-10-19 04:40:40 UTC
view on stackexchange narkive permalink

RS485'in bir protokol olmadığını, fiziksel bir taşıma katmanının tanımı olduğunu unutmayın. Mega ile seçtiğiniz gibi seri 1,2,3'ü kullanabilir ve bunları tam çift yönlü modda RS485 ağı e.e. üzerinden çalıştırabilirsiniz. gönderdiklerini alabilirsin.

Çok yöneticili bir yapılandırmada çalıştırıyorum. Her mega ilettiğinde, gönderdiği şeyi de alır ve geri çekilmesi gerekip gerekmediğini bayt bazında belirleyebilir. Hatta geri gelmeden önceki gecikme, düğüm adresi ve veri yolu kullanılabilir olduğunda belirlenir.

Kod, her baytın kontrol edilmesini sağlayan yazdırma işlevi yerine yazma işlevini kullanacaktır. (Zamanla bunu muhtemelen yazılım yoluyla yapacağım ve CAN'ı taklit edeceğim veya sadece bir kutu kontrolörü kullanacağım). Evet 8 bit gayet iyi çalışıyor, dokuzuncu bit bir eşlik biti veya onu kimin tanımladığına bağlı olarak diğer kullanımlar.

Test sistemim paketlerle çalışıyor ve şu biçimde gönderiyor:

| Uzunluk | Hedef | Kaynak | Sıra | Komut | Bayraklar | Veri | Kontrol

ve yanıt olarak bir ACK bekliyor. Bu görece basit bir ilk geçiştir ve geçersiz kodları yoktur. Tüm baytlar Onaltılıktır ancak istediğinizi kullanabilirsiniz.

Bu, bana Taşıyıcı Duygusu, çoklu erişim, ancak yıkıcı tahkim sağlıyor. Eğer CSMANDA'nın sadece bir CAN fiziksel katmanı kullanmasını veya gönderdiğiniz veriyi bit patlamasını istiyorsanız, o zaman azar azar üzerinde hakemlik yapabilirsiniz. Yazılımda iletim kısmından çok farklı olmayacak.

Ana düğüm olarak Linux ile RPi'yi kullanmayı planlıyorum. 8 bit veri ile gayet iyi çalışacaktır. Yaklaşık iki hafta önce bu konfigürasyona başladım ve şu ana kadar iyi gidiyor. 9600 baud'da çalışıyorum ve bol bol boş vaktim var.

Gil

Çarpışmaları algılamak için nasıl bir şeyler kuruyorsunuz? RS-485 arayüzü için kullandığınız belirli çip, TX hattına veri gönderirken aynı zamanda RX hattındaki bağlantıda algılanan verileri döndürüyor mu ve bunu iletiyor mu?
Sthing
2015-10-30 02:21:25 UTC
view on stackexchange narkive permalink

Nick Gammon'un protokolünü kullanmak istiyorsanız, şunu yararlı bulabilirsiniz: https://github.com/Sthing/Nick-Gammon-RS485

Bu benim uygulamam Protokolün Python'da kullanılması (bir RaspberryPi'de kullanım için). Henüz sadece python'dan python'a test ettim, yapılacaklar listemde Nick Gammon'ın koduna göre test yapmak.

/ Thing

Son zamanlarda, Nick Gammon'un RS485 engellemesiz protokolünü, bir "bir ana, iki bağımlı" senaryosunda üç MEGA arasında test etmede başarılı oldum. Başlıca fiziksel dizilerin kullanımıyla (Yazılım Dizilerinin değil) ortaya çıkan birkaç sorunla karşılaştım. Neyse ki her şeyi düzeltebildim. Çalışan kodu (diğer bazı ayrıntılarla birlikte) burada yayınladım: https://github.com/verzulli/arduino-smart-home - Şu anda bu tür bir proje üzerinde aktif olarak çalışıyorum ve önümüzdeki haftalarda güncellemeyi planlıyorum.
user2024827
2018-04-08 08:53:19 UTC
view on stackexchange narkive permalink

RS485 için CDBUS protokolünün tam olarak istediğiniz şey olduğunu düşünüyorum, CAN veriyolu gibi çakışmaları otomatik olarak önleyen bir tahkim mekanizması sunar. Hatta video akışını bile aktarabilirim:

enter image description here Tam video: https://youtu.be/qX5dh4wcfSk

Raspberry Pi aynı anda video önizleme ve kontrol komutunun çıktısını alabilir. Tanıma sürecini bilgisayardan izleyebiliriz. Bir sorunla karşılaşıldığında, nedenini bilmek ve parametreleri ayarlamak uygun olur ve PC'nin bağlantısının kesilmesi demo işlemini etkilemeyecektir.

enter image description here Ek olarak, Raspberry Pi, PC aracılığıyla internete her zaman erişebilir ve yazılımı güncellemek ve uzaktan kumanda için kolaydır.

CDBUS hakkında ayrıntılar:

Güncelleme: CDCTL-Bx kontrol cihazını bir Arduino ya bağlayın: arduino and cdctl

Videodan bahsetmek, sorulan soru ve bu sitenin Arduino konusu ile ilgisiz ve bu yüzden biraz dikkat dağıtıcı. Böyle bir veri hacmi, zarafetle işlenmediği sürece, sınırlı kaynak sistemlerinde sorunlara neden olabilir. Bu şemayı önermek istiyorsanız, sorunun soruna nasıl uygun olduğunu ve ** bir Arduino için ** ... eğer gerçekten uygunsa, açıklamalısınız.
Arduino için de aynı şey, diyagramımdaki Raspberry Pi Zero'yu bir Arduino ile değiştirin, başka bir deyişle, Arduino için RS485 denetleyicisinin harici bir kalkan kartını ekleyin, I2c veya SPI arabirimi üzerinden iletişim kurun.


Bu Soru-Cevap, otomatik olarak İngilizce dilinden çevrilmiştir.Orijinal içerik, dağıtıldığı cc by-sa 3.0 lisansı için teşekkür ettiğimiz stackexchange'ta mevcuttur.
Loading...