Neuvottelutilojen varaukset haltuun

Kirjoittanut Santeri Siiranen
Julkaistu

Monadin kesäharjoittelijat ratkaisivat monelle tuttua toimistoarjen haastetta. Koronavuosien etätyöjaksojen jälkeen työntekijät ovat hiljalleen löytäneet toimistolle ja samalla myös neuvottelutilojen käyttö on kasvanut. Neukkarien tuplabookkauksilta ei ole voinut meilläkään välttyä. Harjoittelijamme suunnittelivat ja toteuttivat kosketusnäytöllisen varausjärjestelmän käyttöömme. 

Tee-se-itse varausjärjestelmässä keskityttiin helppokäyttöisyyteen

Toukokuun puolivälissä osana Monad.bind(trainee)-ohjelmaa kokoonnuimme viiden monadilaisen voimin ideoimaan neuvotteluhuoneiden varausjärjestelmän rakentamista. 

Projektia oli pohjustettu ostamalla etukäteen  muutama kosketusnäytöllinen E-Ink-sähköpaperilaite. Netistä hakemalla löytyi muutamia esimerkkejä vastaavista kaupallisista järjestelmistä, mutta ideamme oli alusta alkaen rakentaa oma järjestelmä, joka pohjautuisi vapaasti saatavilla olevaan ja edullisempaan laitteeseen. 

Myös laitteeseen kehitetty ohjelmisto olisi tarkoitus lisensoida avoimeen jakeluun myöhemmin. Näin saisimme omaan käyttöömme juuri sellaisen järjestelmän kuin tarvitsemme ja annamme samalla vapaat kädet myös muille käyttää ja halutessa laajentaa tekemäämme ohjelmistoa.

Alkupalaverin lopputulemana oli, että järjestelmän tulisi vastata ainakin seuraaviin tarpeisiin:

  • Näytöstä voi aina nähdä huoneen varaustilanteen. Onko huone vapaa vai varattu?
  • Näytöstä painamalla voi varata neuvotteluhuoneen alkaen heti, esimerkiksi 15 min, 30 min, 60 min jne.
  • Näytöstä näkee aina, milloin huoneen seuraava varaus alkaa ja kauanko se kestää
  • Näytön kautta tehty varaus päivittyy Google-kalenteriin

Lisäksi pyrittiin, että laitteen käyttöliittymä on silmää miellyttävä ja varausjärjestelmän käyttökokemus olisi siedettävällä tasolla ylipäätään. Järjestelmän tarkoitus on kuitenkin helpottaa arkea toimistolla, ei aiheuttaa ylimääräistä päänvaivaa. Yllä olevien vaatimusten pohjalta designerimme Jaakko Kemppainen loi suunnitelman käyttöliittymästä. 

Laitteen käyttöliittymän suunnittelusta vastasi designerimme. Kokonaisuudessa pyrittiin varmistamaan helppokäyttöisyys ja informatiivisuus.

Kuka tahansa voi koodata avoimelle alustalle

M5Paper on pieni, noin puhelimen kokoinen ja hinnaltaan edullinen noin 80 euron laite. Sen etupuolen täyttää lähes kokonaan sähköpaperinäyttö eli tuttavallisemmin E-ink. Hieman alle viiden tuuman kapasitiivisen kosketusnäytön resoluutio on 960×540 pikseliä. Järjestelmän aivoina toimii puolestaan 240 MHz kellotaajuudella pyörivä kaksiytiminen ESP32-systeemisiru, joka mahdollistaa laitteen yhdistämisen WiFi-verkkoihin ja Bluetoothiin. 

Myös kehitystyö onnistuu mutkattomasti ilman ulkoisia laitteita suoraan laitteeseen integroidun USB-C-liittimen kautta. Koska ESP32 on tuettu alusta myös Arduino-kehitysympäristössä, on sille saatavilla myös suuri valikoima avoimen lähdekoodin kirjastoja sekä kehitystyökaluja. Laitteeseen on integroitu noin 1000 mAh kokoinen akku, joten akku pitäisi kestää useita päiviä.

Laitteen takakanteen on kätevästi tulostettu palikkakaavio laitteen sisuksista. Tähän kaavioon on merkitty eri piirien mallinumerot, sekä pinnien numerot piirilevyllä, joita pitkin ne ovat yhdistetty toisiinsa. Tämä auttaa huomattavasti kehittäessä ohjelmistoa laitteelle, sillä esimerkiksi kosketusnäyttöä ohjaavan piirin (GT911) oman käyttöohjeen hakeminen netistä on helppoa. 

Pala kerrallaan matalammalle tasolle

Nico, Santeri ja Eero suunnittelivat ja toteuttivat kesän aikana varausjärjestelmän

Aloitimme kehitystyön kolmen kesätyöntekijän, Santerin, Nicon ja Eeron, voimin M5Stackin tarjoamalla UIFlow Python -kirjastolla. Pythonilla ja UIFlow:lla pääsimme nopeasti alkuun laitteen käytössä ja opimme tuntemaan laitteen ominaisuuksia paremmin. Huomasimme kuitenkin nopeasti, että sulautetun järjestelmän kehityksessä UIFlow:sta ja Pythonista koitui enemmän haittaa kuin hyötyä. 

Esimerkiksi virransäästötilojen käyttäminen Pythonista on liki mahdotonta ja kirjastosta puuttui paljon ominaisuuksia, jotka alustan natiivi C++-kirjasto tarjoaisi suoraan. Vaihdoimme lopulta kehityskieleksi C++:an, jolla saimme alustan kaikki ominaisuudet käyttöön.

C++:n myötä saimme käyttöön myös ESP-IDF:n tarjoamat ominaisuudet. Pystyimme käytännössä vaikuttamaan laitteen toimintaan juuri niin syvällisesti kuin halusimme. Poiketen AVR-pohjaisista kehitysalustoista, joissa on käytössä vain yksi ydin suoritusta varten ja pääohjelmaa ajetaan sillä suoraan, on kaksi ydintä sisältävässä ESP32:ssa oletuksena käytössä FreeRTOS-käyttöjärjestelmä, joka mahdollistaa vuoronnuksen eli pre-emptive schedulingin laitteella.

FreeRTOS:n avulla pystyimme jakamaan pääohjelmamme erillisiin taskeihin, joita muissa käyttöjärjestelmissä näitä nimitetään yleensä säikeiksi, threads. FreeRTOS tarjosi myös kohtuulliset työkalut, vaikkakin melko tyyppiturvattomat, muistin käsittelemiseen ja viestimiseen eri taskien välillä. 

Näin pystyimme suunnittelemaan sovellusarkkitehtuurimme niin, ettei esimerkiksi HTTP-kutsun tekeminen pysäytä käyttöliittymän toimintaa. Toisin sanoen, voimme tehdä useampaa asiaa yhtä aikaa – moniajoa sulautetussa järjestelmässä! 

Oheisessa koodiesimerkissä näkyy viestintä eri taskien välillä

namespace cal {
// This function block is run as a task (kind of a thread)
void task(void* arg) {
    APITask* apiTask = static_cast<APITask*>(arg);

    for (;;) {
        void* reqTemp;
        
        // Fetch new request or block until one arrives
        xQueueReceive(apiTask->_queueHandle, &reqTemp, portMAX_DELAY);

        // While **count** variable is defined (this iteration), the device will stay powered on.
        // Other tasks may keep the device awake as well
        auto count = sleepManager.scopedTaskCount();

        auto req = toSmartPtr<APITask::QueueElement>(reqTemp);
        auto startTime = millis();

        wifiManager.waitWiFi(API_TASK_WIFI_CONNECT_MAX_RETRIES);

        apiTask->_api->refreshAuth();

        // Start the actual HTTP calls
        switch (req->type) {
            case APITask::RequestType::CALENDAR_STATUS: {
                auto func = toSmartPtr<APITask::QueueFuncCalendarStatus>(req->func);
                apiTask->callbackCalendarStatus((*func)());
                break;
            }
            case APITask::RequestType::END_EVENT: {
                auto func = toSmartPtr<APITask::QueueFuncEvent>(req->func);
                apiTask->callbackEndEvent((*func)());
                break;
            }

            // ... other cases here, removed for readability

            default:
                log_e("APITask: Unhandled request type %u", req->type);
                break;
        }

        log_i("Request completed in %u ms.", millis() - startTime);
    }
       // This statement should never run, but is here as a guard to close this task gracefully
    vTaskDelete(NULL);
}
}

Yllä: Otos lopullisen version koodista, jossa suoritetaan HTTP-kutsuja. Silmukka hakee jonosta pyyntöjä erilaisille HTTP-kutsuille ja suorittaa ne yksi kerrallaan. Samaan aikaan laite voi suorittaa muutakin koodia, eikä koko laite jäädy pyynnön suorittamisen ajaksi.

Yksinkertaistettu kuvaus sovelluksen eri taskien välisestä kommunikoinnista.
Jokaista taskia suoritetaan yhtä aikaa FreeRTOS tarjoaman moniajotuen avulla

Ennen lopullisen version tekemistä halusimme kuitenkin varmistaa, että laite oikeasti kykenee siihen, mitä siltä vaadimme. Agile-metodologiassa tätä nimitetään Minimum viable productin eli MVP:n tekemiseksi. 

Rakentamalla mahdollisimman yksinkertaisen laitteen, joka juuri ja juuri täyttää asettamamme vaatimukset, varmistamme projektin onnistumisen (tai epäonnistumisen) hukkaamatta turhaa aikaa, ja samalla ymmärrämme käsittelemäämme ongelmaa paremmin. Erityisesti halusimme varmistua, että laitteen akku oli mahdollista saada kestämään vähintään viikon ajan. Koimme että viikon akkukesto on minimiraja, jolla laite voidaan todeta käyttökelpoiseksi.

Käytännössä tämä tarkoitti sitä että rakensimme pohjan kaikelle tarvitsemallemme toiminnallisuudelle C++:lla. Koodin jäsentelyllä tai sillä oliko toiminnallisuus jonkin tietyn taskin vastuulla ei vielä tässä kohtaa ollut suurta merkitystä. Uudelleenkäytettävyys otettiin kuitenkin huomioon rakentamalla alusta alkaen siistejä rajapintoja. 

Laitteiden virrankulutuksen optimointi ja siten akunkeston kasvattaminen osoittautui projektin haastavimmaksi osuudeksi.

Tarkoitus oli saada jokainen palanen toimimaan. Ohjelman logiikka kulki tässä versiossa yhtä ainutta loputonta silmukkaa, joka pyrki ottamaan huomioon kaiken mahdollisen WiFiin yhdistämisestä laitteen virransäästötilaan siirtymiseen ja kaiken siltä väliltä. Toisin sanoen kaikki koodi pyöri yhdellä taskilla ja esimerkiksi HTTP-kutsun aikana käyttöliittymä pysähtyi täysin.

Vaikeinta MVP:n rakentamisessa oli laitteen virrankulutuksen optimointi. Todellisen virrankulutuksen mittaaminen on haastavaa, sillä sähköpaperinäytöllä näkyy sama kuva, vaikka näytölle ei olisi kytketty virtaa ollenkaan. Sen vuoksi ei aina edes tiedä, onko laite päällä vai ei.

Tuumasimmekin, että ei auta kuin kääriä hihat ja avata laitteen kuori, jotta voisimme mitata laitteen virrankulutusta suoraan sen piirilevyltä. Santeri toi toimistolle hieman järeämmän yleismittarin ja haimme Elektorista SMD-koukut, joilla pääsimme tarkkailemaan laitteen virrankulutusta mikroampeeritasolla.

Korvasimme virrankulutusta optimoidassamme M5Paper:in akun omalla virtalähteellämme, jotta voimme mitata virrankulutusta erittäin tarkasti

Mittausten myötä laitteen passiivinen virrankulutus saatiin onnistuneesti tiputettua 200 milliampeerista noin kahdeksaan. Ainut virtaa kuluttava komponentti oli kosketusnäytön kosketuksia tarkkaileva komponentti, joka tarvitsee myös laitteen nukkuessa noin 8 mA virtaa. Itse ESP32 kuluttaa nukkuessaan alle milliampeerin verran. Ilman virransäästötilaa laitteen akku kesti noin 5–6 tuntia, mutta nyt jopa viikon mittainen akunkesto on mahdollinen.

MVP-versio varausjärjestelmästä valmistui kesäkuun puolivälissä ja se otettiin välittömästi käyttöön kahdessa pienemmässä neuvotteluhuoneessamme.

Iteroimalla entistä paremmaksi kokonaisuudeksi

Jo ennen MVP-version käyttöönottoa oli tiedossa, että laitteen ohjelmistossa on muutama bugi. Pienet virheet eivät kuitenkaan estäneet laitteen käyttöönottoa ja näin saimme nopeasti palautetta laitteen toiminnasta. Heti ensimmäisiä palautteita oli laitteen käyttöliittymän jäätyminen sen tehdessä HTTP-pyyntöä. Tämän arvelimme haittaavan käyttöä jo etukäteen, mutta palautteen pohjalta meillä oli selkeä syy parantaa käyttöliittymän responsiivisuutta. 

Toinen MVP-testauksessa saatu palaute oli varauksen vahvistusruudun tarpeellisuus. Projektin alussa arvelimme, että on hyvä idea vahvistaa varauksen tekeminen käyttäjältä vielä sen jälkeen kuin hän painaa “Varaa 30min” -painiketta. Tämä kuitenkin osoittautui vain turhaksi välivaiheeksi ja pitkitti varauksentekoprosessia. Onneksi rakentamamme konfiguraationhallinnan kautta kyseisen ominaisuuden saattoi kytkeä pois halutessaan.

Suunnittelimme lopulliseen versioon asynkronisen toimintamallin. Laitteen eri toiminnallisuudet on jaettu eri taskeihin, jotka ovat samanaikaisesti ajossa laitteella. Käytännössä tämä tarkoittaa, että laitteen käyttöliittymästä on vastuussa yksi task, HTTP-pyyntöjen suorittamisesta toinen ja muista laitteen ylläpidollisista ominaisuuksista n-määrä laitten omia taskeja. ESP32:n kahdesta ytimestä toinen osoitettiin täysin käyttöliittymän käyttöön. Normaalisti toinen ytimistä ei suorita ohjelmakoodia lainkaan.

Emme olleet myöskään aivan tyytyväisiä M5Stackin tarjoamaan M5EPD-kirjastoon. Kirjasto vaatii esimerkiksi kosketusten rekisteröimiseksi kutsun 100 millisekunnin välein – muuten näytöllä ei tapahdu mitään. Lisäksi laitteen virransäästöominaisuudet eivät toimineet oikein, eikä laitteella voinut käyttää kuin yhtä fonttia per ruutu. 

Osan näistä ominaisuuksista olivat korjanneet M5Paperin muut käyttäjät, mutta kirjaston ylläpitäjä ei ollut hyväksynyt näitä muutoksia mukaan. Onneksi kirjastoa voi laajentaa itsekin. Forkkasimme kirjaston ja hyväksyimme yhteisön tekemät muutokset. Lisäksi laajensimme kirjastoa lisäämällä siihen tuen asynkroniselle kosketukselle. Näin kirjastolle voi määritellä callbackin, jota se kutsuu aina kosketuksen yhteydessä. Päivittämämme kirjasto on saatavilla Githubista.

Asykronisuuden lisäksi rakensimme mm. 

  • Konfiguraatiohallinnan WiFi yhteyden yli – käyttäjää tervehditään ensimmäisellä käyttökerralla WiFi-verkolla ja ruudulla näytettävillä ohjeilla, joilla laitteen saa konfiguroitua käyttöön
  • Laitteen nukkuma-ajat, joilla laitteen saa kokonaan pois päältä esimerkiksi yöksi ja viikonlopuksi lisävirransäästöä varten
  • Animaation, jota näytetään aina käynnistyksen ja muun latauksen yhteydessä
  • Asetussivun ja paransimme laitten lokitusta ylipäätään
  • Automaattiset päivitykset WiFin yli päivittäin
  • Kielivalinnan suomen ja englannin välillä sekä mahdollisuuden lisätä myös muita kieliä
  • Erilaisia 3D-tulostettavia koteloita laitteelle, esimerkiksi seinäkiinnitystä varten

Varausjärjestelmä on valmiina syksyn kiireisiin

Santeri, Nico ja Eero esittelevät valmiita varausjärjestelmän laitteita.

Tämän blogin julkaisuun tultaessa MVP-laitteemme käyttöönotosta on kulunut kaksi kuukautta. Aluksi vain kahdessa pienemmässä neuvotteluhuoneessa käytössä olleet laitteet ovat laajentuneet käyttöön jokaiseen kokoustilaan. Yhteensä laitteita on käytössä kuusi kappaletta. Laitteen koko ohjelmisto on kirjoitettu uudestaan ja se on todettu toimivan luotettavasti.

Yksinkertainen käyttöliittymä ei kaipaa erillistä opastusta ja laitteet ovat integroituneet luonnolliseksi osaksi neuvotteluhuoneiden käyttöä. Laitteen toimivuudesta on saatu erittäin hyvää palautetta ja jopa satunnaisessa palaverikäytössä oleva joogahuoneemme on saanut oman laitteen ovelleen. Suurimmassa neuvotteluhuoneessamme on käytössä jopa kolme laitetta yhtä aikaa. Laitteet molemmilla ovilla ja yksi kokouspöydällä, josta aikaa voi kätevästi lisätä. 

Koska laitteet synkronoituvat pilveen, näkyy yhdellä laitteella tehty varaus kaikkien kyseiseen huoneeseen liitettyjen näyttöjen ruudulla. Laitteiden akku kestää arviolta 8–10 päivää. Toistaiseksi onkin riittänyt, että niitä laittaa kerran viikossa lataukseen esimerkiksi lounaan ajaksi.

Voimme hyvällä mielellä todeta olevamme tyytyväisiä varausjärjestelmäprojektin lopputulokseen. Opimme kaikki paljon uutta IoT-laitteiden kehittämisestä, optimoinnista ja päivittämisestä. 

Laitteen lähdekoodi on lisensoitu avoimen lähdekoodin lisenssin alle (GPLv3) ja sitä saa kuka tahansa hyödyntää. Koko projekti ja ohjeet sen käyttämiseen ovat saatavilla osoitteessa GitHubista.