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
Revision:
0:41f9e684d87e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Jan 31 12:13:30 2018 +0000
@@ -0,0 +1,388 @@
+#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");
+        }
+    }
+}