Kategoria : Sterownik silników

Sterowanie urządzeniami przez ethernet – UDP

Długo to trwało ale wreszcie projekt zaczyna wychodzić z etapu testowania i wchodzi w etap tworzenia konkretnych aplikacji.

Poniżej przedstawiam działający prototyp który odczytuje konfigurację sieciową z karty SD, inicjuje połączenie z komputerem poprzez Ethernet (datagramy UDP) i steruje diodami – te grają tu rolę atrapy urządzenia które może być sterowanie (czego dusza zapragnie – tu docelowo będą silniki krokowe i prądu stałego). Prototyp zbudowany na podstawie Arduino Mega 2560 rev3 z dodatkiem modułu kart SD i modułu Ethernet na ENC28j60. Całość podzielę na części by było to czytelniejsze.

1. Struktura pliku konfiguracyjnego

Plik o nazwie „config.txt” jest zapisany w katalogu głównym karty. Początek każdej linii zaczynać musi się od znaku „;” i kończyć znakiem „:” – są to znaki sterujące sekwencją odczytu i przypisywania wartości do zmiennych w programie. Plik zawiera następujące informacje:

  •  linie 1 – 6: Adres MAC urządzenia, zapisane w formie dziesiętnej (każdy oktet osobno)
  • linie 7 – 10: Adres IP urządzenia, zapisane w formie dziesiętnej (każdy oktet osobno)
  • linie 11 – 14: Adres IP komputera z którym następuje komunikacja, zapisane w formie dziesiętnej (każdy oktet osobno)
  • linia 15: Port na którym nasłuchuje komputer
Przykładowy plik konfiguracyjny:
;00207:
;00112:
;00124:
;00228:
;00138:
;00184:
;00192:
;00168:
;00050:
;00002:
;00192:
;00168:
;00050:
;00001:
;29543:

2. Połączenie

Karta SD:
SD.Miso = Mega.50
SD.Mosi = Mega.51
SD.Sck = Mega.52
SD.CS = Mega.22

!!! WAŻNE !!! Zasilanie kary SD przez nóżkę 3,3V podpiętą pod 5V – inaczej nie da się zainicjalizować karty SD (za niskie napięcie lub błąd w module karty SD) – możliwe że mam jakiś wadliwy moduł a może problem z zasilaniem (mimo że pobierane bezpośrednio z płytki Arduino)

Ethernet:
Ethernet.SI = MEGA.51 (MOSI)
Ethernet.SO = MEGA.50 (MISO)
Ethernet.CS = MEGA.10 (narzucone przez biblioteke !!!)
Ethernet.SCK = MEGA.52 (SCK)

Diody (przez rezystor 470Ohm):
czerwona = MEGA.30;
zielona = MEGA.32;
żółta = MEGA.34;

3. Najważniejsza rzecz!!!!

W kodzie programu użyta jest funkcja „ES_udp_send” jest ona używana ze sterownika EtherShield lecz w standardzie nie jest ona widoczna, trzeba ją dodać do pliku nagłówkowego i pliku źródeł.

Plik nagłówkowy:

void ES_udp_send(uint8_t *buf,char *data,uint8_t datalen,uint16_t sport, uint8_t *dip, uint16_t dport);

Plik źródeł:

 void EtherShield::ES_udp_send(uint8_t *buf,char *data,uint8_t datalen,uint16_t sport, uint8_t *dip, uint16_t dport){
 send_udp(buf,data,datalen,sport, dip, dport);
}

 

4. Kod programu mikrokontrolera

#include <SD.h> //biblioteka obsługi kart SD
#include "EtherShield.h" //biblioteka obsługi moduły ethernet
#include "enc28j60.h" //biblioteka do obsługi wysyłki pakiety udp
//zmienne dotyczące odczytu konfiguracji z karty SD
File confFile; //obiekt pliku konfiguracyjnego
unsigned char configFileLineCounter = 0;
unsigned char configLineCharCounter = -1;
char znakZPlikuKonfiguracji = ' ';
char liniaPlikuKonfiguracji[5] = {0,0,0,0,0};
boolean czyKonfiguracja = false;
//zmienne dotyczące modułu ethernet
uint8_t MAC[6] = {0, 0, 0, 0, 0, 0};
uint8_t IP[4] = {0, 0, 0, 0};
uint8_t IP_COMP[4] = {0, 0, 0, 0};
uint16_t PORT = 0;
uint16_t MYWWWPORT = 80;
#define BUFFER_SIZE 750
static uint8_t buf[BUFFER_SIZE+1];
EtherShield es=EtherShield();
uint16_t dat_p;
//odpowiedz do komputera
char reply[]="Odp_1"; //tekst odpowiedzi
char reply0[]="odebrano";
int var = 0;
//led
int lR = 30;
int lG = 32;
int lY = 34;
//zmienne programu
int msg_counter = 0;
int strToInt(char s[]) //metoda do przekształcenia linii z pliku konfiguracyjnego do integer'a (48 to wartosc dla znaku '0' - zero)
{
 return ((s[0] - 48) * 10000) + ((s[1] - 48) * 1000) + ((s[2] - 48) * 100) + ((s[3] - 48) * 10) + ((s[4] - 48) * 1);
}
void obslugaKomunikatu(uint8_t p[])
{
 Serial.println("obsluga komuniaktu");

 if (char(p[42]) == '1')
 {
 digitalWrite(lR,HIGH);
 }
 else
 {
 digitalWrite(lR,LOW);
 }

 if (char(p[43]) == '1')
 {
 digitalWrite(lG,HIGH);
 }
 else
 {
 digitalWrite(lG,LOW);
 }
if (char(p[44]) == '1')
 {
 digitalWrite(lY,HIGH);
 }
 else
 {
 digitalWrite(lY,LOW);
 }
}
void setup() {
 pinMode(53, OUTPUT); //obowiązkowe ustawienie pin d53 jako output - innaczej nie dziala SPI

 //led
 pinMode(lR, OUTPUT);
 pinMode(lG, OUTPUT);
 pinMode(lY, OUTPUT);
 digitalWrite(lR,LOW);
 digitalWrite(lG,LOW);
 digitalWrite(lY,LOW);

 Serial.begin(9600);//inicjalizacja obsługi portu szeregowego (do debugu)

 if (SD.begin(22)) //Inicjalizacja karty SD - CS na d22
 {
 Serial.println("inicjalizacja karty SD: OK");

 //odczyt konfiguracji z karty SD z pliku "CONFIG.TXT"
 confFile = SD.open("CONFIG.TXT");//otwarcie pliku do odczytu

 if(confFile)
 {
 Serial.println("otwarcie pliku CONFIG.TXT: OK");
 Serial.println("zawartosc pliku CONFIG.TXT:");

 while (confFile.available()) //odczyt linii z pliku
 {
 znakZPlikuKonfiguracji = confFile.read();
if(znakZPlikuKonfiguracji==':')
 {
 czyKonfiguracja = false;
 configLineCharCounter = -1;

 //zapisanie do konkretnej zmiennej
 //MAC
 if(configFileLineCounter == 1) MAC[0] = strToInt(liniaPlikuKonfiguracji);
 if(configFileLineCounter == 2) MAC[1] = strToInt(liniaPlikuKonfiguracji);
 if(configFileLineCounter == 3) MAC[2] = strToInt(liniaPlikuKonfiguracji);
 if(configFileLineCounter == 4) MAC[3] = strToInt(liniaPlikuKonfiguracji);
 if(configFileLineCounter == 5) MAC[4] = strToInt(liniaPlikuKonfiguracji);
 if(configFileLineCounter == 6) MAC[5] = strToInt(liniaPlikuKonfiguracji);
 //IP URZADZENIA
 if(configFileLineCounter == 7) IP[0] = strToInt(liniaPlikuKonfiguracji);
 if(configFileLineCounter == 8) IP[1] = strToInt(liniaPlikuKonfiguracji);
 if(configFileLineCounter == 9) IP[2] = strToInt(liniaPlikuKonfiguracji);
 if(configFileLineCounter == 10) IP[3] = strToInt(liniaPlikuKonfiguracji);
 //IP KOMPUTERA STERUJACEGO
 if(configFileLineCounter == 11) IP_COMP[0] = strToInt(liniaPlikuKonfiguracji);
 if(configFileLineCounter == 12) IP_COMP[1] = strToInt(liniaPlikuKonfiguracji);
 if(configFileLineCounter == 13) IP_COMP[2] = strToInt(liniaPlikuKonfiguracji);
 if(configFileLineCounter == 14) IP_COMP[3] = strToInt(liniaPlikuKonfiguracji);
 //PORT DO ODPOWIEDZI
 if(configFileLineCounter == 15) PORT = strToInt(liniaPlikuKonfiguracji);
 }

 if(czyKonfiguracja)
 {
 configLineCharCounter++;
 liniaPlikuKonfiguracji[configLineCharCounter] = znakZPlikuKonfiguracji;
 }

 if(znakZPlikuKonfiguracji==';')
 {
 configFileLineCounter++;
 czyKonfiguracja = true;
 }

 }
 Serial.println("KONIEC POBIERANIA Z PLIKU CONF");
 confFile.close();
 }
 else
 {
 Serial.println("otwarcie pliku CONFIG.TXT: BLAD");
 }

 Serial.println("proba inicjacji modulu ethernet");
 //inicjacja modułu ethernet
 //port 29543
 es.ES_enc28j60Init(MAC);
 Serial.println("modul ethernet: nadanie MAC");
 es.ES_init_ip_arp_udp_tcp(MAC,IP, MYWWWPORT);
 Serial.println("modul ethernet zainicjowany");

 }
 else
 {
 Serial.println("inicjalizacja karty SD: BLAD");
 }
}
void loop() {
//obsługa PING i odebranie pakietu
 dat_p=es.ES_packetloop_icmp_tcp(buf,es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf)); //odbior pakietu i odpowiedz na PING

 if (buf[IP_PROTO_P]==IP_PROTO_UDP_V) //sprawdzenie czy przetwarzany pakiet jest pakietem udp innym niż PING
 { 
 obslugaKomunikatu(buf);
 es.ES_udp_send(buf,reply0,sizeof(reply0),PORT, IP_COMP, PORT);
 buf[IP_PROTO_P]=0; // tu jest ana razie zagadka 
 }
}

5. Aplikacja komputerowa

Program który wysyła komendy z komputera do urządzenia (widoczny na filmie) pochodzi ze strony KMtronic można z niej go pobrać wraz ze źródłami.

Działanie

Po podłaczeniu i uruchomieniu najlepiej jest puścić ping do urządzenia – powinien dość szybko odpowiadać. Po uruchomieniu aplikacji podaje się adres IP urządzenia oraz port na którym ma słuchać komputer oraz wciskamy przycisk „Open”. Sterując słowem (3 znaki „0” lub „1”) sterujemy odpowiednio diodami (działanie pokazane na filmie).

Silnik krokowy – tak mało pinów a tak dużo silników

No to stało się jasne że bez dodatkowych nakładów pracy nie ma możliwości by do atm168/328 podłączyć 6 silników krokowych (takie były założenia). Jako że układ ma w sumie 20 wyjść/wejść, a podłączenie 1 silnika idzie na 4 złączach to nawet fizycznie się tego by nie dało (potzebnych nóżek to przecież 24 sztuki). Należy jeszcze pamiętać że dodatkowo potrzebne są piny do obsługi Ethernetu, karty SD i wyświetlacza LCD (w sumie to 6). I tu się się pojawia kwestia dodatkowych nakładów pracy. Możliwe jest połączenie silnika przez tylko 2 piny, traci sie tu dobrodziejstwo półkroków ale założenia nie przewidują robiena za pomocą tego rozwiązania maszyn dokładnych (np. frezarek CNC – do tego wystarczą 3 silniki które da się podłączyć do tego układu w takiej konfiguracji poprzez 4 rzyłowe linie sterujące).

Na razie i tak jedzie do mnie tylko 1 silnik to potrenuję na nim sterowanie na 4 kablach. Poniżej podaję schemat połączenia na ULN2003 dla sterowania na 2 kablach. W przyszłości trzeba będzie poprostu przygotować sobie dedykowane płytki tego typu (myślę żeby za jednym zamachem zrobić 10 sztuk, układ w smd kosztuje 90 gr do tego dioda i rezystory bez robocizny układ powinien kosztować nie więcej jak 3,50 – 4 złote).

Chytry dwa razy traci….

No może nie chytry ale niedofinansowany :). Chciał człowiek trochę zaoszczędzić i zamiast modułu z ATMega328 zakupił taki z ATMega168, no i z tej oszczędności wyszło tyle że i tak musi zakupić ten pierwszy. Powód jest prosty. By obsłużyć zapis na karcie SD musi być więcej RAMu niż skromne 1KB jakie oferuje ATM168… szkoda bo przerywa to fajną zabawę. Ale to taka przestroga na przyszłość 🙂

W sumie to pojawił się jeszcze jeden powód. Poprzedni program operujący na 3 diodach po kompilacji zajmował około 1,5KB, Kod wykorzystujący bibliotekę do obsługi karty SD z paroma liniami do obsługi diod i konfiguracji po konfiguracji zajmuje około 13,5KB… :/ zaczynam się zastanawiać czy 32KB pamięci flash w ATM328 wystarczy by zapewnić obsługę karty SD i kontrolera ethernet… czas pokaże :).

Edit

No i zastanawiam się czy wystarczy ramu do obsługi karty SD i ethernetu… może faktycznie lepiej uderzyć od razu w płytkę z ATM2560… 8KB ramu i 256KB flash…. wydaje się że to nie będzie ograniczało.