project:brmhive:sketch
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revision | |||
project:brmhive:sketch [2010/11/02 04:09] – pasky | project:brmhive:sketch [2011/01/07 18:40] (current) – pasky | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | < | ||
+ | /* | ||
+ | * brmhive -- low-power sensor data logger | ||
+ | * | ||
+ | * You can force wake and trigger resonr re-read using the Alert button. | ||
+ | * | ||
+ | * The info led will blink happily when doing all the work and stay dark | ||
+ | * while asleep. Short blinks (up to a second) are o.k. Long blinks (more | ||
+ | * than 1.5 seconds) indicate error. | ||
+ | * | ||
+ | * In case of trouble, try pressing Reset. | ||
+ | */ | ||
+ | /* PCF8583 uses pasky' | ||
+ | /* Required modification of cores/ | ||
+ | * Change RX_BUFFER_SIZE to 2, head and tail to uint8_t. */ | ||
+ | |||
+ | /* TODO: Test w/o RX_BUFFER_SIZE hack. */ | ||
+ | /* TODO: Move taddrs[] to PROGMEM. */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | // #define DEBUG // Temperature bus scan. | ||
+ | |||
+ | #define NUM_FSR 4 // number of weight sensors | ||
+ | #define NUM_TEMP 55 // number of temperature sensors | ||
+ | |||
+ | /* Pin definitions. */ | ||
+ | #define auxPowerPin 6 /* Peripherial power transistor base. */ | ||
+ | #define alertPin 2 /* Alert button. */ | ||
+ | #define RTCPin 3 /* RTC alarm. */ | ||
+ | #define infoPin 7 /* Info LED. */ | ||
+ | |||
+ | #define oneWirePin 6 /* One-wire temperature sensors. */ | ||
+ | |||
+ | /* SD Card is occupying fixed pins - SPI. */ | ||
+ | /* RTC clock is occupying fixed pins - I2C. */ | ||
+ | |||
+ | void powerdown_pins(void) | ||
+ | { | ||
+ | /* Before going to sleep, bring pins to the most low-power state. */ | ||
+ | for (int i = 0; i <= 13; i++) { | ||
+ | if (i == alertPin || i == RTCPin || i == infoPin | ||
+ | || i == oneWirePin || i == auxPowerPin) | ||
+ | continue; | ||
+ | pinMode(i, | ||
+ | } | ||
+ | /* TODO: Stop reading analog pins? */ | ||
+ | } | ||
+ | |||
+ | |||
+ | void blink(int count, int length) | ||
+ | { | ||
+ | for (int i = 0; i < count; i++) { | ||
+ | digitalWrite(infoPin, | ||
+ | delay(length); | ||
+ | digitalWrite(infoPin, | ||
+ | delay(length); | ||
+ | } | ||
+ | digitalWrite(infoPin, | ||
+ | } | ||
+ | |||
+ | |||
+ | OneWire oneWire(oneWirePin); | ||
+ | PCF8583 RTC(0xA0); | ||
+ | |||
+ | |||
+ | Sd2Card card; | ||
+ | SdVolume volume; | ||
+ | SdFile root; | ||
+ | SdFile file; | ||
+ | |||
+ | void SD_init(void) | ||
+ | { | ||
+ | PRR &= ~(1<< | ||
+ | if (!card.init(4/ | ||
+ | Serial.println(" | ||
+ | Serial.println(card.errorCode(), | ||
+ | Serial.println(card.type(), | ||
+ | blink(2, 2000); | ||
+ | return; | ||
+ | } | ||
+ | if (!volume.init(& | ||
+ | Serial.println(" | ||
+ | blink(3, 2000); | ||
+ | return; | ||
+ | } | ||
+ | if (!root.openRoot(& | ||
+ | Serial.println(" | ||
+ | blink(4, 2000); | ||
+ | return; | ||
+ | } | ||
+ | if (!file.open(& | ||
+ | Serial.println(" | ||
+ | blink(5, 2000); | ||
+ | return; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void SD_done(void) | ||
+ | { | ||
+ | /* We must proceed in case of errors - open will fail if we did not | ||
+ | * close the file. */ | ||
+ | if (file.writeError) { | ||
+ | Serial.println(" | ||
+ | blink(2, 2000); | ||
+ | } | ||
+ | if (!file.close()) { | ||
+ | Serial.println(" | ||
+ | blink(3, 2000); | ||
+ | } | ||
+ | if (!root.close()) { | ||
+ | Serial.println(" | ||
+ | blink(4, 2000); | ||
+ | } | ||
+ | /* Turn off SPI. */ | ||
+ | SPCR &= ~(1 << SPE); | ||
+ | PRR |= (1<< | ||
+ | } | ||
+ | |||
+ | |||
+ | // 0: asleep; 1: reset/ | ||
+ | volatile char wake_cause = 1; | ||
+ | |||
+ | void alert_int(void) | ||
+ | { | ||
+ | wake_cause = 2; | ||
+ | } | ||
+ | |||
+ | void rtc_int(void) | ||
+ | { | ||
+ | wake_cause = 3; | ||
+ | /* Avoid infinite low; and we cannot ack_timer() from within. */ | ||
+ | detachInterrupt(RTCPin - 2); | ||
+ | } | ||
+ | |||
+ | |||
+ | void init_device(void) | ||
+ | { | ||
+ | pinMode(infoPin, | ||
+ | digitalWrite(infoPin, | ||
+ | delay(100); | ||
+ | |||
+ | Serial.begin(9600); | ||
+ | |||
+ | pinMode(auxPowerPin, | ||
+ | |||
+ | pinMode(alertPin, | ||
+ | pinMode(RTCPin, | ||
+ | |||
+ | digitalWrite(infoPin, | ||
+ | delay(200); | ||
+ | |||
+ | Serial.println(" | ||
+ | |||
+ | blink(5, 100); | ||
+ | delay(1000); | ||
+ | } | ||
+ | |||
+ | void sleep(void) | ||
+ | { | ||
+ | powerdown_pins(); | ||
+ | |||
+ | wake_cause = 0; | ||
+ | set_sleep_mode(SLEEP_MODE_PWR_DOWN); | ||
+ | sleep_enable(); | ||
+ | |||
+ | RTC.set_timer(RTC_TIMER_HOURS, | ||
+ | attachInterrupt(RTCPin - 2, rtc_int, LOW); | ||
+ | attachInterrupt(alertPin - 2, alert_int, LOW); | ||
+ | |||
+ | sleep_mode(); | ||
+ | |||
+ | detachInterrupt(alertPin - 2); | ||
+ | detachInterrupt(RTCPin - 2); | ||
+ | RTC.ack_timer(); | ||
+ | sleep_disable(); | ||
+ | } | ||
+ | |||
+ | |||
+ | char scratch[50]; | ||
+ | void append_scratch(void) | ||
+ | { | ||
+ | Serial.print(scratch); | ||
+ | file.print(scratch); | ||
+ | } | ||
+ | |||
+ | void get_timestamp(void) | ||
+ | { | ||
+ | RTC.get_time(); | ||
+ | |||
+ | snprintf(scratch, | ||
+ | " | ||
+ | (int)wake_cause, | ||
+ | RTC.hour, RTC.minute, RTC.second); | ||
+ | append_scratch(); | ||
+ | |||
+ | blink(2, 500); | ||
+ | } | ||
+ | |||
+ | void collect_weights(void) | ||
+ | { | ||
+ | for (int i = 0; i < NUM_FSR; i++) { | ||
+ | digitalWrite(infoPin, | ||
+ | uint16_t weight = analogRead(i); | ||
+ | delay(100); | ||
+ | digitalWrite(infoPin, | ||
+ | snprintf(scratch, | ||
+ | i, weight, i == NUM_FSR - 1 ? ' | ||
+ | append_scratch(); | ||
+ | delay(50); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | static const uint16_t PROGMEM taddrs[NUM_TEMP] = { | ||
+ | #if 0 | ||
+ | 0x65c1, // 2 | ||
+ | 0x1b7f, // 3 | ||
+ | 0x29a2, // 4 | ||
+ | #else | ||
+ | 0xD269, 0x9BC6, 0x65C1, 0x1B7F, 0x29A2, 0xABB8, 0x39A8, 0x3697, 0x3A8D, 0xB77B, 0xA8B2, 0xF4D0, 0xAEC9, 0x4CDB, 0x4BC8, 0x03A3, 0xECAB, 0xCA84, 0x9DC5, 0xBB6E, 0x5989, 0x61D7, 0xBD72, 0x3E86, 0x1FCA, 0x4F68, 0xBFC8, 0x3978, 0xBEA8, 0x9EDB, 0x2468, 0x9FCA, 0x467B, 0x0B7C, 0xA0D1, 0x568D, 0x5FCC, 0x0BDF, 0x76BB, 0x3FC7, 0x63D0, 0x9ABF, 0x91C4, 0x01B3, 0x5ABA, 0x9FAF, 0x9190, 0xCEB6, 0x506A, 0x98BE, 0x808A, 0x1F9E, 0x3A68, 0xBCCF, 0x0AAE | ||
+ | #endif | ||
+ | }; | ||
+ | |||
+ | void collect_temps(void) | ||
+ | { | ||
+ | byte addr[8]; | ||
+ | |||
+ | #ifdef DEBUG | ||
+ | while (oneWire.search(addr)) { | ||
+ | Serial.print(" | ||
+ | for (int i = 0; i < 8; i++) { | ||
+ | Serial.print(addr[i], | ||
+ | Serial.print(" | ||
+ | } | ||
+ | Serial.print(" | ||
+ | } | ||
+ | oneWire.reset_search(); | ||
+ | #endif | ||
+ | |||
+ | for (int i = 0; i < NUM_TEMP; i++) { | ||
+ | addr[0] = 0x28; | ||
+ | addr[1] = taddrs[i] >> 8; | ||
+ | addr[2] = taddrs[i] & 0xff; | ||
+ | addr[3] = 0x7d; | ||
+ | addr[4] = 2; | ||
+ | addr[5] = 0; | ||
+ | addr[6] = 0; | ||
+ | addr[7] = OneWire:: | ||
+ | |||
+ | digitalWrite(infoPin, | ||
+ | oneWire.reset(); | ||
+ | oneWire.select(addr); | ||
+ | oneWire.write(0x44); | ||
+ | delay(800); | ||
+ | |||
+ | int present = oneWire.reset(); | ||
+ | if (present) { | ||
+ | oneWire.select(addr); | ||
+ | oneWire.write(0xbe); | ||
+ | byte data[12]; | ||
+ | for (int j = 0; j < 9; j++) | ||
+ | data[j] = oneWire.read(); | ||
+ | digitalWrite(infoPin, | ||
+ | |||
+ | int16_t temp = (data[1] << 8) | data[0]; | ||
+ | /* data is in 0.0625 increments, fixed-point *100. */ | ||
+ | temp = temp * 6 + temp / 4; | ||
+ | snprintf(scratch, | ||
+ | i, temp, i == NUM_TEMP - 1 ? ' | ||
+ | append_scratch(); | ||
+ | } else { | ||
+ | blink(3, 1500); | ||
+ | delay(1500); | ||
+ | snprintf(scratch, | ||
+ | i, i == NUM_TEMP - 1 ? ' | ||
+ | append_scratch(); | ||
+ | } | ||
+ | delay(100); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void collect_data(void) | ||
+ | { | ||
+ | digitalWrite(auxPowerPin, | ||
+ | SD_init(); | ||
+ | |||
+ | get_timestamp(); | ||
+ | |||
+ | collect_weights(); | ||
+ | |||
+ | blink(2, 500); | ||
+ | delay(500); | ||
+ | |||
+ | collect_temps(); | ||
+ | |||
+ | strcpy(scratch, | ||
+ | append_scratch(); | ||
+ | |||
+ | SD_done(); | ||
+ | digitalWrite(auxPowerPin, | ||
+ | blink(3, 100); | ||
+ | |||
+ | Serial.println(" | ||
+ | } | ||
+ | |||
+ | |||
+ | /* This is the one-time routine called when we are turned on. It should | ||
+ | * set up the whole device and go through the drill. */ | ||
+ | void setup(void) | ||
+ | { | ||
+ | init_device(); | ||
+ | collect_data(); | ||
+ | sleep(); | ||
+ | } | ||
+ | |||
+ | /* This is the repeat routine called all the time after setup(). | ||
+ | * We sleep() between each two calls to it. */ | ||
+ | void loop(void) | ||
+ | { | ||
+ | if (wake_cause == 1 || wake_cause == 2 || wake_cause == 3) { | ||
+ | blink(2, 200); | ||
+ | delay(1000); | ||
+ | collect_data(); | ||
+ | sleep(); | ||
+ | } else { | ||
+ | /* Something wrong happenned. Blink around a bit and try to go | ||
+ | * to sleep. */ | ||
+ | snprintf(scratch, | ||
+ | wake_cause); | ||
+ | append_scratch(); | ||
+ | blink(3, 3000); | ||
+ | sleep(); | ||
+ | } | ||
+ | } | ||
+ | </ |