/* Copyright (C) 2014 olie.xdev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see */ #include #include #include #include #include #include #define SEG_1_1 4 #define SEG_1_2 5 #define SEG_2_1 6 #define SEG_2_2 7 #define SEG_3_1 8 #define SEG_3_2 9 #define SEG_4_1 10 #define SEG_4_2 11 #define UP 12 #define C0 A0 #define C1 A1 #define C2 A2 #define C3 A3 #define WAKEUP_PIN 3 #define EXT_SWITCH_PIN 13 #define MAX_SAMPLE_SIZE 6 #define MAX_NO_ACTIVITY_CYCLES 32 I2C_eeprom eeprom(0x50); char port_control; char port_digital_pinA; char port_digital_pinB; int control_bit[4]; int seg_raw_1_1[4]; int seg_raw_1_2[4]; int seg_raw_2_1[4]; int seg_raw_2_2[4]; int seg_raw_3_1[4]; int seg_raw_3_2[4]; int seg_raw_4_1[4]; int seg_raw_4_2[4]; char seg_value_1; char seg_value_2; char seg_value_3; char seg_value_4; RunningMedian seg_samples_1; RunningMedian seg_samples_2; RunningMedian seg_samples_3; RunningMedian seg_samples_4; int sample_count = 0; int no_activity_cycles = 0; volatile boolean sleep_state = true; volatile boolean repeat_state = false; int measured_weight = -1; int measured_fat = -1; int measured_water = -1; int measured_muscle = -1; typedef struct scale_data{ int year; byte month; byte day; byte hour; byte minute; int weight; int fat; int water; int muscle; int checksum; } __attribute__ ((packed)); // avoiding byte padding in this struct. Important for continuous writing/reading to/from eeprom! void interrupt_handler() { sleep_state = false; } void setup() { Serial.begin(9600); pinMode(SEG_1_1, INPUT); pinMode(SEG_1_2, INPUT); pinMode(SEG_2_1, INPUT); pinMode(SEG_2_2, INPUT); pinMode(SEG_3_1, INPUT); pinMode(SEG_3_2, INPUT); pinMode(SEG_4_1, INPUT); pinMode(SEG_4_2, INPUT); pinMode(UP, OUTPUT); pinMode(C0, INPUT); pinMode(C1, INPUT); pinMode(C2, INPUT); pinMode(C3, INPUT); pinMode(WAKEUP_PIN, INPUT); pinMode(EXT_SWITCH_PIN, OUTPUT); digitalWrite(EXT_SWITCH_PIN, HIGH); } void set_seg_raw(int cycle_n) { seg_raw_1_1[cycle_n] = (port_digital_pinA & (1 << 4)) ? 1 : 0; seg_raw_1_2[cycle_n] = (port_digital_pinA & (1 << 5)) ? 1 : 0; seg_raw_2_1[cycle_n] = (port_digital_pinA & (1 << 6)) ? 1 : 0; seg_raw_2_2[cycle_n] = (port_digital_pinA & (1 << 7)) ? 1 : 0; seg_raw_3_1[cycle_n] = (port_digital_pinB & (1 << 0)) ? 1 : 0; seg_raw_3_2[cycle_n] = (port_digital_pinB & (1 << 1)) ? 1 : 0; seg_raw_4_1[cycle_n] = (port_digital_pinB & (1 << 2)) ? 1 : 0; seg_raw_4_2[cycle_n] = (port_digital_pinB & (1 << 3)) ? 1 : 0; } char decode_seg(int seg_x[4], int seg_y[4]) { boolean b = seg_x[0]; boolean c = seg_x[1]; boolean e = seg_x[2]; boolean f = seg_x[3]; boolean a = seg_y[0]; boolean d = seg_y[1]; boolean g = seg_y[2]; boolean x = seg_y[3]; if (!e && !c && !b && !f && !g && !d && !a) return ' '; if (e && !c && b && f && g && d && a) return '0'; if (e && !c && b && !f && !g && !d && !a) return '1'; if (!e && c && b && f && g && !d && a) return '2'; if (e && c && b && f && !g && !d && a) return '3'; if (e && c && b && !f && !g && d && !a) return '4'; if (e && c && !b && f && !g && d && a) return '5'; if (e && c && !b && f && g && d && a) return '6'; if (e && !c && b && !f && !g && !d && a) return '7'; if (e && c && b && f && g && d && a) return '8'; if (e && c && b && f && !g && d && a) return '9'; if (!e && c && !b && !f && !g && !d && !a) return '-'; if (!e && c && b && !f && g && d && a) return 'P'; if (e && !c && b && !f && g && d && a) return 'M'; if (!e && c && !b && f && g && d && a) return 'E'; if (!e && c && !b && !f && g && d && a) return 'F'; if (e && c && b && !f && g && d && !a) return 'H'; return -1; } void before_sleep_event() { Serial.println("$I$ going to sleep in 3 seconds!"); if (measured_weight != -1 && measured_fat != -1 && measured_water != -1 && measured_muscle != -1) { write_scale_data(measured_weight, measured_fat, measured_water, measured_muscle); delay(100); } send_scale_data(); delay(3000); digitalWrite(EXT_SWITCH_PIN, LOW); } void after_sleep_event() { digitalWrite(EXT_SWITCH_PIN, HIGH); measured_weight = -1; measured_fat = -1; measured_water = -1; measured_muscle = -1; delay(4000); digitalWrite(UP, HIGH); delay(500); digitalWrite(UP, LOW); setSyncProvider(RTC.get); if (timeStatus() != timeSet) { Serial.println("$E$ Can't sync to RTC clock!"); } else { Serial.println("$I$ Successful sync to RTC clock"); } Serial.print("$I$ Time: "); Serial.print(hour()); Serial.write(':'); Serial.print(minute()); Serial.write(':'); Serial.print(second()); Serial.print(" Date: "); Serial.print(day()); Serial.write('/'); Serial.print(month()); Serial.write('/'); Serial.print(year()); Serial.println(); Serial.println("$I$ openScale MCU ready!"); } void check_display_activity() { if (no_activity_cycles > MAX_NO_ACTIVITY_CYCLES) { sleep_state = true; no_activity_cycles = 0; } if (sleep_state == true) { before_sleep_event(); // Allow wake up pin to trigger interrupt on rising edge. attachInterrupt(1, interrupt_handler, RISING); // Enter power down state with ADC and BOD module disabled. // Wake up when wake up pin is rising. LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); // Disable external pin interrupt on wake up pin. detachInterrupt(1); after_sleep_event(); } } int calc_checksum(struct scale_data* wdata) { int checksum = 0; checksum ^= wdata->year; checksum ^= wdata->month; checksum ^= wdata->day; checksum ^= wdata->hour; checksum ^= wdata->minute; checksum ^= (int)((float)wdata->weight / 10.0f); checksum ^= (int)((float)wdata->fat / 10.0f); checksum ^= (int)((float)wdata->water / 10.0f); checksum ^= (int)((float)wdata->muscle / 10.0f); return checksum; } void write_scale_data(int weight, int fat, int water, int muscle) { int data_size = 0; struct scale_data wdata; eeprom.readBlock(0, (uint8_t*)&data_size, sizeof(data_size)); wdata.year = year(); wdata.month = month(); wdata.day = day(); wdata.hour = hour(); wdata.minute = minute(); wdata.weight = weight; wdata.fat = fat; wdata.water = water; wdata.muscle = muscle; wdata.checksum = calc_checksum(&wdata); if (eeprom.writeBlock(sizeof(data_size)+data_size*sizeof(wdata), (uint8_t*)&wdata, sizeof(wdata)) != 0) { Serial.println("$E$ Error writing data to eeprom"); } delay(100); data_size++; if (eeprom.writeBlock(0, (uint8_t*)&data_size, sizeof(data_size)) != 0) { Serial.println("$E$ Error writing data to eeprom"); } } void send_scale_data() { int data_size = 0; struct scale_data wdata; eeprom.readBlock(0, (uint8_t*)&data_size, sizeof(data_size)); Serial.print("$S$"); Serial.println(data_size); for (int i=0; i < data_size; i++) { eeprom.readBlock(sizeof(data_size)+i*sizeof(wdata), (uint8_t*)&wdata, sizeof(wdata)); if (wdata.checksum != calc_checksum(&wdata)) { Serial.print("$E$ Wrong Checksum for data "); Serial.print(i); Serial.println(); } Serial.print("$D$"); Serial.print(i); Serial.print(','); Serial.print(wdata.year); Serial.print(','); Serial.print(wdata.month); Serial.print(','); Serial.print(wdata.day); Serial.print(','); Serial.print(wdata.hour); Serial.print(','); Serial.print(wdata.minute); Serial.print(','); Serial.print((float)wdata.weight / 10.0f); Serial.print(','); Serial.print((float)wdata.fat / 10.0f); Serial.print(','); Serial.print((float)wdata.water / 10.0f); Serial.print(','); Serial.print((float)wdata.muscle / 10.0f); Serial.print(','); Serial.print(wdata.checksum); Serial.print('\n'); } } void clear_scale_data() { int data_size = 0; eeprom.writeBlock(0, (uint8_t*)&data_size, sizeof(data_size)); } void loop() { check_display_activity(); port_control = PINC; port_digital_pinA = PIND; port_digital_pinB = PINB; control_bit[0] = (port_control & (1 << 0)) ? 1 : 0; control_bit[1] = (port_control & (1 << 1)) ? 1 : 0; control_bit[2] = (port_control & (1 << 2)) ? 1 : 0; control_bit[3] = (port_control & (1 << 3)) ? 1 : 0; if (control_bit[0] == LOW && control_bit[1] == HIGH && control_bit[2] == HIGH && control_bit[3] == HIGH) { set_seg_raw(0); } else if (control_bit[0] == HIGH && control_bit[1] == LOW && control_bit[2] == HIGH && control_bit[3] == HIGH) { set_seg_raw(1); } else if (control_bit[0] == HIGH && control_bit[1] == HIGH && control_bit[2] == LOW && control_bit[3] == HIGH) { set_seg_raw(2); } else if (control_bit[0] == HIGH && control_bit[1] == HIGH && control_bit[2] == HIGH && control_bit[3] == LOW) { set_seg_raw(3); } else if (control_bit[0] == HIGH && control_bit[1] == HIGH && control_bit[2] == HIGH && control_bit[3] == HIGH) { no_activity_cycles++; } seg_value_1 = decode_seg(seg_raw_1_1, seg_raw_1_2); seg_value_2 = decode_seg(seg_raw_2_1, seg_raw_2_2); seg_value_3 = decode_seg(seg_raw_3_1, seg_raw_3_2); seg_value_4 = decode_seg(seg_raw_4_1, seg_raw_4_2); if (seg_value_1 != -1 && seg_value_2 != -1 && seg_value_3 != -1 && seg_value_4 != -1) { seg_samples_1.add(seg_value_1); seg_samples_2.add(seg_value_2); seg_samples_3.add(seg_value_3); seg_samples_4.add(seg_value_4); sample_count++; } if (sample_count > MAX_SAMPLE_SIZE) { seg_samples_1.getMedian(seg_value_1); seg_samples_2.getMedian(seg_value_2); seg_samples_3.getMedian(seg_value_3); seg_samples_4.getMedian(seg_value_4); if (seg_value_4 == ' ') { measured_weight = char_to_int(seg_value_1) + char_to_int(seg_value_2)*10 + char_to_int(seg_value_3)*100; } if (seg_value_4 == 'F') { measured_fat = char_to_int(seg_value_1) + char_to_int(seg_value_2)*10 + char_to_int(seg_value_3)*100; } if (seg_value_4 == 'H') { measured_water = char_to_int(seg_value_1) + char_to_int(seg_value_2)*10 + char_to_int(seg_value_3)*100; } if (seg_value_4 == 'M') { measured_muscle = char_to_int(seg_value_1) + char_to_int(seg_value_2)*10 + char_to_int(seg_value_3)*100; } sample_count = 0; } delay(10); if (Serial.available() > 0) { char command = Serial.read(); switch(command) { case '0': Serial.println("$I$ openScale MCU Version 1.0"); break; case '1': Serial.println("$I$ Sending scale data!"); send_scale_data(); break; case '9': clear_scale_data(); Serial.println("$I$ Scale data cleared!"); break; } } } int char_to_int(char c) { if (c == ' ') return 0; return (c - '0'); } void print_debug_output() { Serial.print("Debug Ausgabe\n"); Serial.print("-----------------------------------\n"); Serial.print("\nSeg 1\n"); for (int i=0; i<4; i++) { Serial.print(seg_raw_1_1[i]); } Serial.print("\n"); for (int i=0; i<4; i++) { Serial.print(seg_raw_1_2[i]); } Serial.print("\nSeg 2\n"); for (int i=0; i<4; i++) { Serial.print(seg_raw_2_1[i]); } Serial.print("\n"); for (int i=0; i<4; i++) { Serial.print(seg_raw_2_2[i]); } Serial.print("\nSeg 3\n"); for (int i=0; i<4; i++) { Serial.print(seg_raw_3_1[i]); } Serial.print("\n"); for (int i=0; i<4; i++) { Serial.print(seg_raw_3_2[i]); } Serial.print("\nSeg 4\n"); for (int i=0; i<4; i++) { Serial.print(seg_raw_4_1[i]); } Serial.print("\n"); for (int i=0; i<4; i++) { Serial.print(seg_raw_4_2[i]); } Serial.print("\n-----------------------------------\n"); }