Kategoria : Arduino

Arduino Uno na płytce stykowej

Po dłuższej przerwie nadszedł czas na powrót do arduino. Tym razem nic skomplikowanego jednak będącego podstawą do projektów które będą przenoszone z płytek stykowych na płytki obwodów drukowanych – czyli arduino (Uno) stworzone od zera na płytce stykowej i programowane przez interfejs ISP.

Schemat:

arduino Uno simple schema

 

Schemat połączeń jest prosty i sprawdzony. Po złożeniu układu i podłączeniu go do programatora (Ja używam USBasp) trzeba wgrać bootloader, tu spotkałem się z małym problemem – programator nie umiał się skomunikować z układem. Powodem było ustawienie programatora na  programowanie układów taktowanych częstotliwością większa niż 1,5 MHz a ponieważ był to układ świeżo zakupiony, to był taktowany zegarem wewnętrznym 1kHz (z tego co pamiętam). Przestawienie programatora na „niską częstotliwość” załatwiło sprawę.

Bootloader wgrałem dla poniższych ustawień płytki (w pliku boards.txt):

uno.name=Arduino Uno
uno.upload.protocol=stk500
uno.upload.maximum_size=32256
uno.upload.speed=115200
uno.bootloader.low_fuses=0xff
uno.bootloader.high_fuses=0xde
uno.bootloader.extended_fuses=0x05
uno.bootloader.path=optiboot
uno.bootloader.file=optiboot_atmega328.hex
uno.bootloader.unlock_bits=0x3F
uno.bootloader.lock_bits=0x0F
uno.build.mcu=atmega328p
uno.build.f_cpu=16000000L
uno.build.core=arduino
uno.build.variant=standard

Po wgraniu bootloadera należy przestawić programator na „wysoką częstotliwość” i można wgrywać już programy (np. standardowy przykład z migającą diodą na D13). I tu się pojawił kolejny problem – nie mogłem wgrać programu. Chwile googlowania i po dopisaniu do pliku boards.txt linii „uno.build.variant=standard” wszystko zaczęło działać.

To be continued…. 🙂

 

 

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).

Arduino mega 2560

No i przyjechał długo wyczekiwany arduino mega 2560…. i od razu pierwsze wrażenie: jakie to jest maleństwo :D. I teraz siedzę i myślę co można z nim zrobić… i problem mam wielkiej wagi…. bo jego możliwości mnie trochę onieśmielają :). Ale na pierwszy ogień pójdzie ponowne podejście do tematu zapisy danych na karcie SD (co było dotychczas problemem ponieważ na atm168 było mało ramu (1KB), tu jest go pod dostatkiem.

All I want for Xmas is…. Arduino :)

Święta, Święta i po świętach. A na po świętach trzeba sobie prezent zrobić 🙂 i tak przybyły dwa nowe uC: atmega88 i atmega328, 4 silniki krokowe (taka ilość to już wystarczy do wszystkiego 🙂 ) oraz czujnik zbliżeniowy (tak by poeksperymentować sobie).

W kolejce czeka jeszcze Arduino MEGA 2560 ale by to sfinalizować czekam na dobrą ofertę :).

Dodatkowo zakupiłem ogniwa do gąsienic z LEGO TECHNIC (dziękować bogom za Lego Education 🙂 i firmie akces.poznan.pl ). Teraz to mam zagadkę jak wkomponować silnik krokowy do modelu z lego….

Arduino: silnik krokowy 28BYJ-48

Układ testujący silnik krokowy 28BYJ-48. Tu potrzebne jest kilka słów wyjaśnienia, silnik posiada 64 kroków (po 5,625 stopni) oraz przekładnię 1:32, informacja o przekładni jest dość istotna ponieważ w nocie technicznej znalazłem zapis o przekładni 1:64 co okazuje się błędem (sprawdzone eksperymentalnie oraz wyczytane w odmętach internetu).

Przykład prezentuje jeden pełen obrót oraz obrót powrotny stopniowo zwalniający.

Układ:

Połączenie silnika:

in1 = ard.8

in2 = ard.10

in3 = ard.11

in4 = ard.9

UWAGA: w przykładach arduino wydaje się że jest błąd (tam połączenie jest podane w kolejności 8,9,10,11)

Działanie:

Kod:

#include <Stepper.h>
#define STEPS 2048 //definicja ilości kroków na pełen obrót
 // 64 kroków na silniku (po 5.625 stopnia) razy 32 -
 // 1:32 (przekładnia, choć niestety w dokumentacja mówi o 1:64)
// inicjalizacja obiektu do kontroli silnika
Stepper myStepper(STEPS, 8,10,9,11);
void setup() {
myStepper.setSpeed(14.99); //to się wydaje być prędkość maksymalna 
 //15 RPM jest już wartością przy której silnik już nie działa
 myStepper.step(2048); //pełen obrót
 delay(1000);
 myStepper.setSpeed(14); //pełen obrót w drugą stronę - zwalniające
 myStepper.step(-146); 
 myStepper.setSpeed(13);
 myStepper.step(-146);
 myStepper.setSpeed(12);
 myStepper.step(-146);
 myStepper.setSpeed(11);
 myStepper.step(-146);
 myStepper.setSpeed(10);
 myStepper.step(-146);
 myStepper.setSpeed(9);
 myStepper.step(-146);
 myStepper.setSpeed(8);
 myStepper.step(-146);
 myStepper.setSpeed(7);
 myStepper.step(-146);
 myStepper.setSpeed(6);
 myStepper.step(-146);
 myStepper.setSpeed(5);
 myStepper.step(-146);
 myStepper.setSpeed(4);
 myStepper.step(-146);
 myStepper.setSpeed(3);
 myStepper.step(-146);
 myStepper.setSpeed(2);
 myStepper.step(-146);
 myStepper.setSpeed(1);
 myStepper.step(-150); //uzupełniono o utracone 4 kroki
}
void loop() {
}

 

 

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).

Ram potrzebny od zaraz

A tak w ogóle to chyba kolejny raz stykam się z problemem za małej ilości pamięci operacyjnej :(. Tym razem jest problem z funkcjami konwertującymi int() i char() oraz z przypisywaniem wartości do elementu w tabeli np: zmiennaTabChar[0] = ‚a’. Tu znowu kłaniają się ograniczenia ATM168. No nic, zakup ATM328 jest już przesądzony, może tydzień, może dwa i będzie. Ważą się też decyzje nad zakupem ARDUINO Mega :), zobaczymy jak budżet będzie wyglądał :). A na razie czekam na silnik krokowy ze sterownikiem ULN2003 oraz ekran LCD z nokii 3310/5110. Szczególnie czekam na wyświetlacz – ostatni niestety przez własne lenistwo zniszczyłem przez co porzuciłem na razie inny projekt – wygląda na to że jednak do niego powrócę :).

Kaprysy modułu karty SD

Spotkałem się z dziwnym problemem związanym z zasilaniem układu. Chodzi o problem z inicjalizacją karty
SD na zasilaniu zewnętrznym przez zasilacz i stabilizator L7805CV. Układ zasilany przez programator (poprzez łącze ISP) nie sprawia żadnych problemów. Zmierzyłem napięcie na uC (zasilanie z programatora) i na stabilizatorze, jako że mój miernik nawet nie ma określonej klasy błędu (takie małe gówienko za 20 zł 🙂 ale do tych celów wystarczy) to i odczyty są obarczone błędem ale z uC dostają napięcie 5,05V a ze stabilizatora 4,95V. Zastanawia mnie czy różnica 0,1V może sprawiać karcie SD tak wielki problem…. takie to dość zastanawiające – może kiedyś poznam odpowiedź.

Zrobiłem też dość ryzykowny eksperyment. Przy zasilaniu przez stabilizator, napięcie do karty SD podałem na nóżkę dedykowaną dla 3,3V, najlepsze w tym jest to że przy takim połączeniu wszystko chodzi stabilnie :D. Pewnie układ/karta nie zniesie takiego połączenia za długo ale dobrze wiedzieć ze jest taka możliwość…. albo przynajmniej podawać tam 3,7V – to może być bezpieczniejsze.

Oby jak najmniej takich niespodzianek.

Arduino – co dalej??

Do przetestowania jest jeszcze sterowanie silnikiem krokowym – jest to chyba ostatnia technika jaką trzeba opanować przed przystąpieniem do pisania programu który zbierze wszystko „do kupy”. Po drodze zrobię jeszcze test obsługi wyświetlacza z nokii 5110/3310 – to się przyda do innego projektu, może wplotę go do tego prototypu również.

Kolejnym etapem będzie opanowanie jednoczesnego obsługiwania karty SD i karty sieciowej a w później dodanie do tego obsługi silnika krokowego… i to by było już pewnie na tyle – prototyp byłby gotowy. Później jeszcze trzeba będzie zrobić model którym będzie to wszystko sterowało.

No a po tym to już tylko zostanie przeniesienie prototypu na dedykowaną płytkę i do dedykowanej obudowy, ale to jest raczej pieśń przyszłości…

Komunikacja sieciowa – datagramy UDP na ENC28j60 przy użyciu biblioteki EtherShield

Opis:

Układ i program realizujący minimalne ustawienia które umożliwiają na przesłanie informacji z komputera na uC i z powrotem poprzez datagramy UDP. Do sprawdzenia działania przydaje się jakiś monitor sieciowy (np. Wireshark) oraz mały program umożliwiający wysłanie i odbiór pakietu UDP. Jako że wpis jest napisany na podstawie wiedzy zawartej na stronie KMTronic, tam też można znaleźć wspomniany program do wysyłki i odbioru pakietów wraz z jego kodem źródłowym (C#).

Sam program nie robi za wiele, czeka na pakiet UDP i jak go dostaje to odpowiada na niego ustalonym tekstem (odpowiedź pokazywana w monitorze sieciowym lub programie wys/odb.)

Układ:

 

 

Działanie:

Kod:

#include "EtherShield.h" //dodanie obslugi wymaganej biblioteki
uint8_t mymac[6] = {0xCF,0x70,0x7C,0xE4,0x8A,0xB8}; //mac adres
uint8_t myip[4] = {192,168,50,2}; //adres ip 
uint16_t portNasluchuUDP = 12345; //port odpowiedzi 
uint16_t MYWWWPORT = 80; //port dla serwisu WWW ale po co on tu jest to nie wiadomo :) 
 //EDIT: najwidoczniej jest potrzebny bo biblioteka Ether Shield zawiera w sobie namiastke serwera WWW
 // portu po stronie mikrokontrolera nie ma
#define BUFFER_SIZE 750 //definicja wielkosci bufora
static uint8_t buf[BUFFER_SIZE+1]; //definicja bufora
char reply[]="raz, dwa, trzy: proba mikrofonu"; //tekst odpowiedzi
EtherShield es=EtherShield(); //obiekt bosługujacy komunikacje
uint16_t dat_p; //zmianna przechowujaca odebrany pakiet (??)
void setup()
{
 es.ES_enc28j60Init(mymac); //nadanie MAC adresu
 es.ES_init_ip_arp_udp_tcp(mymac,myip, MYWWWPORT); //inicjalizacja stosu tcp/udp.... chyba :)
}
void loop()
{
dat_p=es.ES_packetloop_icmp_tcp(buf,es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf)); //odbior pakietu
 
 if (buf[IP_PROTO_P]==IP_PROTO_UDP_V) //sprawdzenie czy przzetwarzany pakiet jest pakietem udp
 {
 es.ES_make_udp_reply_from_request(buf,reply,sizeof(reply),portNasluchuUDP); //wysłanie odpowiedzi do nadajacego
 buf[IP_PROTO_P]=0; // tu jest ana razie zagadka 
 }
}