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.
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(¤t)); // 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(¤t); 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"); } } }