5 Hälytinantureita Arduinon kautta Raspberryyn.

Arduino on mukava kapistus kun halutaan lukea antureiden tilatietoja.
Toki tämä onnistuisi Raspberryllä suoraankin, mutta on rahallisesti eri asia
tuhota testailuissa muutaman euron Arduino Nano, kuin usean kympin Raspberry.
Kytkennöissä aina pitäisi miettiä vikatilanteetkin, esimerkiksi jos anturin johdot oikosulkeutuu.
Tällöin koko Raspberry luonnollisesti sammuu, jos virran syöttö on sen kautta.
Eli lopullisessa ratkaisussa Arduinon virransyöttö voisi olla erillisellä kännykän muuntajalla.

Haluan kaikki laitteistoni keskenään yhteensopivaksi RS485-väylän kautta, joten päätin asian toteuttaa samalla sanomarakenteella kuin edellisetkin projektit.
Kytkentää voi kuitenkin yksinkertaistaa, jos haluaa että Raspberryn ja Arduinon välinen liikenne menee USB-kaapelin kautta.

Kykennässä on yksi liiketunnistin ja kaksi kytkintä esittämässä oven magneettikytkintä.

Yksinkertaistettu kytkentä:

Oletuksena Anturien tulot hälyttää silloin, kun se on HIGH arvossa.Joskus pitäisi kuitenkin hälyttää silloin, kun tulo on LOW tilassa.
Tulo muutetaan silloin käänteiseksi rivillä
const int tuloKaantaiset = B00000110;

tuon rivin oikeanpuoleisin bitti tarkoittaa rivin
const int tuloPinni[] = {12, 11, 10}; 
ensimmäistä anturia, eli 12.

Antureille voi antaa lyhyet, vaikka kolmikirjaimiset nimet, joilla ne tunnistaa helpommin:
const String anturinNimi[] {"ETE", "OVI", "KEI"};

Eli tässä esimerkissä anturia "ETE" ei muuteta käänteiseksi, mutta "OVI" ja "KEI" muutetaan.

Raspberryn päässä luettaessa tarkoittaa  1 aina että kyseinen anturi on hälyttävässä tilassa ja 0 että se ei ole hälyttävässä tilassa.

Koodi Arduinoon
[halyt.ino]

#define RS485ENABLE  #RS485 käytössä Kommentoi tämä rivi, jos et halua RS485 käyttöön
const int tuloPinni[] = {12, 11, 10};         //määrittele tässä arduinon KAIKKI KÄYTETTÄVÄT sisääntulonastat
const String anturinNimi[] {"ETE", "OVI", "KEI"}; //antureiden nimet
const int tuloKaantaiset = B00000000;  //ykkösellä merkityt tulot käännetään, eli LOW=hälytys huom! käänteinen järjestys tuloPinni nähden. eli tässä oikeanpuoleisin bitti on tuloPinni[] vasemmanpuoleisin
const int tulojenMaara = sizeof (tuloPinni) / sizeof(int); //Lasketaan tulopinnien määrä
String viimAnturientila = ""; //Anturien viimeisin tunnettu tilatieto muodossa "ETE=0;OVI=1;KEI=1;"
unsigned int kierros = 0; //Loopin suorituskierrosten laskija
const int lahetaKierros = 250; //Joka nn kierros lähetetään anturien tila, vaikka se ei olisi muuttunutkaan.
const char omaId = 'C'; //Tämän laitteen tunnus huomaa että '' eikä "" !
const char isantaId = 'M'; //Laite jolle lähetys osoitetaan
#ifdef RS485ENABLE
const int lahetysPin = 13; //tässä pinnissä RS485-moduulin ohjaus rx/tx #485#
#endif //RS485ENABLE
//------------------------------------------------------------------------------------------

void setup() {
  Serial.begin(9600);
  Serial.println(tulojenMaara);
#ifdef RS485ENABLE
  pinMode(lahetysPin, OUTPUT); //RS485 RX/TX PINNI #485#
  digitalWrite(lahetysPin, LOW); // RS485 vastaanotto-modeen #485#
#endif //RS485ENABLE
  for (int a = 0; a < tulojenMaara; a++) { //Asetetaan tulopinnit inputeiksi
    pinMode(tuloPinni[a], INPUT_PULLUP); //Käytetään Arduinon sisäisiä pullup-vastuksia
  }
}

void loop() {
  String tila = lueAnturienTila(); //Pyydetään anturien nykyinen tilatieto
  if (tila != viimAnturientila) { //Jos anturien tila on muuttunut edellisestä tunnetusta
    viimAnturientila = tila; //Tallennetaan tilamuutos
    laheta('H', tila); //Lähetetään sanoma H (hälytys) ja parametrinä anturien tila
  }
  else if (kierros % lahetaKierros == 0) { //Joka nn kierros lähetetään anturien tieto, vaikka ei olisikaan muuttunut
    laheta('I', tila); //Lähetetään sanoma I (info) ja parametrinä anturien tila
  }
  delay(50);
  kierros++;
}

String lueAnturienTila() { //Palautetaan anturien tilatiedot muodossa "ETE=1;OVI=0;KEI=1;"
  String anturienTilaPalauta = "";
  for ( int a = 0; a < tulojenMaara; a++) { //Luetaan pinnien tilatiedot yksi kerrallaan
    bool arvo = digitalRead(tuloPinni[a]); //Luetaan yksittäisen pinnin tila
    if (bitRead(tuloKaantaiset, a)) { //Jos kyseisen anturin tilatieto kuuluu muuttaa käänteiseksi
      arvo = !arvo; //Muutetaan anturin tilatieto käänteiseksi
    }
    anturienTilaPalauta += anturinNimi[a] + "=" + arvo + ";";
  }
  return anturienTilaPalauta;
}

//------------------------------------------------------------------------------------------
void laheta(char sanoma, String parametri) { //Parametreinä vain Sanoma ja Parametri.
  String lahetaRivi = lisaaCRC(String(isantaId) + String(omaId) + String(sanoma) + parametri); //Lisätään itse vastaanottajaksi isäntäkone, lisätään oma tunnus
#ifdef RS485ENABLE
  digitalWrite(lahetysPin, HIGH); // RS485 lähetystilaan
#endif //RS485ENABLE
  Serial.println(lahetaRivi);
  Serial.flush(); //odotetaan kunnes lähetys valmis
  delay(50);
#ifdef RS485ENABLE
  digitalWrite(lahetysPin, LOW); // RS485 vastaanottotilaan
#endif //RS485ENABLE
}

String lisaaCRC(String dataStr) {
  byte sum1 = 0;
  byte sum2 = 0;
  for (int i = 0; i < dataStr.length(); i++) {
    byte arvo = byte(dataStr[i]);
    sum1 = (sum1 + arvo) % 94;
    sum2 = (sum1 + sum2) % 94;
  }
  return String(">" + dataStr + String(char(sum1 + 33)) + String(char(sum2 + 33)) + "<");
}


Raspberryssä
[vastaanota.py]

#!/usr/bin/env python3
#>MCIETE=0;OVI=1;KEI=1;uv<
import serial, time

def laskeTarkiste(sanoma):
    sum1 = 0;
    sum2 = 0;
    for a in range(len(sanoma)):
        arvo = ord(sanoma[a]);
        sum1 = (sum1 + arvo) % 94;
        sum2 = (sum1 + sum2) % 94;
    return ">"+sanoma+chr(sum1 + 33) + chr(sum2 + 33)+"<"

#------------------------------------------------------------------------------------------
sarjaportti=serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=0.05)
while True:
    try:
        arvo=(sarjaportti.readline().decode('iso-8859-1', errors='replace').replace("\r\n","")) #Luetaan sarjaportista String ilman rivinvaihtomerkkejä
        if(len(arvo)>0):
            sisalto=arvo[1:-3] #sanomat varsinainen sisältö ilman alku, varmenne ja loppu -merkkejä
            lasketullaVarmisteella=laskeTarkiste(sisalto)
            if(lasketullaVarmisteella == arvo): #saatu varmennemerkki täsmää itse lasketun kanssa
                sanomanTyyppi=arvo[3] #I=määräajoin saatava tieto anturien tilasta, H=halytys
                if(sanomanTyyppi=="I"):
                    print("Info:")
                elif(sanomanTyyppi=="H"):
                    print("Hälytys:")
                anturiData=arvo[4:-3] # ETE=0;OVI=1;KEI=1;
                while(anturiData.find(";")>1): #käydään anturien arvoja läpi niin kauan kuin niitä riittää
                    antNimi=anturiData[0:3]
                    antArvo=anturiData[4:5]
                    print(antNimi+"="+antArvo)
                    loppu=anturiData.find(";") #anturin arvo päättyy ";"-merkkiin, etsitään sen paikka
                    anturiData=anturiData[loppu+1:] #Poistetaan jo käsitelty osuus merkkijonosta
                print("-----")
            else:
                print("Saatiin sanoma, jossa on kuitenkin virhe tarkisteessa: "+arvo+ " LASKETTU: "+lasketullaVarmisteella)
    except Exception as e:
        print("***VIRHE***")
        print(e)
    time.sleep(0.2)


RS485-kytkentä voisi olla vaikkapa tällainen:



Arduinon lähettämä viestiliikenne on tämankaltaista:

>MCHETE=0;OVI=1;KEI=1;tc<
>MCHETE=1;OVI=1;KEI=1;uq<
>MCIETE=1;OVI=1;KEI=1;v&<
>MCHETE=0;OVI=1;KEI=1;tc<
>MCHETE=1;OVI=1;KEI=1;uq<
>MCHETE=0;OVI=1;KEI=1;tc<
>MCIETE=0;OVI=1;KEI=1;uv<
>MCHETE=1;OVI=1;KEI=1;uq<
>MCHETE=0;OVI=1;KEI=1;tc<
>MCIETE=0;OVI=1;KEI=1;uv<
|||||                 |
|||||                 | Loppumerkki
|||||                 Tarkaste
||||Parametri
|||Sanoma
||Lähettäjä
|Vastaanottaja
Alkumerkki

Kommentit

Tämän blogin suosituimmat tekstit

0 Nyymi Nysvää

2 Ensimmäinen projekti: Lämpömittari RS485-väylään

4 LoRa-radiomoduulien testailua