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
Revision 0:41f9e684d87e, committed 2018-01-31
- Comitter:
- amateusz
- Date:
- Wed Jan 31 12:13:30 2018 +0000
- Commit message:
- last working version the program.; RTC broke before deployment, so it is non functional in program
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DS1307.lib Wed Jan 31 12:13:30 2018 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/amateusz/code/DS1307/#8c0c306cee03
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DS1820.lib Wed Jan 31 12:13:30 2018 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/amateusz/code/DS1820/#1020ba9ab5d2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DSM501.lib Wed Jan 31 12:13:30 2018 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/amateusz/code/DSM501/#2501a0385c68
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GP2Y1010AU0F.lib Wed Jan 31 12:13:30 2018 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/amateusz/code/GP2Y1010AU0F/#f4e3a6f03b9c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/I2CEeprom.lib Wed Jan 31 12:13:30 2018 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/amateusz/code/I2CEeprom/#b7877755371e
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDFileSystem.lib Wed Jan 31 12:13:30 2018 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/amateusz/code/SDFileSystem/#aac017a027c0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TextLCD.lib Wed Jan 31 12:13:30 2018 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/amateusz/code/TextLCD_YWROBOT/#6c5cf2a0d238
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WakeUp.lib Wed Jan 31 12:13:30 2018 +0000 @@ -0,0 +1,1 @@ +http://developer.mbed.org/users/Sissors/code/WakeUp/#65c04a02ad45
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/keypad_ADC.lib Wed Jan 31 12:13:30 2018 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/amateusz/code/keypad_ADC/#9b7cc7ebdffd
--- /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(¤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"); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Wed Jan 31 12:13:30 2018 +0000 @@ -0,0 +1,1 @@ +https://mbed.org/users/mbed_official/code/mbed/builds/a330f0fddbec \ No newline at end of file