This is some program for air quality monitoring device. Its capabilities span quite a broad range: -read sensor (selection of 2 kinds - both worthless actually) -store measurements on SD card (nice formatting included ®) -display current measurement on LCD -sleep between measurement (but on my Nucleo platform power draw was still unacceptably high) -change measurement interval with on-board keys

Dependencies:   DS1307 DS1820 DSM501 GP2Y1010AU0F I2CEeprom SDFileSystem TextLCD_YWROBOT WakeUp keypad_ADC mbed

The device has no upload capabilities. It logs data onto local SD card if one is present. It displays current measurement nevertheless.

/media/uploads/amateusz/screenshot_20180131_132347.png

I used some libraries: some purpose-made, rest forked public libraries. main.cpp file contains the program. First it initializes modules (EEPROM, RTC, thermometer, dust sensor, LCD, SDcard), then enters endless loop of following sequence:

  • take a measurement
  • display measurement and save it to the SD card
  • enter sleep for *sleep* period
  • repeat

main.cpp

Committer:
amateusz
Date:
2018-01-31
Revision:
0:41f9e684d87e

File content as of revision 0:41f9e684d87e:

#include "mbed.h"
//#include <errno.h>
//#include <stdio.h>
#include "SDFileSystem.h" // sd card
#include "I2CEeprom.h" // eeprom present on the tinyRTC module
#include "DS1307.h"
#include "DS1820.h" // thermometer present on the tinyRTC module
#include "TextLCD.h" // D1 robot LCD & keypad shield
#include <string>
#include <iomanip> // setprecision
#include <sstream> // stringstream
#include <algorithm> // replace
//#include "DSM501.cpp" // dust sensor
#include "GP2Y1010AU0F.cpp"
#include "keypad_ADC.cpp" // keys on the shield
#include "WakeUp.h"

LowPowerTimeout oneSecondTimeout;
bool oneSecondTimeoutExpired = false;

DS1820 thermometer(PC_9); // rom: wlutowany w płytkę z RTC 0x28ff2b0586160356
Serial pc(USBTX, USBRX, 57600); // tx, rx  for debug and usb pc comunications
I2C i2c(PB_9, PB_8);
I2CEeprom eeprom(&i2c, 0xA0, 1, 4096); // memory map: 0 - interval is [s] for measurement, 1 - file number. auto incremented, 2 - same file (0) or new everytime (1), 9 - measureTime upper byte
//DS1307 rtc(&i2c); // start DS1307 class and give it pins for connections of the DS1307 device
TextLCD lcd(D8, D9, D4, D5, D6, D7, TextLCD::LCD16x2, D10);
SDFileSystem sd(PC_12, PC_11, PC_10, PD_2, "sd", NC, SDFileSystem::SWITCH_NONE, 5000000);
//DSM501 dust(PC_1, PC_0); // vout2, vout1 | blue , yellow od góry
GP2Y1010AU0F dust(A1, PA_4);
DigitalOut led(LED1);
keypad_ADC keys(A0);
DigitalIn button(USER_BUTTON, PullUp);
LowPowerTimeout lcdBacklightTimeout;

int lcd_mount(int passthrough)
{
    if (passthrough == 0) {
        lcd.locate(14,0);
        lcd.putc(7);
        led = 1;
    }
    return passthrough;
}

int lcd_unmount(int passthrough)
{
    if (passthrough == 0) {
        lcd.locate(14,0);
        lcd.putc(' ');
        led = 0;
    }
    return passthrough;
}

void lcdBacklightOff()
{
    lcd.setBacklight(TextLCD::LightOff);
}
void lcdBacklightOn()
{
    lcd.setBacklight(TextLCD::LightOn);
}
string floatToString(float value, bool dotToComma, char precision = 2)
{
// nice temperature foramting
    std::ostringstream temperature_str_dot;
    temperature_str_dot << fixed << setprecision(precision) << value;//thermometer.temperature();
    std::string temperature_str_comma(temperature_str_dot.str());
    if (dotToComma) std::replace( temperature_str_comma.begin(), temperature_str_comma.end(), '.', ','); // replace all 'x' to 'y'
    return temperature_str_comma;
    // end of temperature
}
string intToString(int value, int fillWithZeros = 0)
{
    std::ostringstream ss;
    if (fillWithZeros > 0) ss << setfill('0') << setw(fillWithZeros);
    ss << value;
    return ss.str();
}
bool write_with_progress_bar(FileHandle* file, const void * buffer, size_t len, void (*everytick)(char) = NULL)
{
    char parts=4;
    char * write_buffer = new char[len];
    for (unsigned int i = 0; i < len; i++)
        write_buffer[i] = ((char*)buffer)[i];
    const size_t chunk_at_once = len/parts;
    unsigned int data_written = 0;
    char counter = 0;
    while (data_written < len) {
        data_written += file->write(write_buffer + data_written, min(len-data_written, chunk_at_once));
//        pc.printf("count: %d", counter);
        if (everytick != NULL)
            (*everytick)(counter++);
    }
    delete write_buffer;
    if (everytick != NULL)
        (*everytick)(0); // success, zero it
    return true;
}

void showProgress(char value)
{
    char posX = 15, posY = 0;
    lcd.locate(posX, posY);
    char charToPut = ' ';
    if (value > 0) {
        charToPut = value-1 > 3 ? 3 : value -1;
        // if (value >= 8)
//            charToPut = 0xFF;
    }
    lcd.putc(charToPut);
    wait(.015);
}
void sdInit()
{
    sd.crc(true);
//    sd.large_frames(true);
    sd.write_validation(false);
}

void  thermometerInit()
{
    thermometer.search_ROM_setup();
    thermometer.search_ROM();
    thermometer.set_configuration_bits(12);
}
void lcdInit()
{
    lcd.setCursor(TextLCD::CurOff_BlkOff);
    lcd.cls();
    lcd.setBacklight(TextLCD::LightOn);
    char progress[][8] = {
        {0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x7},
        {0x0, 0x0,0x0,0x0,0x0,0x7,0x7,0x7},
        {0x0, 0x0,0x0,0x7,0x7,0x7,0x7,0x7},
        {0x0, 0x7,0x7,0x7,0x7,0x7,0x7,0x7}
    };
    for (unsigned int i=0; i <4; i++)
        lcd.setUDC(i, (char *) progress[i] );

    char microSign[] = {     0b01001,    0b01001,    0b01001,    0b01110,    0b01000,    0b01000, 0b00000,    0b00000};
    lcd.setUDC(4, (char*) microSign);
    char gChar[] = {    0b01111,    0b10001,    0b10001,    0b01111,    0b00001,    0b01110, 0b00000,    0b00000};
    lcd.setUDC(5, (char*) gChar);
    char m_pow_3[] = {    0b11000,    0b00100,    0b11000,    0b00100,    0b11000,    0b00000,    0b00000,    0b00000};
    lcd.setUDC(6, (char*) m_pow_3);
    char microSD[] = {0x1c,0x1c,0x1e,0x1c,0x1e,0x1f,0x1f};
    lcd.setUDC(7, (char*) microSD);
}
//#define USR_POWERDOWN    (0x104)
//int semihost_powerdown()
//{
//    uint32_t arg;
//    return __semihost(USR_POWERDOWN, &arg);
//}
void oneSecondTimeoutCallback(void)
{
    oneSecondTimeoutExpired = true;
}

int main()
{
//    semihost_powerdown();
    sdInit();
    thermometerInit();
    lcdInit();
//    eeprom.write(1, 0); // zero index couter
    unsigned short measureTime;
    char temp;
    eeprom.read(0, temp);
    eeprom.read(9, measureTime);
    measureTime <<= 8;
    measureTime |= temp;
    if (measureTime < 1) measureTime = 1;
    //
//    DS1307::Time_rtc time_now;
//    rtc.getTime(time_now);
    lcd.locate(4,1);
    //if (time_now.hour <= 9)
//        lcd.printf(" ");
//    else lcd.printf("%.1D", (time_now.hour/10));
//    lcd.printf("%.1D:%.2D:%.2D", time_now.hour%10, time_now.min, time_now.sec);
    lcd.printf("RTC FAIL!");
    wait(1.0);
    //
    lcd.locate(0,0);
    lcd.printf("karta: ");
    wait(.7);
    if (lcd_mount(sd.mount()) == 0) {      //Perform a write test
        pc.printf("Capacity: %.1fMB\r\n", sd.disk_sectors() / 2048.0);
        lcd.locate(6,0);
        lcd.printf("%5dMB",sd.disk_sectors() / 2048);
        wait(.3);
        lcd_unmount(sd.unmount());
    } else lcd.printf("!klops!");
    wait(1.2);
    lcdBacklightTimeout.attach(&lcdBacklightOff, 2.0f);
    lcd.cls();
//    pc.printf("hello!\r\npress button to start\r\n");
//    lcd.printf("gotowy. nacisnij");
    char index;
    wait(.1);
    if (eeprom.read(1, index) != 0) {
        eeprom.write(1, index+1);
    }
    else index = 254;
    std::string filename = intToString(index, 4);
    filename = filename.substr(1,3);
    filename +=  ".txt";
    filename = "pomiary.txt";

    while(true) {
//        while(button);
//        rtc.setLocalTime(); // RTC fail
        time_t current = time(NULL);
        printf("Time as a basic string = %s\r\n", ctime(&current));
        // measure dust level
//        dust.measure(measureTime);
        unsigned int pm1, pm2_5 = 0;
        unsigned long epoch_now = time(NULL);
        thermometer.convert_temperature(false);
//        while(!dust.getMeasurement(pm1, pm2_5)) {

        while( (signed short) (measureTime - (time(NULL) - epoch_now)) > 0) {

            oneSecondTimeoutExpired = false;
            oneSecondTimeout.attach(oneSecondTimeoutCallback, 1.0f);
            //Ensure that we only continue the program flow here after the timeout expired.
            lcd.locate(15,0);
            lcd.printf("S");
            while(!oneSecondTimeoutExpired) sleep();
            lcd.locate(15,0);
            lcd.printf(" ");
//            WakeUp::set_ms(2000);
//            lcd.locate(15,0);
//            lcd.printf("S");
            //Enter deepsleep, the program won't go beyond this point until it is woken up
//            deepsleep();
//            lcd.locate(15,0);
//            lcd.printf(" ");

            keypad_ADC::keys keyPress = keys.read(); // read
            if (keyPress != keypad_ADC::none) {
                lcdBacklightOn();
//                printf("key: %d\r\n", keyPress);
                if (keyPress == keypad_ADC::right) {
                    // edit measureTime
                    lcd.cls();
                    lcd.locate(8,0);
                    lcd.printf("%5d?", measureTime);
                    while (keys.read() != keypad_ADC::none);
                    keyPress = keys.read();
                    bool minutesNotSeconds = false;
                    while (keyPress != keypad_ADC::right) {
                        lcdBacklightOn();
                        printf("key: %d\r\n", keyPress);
                        switch (keyPress) {
                            case keypad_ADC::left:
                                minutesNotSeconds = ! minutesNotSeconds;
                                if(minutesNotSeconds) measureTime /= 60;
                                else measureTime *= 60;
                                lcd.locate(8,0);
                                lcd.printf("%5d", measureTime);
                                lcd.locate(10,1);
                                lcd.printf("%s", (minutesNotSeconds)?"min":"sek");
                                while (keys.read() != keypad_ADC::none);
                                break;
                            case keypad_ADC::up:
                                measureTime++;
                                if(minutesNotSeconds && measureTime > 1091) measureTime = 0;
                                lcd.cls();
                                lcd.locate(8,0);
                                lcd.printf("%5d", measureTime);
                                lcd.locate(10,1);
                                lcd.printf("%s", (minutesNotSeconds)?"min":"sek");
                                while (keys.read() != keypad_ADC::none);
                                break;
                            case keypad_ADC::down:
                                measureTime--;
                                if(minutesNotSeconds && measureTime > 1091) measureTime = 1091;
                                lcd.cls();
                                lcd.locate(8,0);
                                lcd.printf("%5d", measureTime);
                                lcd.locate(10,1);
                                lcd.printf("%s", (minutesNotSeconds)?"min":"sek");
                                while (keys.read() != keypad_ADC::none);
                                break;
                            default:
                                break;
                        }
                        keyPress = keys.read();
                    }
                    if (minutesNotSeconds) measureTime *= 60;
                    lcd.locate(13,0);
                    lcd.printf(" ");
                    eeprom.write(0, measureTime & 0xFF);
                    eeprom.write(9, measureTime >> 8);
                    epoch_now = time(NULL);
//                    dust.measure(measureTime);
                }
                while (keys.read() != keypad_ADC::none);
                lcdBacklightTimeout.attach(&lcdBacklightOff, 2.4);
            }
            // display counter
            lcd.locate(8,0);
            lcd.printf("%5d", measureTime - (time(NULL) - epoch_now));
            // print temperature
            lcd.locate(10,1);//
            lcd.printf("%s", floatToString(thermometer.temperature(), true).substr(0,4).c_str());
            lcd.putc(223); // degree sign
            lcd.putc('C');
        }
        lcd.locate(11,0);
        lcd.printf("  ");

        double result;
        const unsigned short resultCounter = 100;
        unsigned short counterAll = 0;
        unsigned short counterValid = 0;
        double sum = 0;

        while (counterAll++ < resultCounter) {
            dust.measure();
            float resultInterm;
            while(!dust.getVoltage(resultInterm))
                pc.printf("%d\r", counterAll);
            if (dust.convertVoltageToMg(resultInterm) > 0) {
                sum += resultInterm;
                counterValid++;
            }
            dust.tryTosetAoutAtNoDust(resultInterm);
            lcd.locate( ((counterAll/48)%2)?1:0 ,0);
            lcd.printf(" <pomiar> ", result);
        }
        pc.printf("%d/%d\r\n", counterAll, counterValid);
        result = sum/counterValid;
        result = dust.convertVoltageToMg(result);
        lcd.cls();
        lcd.locate(0,0);
        lcd.printf("%s\n \x04\x05/m\x06", floatToString(result, true, 3));
//        lcd.printf("pm1:  %2d%%", pm1);
//        lcd.locate(0,1);
//        lcd.printf("pm2,5:%2d%%", pm2_5);

        //Mount the filesystem
        if (lcd_mount(sd.mount()) == 0) {      //Perform a write test
            printf("\r\nWriting to SD card...");
            // get global index from eeprom
            FileHandle* file = sd.open(filename.c_str(), O_WRONLY | O_CREAT ); // | O_TRUNC
            if (file != NULL) { // write
                std::string toWrite = ctime(&current);
                toWrite = toWrite.substr(0, toWrite.size()-1); // cut last char of ctime, because it is \r
//                toWrite += ", pm1:, ";
//                toWrite += intToString(pm1).c_str();
//                toWrite += ", pm2.5:, ";
//                toWrite += intToString(pm2_5).c_str();
                toWrite += ", ";
                toWrite += floatToString(result, false, 4);
                toWrite += ", ";
                toWrite += floatToString(dust.getAoutAtNoDust(), false, 5);
                toWrite += ", ";
                toWrite += floatToString(thermometer.temperature(), false, 3);
                toWrite += ", C, ";
                toWrite += intToString(measureTime);
                toWrite += ", ";
                toWrite += intToString(index);
                toWrite += "\r\n";
//                pc.printf("tell: %d\tsize: %d\r\n", file->tell(), file->size());
                pc.printf("Writing to file \"%s\": %s\r\n", filename.c_str(), toWrite.c_str());
                file->seek(0, SEEK_END);
                write_with_progress_bar(file, toWrite.c_str(), toWrite.length(), showProgress);
//            file->write(toWrite.c_str(), toWrite.length()); // strlen(toWrite)
                file->close();
                printf("success writing!\r\n");
            } else {
                printf("failed to write!\r\n");
            }
            //Unmount the filesystem
            lcd_unmount(sd.unmount());

            pc.printf("all done.\r\n");
        } else { // unable to mount
            lcd.locate(0,0);
            lcd.printf("brak karty!");
            pc.printf("unable to mount! what a failure!\r\n");
        }
    }
}