Skip to content

Commit

Permalink
Merge pull request #262 from developer79433/master
Browse files Browse the repository at this point in the history
Containerise this
  • Loading branch information
rayshobby authored May 12, 2024
2 parents 613efae + 1b9a14f commit 7120a47
Show file tree
Hide file tree
Showing 19 changed files with 313 additions and 91 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/c-cpp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: C/C++ CI

on:
push:
branches: [ "*" ]
pull_request:
branches: [ "*" ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: make
run: sudo apt install -y libmosquittopp-dev && make
44 changes: 44 additions & 0 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Docker Image CI

on:
push:
branches: [ "*" ]
pull_request:
branches: [ "*" ]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push-image:
runs-on: ubuntu-latest

permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Build and push Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ testmode.h
build-1284/*
.vscode
.pio
*.o
41 changes: 41 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
FROM debian:bookworm-slim as base

########################################
## 1st stage compiles OpenSprinkler runtime dependency raspi-gpio
FROM base as raspi-gpio-build

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y git gcc make automake && rm -rf /var/lib/apt/lists/*
RUN mkdir /raspi-gpio && cd /raspi-gpio && git clone --depth 1 https://github.com/RPi-Distro/raspi-gpio.git . && autoreconf -f -i && (./configure || cat config.log) && make

########################################
## 2nd stage compiles OpenSprinkler code
FROM base as os-build

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y bash g++ make libmosquittopp-dev && rm -rf /var/lib/apt/lists/*
COPY . /OpenSprinkler
RUN cd /OpenSprinkler && make

########################################
## 3rd stage is minimal runtime + executable
FROM base

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y libstdc++6 libmosquittopp1 && rm -rf /var/lib/apt/lists/* \
&& \
mkdir /OpenSprinkler && \
mkdir -p /data/logs

COPY --from=os-build /OpenSprinkler/OpenSprinkler /OpenSprinkler/OpenSprinkler
COPY --from=raspi-gpio-build /raspi-gpio/raspi-gpio /usr/bin/raspi-gpio
WORKDIR /OpenSprinkler

#-- Logs and config information go into the volume on /data
VOLUME /data

#-- OpenSprinkler interface is available on 8080
EXPOSE 8080

#-- By default, start OS using /data for saving data/NVM/log files
CMD [ "/OpenSprinkler/OpenSprinkler", "-d", "/data" ]
27 changes: 27 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
CXX=g++
# -std=gnu++17
CXXFLAGS=-std=gnu++14 -DOSPI -Wall
LD=$(CXX)
LIBS=pthread mosquitto
LDFLAGS=$(addprefix -l,$(LIBS))
BINARY=OpenSprinkler
SOURCES=main.cpp OpenSprinkler.cpp program.cpp opensprinkler_server.cpp utils.cpp weather.cpp gpio.cpp etherport.cpp mqtt.cpp
HEADERS=$(wildcard *.h)
OBJECTS=$(SOURCES:.cpp=.o)

.PHONY: all
all: $(BINARY)

%.o: %.cpp $(HEADERS)
$(CXX) -c -o "$@" $(CXXFLAGS) "$<"

$(BINARY): $(OBJECTS)
$(CXX) -o $(BINARY) $(OBJECTS) $(LDFLAGS)

.PHONY: clean
clean:
rm -f $(OBJECTS) $(BINARY)

.PHONY: container
container:
docker build .
44 changes: 21 additions & 23 deletions OpenSprinkler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,23 +41,23 @@ byte OpenSprinkler::station_bits[MAX_NUM_BOARDS];
byte OpenSprinkler::engage_booster;
uint16_t OpenSprinkler::baseline_current;

ulong OpenSprinkler::sensor1_on_timer;
ulong OpenSprinkler::sensor1_off_timer;
ulong OpenSprinkler::sensor1_active_lasttime;
ulong OpenSprinkler::sensor2_on_timer;
ulong OpenSprinkler::sensor2_off_timer;
ulong OpenSprinkler::sensor2_active_lasttime;
ulong OpenSprinkler::raindelay_on_lasttime;
ulong OpenSprinkler::pause_timer;

ulong OpenSprinkler::flowcount_log_start;
ulong OpenSprinkler::flowcount_rt;
byte OpenSprinkler::button_timeout;
ulong OpenSprinkler::checkwt_lasttime;
ulong OpenSprinkler::checkwt_success_lasttime;
ulong OpenSprinkler::powerup_lasttime;
time_t OpenSprinkler::sensor1_on_timer;
time_t OpenSprinkler::sensor1_off_timer;
time_t OpenSprinkler::sensor1_active_lasttime;
time_t OpenSprinkler::sensor2_on_timer;
time_t OpenSprinkler::sensor2_off_timer;
time_t OpenSprinkler::sensor2_active_lasttime;
time_t OpenSprinkler::raindelay_on_lasttime;
ulong OpenSprinkler::pause_timer;

ulong OpenSprinkler::flowcount_log_start;
ulong OpenSprinkler::flowcount_rt;
byte OpenSprinkler::button_timeout;
time_t OpenSprinkler::checkwt_lasttime;
time_t OpenSprinkler::checkwt_success_lasttime;
time_t OpenSprinkler::powerup_lasttime;
uint8_t OpenSprinkler::last_reboot_cause = REBOOT_CAUSE_NONE;
byte OpenSprinkler::weather_update_flag;
byte OpenSprinkler::weather_update_flag;

// todo future: the following attribute bytes are for backward compatibility
byte OpenSprinkler::attrib_mas[MAX_NUM_BOARDS];
Expand Down Expand Up @@ -703,7 +703,7 @@ bool OpenSprinkler::load_hardware_mac(byte* mac, bool wired) {
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0) return true;

// Returns the mac address of the first interface if multiple active
for (int i = 0; i < sizeof(if_names)/sizeof(const char *); i++) {
for (unsigned int i = 0; i < sizeof(if_names)/sizeof(const char *); i++) {
strncpy(ifr.ifr_name, if_names[i], sizeof(ifr.ifr_name));
if (ioctl(fd, SIOCGIFHWADDR, &ifr) != -1) {
memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
Expand All @@ -729,7 +729,7 @@ void OpenSprinkler::reboot_dev(uint8_t cause) {
/** Launch update script */
void OpenSprinkler::update_dev() {
char cmd[1000];
sprintf(cmd, "cd %s && ./updater.sh", get_runtime_path());
sprintf(cmd, "cd %s && ./updater.sh", get_data_dir());
system(cmd);
}
#endif // end network init functions
Expand Down Expand Up @@ -1238,8 +1238,6 @@ void OpenSprinkler::apply_all_station_bits() {
}
}
}

byte bid, s, sbits;
#else
digitalWrite(PIN_SR_LATCH, LOW);
byte bid, s, sbits;
Expand Down Expand Up @@ -1287,7 +1285,7 @@ void OpenSprinkler::apply_all_station_bits() {
// we refresh the station that's next in line
static byte next_sid_to_refresh = MAX_NUM_STATIONS>>1;
static byte lastnow = 0;
ulong curr_time = now_tz();
time_t curr_time = now_tz();
byte _now = (curr_time & 0xFF);
if (lastnow != _now) { // perform this no more than once per second
lastnow = _now;
Expand All @@ -1312,7 +1310,7 @@ void OpenSprinkler::apply_all_station_bits() {
}

/** Read rain sensor status */
void OpenSprinkler::detect_binarysensor_status(ulong curr_time) {
void OpenSprinkler::detect_binarysensor_status(time_t curr_time) {
// sensor_type: 0 if normally closed, 1 if normally open
if(iopts[IOPT_SENSOR1_TYPE]==SENSOR_TYPE_RAIN || iopts[IOPT_SENSOR1_TYPE]==SENSOR_TYPE_SOIL) {
if(hw_rev>=2) pinModeExt(PIN_SENSOR1, INPUT_PULLUP); // this seems necessary for OS 3.2
Expand Down Expand Up @@ -1376,7 +1374,7 @@ void OpenSprinkler::detect_binarysensor_status(ulong curr_time) {
}

/** Return program switch status */
byte OpenSprinkler::detect_programswitch_status(ulong curr_time) {
byte OpenSprinkler::detect_programswitch_status(time_t curr_time) {
byte ret = 0;
if(iopts[IOPT_SENSOR1_TYPE]==SENSOR_TYPE_PSWITCH) {
static byte sensor1_hist = 0;
Expand Down
24 changes: 12 additions & 12 deletions OpenSprinkler.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,21 +234,21 @@ class OpenSprinkler {
static byte masters[NUM_MASTER_ZONES][NUM_MASTER_OPTS];

// variables for time keeping
static ulong sensor1_on_timer; // time when sensor1 is detected on last time
static ulong sensor1_off_timer; // time when sensor1 is detected off last time
static ulong sensor1_active_lasttime; // most recent time sensor1 is activated
static ulong sensor2_on_timer; // time when sensor2 is detected on last time
static ulong sensor2_off_timer; // time when sensor2 is detected off last time
static ulong sensor2_active_lasttime; // most recent time sensor1 is activated
static ulong raindelay_on_lasttime; // time when the most recent rain delay started
static time_t sensor1_on_timer; // time when sensor1 is detected on last time
static time_t sensor1_off_timer; // time when sensor1 is detected off last time
static time_t sensor1_active_lasttime; // most recent time sensor1 is activated
static time_t sensor2_on_timer; // time when sensor2 is detected on last time
static time_t sensor2_off_timer; // time when sensor2 is detected off last time
static time_t sensor2_active_lasttime; // most recent time sensor1 is activated
static time_t raindelay_on_lasttime; // time when the most recent rain delay started
static ulong pause_timer; // count down timer in paused state
static ulong flowcount_rt; // flow count (for computing real-time flow rate)
static ulong flowcount_log_start; // starting flow count (for logging)

static byte button_timeout; // button timeout
static ulong checkwt_lasttime; // time when weather was checked
static ulong checkwt_success_lasttime; // time when weather check was successful
static ulong powerup_lasttime; // time when controller is powered up most recently
static time_t checkwt_lasttime; // time when weather was checked
static time_t checkwt_success_lasttime; // time when weather check was successful
static time_t powerup_lasttime; // time when controller is powered up most recently
static uint8_t last_reboot_cause; // last reboot cause
static byte weather_update_flag;
// member functions
Expand Down Expand Up @@ -306,8 +306,8 @@ class OpenSprinkler {
static void disable(); // disable controller operation, all stations will be closed immediately
static void raindelay_start(); // start raindelay
static void raindelay_stop(); // stop rain delay
static void detect_binarysensor_status(ulong);// update binary (rain, soil) sensor status
static byte detect_programswitch_status(ulong); // get program switch status
static void detect_binarysensor_status(time_t curr_time);// update binary (rain, soil) sensor status
static byte detect_programswitch_status(time_t curr_time); // get program switch status
static void sensor_resetall();

static uint16_t read_current(); // read current sensing value
Expand Down
36 changes: 36 additions & 0 deletions README_Docker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Running OpenSprinkler via Docker

## Building the container
From this directory, create the image:

```sh
$ docker build -t opensprinkler .
```

Notes:
* This requires a version of Docker that supports multi-stage builds
(i.e., version >= 17.06).
* The container is built for the armhf architecture.

## Running the container
Once the container is built, it can be started via:

```sh
$ mkdir ~/opensprinkler
$ docker run -d \
--name opensprinkler \ # Give it a handy name
--privileged \ # Privileged because we need access to devices
--publish 8080:8080 \ # Make the web interface accessible
--restart always \ # Auto restart on crash/exit
--volume /dev:/dev \ # Provide access to /dev
--volume ~/opensprinkler:/data \ # Where to save NVM & logs
opensprinkler # The image (from above)
```

The persistent data generated by OS is written to the volume mounted at `/data`
within the container. This includes the NVM files, IFTTT key file, and log
directory. In the above sample command line, these files will end up in the
user's home directory (`${HOME}/opensprinkler`). Other options, including using
a docker data volume are possible. NOTE: If you don't specify a volume or dir to
mount here, your OpenSprinkler configuration will not persist across restarts of
the container.
5 changes: 3 additions & 2 deletions gpio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ static byte GPIOExport(int pin) {
return 1;
}

#if 0
/** Unexport gpio pin */
static byte GPIOUnexport(int pin) {
char buffer[BUFFER_MAX];
Expand All @@ -237,11 +238,12 @@ static byte GPIOUnexport(int pin) {
close(fd);
return 1;
}
#endif

/** Set interrupt edge mode */
static byte GPIOSetEdge(int pin, const char *edge) {
char path[BUFFER_MAX];
int fd, len;
int fd;

snprintf(path, BUFFER_MAX, "/sys/class/gpio/gpio%d/edge", pin);

Expand Down Expand Up @@ -413,7 +415,6 @@ void attachInterrupt(int pin, const char* mode, void (*isr)(void)) {

char path[BUFFER_MAX];
snprintf(path, BUFFER_MAX, "/sys/class/gpio/gpio%d/value", pin);
int fd;

// open gpio file
if(sysFds[pin]==-1) {
Expand Down
Loading

0 comments on commit 7120a47

Please sign in to comment.