Mercurial > templog
changeset 212:fbdd16b45b21
merge
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Thu, 26 Jun 2014 23:03:32 +0800 |
parents | 59379b2bd056 (current diff) 257056dcd8b5 (diff) |
children | 7caa5f3ec12c |
files | Makefile crc8.c crc8.h ds18x20.h main.c onewire.c onewire.h server/config.py server/dump.py server/ts.py server/utils.py simple_ds18b20.c simple_ds18b20.h |
diffstat | 27 files changed, 2725 insertions(+), 2700 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Thu Jun 26 23:02:23 2014 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,112 +0,0 @@ -# Name: Makefile -# Author: <insert your name here> -# Copyright: <insert your copyright message here> -# License: <insert your license reference here> - -# This is a prototype Makefile. Modify it according to your needs. -# You should at least check the settings for -# DEVICE ....... The AVR device you compile for -# CLOCK ........ Target AVR clock rate in Hertz -# OBJECTS ...... The object files created from your source files. This list is -# usually the same as the list of source files with suffix ".o". -# PROGRAMMER ... Options to avrdude which define the hardware you use for -# uploading to the AVR and the interface where this hardware -# is connected. We recommend that you leave it undefined and -# add settings like this to your ~/.avrduderc file: -# default_programmer = "stk500v2" -# default_serial = "avrdoper" -# FUSES ........ Parameters for avrdude to flash the fuses appropriately. - -DEVICE = atmega328 -PROGDEVICE = atmega328p -CLOCK = 2000000 -PROGRAMMER = #-c stk500v2 -P avrdoper -PROGRAMMER = -c stk500 -P ~/dev/stk500 -p $(PROGDEVICE) -B 2 -SOURCE_1WIRE = onewire.c simple_ds18b20.c crc8.c -SOURCE = main.c -SOURCE += $(SOURCE_1WIRE) -LIBS = -lm - -# default but 2mhz -FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x62:m - -# ATMega8 fuse bits used above (fuse bits for other devices are different!): -# Example for 8 MHz internal oscillator -# Fuse high byte: -# 0xd9 = 1 1 0 1 1 0 0 1 <-- BOOTRST (boot reset vector at 0x0000) -# ^ ^ ^ ^ ^ ^ ^------ BOOTSZ0 -# | | | | | +-------- BOOTSZ1 -# | | | | +---------- EESAVE (set to 0 to preserve EEPROM over chip erase) -# | | | +-------------- WDTON -# | | +---------------- SPIEN (if set to 1, serial programming is disabled) -# | +------------------ DWEN -# +-------------------- RSTDISBL (if set to 0, RESET pin is disabled) -# Fuse low byte: -# 0x62 = 0 1 1 0 0 0 1 0 -# ^ ^ \ / \--+--/ -# | | | +------- CKSEL 3..0 (8M internal RC) -# | | +--------------- SUT 1..0 (slowly rising power) -# | +------------------ CKOUT -# +-------------------- CLKDIV8 -# -# For computing fuse byte values for other devices and options see -# the fuse bit calculator at http://www.engbedded.com/fusecalc/ - - -# Tune the lines below only if you know what you are doing: - -AVRDUDE = avrdude $(PROGRAMMER) -COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -g -std=c99 -mcall-prologues -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--relax --combine -fwhole-program -Wl,-u,vfprintf -lprintf_flt -lm - -# symbolic targets: -all: main.hex - -.c.o: - $(COMPILE) -c $< -o $@ - -.S.o: - $(COMPILE) -x assembler-with-cpp -c $< -o $@ -# "-x assembler-with-cpp" should not be necessary since this is the default -# file type for the .S (with capital S) extension. However, upper case -# characters are not always preserved on Windows. To ensure WinAVR -# compatibility define the file type manually. - -.c.s: - $(COMPILE) -S $< -o $@ - -flash: all - $(AVRDUDE) -U flash:w:main.hex:i - -checkprog: - $(AVRDUDE) -v - -fuse: - $(AVRDUDE) $(FUSES) - -# Xcode uses the Makefile targets "", "clean" and "install" -install: flash - -# if you use a bootloader, change the command below appropriately: -load: all - bootloadHID main.hex - -clean: - rm -f main.hex main.elf $(OBJECTS) - -# file targets: -main.elf: $(SOURCE) - $(COMPILE) -o main.elf $(SOURCE) $(LIBS) - -main.hex: main.elf - rm -f main.hex - avr-objcopy -j .text -j .data -O ihex main.elf main.hex - avr-size --format=avr --mcu=$(DEVICE) main.elf -# If you have an EEPROM section, you must also create a hex file for the -# EEPROM and add it to the "flash" target. - -# Targets for code debugging and analysis: -disasm: main.elf - avr-objdump -d main.elf - -cpp: - $(COMPILE) -E main.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Thu Jun 26 23:03:32 2014 +0800 @@ -0,0 +1,25 @@ +Wort fermentation control. + +py/ code runs on a raspberry pi with ds18b20 sensors. +Dependencies can be installed with pip from requirements.txt + +This is implemented using gevent so the fridge, sensors, uploader +etc are each written as independent tasks. + +--- + +web/ code is a Bottle web app for showing graphs and also +controlling the temperature from a phone-optimised UI + +https://evil.ucc.asn.au/~matt/templog/ is live + +--- + +old/ is the previous version that ran on an avr talking over a serial bluetooth device +with a separate internet-connected router relaying to the web interface. + + +Matt Johnston [email protected] + +This code may be freely used, distributed, relicensed, and modified for any purpose.
--- a/crc8.c Thu Jun 26 23:02:23 2014 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/* please read copyright-notice at EOF */ - -#include <stdint.h> - -#define CRC8INIT 0x00 -#define CRC8POLY 0x18 //0X18 = X^8+X^5+X^4+X^0 - -uint8_t crc8( uint8_t *data, uint16_t number_of_bytes_in_data ) -{ - uint8_t crc; - uint16_t loop_count; - uint8_t bit_counter; - uint8_t b; - uint8_t feedback_bit; - - crc = CRC8INIT; - - for (loop_count = 0; loop_count != number_of_bytes_in_data; loop_count++) - { - b = data[loop_count]; - - bit_counter = 8; - do { - feedback_bit = (crc ^ b) & 0x01; - - if ( feedback_bit == 0x01 ) { - crc = crc ^ CRC8POLY; - } - crc = (crc >> 1) & 0x7F; - if ( feedback_bit == 0x01 ) { - crc = crc | 0x80; - } - - b = b >> 1; - bit_counter--; - - } while (bit_counter > 0); - } - - return crc; -} - -/* -This code is from Colin O'Flynn - Copyright (c) 2002 -only minor changes by M.Thomas 9/2004 - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/
--- a/crc8.h Thu Jun 26 23:02:23 2014 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -#ifndef CRC8_H_ -#define CRC8_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdint.h> - -uint8_t crc8( uint8_t* data, uint16_t number_of_bytes_in_data ); - -#ifdef __cplusplus -} -#endif - -#endif - -/* -This is based on code from : - -Copyright (c) 2002 Colin O'Flynn - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ -
--- a/ds18x20.h Thu Jun 26 23:02:23 2014 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,140 +0,0 @@ -#ifndef DS18X20_H_ -#define DS18X20_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdlib.h> -#include <stdint.h> - -// DS18x20 EERPROM support disabled(0) or enabled(1) : -#define DS18X20_EEPROMSUPPORT 1 -// decicelsius functions disabled(0) or enabled(1): -#define DS18X20_DECICELSIUS 1 -// max. resolution functions disabled(0) or enabled(1): -#define DS18X20_MAX_RESOLUTION 1 -// extended output via UART disabled(0) or enabled(1) : -#define DS18X20_VERBOSE 1 - - -/* return values */ -#define DS18X20_OK 0x00 -#define DS18X20_ERROR 0x01 -#define DS18X20_START_FAIL 0x02 -#define DS18X20_ERROR_CRC 0x03 - -#define DS18X20_INVALID_DECICELSIUS 2000 - -#define DS18X20_POWER_PARASITE 0x00 -#define DS18X20_POWER_EXTERN 0x01 - -#define DS18X20_CONVERSION_DONE 0x00 -#define DS18X20_CONVERTING 0x01 - -/* DS18X20 specific values (see datasheet) */ -#define DS18S20_FAMILY_CODE 0x10 -#define DS18B20_FAMILY_CODE 0x28 -#define DS1822_FAMILY_CODE 0x22 - -#define DS18X20_CONVERT_T 0x44 -#define DS18X20_READ 0xBE -#define DS18X20_WRITE 0x4E -#define DS18X20_EE_WRITE 0x48 -#define DS18X20_EE_RECALL 0xB8 -#define DS18X20_READ_POWER_SUPPLY 0xB4 - -#define DS18B20_CONF_REG 4 -#define DS18B20_9_BIT 0 -#define DS18B20_10_BIT (1<<5) -#define DS18B20_11_BIT (1<<6) -#define DS18B20_12_BIT ((1<<6)|(1<<5)) -#define DS18B20_RES_MASK ((1<<6)|(1<<5)) - -// undefined bits in LSB if 18B20 != 12bit -#define DS18B20_9_BIT_UNDF ((1<<0)|(1<<1)|(1<<2)) -#define DS18B20_10_BIT_UNDF ((1<<0)|(1<<1)) -#define DS18B20_11_BIT_UNDF ((1<<0)) -#define DS18B20_12_BIT_UNDF 0 - -// conversion times in milliseconds -#define DS18B20_TCONV_12BIT 750 -#define DS18B20_TCONV_11BIT DS18B20_TCONV_12_BIT/2 -#define DS18B20_TCONV_10BIT DS18B20_TCONV_12_BIT/4 -#define DS18B20_TCONV_9BIT DS18B20_TCONV_12_BIT/8 -#define DS18S20_TCONV DS18B20_TCONV_12_BIT - -// constant to convert the fraction bits to cel*(10^-4) -#define DS18X20_FRACCONV 625 - -// scratchpad size in bytes -#define DS18X20_SP_SIZE 9 - -// DS18X20 EEPROM-Support -#define DS18X20_WRITE_SCRATCHPAD 0x4E -#define DS18X20_COPY_SCRATCHPAD 0x48 -#define DS18X20_RECALL_E2 0xB8 -#define DS18X20_COPYSP_DELAY 10 /* ms */ -#define DS18X20_TH_REG 2 -#define DS18X20_TL_REG 3 - -#define DS18X20_DECIMAL_CHAR '.' - - -extern uint8_t DS18X20_find_sensor(uint8_t *diff, - uint8_t id[]); -extern uint8_t DS18X20_get_power_status(uint8_t id[]); -extern uint8_t DS18X20_start_meas( uint8_t with_external, - uint8_t id[]); -// returns 1 if conversion is in progress, 0 if finished -// not available when parasite powered -extern uint8_t DS18X20_conversion_in_progress(void); - - -#if DS18X20_DECICELSIUS -extern uint8_t DS18X20_read_decicelsius( uint8_t id[], - int16_t *decicelsius ); -extern uint8_t DS18X20_read_decicelsius_single( uint8_t familycode, - int16_t *decicelsius ); -extern uint8_t DS18X20_format_from_decicelsius( int16_t decicelsius, - char s[], uint8_t n); -#endif /* DS18X20_DECICELSIUS */ - - -#if DS18X20_MAX_RESOLUTION -// temperature unit for max. resolution is �C * 10e-4 -// examples: -250625 -> -25.0625�C, 1250000 -> 125.0000 �C -extern uint8_t DS18X20_read_maxres( uint8_t id[], - int32_t *temperaturevalue ); -extern uint8_t DS18X20_read_maxres_single( uint8_t familycode, - int32_t *temperaturevalue ); -extern uint8_t DS18X20_format_from_maxres( int32_t temperaturevalue, - char s[], uint8_t n); -#endif /* DS18X20_MAX_RESOLUTION */ - - -#if DS18X20_EEPROMSUPPORT -// write th, tl and config-register to scratchpad (config ignored on DS18S20) -uint8_t DS18X20_write_scratchpad( uint8_t id[], - uint8_t th, uint8_t tl, uint8_t conf); -// read scratchpad into array SP -uint8_t DS18X20_read_scratchpad( uint8_t id[], uint8_t sp[], uint8_t n); -// copy values int scratchpad into DS18x20 eeprom -uint8_t DS18X20_scratchpad_to_eeprom( uint8_t with_power_extern, - uint8_t id[] ); -// copy values from DS18x20 eeprom into scratchpad -uint8_t DS18X20_eeprom_to_scratchpad( uint8_t id[] ); -#endif /* DS18X20_EEPROMSUPPORT */ - - -#if DS18X20_VERBOSE -extern void DS18X20_show_id_uart( uint8_t *id, size_t n ); -extern uint8_t DS18X20_read_meas_all_verbose( void ); -#endif /* DS18X20_VERBOSE */ - - -#ifdef __cplusplus -} -#endif - -#endif
--- a/main.c Thu Jun 26 23:02:23 2014 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1200 +0,0 @@ -#include <stdio.h> -#include <string.h> -#include <stddef.h> -#include <stdbool.h> -#include <stdlib.h> -#include <avr/io.h> -#include <avr/interrupt.h> -#include <avr/sleep.h> -#include <util/delay.h> -#include <avr/pgmspace.h> -#include <avr/eeprom.h> -#include <avr/wdt.h> -#include <util/atomic.h> -#include <util/crc16.h> - -#include "simple_ds18b20.h" -#include "onewire.h" - -// configuration params -// - measurement interval -// - transmit interval -// - bluetooth params -// - number of sensors (and range?) - -#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) -#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) - -// TICK should be 8 or less (8 untested). all timers need -// to be a multiple. - -#define TICK 6 -// we have 1024 prescaler, 32768 crystal. -#define SLEEP_COMPARE (32*TICK-1) - -#define VALUE_NOSENSOR 0x07D0 // 125 degrees -#define VALUE_BROKEN 0x07D1 // 125.0625 - -#define OVERSHOOT_MAX_DIV 1800.0 // 30 mins -#define WORT_INVALID_TIME 900 // 15 mins -// fridge min/max are only used if the wort sensor is invalid -#define FRIDGE_AIR_MIN_RANGE 40 // 4º -#define FRIDGE_AIR_MAX_RANGE 40 // 4º - -#define BAUD 19200 -#define UBRR ((F_CPU)/8/(BAUD)-1) - -#define PORT_LED PORTC -#define DDR_LED DDRC -#define PIN_LED PC4 - -#define PORT_SHDN PORTD -#define DDR_SHDN DDRD -#define PIN_SHDN PD7 - -#define PORT_FRIDGE PORTD -#define DDR_FRIDGE DDRD -#define PIN_FRIDGE PD6 - -// total amount of 16bit values available for measurements. -// adjust emperically, be sure to allow enough stack space too -#define TOTAL_MEASUREMENTS 800 - -// each sensor slot uses 8 bytes -#define MAX_SENSORS 6 - -// fixed at 8, have a shorter name -#define ID_LEN OW_ROMCODE_SIZE - -// #define HAVE_UART_ECHO - -// stores a value of clock_epoch combined with the remainder of TCNT2, -// for 1/32 second accuracy -struct epoch_ticks -{ - uint32_t ticks; - // remainder - uint8_t rem; -}; - -// eeprom-settable parameters. all timeouts should -// be a multiple of TICK (6 seconds probably) -static uint16_t measure_wake = 61; // not a divisor of comms_wake -static uint16_t comms_wake = 600; -static uint8_t wake_secs = 30; -// decidegrees -static int16_t fridge_setpoint = 180; // 18.0ºC -static uint16_t fridge_difference = 3; // 0.3ºC -static uint16_t fridge_delay = 600; // seconds - -static uint16_t overshoot_delay = 720; // 12 mins -static uint8_t overshoot_factor = 10; // 1.0ºC - -// ---- Atomic guards required accessing these variables -// clock_epoch in seconds -static uint32_t clock_epoch; -static uint16_t comms_count; -static uint16_t measure_count; -// ---- End atomic guards required - -static uint16_t n_measurements; - -// calculated at startup as TOTAL_MEASUREMENTS/n_sensors -static uint16_t max_measurements; - -static uint16_t measurements[TOTAL_MEASUREMENTS]; - -static struct epoch_ticks first_measurement_clock; -// last_measurement_clock is redundant but checks that we're not missing -// samples -static struct epoch_ticks last_measurement_clock; -static struct epoch_ticks last_comms_clock; - -// boolean flags -static uint8_t need_measurement; -static uint8_t need_comms; -static uint8_t uart_enabled; -static uint8_t stay_awake; -static uint8_t button_pressed; - -// counts down from WAKE_SECS to 0, goes to deep sleep when hits 0 -static uint8_t comms_timeout; - -static uint8_t readpos; -static char readbuf[30]; -static uint8_t have_cmd; - -static uint8_t n_sensors; -static uint8_t sensor_id[MAX_SENSORS][ID_LEN]; - -static int16_t last_fridge = DS18X20_INVALID_DECICELSIUS; -static int16_t last_wort = DS18X20_INVALID_DECICELSIUS; -static struct epoch_ticks fridge_off_clock = {0}; -static struct epoch_ticks fridge_on_clock = {0}; -static struct epoch_ticks wort_valid_clock = {0}; - -int uart_putchar(char c, FILE *stream); -static void long_delay(int ms); -static void blink(); -static uint16_t adc_vcc(); - -static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, - _FDEV_SETUP_WRITE); - -static uint16_t crc_out; -static FILE _crc_stdout = FDEV_SETUP_STREAM(uart_putchar, NULL, - _FDEV_SETUP_WRITE); -// convenience -static FILE *crc_stdout = &_crc_stdout; - - -// thanks to http://projectgus.com/2010/07/eeprom-access-with-arduino/ -#define eeprom_read_to(dst_p, eeprom_field, dst_size) eeprom_read_block((dst_p), (void *)offsetof(struct __eeprom_data, eeprom_field), (dst_size)) -#define eeprom_read(dst, eeprom_field) eeprom_read_to((&dst), eeprom_field, sizeof(dst)) -#define eeprom_write_from(src_p, eeprom_field, src_size) eeprom_write_block((src_p), (void *)offsetof(struct __eeprom_data, eeprom_field), (src_size)) -#define eeprom_write(src, eeprom_field) { eeprom_write_from(&src, eeprom_field, sizeof(src)); } - -#define EXPECT_MAGIC 0x67c9 - -struct __attribute__ ((__packed__)) __eeprom_data { - uint16_t measure_wake; - uint16_t comms_wake; - uint8_t wake_secs; - - int16_t fridge_setpoint; // decidegrees - uint16_t fridge_difference; // decidegrees - uint16_t fridge_delay; - - uint16_t overshoot_delay; - uint8_t overshoot_factor; // decidegrees - -#if 0 - static uint8_t wort_id[ID_LEN]; - static uint8_t fridge_id[ID_LEN]; -#endif - - uint16_t magic; -}; - -static const uint8_t fridge_id[ID_LEN] = - {0x28,0xCE,0xB2,0x1A,0x03,0x00,0x00,0x99}; -static const uint8_t wort_id[ID_LEN] = - {0x28,0x49,0xBC,0x1A,0x03,0x00,0x00,0x54}; - -static void deep_sleep(); - -// 0 or 1 -static uint8_t -is_fridge_on() -{ - if (PORT_FRIDGE & _BV(PIN_FRIDGE)) - { - return 1; - } - else - { - return 0; - } -} - -// Very first setup -static void -setup_chip() -{ - cli(); - - // stop watchdog timer (might have been used to cause a reset) - wdt_reset(); - MCUSR &= ~_BV(WDRF); - WDTCSR |= _BV(WDCE) | _BV(WDE); - WDTCSR = 0; - - // Set clock to 2mhz - CLKPR = _BV(CLKPCE); - // divide by 4 - CLKPR = _BV(CLKPS1); - - // enable pullups - PORTB = 0xff; // XXX change when using SPI - PORTD = 0xff; - PORTC = 0xff; - - // 3.3v power for bluetooth and SD - DDR_LED |= _BV(PIN_LED); - DDR_SHDN |= _BV(PIN_SHDN); - - PORT_FRIDGE &= ~_BV(PIN_FRIDGE); - DDR_FRIDGE |= _BV(PIN_FRIDGE); - - // set pullup - PORTD |= _BV(PD2); - // INT0 setup - EICRA = (1<<ISC01); // falling edge - data sheet says it won't work? - EIMSK = _BV(INT0); - - // comparator disable - ACSR = _BV(ACD); - - // disable adc pin input buffers - DIDR0 = 0x3F; // acd0-adc5 - DIDR1 = (1<<AIN1D)|(1<<AIN0D); // ain0/ain1 - - sei(); -} - -static void -set_aux_power(uint8_t on) -{ - if (on) - { - PORT_SHDN &= ~_BV(PIN_SHDN); - } - else - { - PORT_SHDN |= _BV(PIN_SHDN); - } -} - -static void -get_epoch_ticks(struct epoch_ticks *t) -{ - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) - { - t->ticks = clock_epoch; - t->rem = TCNT2; - } -} - -static void -set_measurement(uint8_t sensor, uint16_t measurement, uint16_t reading) -{ - measurements[sensor*max_measurements + measurement] = reading; -} - -static uint16_t -get_measurement(uint8_t sensor, uint16_t measurement) -{ - return measurements[sensor*max_measurements + measurement]; -} - -static void -setup_tick_counter() -{ - // set up counter2. - // COM21 COM20 Set OC2 on Compare Match (p116) - // WGM21 Clear counter on compare - //TCCR2A = _BV(COM2A1) | _BV(COM2A0) | _BV(WGM21); - // toggle on match - TCCR2A = _BV(COM2A0); - // CS22 CS21 CS20 clk/1024 - TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20); - // set async mode - ASSR |= _BV(AS2); - TCNT2 = 0; - OCR2A = SLEEP_COMPARE; - // interrupt - TIMSK2 = _BV(OCIE2A); -} - -static void -uart_on() -{ - // Power reduction register - PRR &= ~_BV(PRUSART0); - - // All of this needs to be done each time after turning off the PRR - // baud rate - UBRR0H = (unsigned char)(UBRR >> 8); - UBRR0L = (unsigned char)UBRR; - // set 2x clock, improves accuracy of UBRR - UCSR0A |= _BV(U2X0); - UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0); - //8N1 - UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); - uart_enabled = 1; -} - -static void -uart_off() -{ - // Turn off interrupts and disable tx/rx - UCSR0B = 0; - uart_enabled = 0; - - // Power reduction register - PRR |= _BV(PRUSART0); -} - -int -uart_putchar(char c, FILE *stream) -{ - if (!uart_enabled) - { - return EOF; - } - // XXX could perhaps sleep in the loop for power. - if (c == '\n') - { - loop_until_bit_is_set(UCSR0A, UDRE0); - UDR0 = '\r'; - } - loop_until_bit_is_set(UCSR0A, UDRE0); - UDR0 = c; - if (stream == crc_stdout) - { - crc_out = _crc_ccitt_update(crc_out, c); - } - if (c == '\r') - { - loop_until_bit_is_set(UCSR0A, UDRE0); - UDR0 = '\n'; - if (stream == crc_stdout) - { - crc_out = _crc_ccitt_update(crc_out, '\n'); - } - } - return (unsigned char)c; -} - -static void -cmd_fetch() -{ - crc_out = 0; - - fprintf_P(crc_stdout, PSTR("START\n")); - { - struct epoch_ticks now; - get_epoch_ticks(&now); - fprintf_P(crc_stdout, PSTR("now=%lu\n"), now.ticks); - fprintf_P(crc_stdout, PSTR("now_rem=%hhu\n"), now.rem); - } - fprintf_P(crc_stdout, PSTR("time_step=%hu\n"), measure_wake); - fprintf_P(crc_stdout, PSTR("first_time=%lu\n"), first_measurement_clock.ticks); - fprintf_P(crc_stdout, PSTR("first_time_rem=%hhu\n"), first_measurement_clock.rem); - fprintf_P(crc_stdout, PSTR("last_time=%lu\n"), last_measurement_clock.ticks); - fprintf_P(crc_stdout, PSTR("last_time_rem=%hhu\n"), last_measurement_clock.rem); - fprintf_P(crc_stdout, PSTR("comms_time=%lu\n"), last_comms_clock.ticks); - fprintf_P(crc_stdout, PSTR("comms_time_rem=%hhu\n"), last_comms_clock.rem); - fprintf_P(crc_stdout, PSTR("voltage=%hu\n"), adc_vcc()); - fprintf_P(crc_stdout, PSTR("measure=%hu\n"), measure_wake); - fprintf_P(crc_stdout, PSTR("comms=%hu\n"), comms_wake); - fprintf_P(crc_stdout, PSTR("wake=%hhu\n"), wake_secs); - fprintf_P(crc_stdout, PSTR("fridge=%.1f\n"), fridge_setpoint/10.0); - fprintf_P(crc_stdout, PSTR("fridge_diff=%.1f\n"), fridge_difference/10.0); - fprintf_P(crc_stdout, PSTR("fridge_delay=%hu\n"), fridge_delay); - fprintf_P(crc_stdout, PSTR("overshoot_factor=%.1f\n"), overshoot_factor/10.0); - fprintf_P(crc_stdout, PSTR("overshoot_delay=%hu\n"), overshoot_delay); - fprintf_P(crc_stdout, PSTR("fridge_status=%hhu\n"), is_fridge_on()); - fprintf_P(crc_stdout, PSTR("fridge_last_on=%lu\n"), fridge_on_clock.ticks); - fprintf_P(crc_stdout, PSTR("fridge_last_off=%lu\n"), fridge_off_clock.ticks); - fprintf_P(crc_stdout, PSTR("last_fridge=%hu\n"), last_fridge); - fprintf_P(crc_stdout, PSTR("last_wort=%hu\n"), last_wort); - fprintf_P(crc_stdout, PSTR("tick_secs=%d\n"), TICK); - fprintf_P(crc_stdout, PSTR("tick_wake=%d\n"), SLEEP_COMPARE); - fprintf_P(crc_stdout, PSTR("maxsens=%hhu\n"), MAX_SENSORS); - fprintf_P(crc_stdout, PSTR("totalmeas=%hu\n"), TOTAL_MEASUREMENTS); - fprintf_P(crc_stdout, PSTR("sensors=%hhu\n"), n_sensors); - for (uint8_t s = 0; s < n_sensors; s++) - { - fprintf_P(crc_stdout, PSTR("sensor_id%hhu="), s); - printhex(sensor_id[s], ID_LEN, crc_stdout); - fputc('\n', crc_stdout); - } - fprintf_P(crc_stdout, PSTR("measurements=%hu\n"), n_measurements); - for (uint16_t n = 0; n < n_measurements; n++) - { - fprintf_P(crc_stdout, PSTR("meas%hu="), n); - for (uint8_t s = 0; s < n_sensors; s++) - { - fprintf_P(crc_stdout, PSTR(" %04hx"), get_measurement(s, n)); - } - fputc('\n', crc_stdout); - } - fprintf_P(crc_stdout, PSTR("END\n")); - fprintf_P(stdout, PSTR("CRC=%hu\n"), crc_out); -} - -static void -cmd_clear() -{ - n_measurements = 0; - printf_P(PSTR("cleared\n")); -} - -static void -cmd_btoff() -{ - uint8_t rem; - uint16_t count_copy; - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) - { - count_copy = comms_count; - rem = TCNT2; - } - printf_P(PSTR("next_wake=%hu,"), comms_wake-count_copy); - printf_P(PSTR("rem=%hhu,"), rem); - printf_P(PSTR("tick_secs=%hhu,"), TICK); - printf_P(PSTR("tick_wake=%hhu\n"), SLEEP_COMPARE); - _delay_ms(100); - comms_timeout = 0; - stay_awake = 0; -} - -static void -cmd_reset() -{ - printf_P(PSTR("reset\n")); - _delay_ms(100); - cli(); // disable interrupts - wdt_enable(WDTO_15MS); // enable watchdog - while(1); // wait for watchdog to reset processor -} - -static void -cmd_measure() -{ - printf_P(PSTR("measuring\n")); - need_measurement = 1; -} - -static void -cmd_sensors() -{ - uint8_t ret = simple_ds18b20_start_meas(NULL); - printf_P(PSTR("All sensors, ret %hhu, waiting...\n"), ret); - long_delay(DS18B20_TCONV_12BIT); - simple_ds18b20_read_all(); -} - -static void -init_sensors() -{ - uint8_t id[OW_ROMCODE_SIZE]; - printf_P(PSTR("init sensors\n")); - ow_reset(); - for( uint8_t diff = OW_SEARCH_FIRST; diff != OW_LAST_DEVICE; ) - { - diff = ow_rom_search( diff, &id[0] ); - if( diff == OW_PRESENCE_ERR ) { - printf_P( PSTR("No Sensor found\r") ); - return; - } - - if( diff == OW_DATA_ERR ) { - printf_P( PSTR("Bus Error\r") ); - return; - } - - if (n_sensors < MAX_SENSORS) - { - memcpy(sensor_id[n_sensors], id, ID_LEN); - printf_P(PSTR("Added sensor %hhu : "), n_sensors); - printhex(id, ID_LEN, stdout); - putchar('\n'); - n_sensors++; - } - else - { - printf_P(PSTR("Too many sensors\n")); - } - } - - max_measurements = TOTAL_MEASUREMENTS / n_sensors; -} - -static void -load_params() -{ - uint16_t magic; - eeprom_read(magic, magic); - if (magic == EXPECT_MAGIC) - { - eeprom_read(measure_wake, measure_wake); - eeprom_read(comms_wake, comms_wake); - eeprom_read(wake_secs, wake_secs); - eeprom_read(fridge_setpoint, fridge_setpoint); - eeprom_read(fridge_difference, fridge_difference); - eeprom_read(fridge_delay, fridge_delay); - eeprom_read(overshoot_delay, overshoot_delay); - eeprom_read(overshoot_factor, overshoot_factor); - } -} - -static void -cmd_get_params() -{ - printf_P(PSTR("measure %hu\n"), measure_wake); - printf_P(PSTR("comms %hu\n"), comms_wake); - printf_P(PSTR("wake %hhu\n"), wake_secs); - printf_P(PSTR("tick %d\n"), TICK); - printf_P(PSTR("fridge %.1fº\n"), fridge_setpoint / 10.0f); - printf_P(PSTR("fridge difference %.1fº\n"), fridge_difference / 10.0f); - printf_P(PSTR("fridge_delay %hu\n"), fridge_delay); - printf_P(PSTR("overshoot factor %.1fº\n"), overshoot_factor / 10.0f); - printf_P(PSTR("overshoot delay %hu\n"), overshoot_delay); - printf_P(PSTR("sensors %hhu (%hhu)\n"), - n_sensors, MAX_SENSORS); - printf_P(PSTR("meas %hu (%hu)\n"), - max_measurements, TOTAL_MEASUREMENTS); -} - -static void -cmd_set_params(const char *params) -{ - uint16_t new_measure_wake; - uint16_t new_comms_wake; - uint8_t new_wake_secs; - int ret = sscanf_P(params, PSTR("%hu %hu %hhu"), - &new_measure_wake, &new_comms_wake, &new_wake_secs); - - if (ret != 3) - { - printf_P(PSTR("Bad values\n")); - } - else - { - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) - { - eeprom_write(new_measure_wake, measure_wake); - eeprom_write(new_comms_wake, comms_wake); - eeprom_write(new_wake_secs, wake_secs); - uint16_t magic = EXPECT_MAGIC; - eeprom_write(magic, magic); - } - printf_P(PSTR("set_params for next boot\n")); - printf_P(PSTR("measure %hu comms %hu wake %hhu\n"), - new_measure_wake, new_comms_wake, new_wake_secs); - } -} - -// returns true if eeprom was written -static bool -set_initial_eeprom() -{ - uint16_t magic; - eeprom_read(magic, magic); - if (magic == EXPECT_MAGIC) - { - return false; - } - - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) - { - eeprom_write(measure_wake, measure_wake); - eeprom_write(comms_wake, comms_wake); - eeprom_write(wake_secs, wake_secs); - eeprom_write(fridge_setpoint, fridge_setpoint); - eeprom_write(fridge_difference, fridge_difference); - eeprom_write(fridge_delay, fridge_delay); - eeprom_write(overshoot_delay, overshoot_delay); - eeprom_write(overshoot_factor, overshoot_factor); - magic = EXPECT_MAGIC; - eeprom_write(magic, magic); - } - - return true; -} - -static void -cmd_set_fridge_setpoint(char *params) -{ - float new_f = atof(params); - if (new_f < 2 || new_f > 30) - { - printf_P(PSTR("Bad fridge value %f\n"), new_f); - return; - } - - int16_t old_setpoint = fridge_setpoint; - - fridge_setpoint = new_f * 10; - bool written = set_initial_eeprom(); - if (!written) - { - if (old_setpoint != fridge_setpoint) - { - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) - { - eeprom_write(fridge_setpoint, fridge_setpoint); - } - } - } - printf_P(PSTR("old fridge %.1fº new fridge %.1fº\n"), - old_setpoint / 10.0f, fridge_setpoint / 10.0f); -} - -static void -cmd_set_fridge_difference(char *params) -{ - float new_f = atof(params); - if (new_f < 0 || new_f > 30) - { - printf_P(PSTR("Bad fridge value %f\n"), new_f); - return; - } - - fridge_difference = new_f * 10; - bool written = set_initial_eeprom(); - if (!written) - { - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) - { - eeprom_write(fridge_difference, fridge_difference); - } - } - printf_P(PSTR("new fridge difference %.1fº\n"), fridge_difference / 10.0f); -} - -static void -cmd_set_fridge_delay(char *params) -{ - uint16_t new_delay = atoi(params); - if (new_delay < 5) - { - printf_P(PSTR("Bad fridge delay %d\n"), new_delay); - return; - } - - fridge_delay = new_delay; - bool written = set_initial_eeprom(); - if (!written) - { - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) - { - eeprom_write(fridge_delay, fridge_delay); - } - } - printf_P(PSTR("new fridge delay %hu\n"), fridge_delay); -} - -static void -cmd_set_overshoot_factor(char *params) -{ - float new_f = atof(params); - if (new_f <= 0 || new_f > 20) - { - printf_P(PSTR("Bad overshoot factor %f\n"), new_f); - return; - } - - uint8_t old = overshoot_factor; - - overshoot_factor = new_f * 10; - bool written = set_initial_eeprom(); - if (!written) - { - if (old != overshoot_factor) - { - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) - { - eeprom_write(overshoot_factor, overshoot_factor); - } - } - } - printf_P(PSTR("old factor %.1fº new factor %.1fº\n"), - old / 10.0f, overshoot_factor / 10.0f); -} - -static void -cmd_set_overshoot_delay(char *params) -{ - uint16_t new_delay = atoi(params); - if (new_delay < 5) - { - printf_P(PSTR("Bad overshoot delay %d\n"), new_delay); - return; - } - - overshoot_delay = new_delay; - bool written = set_initial_eeprom(); - if (!written) - { - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) - { - eeprom_write(overshoot_delay, overshoot_delay); - } - } - printf_P(PSTR("new overshoot delay %hu\n"), overshoot_delay); -} - -static void -cmd_awake() -{ - stay_awake = 1; - printf_P(PSTR("awake\n")); -} - -static void -read_handler() -{ - if (strcmp_P(readbuf, PSTR("fetch")) == 0) - { - cmd_fetch(); - } - else if (strcmp_P(readbuf, PSTR("clear")) == 0) - { - cmd_clear(); - } - else if (strcmp_P(readbuf, PSTR("btoff")) == 0) - { - cmd_btoff(); - } - else if (strcmp_P(readbuf, PSTR("measure")) == 0) - { - cmd_measure(); - } - else if (strcmp_P(readbuf, PSTR("sensors")) == 0) - { - cmd_sensors(); - } - else if (strcmp_P(readbuf, PSTR("get_params")) == 0) - { - cmd_get_params(); - } - else if (strncmp_P(readbuf, PSTR("set_params "), 11) == 0) - { - cmd_set_params(&readbuf[11]); - } - else if (strcmp_P(readbuf, PSTR("awake")) == 0) - { - cmd_awake(); - } - else if (strncmp_P(readbuf, PSTR("fridge_setpoint "), 16) == 0) - { - cmd_set_fridge_setpoint(&readbuf[16]); - } - else if (strncmp_P(readbuf, PSTR("fridge_diff "), 12) == 0) - { - cmd_set_fridge_difference(&readbuf[12]); - } - else if (strncmp_P(readbuf, PSTR("fridge_delay "), 13) == 0) - { - cmd_set_fridge_delay(&readbuf[13]); - } - else if (strncmp_P(readbuf, PSTR("overshoot_delay "), 16) == 0) - { - cmd_set_overshoot_delay(&readbuf[16]); - } - else if (strncmp_P(readbuf, PSTR("overshoot_factor "), 17) == 0) - { - cmd_set_overshoot_factor(&readbuf[17]); - } - else if (strcmp_P(readbuf, PSTR("reset")) == 0) - { - cmd_reset(); - } - else - { - printf_P(PSTR("Bad command '%s'\n"), readbuf); - } -} - -ISR(INT0_vect) -{ - button_pressed = 1; - blink(); - _delay_ms(100); - blink(); -} - - -ISR(USART_RX_vect) -{ - char c = UDR0; -#ifdef HAVE_UART_ECHO - uart_putchar(c, NULL); -#endif - if (c == '\r' || c == '\n') - { - if (readpos > 0) - { - readbuf[readpos] = '\0'; - have_cmd = 1; - readpos = 0; - } - } - else - { - readbuf[readpos] = c; - readpos++; - if (readpos >= sizeof(readbuf)) - { - readpos = 0; - } - } -} - -ISR(TIMER2_COMPA_vect) -{ - TCNT2 = 0; - measure_count += TICK; - comms_count += TICK; - - clock_epoch += TICK; - - if (comms_timeout != 0) - { - comms_timeout -= TICK; - } - - if (measure_count >= measure_wake) - { - measure_count = 0; - need_measurement = 1; - } - - if (comms_count >= comms_wake) - { - comms_count = 0; - need_comms = 1; - } -} - -static void -deep_sleep() -{ - // p119 of manual - OCR2A = SLEEP_COMPARE; - loop_until_bit_is_clear(ASSR, OCR2AUB); - - set_sleep_mode(SLEEP_MODE_PWR_SAVE); - sleep_mode(); -} - -static void -idle_sleep() -{ - set_sleep_mode(SLEEP_MODE_IDLE); - sleep_mode(); -} - -static uint16_t -adc_vcc() -{ - PRR &= ~_BV(PRADC); - - // /16 prescaler - ADCSRA = _BV(ADEN) | _BV(ADPS2); - - // set to measure 1.1 reference - ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); - // average a number of samples - uint16_t sum = 0; - uint8_t num = 0; - for (uint8_t n = 0; n < 20; n++) - { - ADCSRA |= _BV(ADSC); - loop_until_bit_is_clear(ADCSRA, ADSC); - - uint8_t low_11 = ADCL; - uint8_t high_11 = ADCH; - uint16_t val = low_11 + (high_11 << 8); - - if (n >= 4) - { - sum += val; - num++; - } - } - ADCSRA = 0; - PRR |= _BV(PRADC); - - //float res_volts = 1.1 * 1024 * num / sum; - //return 1000 * res_volts; - return ((uint32_t)1100*1024*num) / sum; -} - -static void -do_fridge() -{ - struct epoch_ticks now; - get_epoch_ticks(&now); - uint32_t off_time = now.ticks - fridge_off_clock.ticks; - bool wort_valid = last_wort != DS18X20_INVALID_DECICELSIUS; - bool fridge_valid = last_fridge != DS18X20_INVALID_DECICELSIUS; - - int16_t wort_max = fridge_setpoint + fridge_difference; - int16_t wort_min = fridge_setpoint; - - // the fridge min/max only apply if the wort sensor is broken - int16_t fridge_min = fridge_setpoint - FRIDGE_AIR_MIN_RANGE; - int16_t fridge_max = fridge_setpoint + FRIDGE_AIR_MAX_RANGE; - - uint8_t fridge_on = PORT_FRIDGE & _BV(PIN_FRIDGE); - printf_P(PSTR("last_wort %hd (%hd, %hd), last_fridge %hd (%hd, %hd), setpoint %hd, diff %hd, fridge_on %hhu\n"), - last_wort, wort_min, wort_max, - last_fridge, fridge_min, fridge_max, - fridge_setpoint, fridge_difference, fridge_on); - - if (off_time < fridge_delay) - { - printf_P(PSTR("waiting for fridge delay current %hu, wait %hu\n"), - off_time, fridge_delay); - return; - } - - // handle failure of the wort sensor. if it is a short (intermittent?) - // failure we wait until it has been broken for a period of time - // (WORT_INVALID_TIME) before doing anything. - if (wort_valid) - { - wort_valid_clock = now; - } - else - { - printf_P(PSTR("wort sensor is invalid\n")); - uint32_t invalid_time = now.ticks - wort_valid_clock.ticks; - if (invalid_time < WORT_INVALID_TIME) - { - printf("only been invalid for %ld, waiting\n", invalid_time); - return; - } - } - - if (!fridge_valid) - { - printf_P(PSTR("fridge sensor is invalid\n")); - } - - if (fridge_on) - { - bool turn_off = false; - uint16_t on_time = now.ticks - fridge_on_clock.ticks; - - uint16_t overshoot = 0; - if (on_time > overshoot_delay) - { - overshoot = overshoot_factor * MIN(OVERSHOOT_MAX_DIV, on_time) / OVERSHOOT_MAX_DIV; - } - - printf_P(PSTR("on_time %hu, overshoot %hu\n"), on_time, overshoot); - - // wort has cooled enough. will probably cool a bit more by itself - if (wort_valid) - { - if ((last_wort - overshoot) < fridge_setpoint) - { - printf_P(PSTR("wort has cooled enough, overshoot %hu on_time %hu\n"), overshoot, on_time); - turn_off = true; - } - } - else - { - if (fridge_valid && last_fridge < fridge_min) - { - printf_P(PSTR("fridge off fallback\n")); - turn_off = true; - } - } - - if (turn_off) - { - // too cold, turn off - printf_P(PSTR("Turning fridge off\n")); - PORT_FRIDGE &= ~_BV(PIN_FRIDGE); - fridge_off_clock = now; - } - } - else - { - bool turn_on = false; - - if (wort_valid) - { - if (last_wort >= wort_max) - { - printf_P(PSTR("wort is too hot\n")); - turn_on = true; - } - } - else - { - if (fridge_valid && last_fridge >= fridge_max) - { - printf_P(PSTR("fridge on fallback\n")); - turn_on = true; - } - } - - if (turn_on) - { - // too hot, turn on - printf_P(PSTR("Turning fridge on\n")); - PORT_FRIDGE |= _BV(PIN_FRIDGE); - fridge_on_clock = now; - } - } -} - -static void -do_measurement() -{ - blink(); - - /* Take the timer here since deep_sleep() below could take 6 seconds */ - get_epoch_ticks(&last_measurement_clock); - if (n_measurements == 0) - { - first_measurement_clock = last_measurement_clock; - } - - simple_ds18b20_start_meas(NULL); - _delay_ms(DS18B20_TCONV_12BIT); - - if (n_measurements == max_measurements) - { - n_measurements = 0; - } - - for (uint8_t s = 0; s < n_sensors; s++) - { - uint16_t reading; - uint8_t ret = simple_ds18b20_read_raw(sensor_id[s], &reading); - if (ret != DS18X20_OK) - { - reading = VALUE_BROKEN; - } - set_measurement(s, n_measurements, reading); - - if (memcmp(sensor_id[s], fridge_id, sizeof(fridge_id)) == 0) - { - last_fridge = ds18b20_raw16_to_decicelsius(reading); - } - if (memcmp(sensor_id[s], wort_id, sizeof(wort_id)) == 0) - { - last_wort = ds18b20_raw16_to_decicelsius(reading); - } - } - - n_measurements++; -} - -static void -do_comms() -{ - get_epoch_ticks(&last_comms_clock); - - // turn on bluetooth - set_aux_power(1); - // avoid receiving rubbish, perhaps - _delay_ms(50); - uart_on(); - - // write sd card here? same 3.3v regulator... - - for (comms_timeout = wake_secs; - comms_timeout > 0 || stay_awake; - ) - { - if (need_measurement) - { - need_measurement = 0; - do_measurement(); - do_fridge(); - continue; - } - - if (have_cmd) - { - have_cmd = 0; - read_handler(); - continue; - } - - // wait for commands from the master - idle_sleep(); - } - - uart_off(); - // in case bluetooth takes time to flush - _delay_ms(100); - set_aux_power(0); -} - -static void -blink() -{ - PORT_LED &= ~_BV(PIN_LED); - _delay_ms(1); - PORT_LED |= _BV(PIN_LED); -} - -static void -long_delay(int ms) -{ - int iter = ms / 100; - - for (int i = 0; i < iter; i++) - { - _delay_ms(100); - } -} - -ISR(BADISR_vect) -{ - //uart_on(); - printf_P(PSTR("Bad interrupt\n")); -} - -int main(void) -{ - setup_chip(); - blink(); - - set_aux_power(0); - - stdout = &mystdout; - uart_on(); - - printf(PSTR("Started.\n")); - - load_params(); - - init_sensors(); - - uart_off(); - - // turn off everything except timer2 - PRR = _BV(PRTWI) | _BV(PRTIM0) | _BV(PRTIM1) | _BV(PRSPI) | _BV(PRUSART0) | _BV(PRADC); - - setup_tick_counter(); - - sei(); - - need_comms = 1; - need_measurement = 1; - - stay_awake = 1; - - for(;;) - { - if (button_pressed) - { - // debounce - _delay_ms(200); - need_comms = 1; - comms_timeout = wake_secs; - button_pressed = 0; - continue; - } - - if (need_comms) - { - need_comms = 0; - do_comms(); - continue; - } - - if (need_measurement) - { - need_measurement = 0; - do_measurement(); - do_fridge(); - continue; - } - - deep_sleep(); - } - - return 0; /* never reached */ -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/old/Makefile Thu Jun 26 23:03:32 2014 +0800 @@ -0,0 +1,112 @@ +# Name: Makefile +# Author: <insert your name here> +# Copyright: <insert your copyright message here> +# License: <insert your license reference here> + +# This is a prototype Makefile. Modify it according to your needs. +# You should at least check the settings for +# DEVICE ....... The AVR device you compile for +# CLOCK ........ Target AVR clock rate in Hertz +# OBJECTS ...... The object files created from your source files. This list is +# usually the same as the list of source files with suffix ".o". +# PROGRAMMER ... Options to avrdude which define the hardware you use for +# uploading to the AVR and the interface where this hardware +# is connected. We recommend that you leave it undefined and +# add settings like this to your ~/.avrduderc file: +# default_programmer = "stk500v2" +# default_serial = "avrdoper" +# FUSES ........ Parameters for avrdude to flash the fuses appropriately. + +DEVICE = atmega328 +PROGDEVICE = atmega328p +CLOCK = 2000000 +PROGRAMMER = #-c stk500v2 -P avrdoper +PROGRAMMER = -c stk500 -P ~/dev/stk500 -p $(PROGDEVICE) -B 2 +SOURCE_1WIRE = onewire.c simple_ds18b20.c crc8.c +SOURCE = main.c +SOURCE += $(SOURCE_1WIRE) +LIBS = -lm + +# default but 2mhz +FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x62:m + +# ATMega8 fuse bits used above (fuse bits for other devices are different!): +# Example for 8 MHz internal oscillator +# Fuse high byte: +# 0xd9 = 1 1 0 1 1 0 0 1 <-- BOOTRST (boot reset vector at 0x0000) +# ^ ^ ^ ^ ^ ^ ^------ BOOTSZ0 +# | | | | | +-------- BOOTSZ1 +# | | | | +---------- EESAVE (set to 0 to preserve EEPROM over chip erase) +# | | | +-------------- WDTON +# | | +---------------- SPIEN (if set to 1, serial programming is disabled) +# | +------------------ DWEN +# +-------------------- RSTDISBL (if set to 0, RESET pin is disabled) +# Fuse low byte: +# 0x62 = 0 1 1 0 0 0 1 0 +# ^ ^ \ / \--+--/ +# | | | +------- CKSEL 3..0 (8M internal RC) +# | | +--------------- SUT 1..0 (slowly rising power) +# | +------------------ CKOUT +# +-------------------- CLKDIV8 +# +# For computing fuse byte values for other devices and options see +# the fuse bit calculator at http://www.engbedded.com/fusecalc/ + + +# Tune the lines below only if you know what you are doing: + +AVRDUDE = avrdude $(PROGRAMMER) +COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -g -std=c99 -mcall-prologues -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--relax --combine -fwhole-program -Wl,-u,vfprintf -lprintf_flt -lm + +# symbolic targets: +all: main.hex + +.c.o: + $(COMPILE) -c $< -o $@ + +.S.o: + $(COMPILE) -x assembler-with-cpp -c $< -o $@ +# "-x assembler-with-cpp" should not be necessary since this is the default +# file type for the .S (with capital S) extension. However, upper case +# characters are not always preserved on Windows. To ensure WinAVR +# compatibility define the file type manually. + +.c.s: + $(COMPILE) -S $< -o $@ + +flash: all + $(AVRDUDE) -U flash:w:main.hex:i + +checkprog: + $(AVRDUDE) -v + +fuse: + $(AVRDUDE) $(FUSES) + +# Xcode uses the Makefile targets "", "clean" and "install" +install: flash + +# if you use a bootloader, change the command below appropriately: +load: all + bootloadHID main.hex + +clean: + rm -f main.hex main.elf $(OBJECTS) + +# file targets: +main.elf: $(SOURCE) + $(COMPILE) -o main.elf $(SOURCE) $(LIBS) + +main.hex: main.elf + rm -f main.hex + avr-objcopy -j .text -j .data -O ihex main.elf main.hex + avr-size --format=avr --mcu=$(DEVICE) main.elf +# If you have an EEPROM section, you must also create a hex file for the +# EEPROM and add it to the "flash" target. + +# Targets for code debugging and analysis: +disasm: main.elf + avr-objdump -d main.elf + +cpp: + $(COMPILE) -E main.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/old/crc8.c Thu Jun 26 23:03:32 2014 +0800 @@ -0,0 +1,63 @@ +/* please read copyright-notice at EOF */ + +#include <stdint.h> + +#define CRC8INIT 0x00 +#define CRC8POLY 0x18 //0X18 = X^8+X^5+X^4+X^0 + +uint8_t crc8( uint8_t *data, uint16_t number_of_bytes_in_data ) +{ + uint8_t crc; + uint16_t loop_count; + uint8_t bit_counter; + uint8_t b; + uint8_t feedback_bit; + + crc = CRC8INIT; + + for (loop_count = 0; loop_count != number_of_bytes_in_data; loop_count++) + { + b = data[loop_count]; + + bit_counter = 8; + do { + feedback_bit = (crc ^ b) & 0x01; + + if ( feedback_bit == 0x01 ) { + crc = crc ^ CRC8POLY; + } + crc = (crc >> 1) & 0x7F; + if ( feedback_bit == 0x01 ) { + crc = crc | 0x80; + } + + b = b >> 1; + bit_counter--; + + } while (bit_counter > 0); + } + + return crc; +} + +/* +This code is from Colin O'Flynn - Copyright (c) 2002 +only minor changes by M.Thomas 9/2004 + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/old/crc8.h Thu Jun 26 23:03:32 2014 +0800 @@ -0,0 +1,40 @@ +#ifndef CRC8_H_ +#define CRC8_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +uint8_t crc8( uint8_t* data, uint16_t number_of_bytes_in_data ); + +#ifdef __cplusplus +} +#endif + +#endif + +/* +This is based on code from : + +Copyright (c) 2002 Colin O'Flynn + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/old/ds18x20.h Thu Jun 26 23:03:32 2014 +0800 @@ -0,0 +1,140 @@ +#ifndef DS18X20_H_ +#define DS18X20_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <stdint.h> + +// DS18x20 EERPROM support disabled(0) or enabled(1) : +#define DS18X20_EEPROMSUPPORT 1 +// decicelsius functions disabled(0) or enabled(1): +#define DS18X20_DECICELSIUS 1 +// max. resolution functions disabled(0) or enabled(1): +#define DS18X20_MAX_RESOLUTION 1 +// extended output via UART disabled(0) or enabled(1) : +#define DS18X20_VERBOSE 1 + + +/* return values */ +#define DS18X20_OK 0x00 +#define DS18X20_ERROR 0x01 +#define DS18X20_START_FAIL 0x02 +#define DS18X20_ERROR_CRC 0x03 + +#define DS18X20_INVALID_DECICELSIUS 2000 + +#define DS18X20_POWER_PARASITE 0x00 +#define DS18X20_POWER_EXTERN 0x01 + +#define DS18X20_CONVERSION_DONE 0x00 +#define DS18X20_CONVERTING 0x01 + +/* DS18X20 specific values (see datasheet) */ +#define DS18S20_FAMILY_CODE 0x10 +#define DS18B20_FAMILY_CODE 0x28 +#define DS1822_FAMILY_CODE 0x22 + +#define DS18X20_CONVERT_T 0x44 +#define DS18X20_READ 0xBE +#define DS18X20_WRITE 0x4E +#define DS18X20_EE_WRITE 0x48 +#define DS18X20_EE_RECALL 0xB8 +#define DS18X20_READ_POWER_SUPPLY 0xB4 + +#define DS18B20_CONF_REG 4 +#define DS18B20_9_BIT 0 +#define DS18B20_10_BIT (1<<5) +#define DS18B20_11_BIT (1<<6) +#define DS18B20_12_BIT ((1<<6)|(1<<5)) +#define DS18B20_RES_MASK ((1<<6)|(1<<5)) + +// undefined bits in LSB if 18B20 != 12bit +#define DS18B20_9_BIT_UNDF ((1<<0)|(1<<1)|(1<<2)) +#define DS18B20_10_BIT_UNDF ((1<<0)|(1<<1)) +#define DS18B20_11_BIT_UNDF ((1<<0)) +#define DS18B20_12_BIT_UNDF 0 + +// conversion times in milliseconds +#define DS18B20_TCONV_12BIT 750 +#define DS18B20_TCONV_11BIT DS18B20_TCONV_12_BIT/2 +#define DS18B20_TCONV_10BIT DS18B20_TCONV_12_BIT/4 +#define DS18B20_TCONV_9BIT DS18B20_TCONV_12_BIT/8 +#define DS18S20_TCONV DS18B20_TCONV_12_BIT + +// constant to convert the fraction bits to cel*(10^-4) +#define DS18X20_FRACCONV 625 + +// scratchpad size in bytes +#define DS18X20_SP_SIZE 9 + +// DS18X20 EEPROM-Support +#define DS18X20_WRITE_SCRATCHPAD 0x4E +#define DS18X20_COPY_SCRATCHPAD 0x48 +#define DS18X20_RECALL_E2 0xB8 +#define DS18X20_COPYSP_DELAY 10 /* ms */ +#define DS18X20_TH_REG 2 +#define DS18X20_TL_REG 3 + +#define DS18X20_DECIMAL_CHAR '.' + + +extern uint8_t DS18X20_find_sensor(uint8_t *diff, + uint8_t id[]); +extern uint8_t DS18X20_get_power_status(uint8_t id[]); +extern uint8_t DS18X20_start_meas( uint8_t with_external, + uint8_t id[]); +// returns 1 if conversion is in progress, 0 if finished +// not available when parasite powered +extern uint8_t DS18X20_conversion_in_progress(void); + + +#if DS18X20_DECICELSIUS +extern uint8_t DS18X20_read_decicelsius( uint8_t id[], + int16_t *decicelsius ); +extern uint8_t DS18X20_read_decicelsius_single( uint8_t familycode, + int16_t *decicelsius ); +extern uint8_t DS18X20_format_from_decicelsius( int16_t decicelsius, + char s[], uint8_t n); +#endif /* DS18X20_DECICELSIUS */ + + +#if DS18X20_MAX_RESOLUTION +// temperature unit for max. resolution is �C * 10e-4 +// examples: -250625 -> -25.0625�C, 1250000 -> 125.0000 �C +extern uint8_t DS18X20_read_maxres( uint8_t id[], + int32_t *temperaturevalue ); +extern uint8_t DS18X20_read_maxres_single( uint8_t familycode, + int32_t *temperaturevalue ); +extern uint8_t DS18X20_format_from_maxres( int32_t temperaturevalue, + char s[], uint8_t n); +#endif /* DS18X20_MAX_RESOLUTION */ + + +#if DS18X20_EEPROMSUPPORT +// write th, tl and config-register to scratchpad (config ignored on DS18S20) +uint8_t DS18X20_write_scratchpad( uint8_t id[], + uint8_t th, uint8_t tl, uint8_t conf); +// read scratchpad into array SP +uint8_t DS18X20_read_scratchpad( uint8_t id[], uint8_t sp[], uint8_t n); +// copy values int scratchpad into DS18x20 eeprom +uint8_t DS18X20_scratchpad_to_eeprom( uint8_t with_power_extern, + uint8_t id[] ); +// copy values from DS18x20 eeprom into scratchpad +uint8_t DS18X20_eeprom_to_scratchpad( uint8_t id[] ); +#endif /* DS18X20_EEPROMSUPPORT */ + + +#if DS18X20_VERBOSE +extern void DS18X20_show_id_uart( uint8_t *id, size_t n ); +extern uint8_t DS18X20_read_meas_all_verbose( void ); +#endif /* DS18X20_VERBOSE */ + + +#ifdef __cplusplus +} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/old/main.c Thu Jun 26 23:03:32 2014 +0800 @@ -0,0 +1,1200 @@ +#include <stdio.h> +#include <string.h> +#include <stddef.h> +#include <stdbool.h> +#include <stdlib.h> +#include <avr/io.h> +#include <avr/interrupt.h> +#include <avr/sleep.h> +#include <util/delay.h> +#include <avr/pgmspace.h> +#include <avr/eeprom.h> +#include <avr/wdt.h> +#include <util/atomic.h> +#include <util/crc16.h> + +#include "simple_ds18b20.h" +#include "onewire.h" + +// configuration params +// - measurement interval +// - transmit interval +// - bluetooth params +// - number of sensors (and range?) + +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) +#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) + +// TICK should be 8 or less (8 untested). all timers need +// to be a multiple. + +#define TICK 6 +// we have 1024 prescaler, 32768 crystal. +#define SLEEP_COMPARE (32*TICK-1) + +#define VALUE_NOSENSOR 0x07D0 // 125 degrees +#define VALUE_BROKEN 0x07D1 // 125.0625 + +#define OVERSHOOT_MAX_DIV 1800.0 // 30 mins +#define WORT_INVALID_TIME 900 // 15 mins +// fridge min/max are only used if the wort sensor is invalid +#define FRIDGE_AIR_MIN_RANGE 40 // 4º +#define FRIDGE_AIR_MAX_RANGE 40 // 4º + +#define BAUD 19200 +#define UBRR ((F_CPU)/8/(BAUD)-1) + +#define PORT_LED PORTC +#define DDR_LED DDRC +#define PIN_LED PC4 + +#define PORT_SHDN PORTD +#define DDR_SHDN DDRD +#define PIN_SHDN PD7 + +#define PORT_FRIDGE PORTD +#define DDR_FRIDGE DDRD +#define PIN_FRIDGE PD6 + +// total amount of 16bit values available for measurements. +// adjust emperically, be sure to allow enough stack space too +#define TOTAL_MEASUREMENTS 800 + +// each sensor slot uses 8 bytes +#define MAX_SENSORS 6 + +// fixed at 8, have a shorter name +#define ID_LEN OW_ROMCODE_SIZE + +// #define HAVE_UART_ECHO + +// stores a value of clock_epoch combined with the remainder of TCNT2, +// for 1/32 second accuracy +struct epoch_ticks +{ + uint32_t ticks; + // remainder + uint8_t rem; +}; + +// eeprom-settable parameters. all timeouts should +// be a multiple of TICK (6 seconds probably) +static uint16_t measure_wake = 61; // not a divisor of comms_wake +static uint16_t comms_wake = 600; +static uint8_t wake_secs = 30; +// decidegrees +static int16_t fridge_setpoint = 180; // 18.0ºC +static uint16_t fridge_difference = 3; // 0.3ºC +static uint16_t fridge_delay = 600; // seconds + +static uint16_t overshoot_delay = 720; // 12 mins +static uint8_t overshoot_factor = 10; // 1.0ºC + +// ---- Atomic guards required accessing these variables +// clock_epoch in seconds +static uint32_t clock_epoch; +static uint16_t comms_count; +static uint16_t measure_count; +// ---- End atomic guards required + +static uint16_t n_measurements; + +// calculated at startup as TOTAL_MEASUREMENTS/n_sensors +static uint16_t max_measurements; + +static uint16_t measurements[TOTAL_MEASUREMENTS]; + +static struct epoch_ticks first_measurement_clock; +// last_measurement_clock is redundant but checks that we're not missing +// samples +static struct epoch_ticks last_measurement_clock; +static struct epoch_ticks last_comms_clock; + +// boolean flags +static uint8_t need_measurement; +static uint8_t need_comms; +static uint8_t uart_enabled; +static uint8_t stay_awake; +static uint8_t button_pressed; + +// counts down from WAKE_SECS to 0, goes to deep sleep when hits 0 +static uint8_t comms_timeout; + +static uint8_t readpos; +static char readbuf[30]; +static uint8_t have_cmd; + +static uint8_t n_sensors; +static uint8_t sensor_id[MAX_SENSORS][ID_LEN]; + +static int16_t last_fridge = DS18X20_INVALID_DECICELSIUS; +static int16_t last_wort = DS18X20_INVALID_DECICELSIUS; +static struct epoch_ticks fridge_off_clock = {0}; +static struct epoch_ticks fridge_on_clock = {0}; +static struct epoch_ticks wort_valid_clock = {0}; + +int uart_putchar(char c, FILE *stream); +static void long_delay(int ms); +static void blink(); +static uint16_t adc_vcc(); + +static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, + _FDEV_SETUP_WRITE); + +static uint16_t crc_out; +static FILE _crc_stdout = FDEV_SETUP_STREAM(uart_putchar, NULL, + _FDEV_SETUP_WRITE); +// convenience +static FILE *crc_stdout = &_crc_stdout; + + +// thanks to http://projectgus.com/2010/07/eeprom-access-with-arduino/ +#define eeprom_read_to(dst_p, eeprom_field, dst_size) eeprom_read_block((dst_p), (void *)offsetof(struct __eeprom_data, eeprom_field), (dst_size)) +#define eeprom_read(dst, eeprom_field) eeprom_read_to((&dst), eeprom_field, sizeof(dst)) +#define eeprom_write_from(src_p, eeprom_field, src_size) eeprom_write_block((src_p), (void *)offsetof(struct __eeprom_data, eeprom_field), (src_size)) +#define eeprom_write(src, eeprom_field) { eeprom_write_from(&src, eeprom_field, sizeof(src)); } + +#define EXPECT_MAGIC 0x67c9 + +struct __attribute__ ((__packed__)) __eeprom_data { + uint16_t measure_wake; + uint16_t comms_wake; + uint8_t wake_secs; + + int16_t fridge_setpoint; // decidegrees + uint16_t fridge_difference; // decidegrees + uint16_t fridge_delay; + + uint16_t overshoot_delay; + uint8_t overshoot_factor; // decidegrees + +#if 0 + static uint8_t wort_id[ID_LEN]; + static uint8_t fridge_id[ID_LEN]; +#endif + + uint16_t magic; +}; + +static const uint8_t fridge_id[ID_LEN] = + {0x28,0xCE,0xB2,0x1A,0x03,0x00,0x00,0x99}; +static const uint8_t wort_id[ID_LEN] = + {0x28,0x49,0xBC,0x1A,0x03,0x00,0x00,0x54}; + +static void deep_sleep(); + +// 0 or 1 +static uint8_t +is_fridge_on() +{ + if (PORT_FRIDGE & _BV(PIN_FRIDGE)) + { + return 1; + } + else + { + return 0; + } +} + +// Very first setup +static void +setup_chip() +{ + cli(); + + // stop watchdog timer (might have been used to cause a reset) + wdt_reset(); + MCUSR &= ~_BV(WDRF); + WDTCSR |= _BV(WDCE) | _BV(WDE); + WDTCSR = 0; + + // Set clock to 2mhz + CLKPR = _BV(CLKPCE); + // divide by 4 + CLKPR = _BV(CLKPS1); + + // enable pullups + PORTB = 0xff; // XXX change when using SPI + PORTD = 0xff; + PORTC = 0xff; + + // 3.3v power for bluetooth and SD + DDR_LED |= _BV(PIN_LED); + DDR_SHDN |= _BV(PIN_SHDN); + + PORT_FRIDGE &= ~_BV(PIN_FRIDGE); + DDR_FRIDGE |= _BV(PIN_FRIDGE); + + // set pullup + PORTD |= _BV(PD2); + // INT0 setup + EICRA = (1<<ISC01); // falling edge - data sheet says it won't work? + EIMSK = _BV(INT0); + + // comparator disable + ACSR = _BV(ACD); + + // disable adc pin input buffers + DIDR0 = 0x3F; // acd0-adc5 + DIDR1 = (1<<AIN1D)|(1<<AIN0D); // ain0/ain1 + + sei(); +} + +static void +set_aux_power(uint8_t on) +{ + if (on) + { + PORT_SHDN &= ~_BV(PIN_SHDN); + } + else + { + PORT_SHDN |= _BV(PIN_SHDN); + } +} + +static void +get_epoch_ticks(struct epoch_ticks *t) +{ + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + t->ticks = clock_epoch; + t->rem = TCNT2; + } +} + +static void +set_measurement(uint8_t sensor, uint16_t measurement, uint16_t reading) +{ + measurements[sensor*max_measurements + measurement] = reading; +} + +static uint16_t +get_measurement(uint8_t sensor, uint16_t measurement) +{ + return measurements[sensor*max_measurements + measurement]; +} + +static void +setup_tick_counter() +{ + // set up counter2. + // COM21 COM20 Set OC2 on Compare Match (p116) + // WGM21 Clear counter on compare + //TCCR2A = _BV(COM2A1) | _BV(COM2A0) | _BV(WGM21); + // toggle on match + TCCR2A = _BV(COM2A0); + // CS22 CS21 CS20 clk/1024 + TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20); + // set async mode + ASSR |= _BV(AS2); + TCNT2 = 0; + OCR2A = SLEEP_COMPARE; + // interrupt + TIMSK2 = _BV(OCIE2A); +} + +static void +uart_on() +{ + // Power reduction register + PRR &= ~_BV(PRUSART0); + + // All of this needs to be done each time after turning off the PRR + // baud rate + UBRR0H = (unsigned char)(UBRR >> 8); + UBRR0L = (unsigned char)UBRR; + // set 2x clock, improves accuracy of UBRR + UCSR0A |= _BV(U2X0); + UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0); + //8N1 + UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); + uart_enabled = 1; +} + +static void +uart_off() +{ + // Turn off interrupts and disable tx/rx + UCSR0B = 0; + uart_enabled = 0; + + // Power reduction register + PRR |= _BV(PRUSART0); +} + +int +uart_putchar(char c, FILE *stream) +{ + if (!uart_enabled) + { + return EOF; + } + // XXX could perhaps sleep in the loop for power. + if (c == '\n') + { + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = '\r'; + } + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = c; + if (stream == crc_stdout) + { + crc_out = _crc_ccitt_update(crc_out, c); + } + if (c == '\r') + { + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = '\n'; + if (stream == crc_stdout) + { + crc_out = _crc_ccitt_update(crc_out, '\n'); + } + } + return (unsigned char)c; +} + +static void +cmd_fetch() +{ + crc_out = 0; + + fprintf_P(crc_stdout, PSTR("START\n")); + { + struct epoch_ticks now; + get_epoch_ticks(&now); + fprintf_P(crc_stdout, PSTR("now=%lu\n"), now.ticks); + fprintf_P(crc_stdout, PSTR("now_rem=%hhu\n"), now.rem); + } + fprintf_P(crc_stdout, PSTR("time_step=%hu\n"), measure_wake); + fprintf_P(crc_stdout, PSTR("first_time=%lu\n"), first_measurement_clock.ticks); + fprintf_P(crc_stdout, PSTR("first_time_rem=%hhu\n"), first_measurement_clock.rem); + fprintf_P(crc_stdout, PSTR("last_time=%lu\n"), last_measurement_clock.ticks); + fprintf_P(crc_stdout, PSTR("last_time_rem=%hhu\n"), last_measurement_clock.rem); + fprintf_P(crc_stdout, PSTR("comms_time=%lu\n"), last_comms_clock.ticks); + fprintf_P(crc_stdout, PSTR("comms_time_rem=%hhu\n"), last_comms_clock.rem); + fprintf_P(crc_stdout, PSTR("voltage=%hu\n"), adc_vcc()); + fprintf_P(crc_stdout, PSTR("measure=%hu\n"), measure_wake); + fprintf_P(crc_stdout, PSTR("comms=%hu\n"), comms_wake); + fprintf_P(crc_stdout, PSTR("wake=%hhu\n"), wake_secs); + fprintf_P(crc_stdout, PSTR("fridge=%.1f\n"), fridge_setpoint/10.0); + fprintf_P(crc_stdout, PSTR("fridge_diff=%.1f\n"), fridge_difference/10.0); + fprintf_P(crc_stdout, PSTR("fridge_delay=%hu\n"), fridge_delay); + fprintf_P(crc_stdout, PSTR("overshoot_factor=%.1f\n"), overshoot_factor/10.0); + fprintf_P(crc_stdout, PSTR("overshoot_delay=%hu\n"), overshoot_delay); + fprintf_P(crc_stdout, PSTR("fridge_status=%hhu\n"), is_fridge_on()); + fprintf_P(crc_stdout, PSTR("fridge_last_on=%lu\n"), fridge_on_clock.ticks); + fprintf_P(crc_stdout, PSTR("fridge_last_off=%lu\n"), fridge_off_clock.ticks); + fprintf_P(crc_stdout, PSTR("last_fridge=%hu\n"), last_fridge); + fprintf_P(crc_stdout, PSTR("last_wort=%hu\n"), last_wort); + fprintf_P(crc_stdout, PSTR("tick_secs=%d\n"), TICK); + fprintf_P(crc_stdout, PSTR("tick_wake=%d\n"), SLEEP_COMPARE); + fprintf_P(crc_stdout, PSTR("maxsens=%hhu\n"), MAX_SENSORS); + fprintf_P(crc_stdout, PSTR("totalmeas=%hu\n"), TOTAL_MEASUREMENTS); + fprintf_P(crc_stdout, PSTR("sensors=%hhu\n"), n_sensors); + for (uint8_t s = 0; s < n_sensors; s++) + { + fprintf_P(crc_stdout, PSTR("sensor_id%hhu="), s); + printhex(sensor_id[s], ID_LEN, crc_stdout); + fputc('\n', crc_stdout); + } + fprintf_P(crc_stdout, PSTR("measurements=%hu\n"), n_measurements); + for (uint16_t n = 0; n < n_measurements; n++) + { + fprintf_P(crc_stdout, PSTR("meas%hu="), n); + for (uint8_t s = 0; s < n_sensors; s++) + { + fprintf_P(crc_stdout, PSTR(" %04hx"), get_measurement(s, n)); + } + fputc('\n', crc_stdout); + } + fprintf_P(crc_stdout, PSTR("END\n")); + fprintf_P(stdout, PSTR("CRC=%hu\n"), crc_out); +} + +static void +cmd_clear() +{ + n_measurements = 0; + printf_P(PSTR("cleared\n")); +} + +static void +cmd_btoff() +{ + uint8_t rem; + uint16_t count_copy; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + count_copy = comms_count; + rem = TCNT2; + } + printf_P(PSTR("next_wake=%hu,"), comms_wake-count_copy); + printf_P(PSTR("rem=%hhu,"), rem); + printf_P(PSTR("tick_secs=%hhu,"), TICK); + printf_P(PSTR("tick_wake=%hhu\n"), SLEEP_COMPARE); + _delay_ms(100); + comms_timeout = 0; + stay_awake = 0; +} + +static void +cmd_reset() +{ + printf_P(PSTR("reset\n")); + _delay_ms(100); + cli(); // disable interrupts + wdt_enable(WDTO_15MS); // enable watchdog + while(1); // wait for watchdog to reset processor +} + +static void +cmd_measure() +{ + printf_P(PSTR("measuring\n")); + need_measurement = 1; +} + +static void +cmd_sensors() +{ + uint8_t ret = simple_ds18b20_start_meas(NULL); + printf_P(PSTR("All sensors, ret %hhu, waiting...\n"), ret); + long_delay(DS18B20_TCONV_12BIT); + simple_ds18b20_read_all(); +} + +static void +init_sensors() +{ + uint8_t id[OW_ROMCODE_SIZE]; + printf_P(PSTR("init sensors\n")); + ow_reset(); + for( uint8_t diff = OW_SEARCH_FIRST; diff != OW_LAST_DEVICE; ) + { + diff = ow_rom_search( diff, &id[0] ); + if( diff == OW_PRESENCE_ERR ) { + printf_P( PSTR("No Sensor found\r") ); + return; + } + + if( diff == OW_DATA_ERR ) { + printf_P( PSTR("Bus Error\r") ); + return; + } + + if (n_sensors < MAX_SENSORS) + { + memcpy(sensor_id[n_sensors], id, ID_LEN); + printf_P(PSTR("Added sensor %hhu : "), n_sensors); + printhex(id, ID_LEN, stdout); + putchar('\n'); + n_sensors++; + } + else + { + printf_P(PSTR("Too many sensors\n")); + } + } + + max_measurements = TOTAL_MEASUREMENTS / n_sensors; +} + +static void +load_params() +{ + uint16_t magic; + eeprom_read(magic, magic); + if (magic == EXPECT_MAGIC) + { + eeprom_read(measure_wake, measure_wake); + eeprom_read(comms_wake, comms_wake); + eeprom_read(wake_secs, wake_secs); + eeprom_read(fridge_setpoint, fridge_setpoint); + eeprom_read(fridge_difference, fridge_difference); + eeprom_read(fridge_delay, fridge_delay); + eeprom_read(overshoot_delay, overshoot_delay); + eeprom_read(overshoot_factor, overshoot_factor); + } +} + +static void +cmd_get_params() +{ + printf_P(PSTR("measure %hu\n"), measure_wake); + printf_P(PSTR("comms %hu\n"), comms_wake); + printf_P(PSTR("wake %hhu\n"), wake_secs); + printf_P(PSTR("tick %d\n"), TICK); + printf_P(PSTR("fridge %.1fº\n"), fridge_setpoint / 10.0f); + printf_P(PSTR("fridge difference %.1fº\n"), fridge_difference / 10.0f); + printf_P(PSTR("fridge_delay %hu\n"), fridge_delay); + printf_P(PSTR("overshoot factor %.1fº\n"), overshoot_factor / 10.0f); + printf_P(PSTR("overshoot delay %hu\n"), overshoot_delay); + printf_P(PSTR("sensors %hhu (%hhu)\n"), + n_sensors, MAX_SENSORS); + printf_P(PSTR("meas %hu (%hu)\n"), + max_measurements, TOTAL_MEASUREMENTS); +} + +static void +cmd_set_params(const char *params) +{ + uint16_t new_measure_wake; + uint16_t new_comms_wake; + uint8_t new_wake_secs; + int ret = sscanf_P(params, PSTR("%hu %hu %hhu"), + &new_measure_wake, &new_comms_wake, &new_wake_secs); + + if (ret != 3) + { + printf_P(PSTR("Bad values\n")); + } + else + { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + eeprom_write(new_measure_wake, measure_wake); + eeprom_write(new_comms_wake, comms_wake); + eeprom_write(new_wake_secs, wake_secs); + uint16_t magic = EXPECT_MAGIC; + eeprom_write(magic, magic); + } + printf_P(PSTR("set_params for next boot\n")); + printf_P(PSTR("measure %hu comms %hu wake %hhu\n"), + new_measure_wake, new_comms_wake, new_wake_secs); + } +} + +// returns true if eeprom was written +static bool +set_initial_eeprom() +{ + uint16_t magic; + eeprom_read(magic, magic); + if (magic == EXPECT_MAGIC) + { + return false; + } + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + eeprom_write(measure_wake, measure_wake); + eeprom_write(comms_wake, comms_wake); + eeprom_write(wake_secs, wake_secs); + eeprom_write(fridge_setpoint, fridge_setpoint); + eeprom_write(fridge_difference, fridge_difference); + eeprom_write(fridge_delay, fridge_delay); + eeprom_write(overshoot_delay, overshoot_delay); + eeprom_write(overshoot_factor, overshoot_factor); + magic = EXPECT_MAGIC; + eeprom_write(magic, magic); + } + + return true; +} + +static void +cmd_set_fridge_setpoint(char *params) +{ + float new_f = atof(params); + if (new_f < 2 || new_f > 30) + { + printf_P(PSTR("Bad fridge value %f\n"), new_f); + return; + } + + int16_t old_setpoint = fridge_setpoint; + + fridge_setpoint = new_f * 10; + bool written = set_initial_eeprom(); + if (!written) + { + if (old_setpoint != fridge_setpoint) + { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + eeprom_write(fridge_setpoint, fridge_setpoint); + } + } + } + printf_P(PSTR("old fridge %.1fº new fridge %.1fº\n"), + old_setpoint / 10.0f, fridge_setpoint / 10.0f); +} + +static void +cmd_set_fridge_difference(char *params) +{ + float new_f = atof(params); + if (new_f < 0 || new_f > 30) + { + printf_P(PSTR("Bad fridge value %f\n"), new_f); + return; + } + + fridge_difference = new_f * 10; + bool written = set_initial_eeprom(); + if (!written) + { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + eeprom_write(fridge_difference, fridge_difference); + } + } + printf_P(PSTR("new fridge difference %.1fº\n"), fridge_difference / 10.0f); +} + +static void +cmd_set_fridge_delay(char *params) +{ + uint16_t new_delay = atoi(params); + if (new_delay < 5) + { + printf_P(PSTR("Bad fridge delay %d\n"), new_delay); + return; + } + + fridge_delay = new_delay; + bool written = set_initial_eeprom(); + if (!written) + { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + eeprom_write(fridge_delay, fridge_delay); + } + } + printf_P(PSTR("new fridge delay %hu\n"), fridge_delay); +} + +static void +cmd_set_overshoot_factor(char *params) +{ + float new_f = atof(params); + if (new_f <= 0 || new_f > 20) + { + printf_P(PSTR("Bad overshoot factor %f\n"), new_f); + return; + } + + uint8_t old = overshoot_factor; + + overshoot_factor = new_f * 10; + bool written = set_initial_eeprom(); + if (!written) + { + if (old != overshoot_factor) + { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + eeprom_write(overshoot_factor, overshoot_factor); + } + } + } + printf_P(PSTR("old factor %.1fº new factor %.1fº\n"), + old / 10.0f, overshoot_factor / 10.0f); +} + +static void +cmd_set_overshoot_delay(char *params) +{ + uint16_t new_delay = atoi(params); + if (new_delay < 5) + { + printf_P(PSTR("Bad overshoot delay %d\n"), new_delay); + return; + } + + overshoot_delay = new_delay; + bool written = set_initial_eeprom(); + if (!written) + { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + eeprom_write(overshoot_delay, overshoot_delay); + } + } + printf_P(PSTR("new overshoot delay %hu\n"), overshoot_delay); +} + +static void +cmd_awake() +{ + stay_awake = 1; + printf_P(PSTR("awake\n")); +} + +static void +read_handler() +{ + if (strcmp_P(readbuf, PSTR("fetch")) == 0) + { + cmd_fetch(); + } + else if (strcmp_P(readbuf, PSTR("clear")) == 0) + { + cmd_clear(); + } + else if (strcmp_P(readbuf, PSTR("btoff")) == 0) + { + cmd_btoff(); + } + else if (strcmp_P(readbuf, PSTR("measure")) == 0) + { + cmd_measure(); + } + else if (strcmp_P(readbuf, PSTR("sensors")) == 0) + { + cmd_sensors(); + } + else if (strcmp_P(readbuf, PSTR("get_params")) == 0) + { + cmd_get_params(); + } + else if (strncmp_P(readbuf, PSTR("set_params "), 11) == 0) + { + cmd_set_params(&readbuf[11]); + } + else if (strcmp_P(readbuf, PSTR("awake")) == 0) + { + cmd_awake(); + } + else if (strncmp_P(readbuf, PSTR("fridge_setpoint "), 16) == 0) + { + cmd_set_fridge_setpoint(&readbuf[16]); + } + else if (strncmp_P(readbuf, PSTR("fridge_diff "), 12) == 0) + { + cmd_set_fridge_difference(&readbuf[12]); + } + else if (strncmp_P(readbuf, PSTR("fridge_delay "), 13) == 0) + { + cmd_set_fridge_delay(&readbuf[13]); + } + else if (strncmp_P(readbuf, PSTR("overshoot_delay "), 16) == 0) + { + cmd_set_overshoot_delay(&readbuf[16]); + } + else if (strncmp_P(readbuf, PSTR("overshoot_factor "), 17) == 0) + { + cmd_set_overshoot_factor(&readbuf[17]); + } + else if (strcmp_P(readbuf, PSTR("reset")) == 0) + { + cmd_reset(); + } + else + { + printf_P(PSTR("Bad command '%s'\n"), readbuf); + } +} + +ISR(INT0_vect) +{ + button_pressed = 1; + blink(); + _delay_ms(100); + blink(); +} + + +ISR(USART_RX_vect) +{ + char c = UDR0; +#ifdef HAVE_UART_ECHO + uart_putchar(c, NULL); +#endif + if (c == '\r' || c == '\n') + { + if (readpos > 0) + { + readbuf[readpos] = '\0'; + have_cmd = 1; + readpos = 0; + } + } + else + { + readbuf[readpos] = c; + readpos++; + if (readpos >= sizeof(readbuf)) + { + readpos = 0; + } + } +} + +ISR(TIMER2_COMPA_vect) +{ + TCNT2 = 0; + measure_count += TICK; + comms_count += TICK; + + clock_epoch += TICK; + + if (comms_timeout != 0) + { + comms_timeout -= TICK; + } + + if (measure_count >= measure_wake) + { + measure_count = 0; + need_measurement = 1; + } + + if (comms_count >= comms_wake) + { + comms_count = 0; + need_comms = 1; + } +} + +static void +deep_sleep() +{ + // p119 of manual + OCR2A = SLEEP_COMPARE; + loop_until_bit_is_clear(ASSR, OCR2AUB); + + set_sleep_mode(SLEEP_MODE_PWR_SAVE); + sleep_mode(); +} + +static void +idle_sleep() +{ + set_sleep_mode(SLEEP_MODE_IDLE); + sleep_mode(); +} + +static uint16_t +adc_vcc() +{ + PRR &= ~_BV(PRADC); + + // /16 prescaler + ADCSRA = _BV(ADEN) | _BV(ADPS2); + + // set to measure 1.1 reference + ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); + // average a number of samples + uint16_t sum = 0; + uint8_t num = 0; + for (uint8_t n = 0; n < 20; n++) + { + ADCSRA |= _BV(ADSC); + loop_until_bit_is_clear(ADCSRA, ADSC); + + uint8_t low_11 = ADCL; + uint8_t high_11 = ADCH; + uint16_t val = low_11 + (high_11 << 8); + + if (n >= 4) + { + sum += val; + num++; + } + } + ADCSRA = 0; + PRR |= _BV(PRADC); + + //float res_volts = 1.1 * 1024 * num / sum; + //return 1000 * res_volts; + return ((uint32_t)1100*1024*num) / sum; +} + +static void +do_fridge() +{ + struct epoch_ticks now; + get_epoch_ticks(&now); + uint32_t off_time = now.ticks - fridge_off_clock.ticks; + bool wort_valid = last_wort != DS18X20_INVALID_DECICELSIUS; + bool fridge_valid = last_fridge != DS18X20_INVALID_DECICELSIUS; + + int16_t wort_max = fridge_setpoint + fridge_difference; + int16_t wort_min = fridge_setpoint; + + // the fridge min/max only apply if the wort sensor is broken + int16_t fridge_min = fridge_setpoint - FRIDGE_AIR_MIN_RANGE; + int16_t fridge_max = fridge_setpoint + FRIDGE_AIR_MAX_RANGE; + + uint8_t fridge_on = PORT_FRIDGE & _BV(PIN_FRIDGE); + printf_P(PSTR("last_wort %hd (%hd, %hd), last_fridge %hd (%hd, %hd), setpoint %hd, diff %hd, fridge_on %hhu\n"), + last_wort, wort_min, wort_max, + last_fridge, fridge_min, fridge_max, + fridge_setpoint, fridge_difference, fridge_on); + + if (off_time < fridge_delay) + { + printf_P(PSTR("waiting for fridge delay current %hu, wait %hu\n"), + off_time, fridge_delay); + return; + } + + // handle failure of the wort sensor. if it is a short (intermittent?) + // failure we wait until it has been broken for a period of time + // (WORT_INVALID_TIME) before doing anything. + if (wort_valid) + { + wort_valid_clock = now; + } + else + { + printf_P(PSTR("wort sensor is invalid\n")); + uint32_t invalid_time = now.ticks - wort_valid_clock.ticks; + if (invalid_time < WORT_INVALID_TIME) + { + printf("only been invalid for %ld, waiting\n", invalid_time); + return; + } + } + + if (!fridge_valid) + { + printf_P(PSTR("fridge sensor is invalid\n")); + } + + if (fridge_on) + { + bool turn_off = false; + uint16_t on_time = now.ticks - fridge_on_clock.ticks; + + uint16_t overshoot = 0; + if (on_time > overshoot_delay) + { + overshoot = overshoot_factor * MIN(OVERSHOOT_MAX_DIV, on_time) / OVERSHOOT_MAX_DIV; + } + + printf_P(PSTR("on_time %hu, overshoot %hu\n"), on_time, overshoot); + + // wort has cooled enough. will probably cool a bit more by itself + if (wort_valid) + { + if ((last_wort - overshoot) < fridge_setpoint) + { + printf_P(PSTR("wort has cooled enough, overshoot %hu on_time %hu\n"), overshoot, on_time); + turn_off = true; + } + } + else + { + if (fridge_valid && last_fridge < fridge_min) + { + printf_P(PSTR("fridge off fallback\n")); + turn_off = true; + } + } + + if (turn_off) + { + // too cold, turn off + printf_P(PSTR("Turning fridge off\n")); + PORT_FRIDGE &= ~_BV(PIN_FRIDGE); + fridge_off_clock = now; + } + } + else + { + bool turn_on = false; + + if (wort_valid) + { + if (last_wort >= wort_max) + { + printf_P(PSTR("wort is too hot\n")); + turn_on = true; + } + } + else + { + if (fridge_valid && last_fridge >= fridge_max) + { + printf_P(PSTR("fridge on fallback\n")); + turn_on = true; + } + } + + if (turn_on) + { + // too hot, turn on + printf_P(PSTR("Turning fridge on\n")); + PORT_FRIDGE |= _BV(PIN_FRIDGE); + fridge_on_clock = now; + } + } +} + +static void +do_measurement() +{ + blink(); + + /* Take the timer here since deep_sleep() below could take 6 seconds */ + get_epoch_ticks(&last_measurement_clock); + if (n_measurements == 0) + { + first_measurement_clock = last_measurement_clock; + } + + simple_ds18b20_start_meas(NULL); + _delay_ms(DS18B20_TCONV_12BIT); + + if (n_measurements == max_measurements) + { + n_measurements = 0; + } + + for (uint8_t s = 0; s < n_sensors; s++) + { + uint16_t reading; + uint8_t ret = simple_ds18b20_read_raw(sensor_id[s], &reading); + if (ret != DS18X20_OK) + { + reading = VALUE_BROKEN; + } + set_measurement(s, n_measurements, reading); + + if (memcmp(sensor_id[s], fridge_id, sizeof(fridge_id)) == 0) + { + last_fridge = ds18b20_raw16_to_decicelsius(reading); + } + if (memcmp(sensor_id[s], wort_id, sizeof(wort_id)) == 0) + { + last_wort = ds18b20_raw16_to_decicelsius(reading); + } + } + + n_measurements++; +} + +static void +do_comms() +{ + get_epoch_ticks(&last_comms_clock); + + // turn on bluetooth + set_aux_power(1); + // avoid receiving rubbish, perhaps + _delay_ms(50); + uart_on(); + + // write sd card here? same 3.3v regulator... + + for (comms_timeout = wake_secs; + comms_timeout > 0 || stay_awake; + ) + { + if (need_measurement) + { + need_measurement = 0; + do_measurement(); + do_fridge(); + continue; + } + + if (have_cmd) + { + have_cmd = 0; + read_handler(); + continue; + } + + // wait for commands from the master + idle_sleep(); + } + + uart_off(); + // in case bluetooth takes time to flush + _delay_ms(100); + set_aux_power(0); +} + +static void +blink() +{ + PORT_LED &= ~_BV(PIN_LED); + _delay_ms(1); + PORT_LED |= _BV(PIN_LED); +} + +static void +long_delay(int ms) +{ + int iter = ms / 100; + + for (int i = 0; i < iter; i++) + { + _delay_ms(100); + } +} + +ISR(BADISR_vect) +{ + //uart_on(); + printf_P(PSTR("Bad interrupt\n")); +} + +int main(void) +{ + setup_chip(); + blink(); + + set_aux_power(0); + + stdout = &mystdout; + uart_on(); + + printf(PSTR("Started.\n")); + + load_params(); + + init_sensors(); + + uart_off(); + + // turn off everything except timer2 + PRR = _BV(PRTWI) | _BV(PRTIM0) | _BV(PRTIM1) | _BV(PRSPI) | _BV(PRUSART0) | _BV(PRADC); + + setup_tick_counter(); + + sei(); + + need_comms = 1; + need_measurement = 1; + + stay_awake = 1; + + for(;;) + { + if (button_pressed) + { + // debounce + _delay_ms(200); + need_comms = 1; + comms_timeout = wake_secs; + button_pressed = 0; + continue; + } + + if (need_comms) + { + need_comms = 0; + do_comms(); + continue; + } + + if (need_measurement) + { + need_measurement = 0; + do_measurement(); + do_fridge(); + continue; + } + + deep_sleep(); + } + + return 0; /* never reached */ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/old/onewire.c Thu Jun 26 23:03:32 2014 +0800 @@ -0,0 +1,287 @@ +/* +Access Dallas 1-Wire Devices with ATMEL AVRs +Author of the initial code: Peter Dannegger (danni(at)specs.de) +modified by Martin Thomas (mthomas(at)rhrk.uni-kl.de) + 9/2004 - use of delay.h, optional bus configuration at runtime +10/2009 - additional delay in ow_bit_io for recovery + 5/2010 - timing modifcations, additonal config-values and comments, + use of atomic.h macros, internal pull-up support + 7/2010 - added method to skip recovery time after last bit transfered + via ow_command_skip_last_recovery +*/ + + +#include <avr/io.h> +#include <util/delay.h> +#include <util/atomic.h> + +#include "onewire.h" + +#ifdef OW_ONE_BUS + +#define OW_GET_IN() ( OW_IN & (1<<OW_PIN)) +#define OW_OUT_LOW() ( OW_OUT &= (~(1 << OW_PIN)) ) +#define OW_OUT_HIGH() ( OW_OUT |= (1 << OW_PIN) ) +#define OW_DIR_IN() ( OW_DDR &= (~(1 << OW_PIN )) ) +#define OW_DIR_OUT() ( OW_DDR |= (1 << OW_PIN) ) + +#else + +/* set bus-config with ow_set_bus() */ +uint8_t OW_PIN_MASK; +volatile uint8_t* OW_IN; +volatile uint8_t* OW_OUT; +volatile uint8_t* OW_DDR; + +#define OW_GET_IN() ( *OW_IN & OW_PIN_MASK ) +#define OW_OUT_LOW() ( *OW_OUT &= (uint8_t) ~OW_PIN_MASK ) +#define OW_OUT_HIGH() ( *OW_OUT |= (uint8_t) OW_PIN_MASK ) +#define OW_DIR_IN() ( *OW_DDR &= (uint8_t) ~OW_PIN_MASK ) +#define OW_DIR_OUT() ( *OW_DDR |= (uint8_t) OW_PIN_MASK ) + +void ow_set_bus(volatile uint8_t* in, + volatile uint8_t* out, + volatile uint8_t* ddr, + uint8_t pin) +{ + OW_DDR=ddr; + OW_OUT=out; + OW_IN=in; + OW_PIN_MASK = (1 << pin); + ow_reset(); +} + +#endif + +uint8_t ow_input_pin_state() +{ + return OW_GET_IN(); +} + +void ow_parasite_enable(void) +{ + OW_OUT_HIGH(); + OW_DIR_OUT(); +} + +void ow_parasite_disable(void) +{ + OW_DIR_IN(); +#if (!OW_USE_INTERNAL_PULLUP) + OW_OUT_LOW(); +#endif +} + + +uint8_t ow_reset(void) +{ + uint8_t err; + + OW_OUT_LOW(); + OW_DIR_OUT(); // pull OW-Pin low for 480us + _delay_us(480); + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + // set Pin as input - wait for clients to pull low + OW_DIR_IN(); // input +#if OW_USE_INTERNAL_PULLUP + OW_OUT_HIGH(); +#endif + + _delay_us(64); // was 66 + err = OW_GET_IN(); // no presence detect + // if err!=0: nobody pulled to low, still high + } + + // after a delay the clients should release the line + // and input-pin gets back to high by pull-up-resistor + _delay_us(480 - 64); // was 480-66 + if( OW_GET_IN() == 0 ) { + err = 1; // short circuit, expected low but got high + } + + return err; +} + + +/* Timing issue when using runtime-bus-selection (!OW_ONE_BUS): + The master should sample at the end of the 15-slot after initiating + the read-time-slot. The variable bus-settings need more + cycles than the constant ones so the delays had to be shortened + to achive a 15uS overall delay + Setting/clearing a bit in I/O Register needs 1 cyle in OW_ONE_BUS + but around 14 cyles in configureable bus (us-Delay is 4 cyles per uS) */ +static uint8_t ow_bit_io_intern( uint8_t b, uint8_t with_parasite_enable ) +{ + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { +#if OW_USE_INTERNAL_PULLUP + OW_OUT_LOW(); +#endif + OW_DIR_OUT(); // drive bus low + _delay_us(2); // T_INT > 1usec accoding to timing-diagramm + if ( b ) { + OW_DIR_IN(); // to write "1" release bus, resistor pulls high +#if OW_USE_INTERNAL_PULLUP + OW_OUT_HIGH(); +#endif + } + + // "Output data from the DS18B20 is valid for 15usec after the falling + // edge that initiated the read time slot. Therefore, the master must + // release the bus and then sample the bus state within 15ussec from + // the start of the slot." + _delay_us(15-2-OW_CONF_DELAYOFFSET); + + if( OW_GET_IN() == 0 ) { + b = 0; // sample at end of read-timeslot + } + + _delay_us(60-15-2+OW_CONF_DELAYOFFSET); +#if OW_USE_INTERNAL_PULLUP + OW_OUT_HIGH(); +#endif + OW_DIR_IN(); + + if ( with_parasite_enable ) { + ow_parasite_enable(); + } + + } /* ATOMIC_BLOCK */ + + _delay_us(OW_RECOVERY_TIME); // may be increased for longer wires + + return b; +} + +uint8_t ow_bit_io( uint8_t b ) +{ + return ow_bit_io_intern( b & 1, 0 ); +} + +uint8_t ow_byte_wr( uint8_t b ) +{ + uint8_t i = 8, j; + + do { + j = ow_bit_io( b & 1 ); + b >>= 1; + if( j ) { + b |= 0x80; + } + } while( --i ); + + return b; +} + +uint8_t ow_byte_wr_with_parasite_enable( uint8_t b ) +{ + uint8_t i = 8, j; + + do { + if ( i != 1 ) { + j = ow_bit_io_intern( b & 1, 0 ); + } else { + j = ow_bit_io_intern( b & 1, 1 ); + } + b >>= 1; + if( j ) { + b |= 0x80; + } + } while( --i ); + + return b; +} + + +uint8_t ow_byte_rd( void ) +{ + // read by sending only "1"s, so bus gets released + // after the init low-pulse in every slot + return ow_byte_wr( 0xFF ); +} + + +uint8_t ow_rom_search( uint8_t diff, uint8_t *id ) +{ + uint8_t i, j, next_diff; + uint8_t b; + + if( ow_reset() ) { + return OW_PRESENCE_ERR; // error, no device found <--- early exit! + } + + ow_byte_wr( OW_SEARCH_ROM ); // ROM search command + next_diff = OW_LAST_DEVICE; // unchanged on last device + + i = OW_ROMCODE_SIZE * 8; // 8 bytes + + do { + j = 8; // 8 bits + do { + b = ow_bit_io( 1 ); // read bit + if( ow_bit_io( 1 ) ) { // read complement bit + if( b ) { // 0b11 + return OW_DATA_ERR; // data error <--- early exit! + } + } + else { + if( !b ) { // 0b00 = 2 devices + if( diff > i || ((*id & 1) && diff != i) ) { + b = 1; // now 1 + next_diff = i; // next pass 0 + } + } + } + ow_bit_io( b ); // write bit + *id >>= 1; + if( b ) { + *id |= 0x80; // store bit + } + + i--; + + } while( --j ); + + id++; // next byte + + } while( i ); + + return next_diff; // to continue search +} + + +static void ow_command_intern( uint8_t command, uint8_t *id, uint8_t with_parasite_enable ) +{ + uint8_t i; + + ow_reset(); + + if( id ) { + ow_byte_wr( OW_MATCH_ROM ); // to a single device + i = OW_ROMCODE_SIZE; + do { + ow_byte_wr( *id ); + id++; + } while( --i ); + } + else { + ow_byte_wr( OW_SKIP_ROM ); // to all devices + } + + if ( with_parasite_enable ) { + ow_byte_wr_with_parasite_enable( command ); + } else { + ow_byte_wr( command ); + } +} + +void ow_command( uint8_t command, uint8_t *id ) +{ + ow_command_intern( command, id, 0); +} + +void ow_command_with_parasite_enable( uint8_t command, uint8_t *id ) +{ + ow_command_intern( command, id, 1 ); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/old/onewire.h Thu Jun 26 23:03:32 2014 +0800 @@ -0,0 +1,93 @@ +#ifndef ONEWIRE_H_ +#define ONEWIRE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +/*******************************************/ +/* Hardware connection */ +/*******************************************/ + +/* Define OW_ONE_BUS if only one 1-Wire-Bus is used + in the application -> shorter code. + If not defined make sure to call ow_set_bus() before using + a bus. Runtime bus-select increases code size by around 300 + bytes so use OW_ONE_BUS if possible */ +#define OW_ONE_BUS + +#ifdef OW_ONE_BUS + +#define OW_PIN PB1 +#define OW_IN PINB +#define OW_OUT PORTB +#define OW_DDR DDRB +#define OW_CONF_DELAYOFFSET 0 + +#else +#if ( F_CPU < 1843200 ) +#warning | Experimental multi-bus-mode is not tested for +#warning | frequencies below 1,84MHz. Use OW_ONE_WIRE or +#warning | faster clock-source (i.e. internal 2MHz R/C-Osc.). +#endif +#define OW_CONF_CYCLESPERACCESS 13 +#define OW_CONF_DELAYOFFSET ( (uint16_t)( ((OW_CONF_CYCLESPERACCESS) * 1000000L) / F_CPU ) ) +#endif + +// Recovery time (T_Rec) minimum 1usec - increase for long lines +// 5 usecs is a value give in some Maxim AppNotes +// 30u secs seem to be reliable for longer lines +//#define OW_RECOVERY_TIME 5 /* usec */ +//#define OW_RECOVERY_TIME 300 /* usec */ +#define OW_RECOVERY_TIME 10 /* usec */ + +// Use AVR's internal pull-up resistor instead of external 4,7k resistor. +// Based on information from Sascha Schade. Experimental but worked in tests +// with one DS18B20 and one DS18S20 on a rather short bus (60cm), where both +// sensores have been parasite-powered. +#define OW_USE_INTERNAL_PULLUP 1 /* 0=external, 1=internal */ + +/*******************************************/ + + +#define OW_MATCH_ROM 0x55 +#define OW_SKIP_ROM 0xCC +#define OW_SEARCH_ROM 0xF0 + +#define OW_SEARCH_FIRST 0xFF // start new search +#define OW_PRESENCE_ERR 0xFF +#define OW_DATA_ERR 0xFE +#define OW_LAST_DEVICE 0x00 // last device found + +// rom-code size including CRC +#define OW_ROMCODE_SIZE 8 + +extern uint8_t ow_reset(void); + +extern uint8_t ow_bit_io( uint8_t b ); +extern uint8_t ow_byte_wr( uint8_t b ); +extern uint8_t ow_byte_rd( void ); + +extern uint8_t ow_rom_search( uint8_t diff, uint8_t *id ); + +extern void ow_command( uint8_t command, uint8_t *id ); +extern void ow_command_with_parasite_enable( uint8_t command, uint8_t *id ); + +extern void ow_parasite_enable( void ); +extern void ow_parasite_disable( void ); +extern uint8_t ow_input_pin_state( void ); + +#ifndef OW_ONE_BUS +extern void ow_set_bus( volatile uint8_t* in, + volatile uint8_t* out, + volatile uint8_t* ddr, + uint8_t pin ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/old/server/config.py Thu Jun 26 23:03:32 2014 +0800 @@ -0,0 +1,1 @@ +../web/config.py \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/old/server/dump.py Thu Jun 26 23:03:32 2014 +0800 @@ -0,0 +1,190 @@ +#!/usr/bin/env python2.7 + +BTADDR = "00:12:03:27:70:88" +SLEEP_TIME = 5 +# time that the bluetooth takes to get going? +EXTRA_WAKEUP = 0 + +FETCH_TRIES = 3 + +# avoid turning off the bluetooth etc. +TESTING = False + +import sys +# for wrt +sys.path.append('/root/python') +import httplib +import time +import traceback +import binascii +import hmac +import zlib +import urllib +import urllib2 + +import config + +from utils import monotonic_time, retry, readline, crc16 + +lightblue = None +try: + import lightblue +except ImportError: + import bluetooth + +def get_socket(addr): + if lightblue: + s = lightblue.socket() + s.connect((addr, 1)) + s.settimeout(3) + else: + s = bluetooth.BluetoothSocket( bluetooth.RFCOMM ) + s.connect((addr, 1)) + + s.setblocking(False) + + return s + + +@retry() +def fetch(sock): + print "fetch" + sock.send("fetch\n") + + crc = 0 + + lines = [] + l = readline(sock) + if l != 'START\n': + print>>sys.stderr, "Bad expected START line '%s'\n" % l.rstrip('\n') + return None + crc = crc16(l, crc) + + while True: + l = readline(sock) + + crc = crc16(l, crc) + + if l == 'END\n': + break + + lines.append(l.rstrip('\n')) + + print lines + + l = readline(sock) + recv_crc = None + try: + k, v = l.rstrip('\n').split('=') + print k,v + if k == 'CRC': + recv_crc = int(v) + if recv_crc < 0 or recv_crc > 0xffff: + recv_crc = None + except ValueError: + pass + + if recv_crc is None: + print>>sys.stderr, "Bad expected CRC line '%s'\n" % l.rstrip('\n') + return None + + if recv_crc != crc: + print>>sys.stderr, "Bad CRC: calculated 0x%x vs received 0x%x\n" % (crc, recv_crc) + return None + + return lines + +@retry() +def turn_off(sock): + if TESTING: + return 99 + print>>sys.stderr, "sending btoff" + sock.send("btoff\n"); + # read newline + l = readline(sock) + if not l: + print>>sys.stderr, "Bad response to btoff\n" + return None + + if not l.startswith('off:'): + print>>sys.stderr, "Bad response to btoff '%s'\n" % l + return None + off, next_wake = l.rstrip().split(':') + print>>sys.stderr, "Next wake %s" % next_wake + + return int(next_wake) + +@retry() +def clear_meas(sock): + sock.send("clear\n"); + l = readline(sock) + if l and l.rstrip() == 'cleared': + return True + + print>>sys.stderr, "Bad response to clear %s\n" % str(l) + return False + +def send_results(lines): + enc_lines = binascii.b2a_base64(zlib.compress('\n'.join(lines))) + mac = hmac.new(config.HMAC_KEY, enc_lines).hexdigest() + + url_data = urllib.urlencode( {'lines': enc_lines, 'hmac': mac} ) + con = urllib2.urlopen(config.UPDATE_URL, url_data) + result = con.read(100) + if result == 'OK': + return True + else: + print>>sys.stderr, "Bad result '%s'" % result + return False + +def do_comms(sock): + args = sys.argv[1:] + print "do_comms" + for a in args: + sock.send('%s\n' % a) + + while True: + l = readline(sock) + if not l: + print '.', + sys.stdout.flush() + else: + print l + +testcount = 0 + +def sleep_for(secs): + until = monotonic_time() + secs + while True: + length = until - monotonic_time() + if length <= 0: + return + time.sleep(length) + +def main(): + next_wake_time = 0 + + while True: + sock = None + try: + sock = get_socket(BTADDR) + except Exception, e: + print>>sys.stderr, "Error connecting:" + traceback.print_exc(file=sys.stderr) + sleep_time = SLEEP_TIME + if sock: + next_wake = None + try: + next_wake_interval = do_comms(sock) + next_wake_time = time.time() + next_wake_interval + except Exception, e: + print>>sys.stderr, "Error in do_comms:" + traceback.print_exc(file=sys.stderr) + if next_wake_time > time.time(): + sleep_time = min(next_wake_time - time.time() - EXTRA_WAKEUP, sleep_time) + + print "Sleeping for %d" % sleep_time + sleep_for(sleep_time) + +if __name__ == '__main__': + main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/old/server/ts.py Thu Jun 26 23:03:32 2014 +0800 @@ -0,0 +1,239 @@ +#!/usr/bin/env python2.7 + +# time that the bluetooth takes to get going? +EXTRA_WAKEUP = -3 + +FETCH_TRIES = 3 + +# avoid turning off the bluetooth etc. +TESTING = False + +import sys +# for wrt +sys.path.append('/root/python') +import httplib +import time +import traceback +import binascii +import hmac +import zlib +import urllib +import urllib2 +import logging +import socket + +L = logging.info +W = logging.warning +E = logging.error + +import config + +from utils import monotonic_time, retry, readline, crc16 +import utils + +import bluetooth + +def get_socket(addr): + s = bluetooth.BluetoothSocket( bluetooth.RFCOMM ) + L("connecting") + s.connect((addr, 1)) + s.setblocking(False) + s.settimeout(1) + + return s + + +def flush(sock): + ret = [] + while True: + l = readline(sock) + if l: + ret.append(l) + else: + break + return ret + +def encode_extra(extra_lines): + return ['extra%d=%s' % (n, l.strip()) for (n,l) in enumerate(extra_lines)] + +@retry() +def fetch(sock): + extra_lines = flush(sock) + sock.send("fetch\n") + + crc = 0 + + lines = [] + l = readline(sock) + if not l: + return None + + if l != 'START\n': + W("Bad expected START line '%s'\n" % l.rstrip('\n')) + extra_lines.append(l) + return encode_extra(extra_lines) + crc = crc16(l, crc) + + while True: + l = readline(sock) + + crc = crc16(l, crc) + + if l == 'END\n': + break + + lines.append(l.rstrip('\n')) + + lines += encode_extra(extra_lines) + + for d in lines: + L("Received: %s" % d) + + l = readline(sock) + recv_crc = None + try: + k, v = l.rstrip('\n').split('=') + if k == 'CRC': + recv_crc = int(v) + if recv_crc < 0 or recv_crc > 0xffff: + recv_crc = None + except ValueError: + pass + + if recv_crc is None: + W("Bad expected CRC line '%s'\n" % l.rstrip('\n')) + return None + + if recv_crc != crc: + W("Bad CRC: calculated 0x%x vs received 0x%x\n" % (crc, recv_crc)) + return None + + return lines + +@retry() +def turn_off(sock): + if TESTING: + return 99 + L("Sending btoff") + flush(sock) + sock.send("btoff\n"); + # read newline + l = readline(sock) + if not l: + W("Bad response to btoff") + return None + + if not l.startswith('next_wake'): + W("Bad response to btoff '%s'" % l) + return None + L("Next wake line %s" % l) + + toks = dict(v.split('=') for v in l.split(',')) + + rem = int(toks['rem']) + tick_secs = int(toks['tick_secs']) + tick_wake = int(toks['tick_wake']) + 1 + next_wake = int(toks['next_wake']) + + rem_secs = float(rem) / tick_wake * tick_secs + + next_wake_secs = next_wake - rem_secs + L("next_wake_secs %f\n", next_wake_secs) + return next_wake_secs + +@retry() +def clear_meas(sock): + flush(sock) + sock.send("clear\n"); + l = readline(sock) + if l and l.rstrip() == 'cleared': + return True + + E("Bad response to clear '%s'" % str(l)) + return False + +def send_results(lines): + enc_lines = binascii.b2a_base64(zlib.compress('\n'.join(lines))) + mac = hmac.new(config.HMAC_KEY, enc_lines).hexdigest() + + url_data = urllib.urlencode( {'lines': enc_lines, 'hmac': mac} ) + con = urllib2.urlopen(config.UPDATE_URL, url_data) + result = con.read(100) + if result == 'OK': + return True + else: + W("Bad result '%s'" % result) + return False + +def do_comms(sock): + L("do_comms") + d = None + # serial could be unreliable, try a few times + d = fetch(sock) + if not d: + return + + res = send_results(d) + if not res: + return + + clear_meas(sock) + + next_wake = 600 + #next_wake = turn_off(sock) + #sock.close() + return next_wake + +testcount = 0 + +def sleep_for(secs): + until = monotonic_time() + secs + while True: + length = until - monotonic_time() + if length <= 0: + return + time.sleep(length) + +def setup_logging(): + logging.basicConfig(format='%(asctime)s %(message)s', + datefmt='%m/%d/%Y %I:%M:%S %p', + level=logging.INFO) + +def get_net_socket(host, port): + s = socket.create_connection((host, port)) + s.setblocking(False) + s.settimeout(1) + return s + +def main(): + setup_logging() + + L("Running templog rfcomm server") + + if '--daemon' in sys.argv: + utils.cheap_daemon() + + next_wake_time = 0 + + while True: + sock = None + try: + sock = get_net_socket(config.SERIAL_HOST, config.SERIAL_PORT) + except Exception, e: + #logging.exception("Error connecting") + pass + + if not sock: + sleep_for(config.SLEEP_TIME) + continue + + while True: + try: + do_comms(sock) + sleep_for(config.SLEEP_TIME) + except Exception, e: + logging.exception("Error in do_comms") + break + +if __name__ == '__main__': + main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/old/server/utils.py Thu Jun 26 23:03:32 2014 +0800 @@ -0,0 +1,127 @@ +import os +import sys +#import ctypes +import time +import select +import logging + +L = logging.info +W = logging.warning +E = logging.error + +DEFAULT_TRIES = 3 +READLINE_SELECT_TIMEOUT = 1 + +__all__ = ('monotonic_time', 'retry') + +clock_gettime = None +no_clock_gettime = True +def monotonic_time(): + global clock_gettime + global no_clock_gettime + if no_clock_gettime: + return time.time() + + class timespec(ctypes.Structure): + _fields_ = [ + ('tv_sec', ctypes.c_long), + ('tv_nsec', ctypes.c_long) + ] + if not clock_gettime: + try: + librt = ctypes.CDLL('librt.so.0', use_errno=True) + clock_gettime = librt.clock_gettime + clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)] + except: + W("No clock_gettime(), using fake fallback.") + no_clock_gettime = True + return time.time() + + t = timespec() + CLOCK_MONOTONIC = 1 # see <linux/time.h> + + if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(t)) != 0: + errno_ = ctypes.get_errno() + raise OSError(errno_, os.strerror(errno_)) + return t.tv_sec + t.tv_nsec * 1e-9 + +# decorator, tries a number of times, returns None on failure, sleeps between +# Must be used as "@retry()" if arguments are defaulted +def retry(retries=DEFAULT_TRIES, try_time = 1): + def inner(func): + def new_f(*args, **kwargs): + for i in range(retries): + d = func(*args, **kwargs) + if d is not None: + return d + time.sleep(try_time) + return None + + new_f.func_name = func.func_name + return new_f + return inner + +def readline(sock): + timeout = READLINE_SELECT_TIMEOUT + buf = '' + while True: + (rlist, wlist, xlist) = select.select([sock], [], [], timeout) + if sock not in rlist: + # hit timeout + return None + + c = sock.recv(1) + if c == '': + # lightblue timeout + return None + if c == '\r': + continue + + buf += c + if c == '\n': + return buf + +# from http://blog.stalkr.net/2011/04/pctf-2011-32-thats-no-bluetooth.html +def crc16(buff, crc = 0, poly = 0x8408): + l = len(buff) + i = 0 + while i < l: + ch = ord(buff[i]) + uc = 0 + while uc < 8: + if (crc & 1) ^ (ch & 1): + crc = (crc >> 1) ^ poly + else: + crc >>= 1 + ch >>= 1 + uc += 1 + i += 1 + return crc + +def cheap_daemon(): + L("Daemonising.") + sys.stdout.flush() + sys.stderr.flush() + out = file('/dev/null', 'a+') + os.dup2(out.fileno(), sys.stdout.fileno()) + os.dup2(out.fileno(), sys.stderr.fileno()) + + try: + pid = os.fork() + if pid > 0: + sys.exit(0) + except OSError, e: + E("Bad fork()") + sys.exit(1) + + os.setsid() + + try: + pid = os.fork() + if pid > 0: + sys.exit(0) + except OSError, e: + E("Bad fork()") + sys.exit(1) + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/old/simple_ds18b20.c Thu Jun 26 23:03:32 2014 +0800 @@ -0,0 +1,192 @@ +// Matt Johnston 2012 +// Based on ds18x20.c by Martin Thomas, in turn based on code by +// Peter // Dannegger and others. +// +#include <stdio.h> +#include <avr/pgmspace.h> + +#include "ds18x20.h" +#include "onewire.h" +#include "crc8.h" + +#include "simple_ds18b20.h" + +uint8_t +simple_ds18b20_start_meas(uint8_t id[]) +{ + uint8_t ret; + + ow_reset(); + if( ow_input_pin_state() ) { // only send if bus is "idle" = high + ow_command_with_parasite_enable(DS18X20_CONVERT_T, id); + ret = DS18X20_OK; + } + else { + ret = DS18X20_START_FAIL; + } + + return ret; +} + +static uint8_t +read_scratchpad( uint8_t id[], uint8_t sp[], uint8_t n ) +{ + uint8_t i; + uint8_t ret; + + ow_command( DS18X20_READ, id ); + for ( i = 0; i < n; i++ ) { + sp[i] = ow_byte_rd(); + } + if ( crc8( &sp[0], DS18X20_SP_SIZE ) ) { + ret = DS18X20_ERROR_CRC; + } else { + ret = DS18X20_OK; + } + + return ret; +} + +int16_t +ds18b20_raw16_to_decicelsius(uint16_t measure) +{ + uint8_t negative; + int16_t decicelsius; + uint16_t fract; + + // check for negative + if ( measure & 0x8000 ) { + negative = 1; // mark negative + measure ^= 0xffff; // convert to positive => (twos complement)++ + measure++; + } + else { + negative = 0; + } + + decicelsius = (measure >> 4); + decicelsius *= 10; + + // decicelsius += ((measure & 0x000F) * 640 + 512) / 1024; + // 625/1000 = 640/1024 + fract = ( measure & 0x000F ) * 640; + if ( !negative ) { + fract += 512; + } + fract /= 1024; + decicelsius += fract; + + if ( negative ) { + decicelsius = -decicelsius; + } + + if ( decicelsius == 850 || decicelsius < -550 || decicelsius > 1250 ) { + return DS18X20_INVALID_DECICELSIUS; + } else { + return decicelsius; + } +} + +uint8_t +simple_ds18b20_read_decicelsius( uint8_t id[], int16_t *decicelsius ) +{ + uint16_t reading; + uint8_t ret; + + ret = simple_ds18b20_read_raw(id, &reading); + if (ret == DS18X20_OK) + { + *decicelsius = ds18b20_raw16_to_decicelsius(reading); + } + return ret; +} + +uint8_t +simple_ds18b20_read_raw( uint8_t id[], uint16_t *reading ) +{ + uint8_t sp[DS18X20_SP_SIZE]; + uint8_t ret; + + if (id) + { + ow_reset(); + } + ret = read_scratchpad( id, sp, DS18X20_SP_SIZE ); + if ( ret == DS18X20_OK ) { + *reading = sp[0] | (sp[1] << 8); + } + return ret; +} + +static void +printhex_nibble(const unsigned char b, FILE *stream) +{ + unsigned char c = b & 0x0f; + if ( c > 9 ) { + c += 'A'-10; + } + else { + c += '0'; + } + fputc(c, stream); +} + +void +printhex_byte(const unsigned char b, FILE *stream) +{ + printhex_nibble( b >> 4, stream); + printhex_nibble( b, stream); +} + +void +printhex(uint8_t *id, uint8_t n, FILE *stream) +{ + for (uint8_t i = 0; i < n; i++) + { + if (i > 0) + { + fputc(' ', stream); + } + printhex_byte(id[i], stream); + } +} + + +uint8_t +simple_ds18b20_read_all() +{ + uint8_t id[OW_ROMCODE_SIZE]; + for( uint8_t diff = OW_SEARCH_FIRST; diff != OW_LAST_DEVICE; ) + { + diff = ow_rom_search( diff, &id[0] ); + + if( diff == OW_PRESENCE_ERR ) { + printf_P( PSTR("No Sensor found\r") ); + return OW_PRESENCE_ERR; // <--- early exit! + } + + if( diff == OW_DATA_ERR ) { + printf_P( PSTR("Bus Error\r") ); + return OW_DATA_ERR; // <--- early exit! + } + + int16_t decicelsius; + uint8_t ret = simple_ds18b20_read_decicelsius(NULL, &decicelsius); + if (ret != DS18X20_OK) + { + printf_P(PSTR("Failed reading\r")); + return OW_DATA_ERR; + } + + printf_P(PSTR("DS18B20 %d: "), diff); + if (crc8(id, OW_ROMCODE_SIZE)) + { + printf_P(PSTR("CRC fail")); + } + printhex(id, OW_ROMCODE_SIZE, stdout); + printf_P(PSTR(" %d.%d ºC\n"), decicelsius/10, decicelsius % 10); + } + printf_P(PSTR("Done sensors\n")); + return DS18X20_OK; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/old/simple_ds18b20.h Thu Jun 26 23:03:32 2014 +0800 @@ -0,0 +1,16 @@ +#ifndef SIMPLE_DS18B20_H_ +#define SIMPLE_DS18B20_H_ +#include <stdint.h> +#include <stdio.h> + +#include "ds18x20.h" + +uint8_t simple_ds18b20_start_meas(uint8_t id[]); +void printhex(uint8_t *id, uint8_t n, FILE *stream); +void printhex_byte( const unsigned char b, FILE *stream ); +uint8_t simple_ds18b20_read_decicelsius( uint8_t id[], int16_t *decicelsius ); +int16_t ds18b20_raw16_to_decicelsius(uint16_t measure); +uint8_t simple_ds18b20_read_raw( uint8_t id[], uint16_t *reading ); +uint8_t simple_ds18b20_read_all(); + +#endif // SIMPLE_DS18B20_H_
--- a/onewire.c Thu Jun 26 23:02:23 2014 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,287 +0,0 @@ -/* -Access Dallas 1-Wire Devices with ATMEL AVRs -Author of the initial code: Peter Dannegger (danni(at)specs.de) -modified by Martin Thomas (mthomas(at)rhrk.uni-kl.de) - 9/2004 - use of delay.h, optional bus configuration at runtime -10/2009 - additional delay in ow_bit_io for recovery - 5/2010 - timing modifcations, additonal config-values and comments, - use of atomic.h macros, internal pull-up support - 7/2010 - added method to skip recovery time after last bit transfered - via ow_command_skip_last_recovery -*/ - - -#include <avr/io.h> -#include <util/delay.h> -#include <util/atomic.h> - -#include "onewire.h" - -#ifdef OW_ONE_BUS - -#define OW_GET_IN() ( OW_IN & (1<<OW_PIN)) -#define OW_OUT_LOW() ( OW_OUT &= (~(1 << OW_PIN)) ) -#define OW_OUT_HIGH() ( OW_OUT |= (1 << OW_PIN) ) -#define OW_DIR_IN() ( OW_DDR &= (~(1 << OW_PIN )) ) -#define OW_DIR_OUT() ( OW_DDR |= (1 << OW_PIN) ) - -#else - -/* set bus-config with ow_set_bus() */ -uint8_t OW_PIN_MASK; -volatile uint8_t* OW_IN; -volatile uint8_t* OW_OUT; -volatile uint8_t* OW_DDR; - -#define OW_GET_IN() ( *OW_IN & OW_PIN_MASK ) -#define OW_OUT_LOW() ( *OW_OUT &= (uint8_t) ~OW_PIN_MASK ) -#define OW_OUT_HIGH() ( *OW_OUT |= (uint8_t) OW_PIN_MASK ) -#define OW_DIR_IN() ( *OW_DDR &= (uint8_t) ~OW_PIN_MASK ) -#define OW_DIR_OUT() ( *OW_DDR |= (uint8_t) OW_PIN_MASK ) - -void ow_set_bus(volatile uint8_t* in, - volatile uint8_t* out, - volatile uint8_t* ddr, - uint8_t pin) -{ - OW_DDR=ddr; - OW_OUT=out; - OW_IN=in; - OW_PIN_MASK = (1 << pin); - ow_reset(); -} - -#endif - -uint8_t ow_input_pin_state() -{ - return OW_GET_IN(); -} - -void ow_parasite_enable(void) -{ - OW_OUT_HIGH(); - OW_DIR_OUT(); -} - -void ow_parasite_disable(void) -{ - OW_DIR_IN(); -#if (!OW_USE_INTERNAL_PULLUP) - OW_OUT_LOW(); -#endif -} - - -uint8_t ow_reset(void) -{ - uint8_t err; - - OW_OUT_LOW(); - OW_DIR_OUT(); // pull OW-Pin low for 480us - _delay_us(480); - - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { - // set Pin as input - wait for clients to pull low - OW_DIR_IN(); // input -#if OW_USE_INTERNAL_PULLUP - OW_OUT_HIGH(); -#endif - - _delay_us(64); // was 66 - err = OW_GET_IN(); // no presence detect - // if err!=0: nobody pulled to low, still high - } - - // after a delay the clients should release the line - // and input-pin gets back to high by pull-up-resistor - _delay_us(480 - 64); // was 480-66 - if( OW_GET_IN() == 0 ) { - err = 1; // short circuit, expected low but got high - } - - return err; -} - - -/* Timing issue when using runtime-bus-selection (!OW_ONE_BUS): - The master should sample at the end of the 15-slot after initiating - the read-time-slot. The variable bus-settings need more - cycles than the constant ones so the delays had to be shortened - to achive a 15uS overall delay - Setting/clearing a bit in I/O Register needs 1 cyle in OW_ONE_BUS - but around 14 cyles in configureable bus (us-Delay is 4 cyles per uS) */ -static uint8_t ow_bit_io_intern( uint8_t b, uint8_t with_parasite_enable ) -{ - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { -#if OW_USE_INTERNAL_PULLUP - OW_OUT_LOW(); -#endif - OW_DIR_OUT(); // drive bus low - _delay_us(2); // T_INT > 1usec accoding to timing-diagramm - if ( b ) { - OW_DIR_IN(); // to write "1" release bus, resistor pulls high -#if OW_USE_INTERNAL_PULLUP - OW_OUT_HIGH(); -#endif - } - - // "Output data from the DS18B20 is valid for 15usec after the falling - // edge that initiated the read time slot. Therefore, the master must - // release the bus and then sample the bus state within 15ussec from - // the start of the slot." - _delay_us(15-2-OW_CONF_DELAYOFFSET); - - if( OW_GET_IN() == 0 ) { - b = 0; // sample at end of read-timeslot - } - - _delay_us(60-15-2+OW_CONF_DELAYOFFSET); -#if OW_USE_INTERNAL_PULLUP - OW_OUT_HIGH(); -#endif - OW_DIR_IN(); - - if ( with_parasite_enable ) { - ow_parasite_enable(); - } - - } /* ATOMIC_BLOCK */ - - _delay_us(OW_RECOVERY_TIME); // may be increased for longer wires - - return b; -} - -uint8_t ow_bit_io( uint8_t b ) -{ - return ow_bit_io_intern( b & 1, 0 ); -} - -uint8_t ow_byte_wr( uint8_t b ) -{ - uint8_t i = 8, j; - - do { - j = ow_bit_io( b & 1 ); - b >>= 1; - if( j ) { - b |= 0x80; - } - } while( --i ); - - return b; -} - -uint8_t ow_byte_wr_with_parasite_enable( uint8_t b ) -{ - uint8_t i = 8, j; - - do { - if ( i != 1 ) { - j = ow_bit_io_intern( b & 1, 0 ); - } else { - j = ow_bit_io_intern( b & 1, 1 ); - } - b >>= 1; - if( j ) { - b |= 0x80; - } - } while( --i ); - - return b; -} - - -uint8_t ow_byte_rd( void ) -{ - // read by sending only "1"s, so bus gets released - // after the init low-pulse in every slot - return ow_byte_wr( 0xFF ); -} - - -uint8_t ow_rom_search( uint8_t diff, uint8_t *id ) -{ - uint8_t i, j, next_diff; - uint8_t b; - - if( ow_reset() ) { - return OW_PRESENCE_ERR; // error, no device found <--- early exit! - } - - ow_byte_wr( OW_SEARCH_ROM ); // ROM search command - next_diff = OW_LAST_DEVICE; // unchanged on last device - - i = OW_ROMCODE_SIZE * 8; // 8 bytes - - do { - j = 8; // 8 bits - do { - b = ow_bit_io( 1 ); // read bit - if( ow_bit_io( 1 ) ) { // read complement bit - if( b ) { // 0b11 - return OW_DATA_ERR; // data error <--- early exit! - } - } - else { - if( !b ) { // 0b00 = 2 devices - if( diff > i || ((*id & 1) && diff != i) ) { - b = 1; // now 1 - next_diff = i; // next pass 0 - } - } - } - ow_bit_io( b ); // write bit - *id >>= 1; - if( b ) { - *id |= 0x80; // store bit - } - - i--; - - } while( --j ); - - id++; // next byte - - } while( i ); - - return next_diff; // to continue search -} - - -static void ow_command_intern( uint8_t command, uint8_t *id, uint8_t with_parasite_enable ) -{ - uint8_t i; - - ow_reset(); - - if( id ) { - ow_byte_wr( OW_MATCH_ROM ); // to a single device - i = OW_ROMCODE_SIZE; - do { - ow_byte_wr( *id ); - id++; - } while( --i ); - } - else { - ow_byte_wr( OW_SKIP_ROM ); // to all devices - } - - if ( with_parasite_enable ) { - ow_byte_wr_with_parasite_enable( command ); - } else { - ow_byte_wr( command ); - } -} - -void ow_command( uint8_t command, uint8_t *id ) -{ - ow_command_intern( command, id, 0); -} - -void ow_command_with_parasite_enable( uint8_t command, uint8_t *id ) -{ - ow_command_intern( command, id, 1 ); -} -
--- a/onewire.h Thu Jun 26 23:02:23 2014 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -#ifndef ONEWIRE_H_ -#define ONEWIRE_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdint.h> - -/*******************************************/ -/* Hardware connection */ -/*******************************************/ - -/* Define OW_ONE_BUS if only one 1-Wire-Bus is used - in the application -> shorter code. - If not defined make sure to call ow_set_bus() before using - a bus. Runtime bus-select increases code size by around 300 - bytes so use OW_ONE_BUS if possible */ -#define OW_ONE_BUS - -#ifdef OW_ONE_BUS - -#define OW_PIN PB1 -#define OW_IN PINB -#define OW_OUT PORTB -#define OW_DDR DDRB -#define OW_CONF_DELAYOFFSET 0 - -#else -#if ( F_CPU < 1843200 ) -#warning | Experimental multi-bus-mode is not tested for -#warning | frequencies below 1,84MHz. Use OW_ONE_WIRE or -#warning | faster clock-source (i.e. internal 2MHz R/C-Osc.). -#endif -#define OW_CONF_CYCLESPERACCESS 13 -#define OW_CONF_DELAYOFFSET ( (uint16_t)( ((OW_CONF_CYCLESPERACCESS) * 1000000L) / F_CPU ) ) -#endif - -// Recovery time (T_Rec) minimum 1usec - increase for long lines -// 5 usecs is a value give in some Maxim AppNotes -// 30u secs seem to be reliable for longer lines -//#define OW_RECOVERY_TIME 5 /* usec */ -//#define OW_RECOVERY_TIME 300 /* usec */ -#define OW_RECOVERY_TIME 10 /* usec */ - -// Use AVR's internal pull-up resistor instead of external 4,7k resistor. -// Based on information from Sascha Schade. Experimental but worked in tests -// with one DS18B20 and one DS18S20 on a rather short bus (60cm), where both -// sensores have been parasite-powered. -#define OW_USE_INTERNAL_PULLUP 1 /* 0=external, 1=internal */ - -/*******************************************/ - - -#define OW_MATCH_ROM 0x55 -#define OW_SKIP_ROM 0xCC -#define OW_SEARCH_ROM 0xF0 - -#define OW_SEARCH_FIRST 0xFF // start new search -#define OW_PRESENCE_ERR 0xFF -#define OW_DATA_ERR 0xFE -#define OW_LAST_DEVICE 0x00 // last device found - -// rom-code size including CRC -#define OW_ROMCODE_SIZE 8 - -extern uint8_t ow_reset(void); - -extern uint8_t ow_bit_io( uint8_t b ); -extern uint8_t ow_byte_wr( uint8_t b ); -extern uint8_t ow_byte_rd( void ); - -extern uint8_t ow_rom_search( uint8_t diff, uint8_t *id ); - -extern void ow_command( uint8_t command, uint8_t *id ); -extern void ow_command_with_parasite_enable( uint8_t command, uint8_t *id ); - -extern void ow_parasite_enable( void ); -extern void ow_parasite_disable( void ); -extern uint8_t ow_input_pin_state( void ); - -#ifndef OW_ONE_BUS -extern void ow_set_bus( volatile uint8_t* in, - volatile uint8_t* out, - volatile uint8_t* ddr, - uint8_t pin ); -#endif - -#ifdef __cplusplus -} -#endif - -#endif
--- a/server/config.py Thu Jun 26 23:02:23 2014 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -../web/config.py \ No newline at end of file
--- a/server/dump.py Thu Jun 26 23:02:23 2014 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,190 +0,0 @@ -#!/usr/bin/env python2.7 - -BTADDR = "00:12:03:27:70:88" -SLEEP_TIME = 5 -# time that the bluetooth takes to get going? -EXTRA_WAKEUP = 0 - -FETCH_TRIES = 3 - -# avoid turning off the bluetooth etc. -TESTING = False - -import sys -# for wrt -sys.path.append('/root/python') -import httplib -import time -import traceback -import binascii -import hmac -import zlib -import urllib -import urllib2 - -import config - -from utils import monotonic_time, retry, readline, crc16 - -lightblue = None -try: - import lightblue -except ImportError: - import bluetooth - -def get_socket(addr): - if lightblue: - s = lightblue.socket() - s.connect((addr, 1)) - s.settimeout(3) - else: - s = bluetooth.BluetoothSocket( bluetooth.RFCOMM ) - s.connect((addr, 1)) - - s.setblocking(False) - - return s - - -@retry() -def fetch(sock): - print "fetch" - sock.send("fetch\n") - - crc = 0 - - lines = [] - l = readline(sock) - if l != 'START\n': - print>>sys.stderr, "Bad expected START line '%s'\n" % l.rstrip('\n') - return None - crc = crc16(l, crc) - - while True: - l = readline(sock) - - crc = crc16(l, crc) - - if l == 'END\n': - break - - lines.append(l.rstrip('\n')) - - print lines - - l = readline(sock) - recv_crc = None - try: - k, v = l.rstrip('\n').split('=') - print k,v - if k == 'CRC': - recv_crc = int(v) - if recv_crc < 0 or recv_crc > 0xffff: - recv_crc = None - except ValueError: - pass - - if recv_crc is None: - print>>sys.stderr, "Bad expected CRC line '%s'\n" % l.rstrip('\n') - return None - - if recv_crc != crc: - print>>sys.stderr, "Bad CRC: calculated 0x%x vs received 0x%x\n" % (crc, recv_crc) - return None - - return lines - -@retry() -def turn_off(sock): - if TESTING: - return 99 - print>>sys.stderr, "sending btoff" - sock.send("btoff\n"); - # read newline - l = readline(sock) - if not l: - print>>sys.stderr, "Bad response to btoff\n" - return None - - if not l.startswith('off:'): - print>>sys.stderr, "Bad response to btoff '%s'\n" % l - return None - off, next_wake = l.rstrip().split(':') - print>>sys.stderr, "Next wake %s" % next_wake - - return int(next_wake) - -@retry() -def clear_meas(sock): - sock.send("clear\n"); - l = readline(sock) - if l and l.rstrip() == 'cleared': - return True - - print>>sys.stderr, "Bad response to clear %s\n" % str(l) - return False - -def send_results(lines): - enc_lines = binascii.b2a_base64(zlib.compress('\n'.join(lines))) - mac = hmac.new(config.HMAC_KEY, enc_lines).hexdigest() - - url_data = urllib.urlencode( {'lines': enc_lines, 'hmac': mac} ) - con = urllib2.urlopen(config.UPDATE_URL, url_data) - result = con.read(100) - if result == 'OK': - return True - else: - print>>sys.stderr, "Bad result '%s'" % result - return False - -def do_comms(sock): - args = sys.argv[1:] - print "do_comms" - for a in args: - sock.send('%s\n' % a) - - while True: - l = readline(sock) - if not l: - print '.', - sys.stdout.flush() - else: - print l - -testcount = 0 - -def sleep_for(secs): - until = monotonic_time() + secs - while True: - length = until - monotonic_time() - if length <= 0: - return - time.sleep(length) - -def main(): - next_wake_time = 0 - - while True: - sock = None - try: - sock = get_socket(BTADDR) - except Exception, e: - print>>sys.stderr, "Error connecting:" - traceback.print_exc(file=sys.stderr) - sleep_time = SLEEP_TIME - if sock: - next_wake = None - try: - next_wake_interval = do_comms(sock) - next_wake_time = time.time() + next_wake_interval - except Exception, e: - print>>sys.stderr, "Error in do_comms:" - traceback.print_exc(file=sys.stderr) - if next_wake_time > time.time(): - sleep_time = min(next_wake_time - time.time() - EXTRA_WAKEUP, sleep_time) - - print "Sleeping for %d" % sleep_time - sleep_for(sleep_time) - -if __name__ == '__main__': - main()
--- a/server/ts.py Thu Jun 26 23:02:23 2014 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,239 +0,0 @@ -#!/usr/bin/env python2.7 - -# time that the bluetooth takes to get going? -EXTRA_WAKEUP = -3 - -FETCH_TRIES = 3 - -# avoid turning off the bluetooth etc. -TESTING = False - -import sys -# for wrt -sys.path.append('/root/python') -import httplib -import time -import traceback -import binascii -import hmac -import zlib -import urllib -import urllib2 -import logging -import socket - -L = logging.info -W = logging.warning -E = logging.error - -import config - -from utils import monotonic_time, retry, readline, crc16 -import utils - -import bluetooth - -def get_socket(addr): - s = bluetooth.BluetoothSocket( bluetooth.RFCOMM ) - L("connecting") - s.connect((addr, 1)) - s.setblocking(False) - s.settimeout(1) - - return s - - -def flush(sock): - ret = [] - while True: - l = readline(sock) - if l: - ret.append(l) - else: - break - return ret - -def encode_extra(extra_lines): - return ['extra%d=%s' % (n, l.strip()) for (n,l) in enumerate(extra_lines)] - -@retry() -def fetch(sock): - extra_lines = flush(sock) - sock.send("fetch\n") - - crc = 0 - - lines = [] - l = readline(sock) - if not l: - return None - - if l != 'START\n': - W("Bad expected START line '%s'\n" % l.rstrip('\n')) - extra_lines.append(l) - return encode_extra(extra_lines) - crc = crc16(l, crc) - - while True: - l = readline(sock) - - crc = crc16(l, crc) - - if l == 'END\n': - break - - lines.append(l.rstrip('\n')) - - lines += encode_extra(extra_lines) - - for d in lines: - L("Received: %s" % d) - - l = readline(sock) - recv_crc = None - try: - k, v = l.rstrip('\n').split('=') - if k == 'CRC': - recv_crc = int(v) - if recv_crc < 0 or recv_crc > 0xffff: - recv_crc = None - except ValueError: - pass - - if recv_crc is None: - W("Bad expected CRC line '%s'\n" % l.rstrip('\n')) - return None - - if recv_crc != crc: - W("Bad CRC: calculated 0x%x vs received 0x%x\n" % (crc, recv_crc)) - return None - - return lines - -@retry() -def turn_off(sock): - if TESTING: - return 99 - L("Sending btoff") - flush(sock) - sock.send("btoff\n"); - # read newline - l = readline(sock) - if not l: - W("Bad response to btoff") - return None - - if not l.startswith('next_wake'): - W("Bad response to btoff '%s'" % l) - return None - L("Next wake line %s" % l) - - toks = dict(v.split('=') for v in l.split(',')) - - rem = int(toks['rem']) - tick_secs = int(toks['tick_secs']) - tick_wake = int(toks['tick_wake']) + 1 - next_wake = int(toks['next_wake']) - - rem_secs = float(rem) / tick_wake * tick_secs - - next_wake_secs = next_wake - rem_secs - L("next_wake_secs %f\n", next_wake_secs) - return next_wake_secs - -@retry() -def clear_meas(sock): - flush(sock) - sock.send("clear\n"); - l = readline(sock) - if l and l.rstrip() == 'cleared': - return True - - E("Bad response to clear '%s'" % str(l)) - return False - -def send_results(lines): - enc_lines = binascii.b2a_base64(zlib.compress('\n'.join(lines))) - mac = hmac.new(config.HMAC_KEY, enc_lines).hexdigest() - - url_data = urllib.urlencode( {'lines': enc_lines, 'hmac': mac} ) - con = urllib2.urlopen(config.UPDATE_URL, url_data) - result = con.read(100) - if result == 'OK': - return True - else: - W("Bad result '%s'" % result) - return False - -def do_comms(sock): - L("do_comms") - d = None - # serial could be unreliable, try a few times - d = fetch(sock) - if not d: - return - - res = send_results(d) - if not res: - return - - clear_meas(sock) - - next_wake = 600 - #next_wake = turn_off(sock) - #sock.close() - return next_wake - -testcount = 0 - -def sleep_for(secs): - until = monotonic_time() + secs - while True: - length = until - monotonic_time() - if length <= 0: - return - time.sleep(length) - -def setup_logging(): - logging.basicConfig(format='%(asctime)s %(message)s', - datefmt='%m/%d/%Y %I:%M:%S %p', - level=logging.INFO) - -def get_net_socket(host, port): - s = socket.create_connection((host, port)) - s.setblocking(False) - s.settimeout(1) - return s - -def main(): - setup_logging() - - L("Running templog rfcomm server") - - if '--daemon' in sys.argv: - utils.cheap_daemon() - - next_wake_time = 0 - - while True: - sock = None - try: - sock = get_net_socket(config.SERIAL_HOST, config.SERIAL_PORT) - except Exception, e: - #logging.exception("Error connecting") - pass - - if not sock: - sleep_for(config.SLEEP_TIME) - continue - - while True: - try: - do_comms(sock) - sleep_for(config.SLEEP_TIME) - except Exception, e: - logging.exception("Error in do_comms") - break - -if __name__ == '__main__': - main()
--- a/server/utils.py Thu Jun 26 23:02:23 2014 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,127 +0,0 @@ -import os -import sys -#import ctypes -import time -import select -import logging - -L = logging.info -W = logging.warning -E = logging.error - -DEFAULT_TRIES = 3 -READLINE_SELECT_TIMEOUT = 1 - -__all__ = ('monotonic_time', 'retry') - -clock_gettime = None -no_clock_gettime = True -def monotonic_time(): - global clock_gettime - global no_clock_gettime - if no_clock_gettime: - return time.time() - - class timespec(ctypes.Structure): - _fields_ = [ - ('tv_sec', ctypes.c_long), - ('tv_nsec', ctypes.c_long) - ] - if not clock_gettime: - try: - librt = ctypes.CDLL('librt.so.0', use_errno=True) - clock_gettime = librt.clock_gettime - clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)] - except: - W("No clock_gettime(), using fake fallback.") - no_clock_gettime = True - return time.time() - - t = timespec() - CLOCK_MONOTONIC = 1 # see <linux/time.h> - - if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(t)) != 0: - errno_ = ctypes.get_errno() - raise OSError(errno_, os.strerror(errno_)) - return t.tv_sec + t.tv_nsec * 1e-9 - -# decorator, tries a number of times, returns None on failure, sleeps between -# Must be used as "@retry()" if arguments are defaulted -def retry(retries=DEFAULT_TRIES, try_time = 1): - def inner(func): - def new_f(*args, **kwargs): - for i in range(retries): - d = func(*args, **kwargs) - if d is not None: - return d - time.sleep(try_time) - return None - - new_f.func_name = func.func_name - return new_f - return inner - -def readline(sock): - timeout = READLINE_SELECT_TIMEOUT - buf = '' - while True: - (rlist, wlist, xlist) = select.select([sock], [], [], timeout) - if sock not in rlist: - # hit timeout - return None - - c = sock.recv(1) - if c == '': - # lightblue timeout - return None - if c == '\r': - continue - - buf += c - if c == '\n': - return buf - -# from http://blog.stalkr.net/2011/04/pctf-2011-32-thats-no-bluetooth.html -def crc16(buff, crc = 0, poly = 0x8408): - l = len(buff) - i = 0 - while i < l: - ch = ord(buff[i]) - uc = 0 - while uc < 8: - if (crc & 1) ^ (ch & 1): - crc = (crc >> 1) ^ poly - else: - crc >>= 1 - ch >>= 1 - uc += 1 - i += 1 - return crc - -def cheap_daemon(): - L("Daemonising.") - sys.stdout.flush() - sys.stderr.flush() - out = file('/dev/null', 'a+') - os.dup2(out.fileno(), sys.stdout.fileno()) - os.dup2(out.fileno(), sys.stderr.fileno()) - - try: - pid = os.fork() - if pid > 0: - sys.exit(0) - except OSError, e: - E("Bad fork()") - sys.exit(1) - - os.setsid() - - try: - pid = os.fork() - if pid > 0: - sys.exit(0) - except OSError, e: - E("Bad fork()") - sys.exit(1) - -
--- a/simple_ds18b20.c Thu Jun 26 23:02:23 2014 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,192 +0,0 @@ -// Matt Johnston 2012 -// Based on ds18x20.c by Martin Thomas, in turn based on code by -// Peter // Dannegger and others. -// -#include <stdio.h> -#include <avr/pgmspace.h> - -#include "ds18x20.h" -#include "onewire.h" -#include "crc8.h" - -#include "simple_ds18b20.h" - -uint8_t -simple_ds18b20_start_meas(uint8_t id[]) -{ - uint8_t ret; - - ow_reset(); - if( ow_input_pin_state() ) { // only send if bus is "idle" = high - ow_command_with_parasite_enable(DS18X20_CONVERT_T, id); - ret = DS18X20_OK; - } - else { - ret = DS18X20_START_FAIL; - } - - return ret; -} - -static uint8_t -read_scratchpad( uint8_t id[], uint8_t sp[], uint8_t n ) -{ - uint8_t i; - uint8_t ret; - - ow_command( DS18X20_READ, id ); - for ( i = 0; i < n; i++ ) { - sp[i] = ow_byte_rd(); - } - if ( crc8( &sp[0], DS18X20_SP_SIZE ) ) { - ret = DS18X20_ERROR_CRC; - } else { - ret = DS18X20_OK; - } - - return ret; -} - -int16_t -ds18b20_raw16_to_decicelsius(uint16_t measure) -{ - uint8_t negative; - int16_t decicelsius; - uint16_t fract; - - // check for negative - if ( measure & 0x8000 ) { - negative = 1; // mark negative - measure ^= 0xffff; // convert to positive => (twos complement)++ - measure++; - } - else { - negative = 0; - } - - decicelsius = (measure >> 4); - decicelsius *= 10; - - // decicelsius += ((measure & 0x000F) * 640 + 512) / 1024; - // 625/1000 = 640/1024 - fract = ( measure & 0x000F ) * 640; - if ( !negative ) { - fract += 512; - } - fract /= 1024; - decicelsius += fract; - - if ( negative ) { - decicelsius = -decicelsius; - } - - if ( decicelsius == 850 || decicelsius < -550 || decicelsius > 1250 ) { - return DS18X20_INVALID_DECICELSIUS; - } else { - return decicelsius; - } -} - -uint8_t -simple_ds18b20_read_decicelsius( uint8_t id[], int16_t *decicelsius ) -{ - uint16_t reading; - uint8_t ret; - - ret = simple_ds18b20_read_raw(id, &reading); - if (ret == DS18X20_OK) - { - *decicelsius = ds18b20_raw16_to_decicelsius(reading); - } - return ret; -} - -uint8_t -simple_ds18b20_read_raw( uint8_t id[], uint16_t *reading ) -{ - uint8_t sp[DS18X20_SP_SIZE]; - uint8_t ret; - - if (id) - { - ow_reset(); - } - ret = read_scratchpad( id, sp, DS18X20_SP_SIZE ); - if ( ret == DS18X20_OK ) { - *reading = sp[0] | (sp[1] << 8); - } - return ret; -} - -static void -printhex_nibble(const unsigned char b, FILE *stream) -{ - unsigned char c = b & 0x0f; - if ( c > 9 ) { - c += 'A'-10; - } - else { - c += '0'; - } - fputc(c, stream); -} - -void -printhex_byte(const unsigned char b, FILE *stream) -{ - printhex_nibble( b >> 4, stream); - printhex_nibble( b, stream); -} - -void -printhex(uint8_t *id, uint8_t n, FILE *stream) -{ - for (uint8_t i = 0; i < n; i++) - { - if (i > 0) - { - fputc(' ', stream); - } - printhex_byte(id[i], stream); - } -} - - -uint8_t -simple_ds18b20_read_all() -{ - uint8_t id[OW_ROMCODE_SIZE]; - for( uint8_t diff = OW_SEARCH_FIRST; diff != OW_LAST_DEVICE; ) - { - diff = ow_rom_search( diff, &id[0] ); - - if( diff == OW_PRESENCE_ERR ) { - printf_P( PSTR("No Sensor found\r") ); - return OW_PRESENCE_ERR; // <--- early exit! - } - - if( diff == OW_DATA_ERR ) { - printf_P( PSTR("Bus Error\r") ); - return OW_DATA_ERR; // <--- early exit! - } - - int16_t decicelsius; - uint8_t ret = simple_ds18b20_read_decicelsius(NULL, &decicelsius); - if (ret != DS18X20_OK) - { - printf_P(PSTR("Failed reading\r")); - return OW_DATA_ERR; - } - - printf_P(PSTR("DS18B20 %d: "), diff); - if (crc8(id, OW_ROMCODE_SIZE)) - { - printf_P(PSTR("CRC fail")); - } - printhex(id, OW_ROMCODE_SIZE, stdout); - printf_P(PSTR(" %d.%d ºC\n"), decicelsius/10, decicelsius % 10); - } - printf_P(PSTR("Done sensors\n")); - return DS18X20_OK; -} -
--- a/simple_ds18b20.h Thu Jun 26 23:02:23 2014 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -#ifndef SIMPLE_DS18B20_H_ -#define SIMPLE_DS18B20_H_ -#include <stdint.h> -#include <stdio.h> - -#include "ds18x20.h" - -uint8_t simple_ds18b20_start_meas(uint8_t id[]); -void printhex(uint8_t *id, uint8_t n, FILE *stream); -void printhex_byte( const unsigned char b, FILE *stream ); -uint8_t simple_ds18b20_read_decicelsius( uint8_t id[], int16_t *decicelsius ); -int16_t ds18b20_raw16_to_decicelsius(uint16_t measure); -uint8_t simple_ds18b20_read_raw( uint8_t id[], uint16_t *reading ); -uint8_t simple_ds18b20_read_all(); - -#endif // SIMPLE_DS18B20_H_