--- /dev/null
+.PHONY: all clean
+
+CFLAGS=-Wall -std=gnu99 -m32
+LDFLAGS=-m32
+#CFLAGS+=-DDEBUG
+CFLAGS += -DHAVE_HX711
+
+
+all: bbd
+
+clean:
+ rm -f *.o bbd
+
+bbd: hx711.o c_gpio.o pump.o net.o main.o mgmt.o
+ $(CC) $(LDFLAGS) -o $@ -pthread $^
--- /dev/null
+/*
+Copyright (c) 2012-2015 Ben Croston
+
+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.
+*/
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <string.h>
+#include "c_gpio.h"
+
+#define BCM2708_PERI_BASE_DEFAULT 0x20000000
+#define BCM2709_PERI_BASE_DEFAULT 0x3f000000
+#define GPIO_BASE_OFFSET 0x200000
+#define FSEL_OFFSET 0 // 0x0000
+#define SET_OFFSET 7 // 0x001c / 4
+#define CLR_OFFSET 10 // 0x0028 / 4
+#define PINLEVEL_OFFSET 13 // 0x0034 / 4
+#define EVENT_DETECT_OFFSET 16 // 0x0040 / 4
+#define RISING_ED_OFFSET 19 // 0x004c / 4
+#define FALLING_ED_OFFSET 22 // 0x0058 / 4
+#define HIGH_DETECT_OFFSET 25 // 0x0064 / 4
+#define LOW_DETECT_OFFSET 28 // 0x0070 / 4
+#define PULLUPDN_OFFSET 37 // 0x0094 / 4
+#define PULLUPDNCLK_OFFSET 38 // 0x0098 / 4
+
+#define PAGE_SIZE (4*1024)
+#define BLOCK_SIZE (4*1024)
+
+static volatile uint32_t *gpio_map;
+
+void short_wait(void)
+{
+ int i;
+
+ for (i=0; i<150; i++) { // wait 150 cycles
+ asm volatile("nop");
+ }
+}
+
+int setup(void)
+{
+ int mem_fd;
+ uint8_t *gpio_mem;
+ uint32_t peri_base;
+ uint32_t gpio_base;
+ unsigned char buf[4];
+ FILE *fp;
+ char buffer[1024];
+ char hardware[1024];
+ int found = 0;
+
+ // try /dev/gpiomem first - this does not require root privs
+ if ((mem_fd = open("/dev/gpiomem", O_RDWR|O_SYNC)) > 0)
+ {
+ gpio_map = (uint32_t *)mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, 0);
+ if ((uint32_t)gpio_map < 0) {
+ return SETUP_MMAP_FAIL;
+ } else {
+ return SETUP_OK;
+ }
+ }
+
+ // revert to /dev/mem method - requires root
+
+ // determine peri_base
+ if ((fp = fopen("/proc/device-tree/soc/ranges", "rb")) != NULL) {
+ // get peri base from device tree
+ fseek(fp, 4, SEEK_SET);
+ if (fread(buf, 1, sizeof buf, fp) == sizeof buf) {
+ peri_base = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0;
+ }
+ fclose(fp);
+ } else {
+ // guess peri base based on /proc/cpuinfo hardware field
+ if ((fp = fopen("/proc/cpuinfo", "r")) == NULL)
+ return SETUP_CPUINFO_FAIL;
+
+ while(!feof(fp) && !found) {
+ fgets(buffer, sizeof(buffer), fp);
+ sscanf(buffer, "Hardware : %s", hardware);
+ if (strcmp(hardware, "BCM2708") == 0 || strcmp(hardware, "BCM2835") == 0) {
+ // pi 1 hardware
+ peri_base = BCM2708_PERI_BASE_DEFAULT;
+ found = 1;
+ } else if (strcmp(hardware, "BCM2709") == 0 || strcmp(hardware, "BCM2836") == 0) {
+ // pi 2 hardware
+ peri_base = BCM2709_PERI_BASE_DEFAULT;
+ found = 1;
+ }
+ }
+ fclose(fp);
+ if (!found)
+ return SETUP_NOT_RPI_FAIL;
+ }
+
+ gpio_base = peri_base + GPIO_BASE_OFFSET;
+
+ // mmap the GPIO memory registers
+ if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0)
+ return SETUP_DEVMEM_FAIL;
+
+ if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL)
+ return SETUP_MALLOC_FAIL;
+
+ if ((uint32_t)gpio_mem % PAGE_SIZE)
+ gpio_mem += PAGE_SIZE - ((uint32_t)gpio_mem % PAGE_SIZE);
+
+ gpio_map = (uint32_t *)mmap( (void *)gpio_mem, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, mem_fd, gpio_base);
+
+ if ((uint32_t)gpio_map < 0)
+ return SETUP_MMAP_FAIL;
+
+ return SETUP_OK;
+}
+
+void clear_event_detect(int gpio)
+{
+ int offset = EVENT_DETECT_OFFSET + (gpio/32);
+ int shift = (gpio%32);
+
+ *(gpio_map+offset) |= (1 << shift);
+ short_wait();
+ *(gpio_map+offset) = 0;
+}
+
+int eventdetected(int gpio)
+{
+ int offset, value, bit;
+
+ offset = EVENT_DETECT_OFFSET + (gpio/32);
+ bit = (1 << (gpio%32));
+ value = *(gpio_map+offset) & bit;
+ if (value)
+ clear_event_detect(gpio);
+ return value;
+}
+
+void set_rising_event(int gpio, int enable)
+{
+ int offset = RISING_ED_OFFSET + (gpio/32);
+ int shift = (gpio%32);
+
+ if (enable)
+ *(gpio_map+offset) |= 1 << shift;
+ else
+ *(gpio_map+offset) &= ~(1 << shift);
+ clear_event_detect(gpio);
+}
+
+void set_falling_event(int gpio, int enable)
+{
+ int offset = FALLING_ED_OFFSET + (gpio/32);
+ int shift = (gpio%32);
+
+ if (enable) {
+ *(gpio_map+offset) |= (1 << shift);
+ *(gpio_map+offset) = (1 << shift);
+ } else {
+ *(gpio_map+offset) &= ~(1 << shift);
+ }
+ clear_event_detect(gpio);
+}
+
+void set_high_event(int gpio, int enable)
+{
+ int offset = HIGH_DETECT_OFFSET + (gpio/32);
+ int shift = (gpio%32);
+
+ if (enable)
+ *(gpio_map+offset) |= (1 << shift);
+ else
+ *(gpio_map+offset) &= ~(1 << shift);
+ clear_event_detect(gpio);
+}
+
+void set_low_event(int gpio, int enable)
+{
+ int offset = LOW_DETECT_OFFSET + (gpio/32);
+ int shift = (gpio%32);
+
+ if (enable)
+ *(gpio_map+offset) |= 1 << shift;
+ else
+ *(gpio_map+offset) &= ~(1 << shift);
+ clear_event_detect(gpio);
+}
+
+void set_pullupdn(int gpio, int pud)
+{
+ int clk_offset = PULLUPDNCLK_OFFSET + (gpio/32);
+ int shift = (gpio%32);
+
+ if (pud == PUD_DOWN)
+ *(gpio_map+PULLUPDN_OFFSET) = (*(gpio_map+PULLUPDN_OFFSET) & ~3) | PUD_DOWN;
+ else if (pud == PUD_UP)
+ *(gpio_map+PULLUPDN_OFFSET) = (*(gpio_map+PULLUPDN_OFFSET) & ~3) | PUD_UP;
+ else // pud == PUD_OFF
+ *(gpio_map+PULLUPDN_OFFSET) &= ~3;
+
+ short_wait();
+ *(gpio_map+clk_offset) = 1 << shift;
+ short_wait();
+ *(gpio_map+PULLUPDN_OFFSET) &= ~3;
+ *(gpio_map+clk_offset) = 0;
+}
+
+void setup_gpio(int gpio, int direction, int pud)
+{
+ if ((uint32_t)gpio_map <= 0) return;
+
+ int offset = FSEL_OFFSET + (gpio/10);
+ int shift = (gpio%10)*3;
+
+ set_pullupdn(gpio, pud);
+ if (direction == OUTPUT)
+ *(gpio_map+offset) = (*(gpio_map+offset) & ~(7<<shift)) | (1<<shift);
+ else // direction == INPUT
+ *(gpio_map+offset) = (*(gpio_map+offset) & ~(7<<shift));
+}
+
+// Contribution by Eric Ptak <trouch@trouch.com>
+int gpio_function(int gpio)
+{
+ int offset = FSEL_OFFSET + (gpio/10);
+ int shift = (gpio%10)*3;
+ int value = *(gpio_map+offset);
+ value >>= shift;
+ value &= 7;
+ return value; // 0=input, 1=output, 4=alt0
+}
+
+void output_gpio(int gpio, int value)
+{
+ int offset, shift;
+ if ((uint32_t)gpio_map <= 0) return;
+
+ if (value) // value == HIGH
+ offset = SET_OFFSET + (gpio/32);
+ else // value == LOW
+ offset = CLR_OFFSET + (gpio/32);
+
+ shift = (gpio%32);
+
+ *(gpio_map+offset) = 1 << shift;
+}
+
+int input_gpio(int gpio)
+{
+ int offset, value, mask;
+ if ((uint32_t)gpio_map <= 0) return 0;
+
+ offset = PINLEVEL_OFFSET + (gpio/32);
+ mask = (1 << gpio%32);
+ value = *(gpio_map+offset) & mask;
+ return value;
+}
+
+void cleanup(void)
+{
+ if ((uint32_t)gpio_map <= 0) return;
+ munmap((void *)gpio_map, BLOCK_SIZE);
+}
--- /dev/null
+/*
+Copyright (c) 2012-2015 Ben Croston
+
+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.
+*/
+
+int setup(void);
+void setup_gpio(int gpio, int direction, int pud);
+int gpio_function(int gpio);
+void output_gpio(int gpio, int value);
+int input_gpio(int gpio);
+void set_rising_event(int gpio, int enable);
+void set_falling_event(int gpio, int enable);
+void set_high_event(int gpio, int enable);
+void set_low_event(int gpio, int enable);
+int eventdetected(int gpio);
+void cleanup(void);
+
+#define SETUP_OK 0
+#define SETUP_DEVMEM_FAIL 1
+#define SETUP_MALLOC_FAIL 2
+#define SETUP_MMAP_FAIL 3
+#define SETUP_CPUINFO_FAIL 4
+#define SETUP_NOT_RPI_FAIL 5
+
+#define INPUT 1 // is really 0 for control register!
+#define OUTPUT 0 // is really 1 for control register!
+#define ALT0 4
+
+#define HIGH 1
+#define LOW 0
+
+#define PUD_OFF 0
+#define PUD_DOWN 1
+#define PUD_UP 2
--- /dev/null
+#include <stdbool.h>
+#include <stdint.h>
+#include "hx711.h"
+
+#include <unistd.h>
+#include "c_gpio.h"
+
+
+void HX711_init(uint8_t gain)
+{
+ PD_SCK_SET_OUTPUT;
+ DOUT_SET_INPUT;
+
+ HX711_set_gain(gain);
+
+}
+
+bool HX711_isReady(void)
+{
+ return DOUT_READ == LOW;
+}
+
+void HX711_set_gain(uint8_t gain)
+{
+ switch (gain)
+ {
+ case 128: // channel A, gain factor 128
+ GAIN = 1;
+ break;
+ case 64: // channel A, gain factor 64
+ GAIN = 3;
+ break;
+ case 32: // channel B, gain factor 32
+ GAIN = 2;
+ break;
+ }
+
+ PD_SCK_SET_LOW;
+ HX711_read();
+}
+
+int32_t HX711_read(void)
+{
+ // Will hold the 24-bit value
+ int32_t Count = 0;
+
+ // wait for the chip to become ready
+ while (!HX711_isReady());
+
+ for (uint8_t i=0; i<24; i++)
+ {
+ PD_SCK_SET_HIGH;
+ Count = Count << 1;
+ PD_SCK_SET_LOW;
+
+ if(DOUT_READ)
+ {
+ Count++;
+ }
+ }
+
+ // Set the gain for next time
+ for (uint8_t i = 0; i < GAIN; i++)
+ {
+ PD_SCK_SET_HIGH;
+ PD_SCK_SET_LOW;
+ }
+
+ // XOR the count
+ Count ^= 0x800000;
+
+ return(Count);
+}
+
+int32_t HX711_read_(void)
+{
+ // wait for the chip to become ready
+ while (!HX711_isReady());
+
+ uint32_t value = 0;
+ uint8_t data[3] = { 0 };
+ uint8_t filler = 0x00;
+
+ // pulse the clock pin 24 times to read the data
+ data[2] = shiftIn();
+ data[1] = shiftIn();
+ data[0] = shiftIn();
+
+ // set the channel and the gain factor for the next reading using the clock pin
+ for (uint8_t i = 0; i < GAIN; i++)
+ {
+ PD_SCK_SET_HIGH;
+ PD_SCK_SET_LOW;
+ }
+
+ // Datasheet indicates the value is returned as a two's complement value
+ // Flip all the bits
+ data[2] = ~data[2];
+ data[1] = ~data[1];
+ data[0] = ~data[0];
+
+ // Replicate the most significant bit to pad out a 32-bit signed integer
+ if ( data[2] & 0x80 )
+ {
+ filler = 0xFF;
+ } else if ((0x7F == data[2]) && (0xFF == data[1]) && (0xFF == data[0]))
+ {
+ filler = 0xFF;
+ } else
+ {
+ filler = 0x00;
+ }
+
+ // Construct a 32-bit signed integer
+ value = ( (uint32_t)(filler) << 24
+ | (uint32_t)(data[2]) << 16
+ | (uint32_t)(data[1]) << 8
+ | (uint32_t)(data[0]) );
+
+ // ... and add 1
+ return (int32_t)(++value);
+}
+
+int32_t HX711_read_average(uint8_t times)
+{
+ int32_t sum = 0;
+ for (uint8_t i = 0; i < times; i++)
+ {
+ sum += HX711_read();
+ // TODO: See if yield will work | yield();
+ }
+ return sum / times;
+}
+
+double HX711_get_value(uint8_t times)
+{
+ return HX711_read_average(times) - OFFSET;
+}
+
+float HX711_get_units(uint8_t times)
+{
+ return HX711_get_value(times) / SCALE;
+}
+
+void HX711_tare(uint8_t times)
+{
+ double sum = HX711_read_average(times);
+ HX711_set_offset(sum);
+}
+
+void HX711_set_scale(float scale)
+{
+ SCALE = scale;
+}
+
+float HX711_get_scale()
+{
+ return SCALE;
+}
+
+void HX711_set_offset(int32_t offset)
+{
+ OFFSET = offset;
+}
+
+int32_t HX711_get_offset()
+{
+ return OFFSET;
+}
+
+void HX711_power_down()
+{
+ PD_SCK_SET_LOW;
+ PD_SCK_SET_HIGH;
+ usleep(100);
+}
+
+void HX711_power_up()
+{
+ PD_SCK_SET_LOW;
+}
+
+uint8_t shiftIn(void)
+{
+ uint8_t value = 0;
+
+ for (uint8_t i = 0; i < 8; ++i)
+ {
+ PD_SCK_SET_HIGH;
+ value |= DOUT_READ << (7 - i);
+ PD_SCK_SET_LOW;
+ }
+ return value;
+}
--- /dev/null
+#ifndef HX711_h
+#define HX711_h
+
+ #define PD_SCK_PIN 6
+
+ #define PD_SCK_SET_OUTPUT setup_gpio (PD_SCK_PIN, OUTPUT, PUD_OFF)
+ #define PD_SCK_SET_INPUT setup_gpio (PD_SCK_PIN, INPUT, PUD_OFF)
+
+ #define PD_SCK_SET_HIGH output_gpio(PD_SCK_PIN, HIGH)
+ #define PD_SCK_SET_LOW output_gpio(PD_SCK_PIN, LOW)
+
+ #define DOUT_PIN 5
+ #define DOUT_READ input_gpio(DOUT_PIN)
+
+ #define DOUT_SET_INPUT setup_gpio (DOUT_PIN, INPUT, PUD_OFF)
+
+
+ uint8_t GAIN; // amplification factor
+ int32_t OFFSET; // used for tare weight
+ float SCALE; // used to return weight in grams, kg, ounces, whatever
+
+ // define clock and data pin, channel, and gain factor
+ // channel selection is made by passing the appropriate gain: 128 or 64 for channel A, 32 for channel B
+ // gain: 128 or 64 for channel A; channel B works with 32 gain factor only
+ void HX711_init(uint8_t gain);
+
+ // check if HX711 is ready
+ // from the datasheet: When output data is not ready for retrieval, digital output pin DOUT is high. Serial clock
+ // input PD_SCK should be low. When DOUT goes to low, it indicates data is ready for retrieval.
+ bool HX711_is_ready();
+
+ // set the gain factor; takes effect only after a call to read()
+ // channel A can be set for a 128 or 64 gain; channel B has a fixed 32 gain
+ // depending on the parameter, the channel is also set to either A or B
+ void HX711_set_gain(uint8_t gain);
+
+ // waits for the chip to be ready and returns a reading
+ int32_t HX711_read();
+
+ // returns an average reading; times = how many times to read
+ int32_t HX711_read_average(uint8_t times);
+
+ // returns (read_average() - OFFSET), that is the current value without the tare weight; times = how many readings to do
+ double HX711_get_value(uint8_t times);
+
+ // returns get_value() divided by SCALE, that is the raw value divided by a value obtained via calibration
+ // times = how many readings to do
+ float HX711_get_units(uint8_t times);
+
+ // set the OFFSET value for tare weight; times = how many times to read the tare value
+ void HX711_tare(uint8_t times);
+
+ // set the SCALE value; this value is used to convert the raw data to "human readable" data (measure units)
+ void HX711_set_scale(float scale);
+
+ // get the current SCALE
+ float HX711_get_scale();
+
+ // set OFFSET, the value that's subtracted from the actual reading (tare weight)
+ void HX711_set_offset(int32_t offset);
+
+ // get the current OFFSET
+ int32_t HX711_get_offset();
+
+ // puts the chip into power down mode
+ void HX711_power_down();
+
+ // wakes up the chip after power down mode
+ void HX711_power_up();
+
+ // Sends/receives data. Modified from Arduino source
+ uint8_t shiftIn(void);
+
+#endif /* HX711_h */
--- /dev/null
+/*
+ * log.h
+ *
+ * Created on: Jul 5, 2012
+ * Author: volpol
+ */
+
+#ifndef LOG_H_
+#define LOG_H_
+
+#ifdef DEBUG
+ #define EPRINT(fmt, ...) do { fprintf( stderr, "%s:%s:%d:"fmt, __FILE__, __FUNCTION__, __LINE__ , ##__VA_ARGS__ ); } while (0)
+ #define DPRINT(...) EPRINT(__VA_ARGS__)
+ #define WHOAMI DPRINT("\n");
+#else
+ #define EPRINT(fmt, ...) do { fprintf( stderr, fmt, ##__VA_ARGS__ ); } while (0)
+ #define DPRINT(...)
+ #define WHOAMI
+#endif
+
+
+#endif /* LOG_H_ */
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <time.h>
+
+#include <errno.h>
+#include <poll.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <pthread.h>
+
+#include "c_gpio.h"
+#include "pump.h"
+#include "mgmt.h"
+#include "hx711.h"
+#include "net.h"
+#include "log.h"
+
+static volatile int force_quit;
+
+static void handle_signal(int sig){
+ fprintf (stderr, "SIGNAL: %d\n", sig);
+ switch (sig){
+ case SIGTERM:
+ case SIGINT:
+ force_quit = 1;
+ break;
+ }
+}
+
+//0 - ready, >0 - busy, -1 - err/unknown
+static volatile int mix_state;
+
+static void mix(const char *recipe){
+ int idx;
+ int vol;
+ const char *s;
+ char *n;
+ int sr;
+
+ s = recipe;
+
+ while (!force_quit && s && *s){
+ fprintf (stderr, "#%s#\n", s);
+ idx = strtoul(s, &n, 10);
+ s = n + 1;
+
+ fprintf (stderr, "#%s#\n", s);
+ vol = strtoul(s, &n, 10);
+ s = n + 1;
+
+ fprintf (stderr, "START PUMP: %d VOL %d\n", idx, vol);
+
+ if (0 != pump_add(idx)) break; //or continue?
+ mgmt_start(vol);
+ pump_on(idx);
+ while (!(sr = mgmt_stop()) && !force_quit) usleep(100);
+ pump_off(idx);
+ pump_del(idx);
+ fprintf (stderr, "STOP reason: %d/%d\n", sr, force_quit);
+ if (sr > MGMT_NORMAL_STOP) {
+ mix_state = -1;
+ break;
+ }
+ }
+ HX711_power_down();
+}
+
+static int receive(int fd, int timeout){
+struct pollfd pe;
+int res;
+pe.fd = fd;
+uint8_t buf[1024];
+int bread, total;
+
+total = 0;
+*buf = 0;
+do {
+ pe.events = POLLIN;
+ pe.revents = 0;
+ if( (0 < (res = poll (&pe, 1, timeout))) && pe.revents == POLLIN){
+ errno = 0;
+ bread = read(fd, buf + total, sizeof buf - total);
+ if (bread < 0)
+ switch (errno){
+ case EAGAIN:
+ //case EWOULDBLOCK:
+ case EINTR:
+ break;
+
+ default:
+ res = -1;
+ break;
+ } else
+ if (0 == bread && 0 == errno) { res = -1; }
+ else {
+ total += bread;
+ if ('\n' == buf[total - 1]){
+ buf[total - 1] = 0;
+ fprintf (stderr, "%s\n", buf);
+ if (0 == strncmp((char *)buf, "MIX:", 4)) mix((char *)buf + 4);
+ if (0 == strncmp((char *)buf, "QUIT", 4)) res = -1;
+ *buf = 0;
+ }
+ }
+ }
+ timeout = 50;
+} while ((1 == res) && (pe.revents == POLLIN) && !force_quit);
+return res;
+}
+
+static pthread_t dealer;
+
+static void *deal( void *p ){
+ struct pollfd pas;
+ int err;
+
+ pas.fd = mix_state;
+ pas.events = POLLIN;
+ pas.revents = 0;
+
+ write (pas.fd, "READY\n", 6);
+
+ do {
+ err = receive (pas.fd, 1000);
+ } while (err >= 0 && !force_quit);
+
+ net_close( pas.fd );
+ //don't touch mix_state if it set to error by receive / mix
+ if (mix_state > 0)
+ mix_state = 0;
+ return p;
+}
+
+int bbd(void){
+ int err = -1;
+ int ss, as;
+
+ ss = net_open_server( 0xbbd, "*" );
+ if ( ss < 0 )
+ {
+ EPRINT( "net_open_server failed!\n" );
+ goto DONE;
+ }
+ mgmt_init();
+ while (!force_quit){
+ if ((as = net_accept( ss )) > 0){
+ switch (mix_state){
+ case 0:
+ pthread_join( dealer, NULL);
+ dealer = 0;
+ mix_state = as;
+ err = pthread_create( &dealer, NULL, deal, NULL);
+ if (0 != err)
+ EPRINT( "pthread_create failed!\n" );
+ else
+ DPRINT ("PID: %lX\n", dealer);
+ break;
+ case -1:
+ write (as, "ERROR\n", 6);
+ net_close (as);
+ break;
+ default:
+ write (as, "BUSY\n", 5);
+ net_close (as);
+ break;
+ }
+ }
+ }
+ pthread_join( dealer, NULL);
+ dealer = 0;
+ close(ss);
+DONE:
+ return err;
+}
+
+int main(void){
+ int err;
+
+ //install signal handlers
+ signal(SIGTERM, handle_signal);
+ signal(SIGINT, handle_signal);
+
+ //initialize gpio susbsystem
+ if (SETUP_OK == (err = setup())){
+ err = bbd();
+ cleanup();
+ } else {
+ fprintf (stderr, "setup: %d\n", err);
+ if (SETUP_NOT_RPI_FAIL == err){
+ //limited on PC/non-PI but still
+ err = bbd();
+ }
+ }
+ fprintf (stderr, "bbd: %d\n", err);
+ return err;
+}
--- /dev/null
+#include <time.h>
+#include <stdio.h>
+
+#ifdef HAVE_HX711
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "log.h"
+#include "mgmt.h"
+#include "hx711.h"
+
+#define MAX(x,y) (((x)>(y)) ? (x) : (y))
+#define MIN(x,y) (((x)<(y)) ? (x) : (y))
+static int stop_time;
+static double stop_weight;
+static double start_weight;
+
+static double curweight(void){
+ double w1, w2;
+
+ do {
+ w1 = HX711_get_units(3);
+ w2 = HX711_get_units(3);
+ } while (MAX(w1,w2) - MIN(w1,w2) > 0.5);
+
+ DPRINT ("w: %3.2f\n", w2);
+ return w2;
+}
+
+void mgmt_init(void){
+ HX711_init(128);
+ HX711_set_scale(412);
+ HX711_tare(30);
+ HX711_power_down();
+}
+
+int mgmt_start(int ml){
+ HX711_power_up();
+ HX711_tare(3);
+ stop_time = time(NULL) + 10;
+ start_weight = curweight();
+ stop_weight = start_weight + ml;
+ return 0;
+}
+
+int mgmt_stop(void){
+ int res = MGMT_CONTINUE;
+ double cw = curweight();
+ if (cw >= stop_weight) res = MGMT_NORMAL_STOP; else
+ if (time(NULL) >= stop_time && MAX(cw, start_weight) - MIN(cw, start_weight) < 5) res = MGMT_ABORT;
+ return res;
+}
+
+#else
+
+#define Q 1
+
+static int stop_cond;
+
+void mgmt_init(void){
+
+}
+
+int mgmt_start(int ml){
+ stop_cond = time(NULL) + ml * Q;
+ return 0;
+}
+
+int mgmt_stop(void){
+ return time(NULL) >= stop_cond;
+}
+
+#endif
+
+
--- /dev/null
+#ifndef MGMT_H
+#define MGMT_H 1
+
+#define MGMT_CONTINUE 0
+#define MGMT_NORMAL_STOP 1
+#define MGMT_ABORT 2
+
+void mgmt_init(void);
+int mgmt_start(int ml);
+int mgmt_stop(void);
+
+#endif /* MGMT_H */
+
+
--- /dev/null
+/*
+ * net.c
+ *
+ * Created on: Mar 8, 2013
+ * Author: volpol
+ * Modifed:
+ * 2013-05-31 support bind address, statics no more [uw]
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <unistd.h>
+
+#include "log.h"
+
+
+int net_open_client( const char *host, unsigned short port, const char *bind_addr )
+{
+ int sock = -1;
+ struct sockaddr_in server;
+ struct hostent *he = NULL;
+
+ //WHOAMI;
+ DPRINT( "%s:%d [%s]\n", host, port, bind_addr ? bind_addr : "*" );
+ if ( 0 > ( sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ) )
+ return perror( "socket()" ), -1;
+ server.sin_port = htons( port );
+ server.sin_family = AF_INET;
+ if ( NULL == ( he = gethostbyname( host ) ) )
+ {
+ herror( "gethostbyname()" );
+ goto ERROUT;
+ }
+ if ( NULL != bind_addr )
+ {
+ struct sockaddr_in lob;
+ lob.sin_family = AF_INET;
+ if ( 0 != strcmp( bind_addr, "*" ) )
+ {
+ if ( 0 == strcmp( bind_addr, "localhost" ) )
+ bind_addr = "127.0.0.1";
+ lob.sin_family = AF_INET;
+ if ( 0 == inet_aton( bind_addr, (struct in_addr *)&lob.sin_addr.s_addr ) )
+ {
+ fprintf( stderr, "inet_aton() failed\n" );
+ goto ERROUT;
+ }
+ }
+ else
+ lob.sin_addr.s_addr = INADDR_ANY;
+ if ( 0 > bind( sock, (const struct sockaddr*)&lob, sizeof lob ) )
+ {
+ perror( "bind()" );
+ goto ERROUT;
+ }
+ }
+ server.sin_addr.s_addr = *((in_addr_t*)he->h_addr_list[0]);
+ if ( 0 != connect( sock, (struct sockaddr *)&server, sizeof server ) )
+ {
+ perror( "connect()" );
+ goto ERROUT;
+ }
+ return sock;
+ERROUT:
+ close( sock );
+ return -1;
+}
+
+int net_accept( int sock )
+{
+ WHOAMI;
+ int conn = -1;
+ struct pollfd pe;
+ int res;
+
+ pe.fd = sock;
+ pe.events = POLLIN;
+ pe.revents = 0;
+ if( (0 < (res = poll (&pe, 1, 100))) && pe.revents == POLLIN){
+ if ( 0 > ( conn = accept( sock, NULL, NULL ) ) )
+ perror( "accept()" );
+ else
+ {
+ DPRINT( "accepted conn: %d\n", conn );
+ }
+ } else {
+ return -1;
+ }
+ return conn;
+}
+
+void net_close( int conn )
+{
+ WHOAMI;
+ if ( 0 <= conn )
+ {
+ DPRINT( "shutting down conn: %d\n", conn );
+ shutdown( conn, SHUT_RDWR );
+ close( conn );
+ }
+}
+
+int net_open_server( unsigned short port, const char *bind_addr )
+{
+ int sock;
+ int reuse = 1;
+ struct sockaddr_in lob;
+
+ //WHOAMI;
+ DPRINT( "%d [%s]\n", port, bind_addr ? bind_addr : "*" );
+ if ( 0 > ( sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ) )
+ return perror( "socket()" ), -1;
+ if ( 0 != setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse ) )
+ {
+ perror( "setsockopt()" );
+ goto ERROUT;
+ }
+ lob.sin_family = AF_INET;
+ lob.sin_port = htons( port );
+ if ( NULL != bind_addr && 0 != strcmp( bind_addr, "*" ) )
+ {
+ if ( 0 == strcmp( bind_addr, "localhost" ) )
+ bind_addr = "127.0.0.1";
+ lob.sin_family = AF_INET;
+ if ( 0 == inet_aton( bind_addr, (struct in_addr *)&lob.sin_addr.s_addr ) )
+ {
+ fprintf( stderr, "inet_aton() failed\n" );
+ goto ERROUT;
+ }
+ }
+ else
+ lob.sin_addr.s_addr = INADDR_ANY;
+ if ( 0 > bind( sock, (const struct sockaddr*)&lob, sizeof lob ) )
+ {
+ perror( "bind()" );
+ goto ERROUT;
+ }
+ if ( 0 > listen( sock, 0 ) )
+ {
+ perror( "listen()" );
+ goto ERROUT;
+ }
+ return sock;
+ERROUT:
+ close( sock );
+ return -1;
+}
--- /dev/null
+/*
+ * net.h
+ *
+ * Created on: Mar 8, 2013
+ * Author: volpol
+ */
+
+#ifndef NET_H_
+#define NET_H_
+
+int net_accept( int sock );
+int net_open_server( unsigned short port, const char *bind_addr );
+int net_open_client( const char *host, unsigned short port, const char *bind_addr );
+void net_close( int conn );
+
+#endif /* NET_H_ */
--- /dev/null
+/*
+ * util.c
+ *
+ * Created on: Aug 4, 2010
+ * Author: volpol
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "log.h"
+
+#include "pump.h"
+#include "c_gpio.h"
+
+
+DPRINT ("pump_idx %d", num);
+DPRINT ("pump_gpio %d", gpio);
+#define NUM_PUMPS 12
+static int P2G[NUM_PUMPS] = {
+ 17,
+ 2,
+ 0,
+
+ 0,
+ 0,
+ 0,
+
+ 0,
+ 0,
+ 0,
+
+ 0,
+ 0,
+ 0
+};
+
+int pump_add(int num){
+ int res = -1;
+ DPRINT ("pump_idx %d", num);
+ if (num > 0 && num <= NUM_PUMPS){
+ int gpio = P2G[num];
+ DPRINT ("pump_gpio %d", gpio);
+ setup_gpio(gpio, OUTPUT, PUD_UP);
+ pump_off(num);
+ res = 0;
+ }
+ return res;
+}
+
+int pump_del(int num){
+ int res = -1;
+ DPRINT ("pump_idx %d", num);
+ if (num > 0 && num <= NUM_PUMPS){
+ int gpio = P2G[num];
+ DPRINT ("pump_gpio %d", gpio);
+ setup_gpio(gpio, INPUT, PUD_OFF);
+ res = 0;
+ }
+ return res;
+}
+
+void pump_on(int num){
+ DPRINT ("pump_idx %d", num);
+ if (num > 0 && num <= NUM_PUMPS){
+ int gpio = P2G[num];
+ DPRINT ("pump_gpio %d", gpio);
+ output_gpio(gpio, LOW);
+ }
+}
+
+void pump_off(int num){
+ DPRINT ("pump_idx %d", num);
+ if (num > 0 && num <= NUM_PUMPS){
+ int gpio = P2G[num];
+ DPRINT ("pump_gpio %d", gpio);
+ output_gpio(gpio, HIGH);
+ }
+}
+
+void pump_toggle(int num){
+ pump_on(num);
+ usleep(500000);
+ pump_off(num);
+}
--- /dev/null
+#ifndef PUMP_H
+#define PUMP_H 1
+
+int pump_add(int num);
+int pump_del(int num);
+void pump_toggle(int num);
+void pump_on(int num);
+void pump_off(int num);
+
+#endif /* PUMP_H */