keskiviikko 28. helmikuuta 2018

Lisää micro:bit:in ohjelmointia C++:lla mbed:in kautta

Jatkoin tänään tutustumista micro:bit:in ohjelmointiin C++:lla. Nyt kokeilulistalla oli ulkoiset GPIO-pinnit. micro:bit:in leveät pinnit P0, P1 ja P2 ovat yleispinnejä, eli ne voivat toimia sekä digitaali- että analogiapinneinä niin input kuin output -moodeissa ja pinnin toimintamoodia voi jopa vaihtaa lennossa! Tein tallaisen perinteisen ledin himmentimen, jossa valovastukselta luettu analoginen arvo (0-1024) syötetään sellaisenaan ledille. Kun valon määrä kasvaa, ledi himmenee.

Valovastus ja ledi kytkettynä micro:bit:iin koekytkentälevyn avulla
Koodi on lyhyt:

#include "MicroBit.h"

MicroBit uBit;

int main()
{
    uBit.init();
    
    while(1){
    int valo = uBit.io.P1.getAnalogValue();    
    uBit.io.P0.setAnalogValue(valo);          
    uBit.display.scrollAsync(valo);
    uBit.sleep(10);
    }
}

Valosensorin antamaa arvoa oli kätevä tutkia tulostamalla se ledinäytölle (uBit.display.scroll), mutta tämä aiheutti ohjelmaan hitautta, koska säie jäi odottamaan metodin paluuta. Pienen tutkimisen jälkeen löysin asynkronisen scroll-metodin, joka palaa heti ja jää suorittamaan scrollausta taustalla (uBit.display.scrollAsync). Näin sen homman pitää toimia :-)

Toinen kokeilu liittyi ledin vilkutteluun digitaalimoodissa (on/off) ja kuvioiden tulostamiseen ledinäytölle. A-nappia painamalla ledi alkaa vilkkumaan päälle ja pois sekunnin välein, B-napista tulostuu risti keskelle näyttöä, ja P1-pinnin yhdistäminen GND:n kanssa saa aikaan ristikuvion liikkumisen ledinäytön vasemmasta yläkulmasta oikeaan alakulmaan.

Kuvan saaminen ledinäytölle vaikuttaa helpolta, MicroBitImage-objekti luodaan antamalla ledien valaistusarvot (0-255) merkkijonona vaakariveittäin pilkulla erotettuna (rivin loppussa rivinvaihto \n).

Ledin vilkuttelua ja ristin liikuttelua näytöllä

#include "MicroBit.h"


MicroBit uBit;
MicroBitImage cross("0,255,0\n255,255,255\n0,255,0\n");

void printCross(int x, int y){
        MicroBitImage image(5,5);
        image.paste(cross, x, y);
        uBit.display.print(image);
}

void onButton(MicroBitEvent e)
{
    if (e.source == MICROBIT_ID_BUTTON_A){
       for(int i=0; i<3; i++){
            uBit.io.P0.setDigitalValue(1);
            uBit.sleep(1000);
            uBit.io.P0.setDigitalValue(0);
            uBit.sleep(1000);
            }
        }

    if (e.source == MICROBIT_ID_BUTTON_B)
        printCross(1, 1);

    if (e.source == MICROBIT_ID_IO_P1){
        for(int i=0; i<3; i++){
            printCross(i, i);
            uBit.sleep(1000);
        }
    }
}

int main()
{
    uBit.init();

    uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_EVT_ANY, onButton);
    uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_EVT_ANY, onButton);
    uBit.messageBus.listen(MICROBIT_ID_IO_P1, MICROBIT_EVT_ANY, onButton);
    uBit.io.P1.isTouched();
     
    while (1)
        uBit.sleep(10000);
}  

Ainakin näiden kokeilujen perusteella dokumentaatio on selkeää ja koodin saa toimimaan vaivattomasti. Mainio systeemi tämä mbed micro:bit! 

Ainoa ihmettelyn aihe tänään on se, miksi micro:bit tekee kaiken kolmeen kertaan (teksti skrollaa ruudulla kolme kertaa, ledit vilkkuvat kolme kertaa enemmän kuin koodin mukaan pitäisi). Ehkä tällekin löytyy vielä jokin selitys :-)  

micro:bit:in ohjelmointia C++:lla mbed:in kautta

BBC micro:bit:in ohjelmointi onnistuu monella eri välineellä: graafisesti JS Blocks:in kautta tai Micropython:illa. Näissä on kuitenkin omat rajoitteensa. Jos haluaa päästä katsomaan mitä konepellin alle on piilotettu, kannattaa tarttua kiinni hieman alemman tason rajapintaan ja ohjelmoida suoraan käyttäen microbit-dal:ia (Device Abstraction Layer). Se onnistuu näppärästi nettiselaimessa toimivan mbed - ympäristön avulla. mbed vaatii kirjautumisen ja sen jälkeen voit jo aloittaa lataamalla demokoodeja. Kääntäminen ja hex-tiedoston siirtäminen micro:bit:iin toimii samalla tavalla kuin muissakin selainpohjaisissa ympäristöissä: hex - tiedosto siirretään ladatuista tiedostoista USB-portissa näkyvälle micro:bit-levylle.

mbed-ohjelmointiympäristö toimii nettiselaimessa (tarvittavat microbit-kirjastot latautuivat siihen itsestään)
Yksinkertaisin esimerkki on Hello World, joka saa tekstin skrollaamaan micro:bitin näytöllä:

#include "MicroBit.h"
MicroBit uBit;

int main()
{
    uBit.init();
    uBit.display.scroll("Hello World!");
    release_fiber();
}

Huomattavaa koodissa on se, että Microbit-objektin instanssi (uBit) pitää luoda ennen main():iä, jotta se on globaalimuuttuja. Main:in sisällä se ensin alustetaan init-metodilla ja sen jälkeen sitä käytetään ledimatriisin (display) ohjaamiseen. fiber tarkoittanee lähinnä säiettä, joka lopuksi vapautetaan. 

Tässä esimerkissä reagoidaan nappeihin A, B sekä P0-pinnin kosketukseen GND:n kanssa (laita jotakin sähköäjohtavaa P0:n ja GND:n väliin).

#include "MicroBit.h"
MicroBit uBit;

void onButton(MicroBitEvent e)
{
    if (e.source == MICROBIT_ID_BUTTON_A)
        uBit.display.printChar('A');

    if (e.source == MICROBIT_ID_BUTTON_B)
        uBit.display.printChar('B');

    if (e.source == MICROBIT_ID_IO_P0)
        uBit.display.printChar('0');
}

int main()
{
    uBit.init();
    uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_EVT_ANY, onButton);
    uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_EVT_ANY, onButton);
    uBit.messageBus.listen(MICROBIT_ID_IO_P0, MICROBIT_EVT_ANY, onButton);
    uBit.io.P0.isTouched();

    while (1)
        uBit.sleep(10000);
}

Tässä esimerkissä käytetään erilaisia eventtejä. Jotta ohjelma saa ne, niiden kuunteluun pitää ensin rekisteröityä messageBus.listen - metodilla. Samalla kerrotaan eventhandler-funktion nimi (onButton). Ennen kun koodi laitetaan odottamaan eventtejä (sleep) alustetaan P0 - pinni toimimaan "on/off" moodissa eli reagoimaan kosketukseen kuin painonappi.  Eventhandler-funktiossa tutkitaan eventin tyyppiä ehtolauseiden avulla ja näytetään eri merkkejä näytöllä. 

Sormi johtaa sen verran sähköä, että P0-pinni yhdistyy GND:hen ja syntyy virtapiiri
 Dokumentti tämän kirjaston käyttöön löytyy täältä. Vaikka itse en ole graafisten ympäristöjen ystävä, oppilaiden kanssa niistä on aloitettava. Jää nähtäväksi kuinka monelle se jää yläkoulun aikana liian pieneksi pääsemme kiinni C++:aan...   

tiistai 20. helmikuuta 2018

Ohjelmoinnin valinnaiskurssi: Arduino-ohjelmointia (osa 3)

Jatkoimme sensoreihin tutustumista tekemällä kytkennän Project 7: Temperature Alarm. Tässä lämpötilasensorin antama jännitearvo muunnetaan ensin celsiusasteiksi, jota sitten käytetään ehtolauseessa. Ledin tilalle otetaan summeri, joka laitetaan soimaan kun lämpötila nousee riittävän korkeaksi. Rakensimme kytkennän tämän ohjeen mukaisesti:

Project 7: Temperature Alarm
image source: http://image.dfrobot.com/image//Blog/
Tätäkin kytkentää lähdimme tutkimaan sensori edellä, eli katsoimme ensin sarjamonitorin avulla, millaisia arvoja lämpötilasensori syöttää meille.

int SUMMERI = 8;
int SENSORI = 0;
int arvo = 0;

void setup() {
    pinMode(SUMMERI, OUTPUT);        
    Serial.begin(9600);        
}

void loop() {
    arvo = analogRead(SENSORI);  
    Serial.println(arvo);              
}

Lämpötilan sensorin raaka-arvot liikkuivat 50 (huoneen lämpötila) ja 60 välillä (uloshengitys). 
LM-35 lämpötilasensori on toimii lineaarisesti, eli kun lämpötila kasvaa asteella, sensorin jännite kasvaa 10mV. Sen yhtälö on siis:

Vout = 10mV/°C * T    (LM35 datasheet)

Koska käytämme 5.0V jännitettä ja analogRead muuntaa jännitearvot kokonaisluvuiksi välille 0-1024, sensorin antama raaka-arvo (x) voidaan muuttaa celsiusasteiksi seuraavan kaavan avulla:

T(x) = (5.0 * x * 100.0) / 1024   (Arduino Playground)

Seuraavaksi teimme apufunktion (arvoLampotilaksi), joka muuttaa raaka-arvon lämpötilaksi.

int SUMMERI = 8;
int SENSORI = 0;
int arvo = 0;
double lampotila = 0;

void setup() {
    pinMode(SUMMERI, OUTPUT);        
    Serial.begin(9600);        
}

double arvoLampotilaksi(int x){
  double temp = 0;
  temp = (5.0*x*100)/1024;
  return temp;
}

void loop() {
    arvo = analogRead(SENSORI);  
    lampotila = arvoLampotilaksi(arvo);
    Serial.println(lampotila);
}

Seuraavaksi otimme käytöön ehtolauseen ja summerin. Jos lämpötila nousee riittävän suureksi, summeri alkaa soida.

int SUMMERI = 8;
int SENSORI = 0;
int arvo = 0;
double lampotila = 0;

void setup() {
    pinMode(SUMMERI, OUTPUT);        
    Serial.begin(9600);        
}

double arvoLampotilaksi(int x){
  double temp = 0;
  temp = (5.0*x*100)/1024;
  return temp;
}

void loop() {
    arvo = analogRead(SENSORI);  
    lampotila = arvoLampotilaksi(arvo);
    Serial.println(lampotila);

    if(lampotila > 27){
      tone(SUMMERI, 2000);
      delay(2); 
    } else {
      noTone(SUMMERI);
    }
}

Summerin ääni ei järin korvia huumaa, joten kokeilimme myös mallikoodin mukaista hienompaa hälyytysääntä. Tosin tämän koodaaminen olikin jo hieman haastavampaa monelle ja koodin aukiselittäminenkin jäi tasolla "summerin sisääntuloa vaihdellaan for-loopin avulla"...

int SUMMERI = 8;
int SENSORI = 0;
int arvo = 0;
double lampotila = 0;
float sinVal = 0;
int toneVal = 0;

void setup() {
    pinMode(SUMMERI, OUTPUT);        
    Serial.begin(9600);        
}

double arvoLampotilaksi(int x){
  double temp = 0;
  temp = (5.0*x*100)/1024;
  return temp;
}

void loop() {
    arvo = analogRead(SENSORI);  
    lampotila = arvoLampotilaksi(arvo);
    Serial.println(lampotila);

    if(lampotila > 27){
      for(int x = 0; x < 180; x++){
            sinVal = (sin(x*(3.1412/180)));
            toneVal = 2000+(int(sinVal*1000));
            tone(SUMMERI, toneVal);
            delay(2); 
     }   
    } else {
      noTone(SUMMERI);
    }
}
Temperature Alarm - kytkentä näyttää tältä

NOTE: Tämän projektin kanssa hämmennystä aiheutti summerin + merkki, joka näytti olevan väärällä puolella summeria ja tästä johtui se, että alkuun ääntä ei tullut lainkaan.




maanantai 19. helmikuuta 2018

Ohjelmoinnin valinnaiskurssi: Arduino-ohjelmointia (osa 2)

Toiseksi projektiksi valikoitui Project 9: Auto light, koska siinä otetaan käyttöön ensimmäinen sensori ja sen avulla päästään harjoittelemaan ehtolauseen käyttämistä. Tässä projektissa hyödynnetään aikaisempia tietoja ledeistä ja lisäksi tutustutaan arvon lukemiseen analogisesta pinnistä sekä muutosten seuraamiseen sarjamonitorin avulla.

Kytkentä tehtiin kytkentäkortin Project 9: Auto light avulla (Light sensitive LED):

Project 9: Auto light
image source: http://image.dfrobot.com/image//Blog/
Ensimmäiseksi lähdimme tutkimaan valovastuksen toimintaa sarjamonitorin avulla. Jotta ehtolauseita voitaisiin muodostaa, olisi tiedettävä millaisia arvoja vastus tuottaa. Tämän koodin avulla pystyy monitoroimaan valovastusta:

int LED = 13;   
int SENSORI = 0;                  
int arvo = 0;                      

void setup() {
    pinMode(LED,OUTPUT);          
    Serial.begin(9600);          
}

void loop() {
   arvo = analogRead(SENSORI);       
   Serial.println(arvo);
   delay(10);
}      

Sarjamonitorissa on kaksi toimintoa, lukuarvojen monitorointi sekä serial plotter. Tässä tapauksessa visuaalisempi plotter toimii hyvin, kun valovastusta valaisee kännykän lampulla arvot muuttuvat selvästi. Plotter käynnistetään valikosta: Työkalut : Serial Plotter.

Serial Plotter, käynnistys
Valovastuksen antamia jännitearvoja, alimmat arvot kirkkaalla valolla
Nyt kun valovastuksen toiminnasta on jonkinlainen käsitys, pääsimme tekemään ehtolauseita ledin vilkuttelua varten. Ensimmäinen tehtävä oli tehdä yksi ehtolause, joka sammutaa ledin kun valaistusolosuteet ovat riittävän kirkkaat.

int LED = 13;   
int SENSORI = 0;                  
int arvo = 0;                      

void setup() {
    pinMode(LED,OUTPUT);          
    Serial.begin(9600);          
}

void loop() {
   arvo = analogRead(SENSORI);       
   Serial.println(arvo);
   
   if(arvo < 1000){
    digitalWrite(LED, LOW);
   } else{
    digitalWrite(LED, HIGH);
   }
   
   delay(10);
}      

Lisätehtävänä oli tehtä sisäkkäiset ehtolauseet niin, että ledi vilkkuu kolmella eri tavalla riippuen valaistuksesta. Tässä yksi malliratkaisu:

int LED = 13;   
int SENSORI = 0;                  
int arvo = 0;                      

void setup() {
    pinMode(LED,OUTPUT);          
    Serial.begin(9600);          
}

void loop() {
   arvo = analogRead(SENSORI);       
   Serial.println(arvo);
   
   if(arvo < 1000){
    digitalWrite(LED, LOW);
    } else{
      if(arvo > 1010){
        digitalWrite(LED, HIGH);
        delay(200);
        digitalWrite(LED, LOW);
        delay(200);
      }else{ 
        digitalWrite(LED, HIGH);
      }
    }
   
   delay(10);
}      


Auto Light - kytkentä näyttää tältä

Ohjelmoinnin valinnaiskurssi: Arduino-ohjelmointia (osa 1)

Tänä vuonna pääsimme ensimmäistä kertaa käyttämään kunnolla DFRobot:in Beginner Kit for Arduino - paketteja 8.luokan ohjelmointikurssin kanssa. Teimme paketin mukana tulleiden kytkentäkorttien avulla demoprojekteja, ja koodasimme niihin toimivat ohjelmat. Ryhmässä oli 14 oppilasta ja 10 Arduino Kit:iä, joten osa teki pareittain ja osa yksin. Kolme ensimmäistä projektia toimivat hyvin ja ne saatiin pääpiirteissään tehtyä yhden kaksoistunnin aikana. Ryhmällä oli aikaisempaa kokemusta Arduinoista, joten rakentaminen sujui ilman isompia ihmettelyjä ja yritimme keskittyä enemmän ohjelmointiin. Ainoa hämminkiä aiheuttanut asia oli se, että kyntkentäkortien kuvissa oli vähemmän vihreitä sekä punaisia pinnejä kuin itse laitteessa. Tästäkin selvittiin kun oli tarkkana ja muisti laskea pinnit pienimmästä suurimpaan (ja jättää lopusta kaksi pinniä tyhjäksi).

Project 2: SOS Beacon

Ensimmäinen tavoite oli saada ledi ylipäätään vilkkumaan.

Project 2: SOS Beacon
image source: http://image.dfrobot.com/image//Blog/

Ohjelmakoodi ledin vilkuttamiseen on hyvin yksinkertainen:

int LED = 10;

void setup() {
  pinMode(LED, OUTPUT); 
}

void loop() {
  digitalWrite(LED, HIGH);
  delay(1000);
  digitalWrite(LED, LOW);
  delay(1000);
}

Jotta koodin voi ladata Arduinoon, pitää muistaa valita oikea sarjaportti USB-kaapelille:

Valitse Työkalut-valikosta oikea portti Arduinolle, ennen koodin lataamista siihen
Seuraava tehtävä olikin saada ledi vilkkumaan nopeammin sekä niin että yksi vilkutus on pidempi ja toinen lyhyempi. Kun oppilaat olivat tämän hoksanneet, niin lopullinen tehtävä oli saada aikaan S.O.S morseaakkosilla eli  ... --- ...

Tämä onnistui kopioimalla monta riviä koodia. Osalla rivit alkoivat mennä sekaisin, joten totesimme, että on ehkä kätevämpää tehdä apuohjelma, joka suorittaa yhden vilkutuksen. Koska vilkutuksen pituus vaihtelee, se asetettiin muuttujaksi (pituus).

int LED = 10;
int LYHYT = 200;
int PITKA = 500;

void setup() {
  pinMode(LED, OUTPUT); 
}

void vilkutus(int pituus){
  digitalWrite(LED, HIGH);
  delay(pituus);
  digitalWrite(LED, LOW);
  delay(500);
}

void loop() {
  vilkutus(LYHYT);
  vilkutus(LYHYT);
  vilkutus(LYHYT);
  vilkutus(PITKA);
  vilkutus(PITKA);
  vilkutus(PITKA);
  vilkutus(LYHYT);
  vilkutus(LYHYT);
  vilkutus(LYHYT);
}

Tämä versio koodista todettiin aika selkeäksi, tosin hieman turhaa toistoa sisältäväksi. Lopullinen koodi optimoitiin for-looppien avulla tällaiseksi:

int LED = 10;
int LYHYT = 200;
int PITKA = 500;

void setup() {
  pinMode(LED, OUTPUT); 
}

void vilkutus(int pituus){
  digitalWrite(LED, HIGH);
  delay(pituus);
  digitalWrite(LED, LOW);
  delay(500);
}

void loop() {
  for(int i=0; i<= 2; i++){
    vilkutus(LYHYT);
    } 
  for(int i=0; i<= 2; i++){
    vilkutus(PITKA);
  }
  for(int i=0; i<= 2; i++){
    vilkutus(LYHYT);
  }



SOS Beacon kytkentä näyttää tältä