Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
ARG img_user=ghcr.io/driplineorg
ARG img_repo=dripline-python
#ARG img_tag=develop-dev
ARG img_tag=receiver-test
ARG img_tag=v5.1.0

FROM ${img_user}/${img_repo}:${img_tag}

Expand Down
154 changes: 154 additions & 0 deletions dragonfly/sketch_coolingloopsensor.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#include <DallasTemperature.h>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should establish convention for where example Arduino sketches go. Essentially this is templating a SCPI-lite device, which isn't really a dripline/dragonfly code element.
In Phase II we stashed them in the hardware repo, which was just an ad hoc solution.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We move all our arduino code and raspberryPi code here: https://github.com/project8/MainzDripline3/tree/main/MicroControllerScripts

The main purpose is to have it version controlled and backed up.
In this pull request I wanted to give it as an example and thus it may better be in the example folder

#include <EtherCard.h>
#include <IPAddress.h>
#include <OneWire.h>
#include <string.h>

/* ----------------------------- CONFIGURATION ----------------------------*/

#define DEBUG 1 // 0 disabled, 1 enabled
#define DHCP 1 // 0 disabled, 1 enabled
#define MAC 0 // 0 for astro-yacht, 1 for astro-zabra, 2 for astro-aye

/* ------------------------------------------------------------------------*/

#if DEBUG
#define DEBUG_PRINTLN(x) Serial.println(x)
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTIP(x, y) ether.printIp(x, y)
#else
#define DEBUG_PRINTLN(x)
#define DEBUG_PRINT(x)
#define DEBUG_PRINTIP(x)
#endif

const byte pinFlowmeter = 3; // Interrupt pin (D2)
const byte pinTemperature = 5; // Digital pin (D6)
const byte pinPressure = 6;

volatile unsigned int pulseCount = 0; // Stores number of pulses
const int flowMeasurementDuration = 500; // Microsecond

OneWire oneWire(pinTemperature);
DallasTemperature temperatureSensor(&oneWire);

#if MAC == 0
static byte mymac[] = {0x5a, 0x09, 0xb5, 0xaf, 0xd8, 0x11};
#elif MAC == 1
static byte mymac[] = {0x5a, 0x09, 0xb5, 0xaf, 0xd8, 0x10};
#elif MAC == 2
static byte mymac[] = {0x5a, 0x09, 0xb5, 0xaf, 0xd8, 0x09};
#endif

#if DHCP == 0
static byte myip[] = {192, 168, 1, 10};
// Gateway IP address
static byte gwip[] = {192, 168, 0, 10};
#endif

byte Ethernet::buffer[500]; // TCP/IP send and receive buffer

void pulseISR() {
pulseCount++; // Interrupt service routine: Increment the pulse count on each interrupt
}

float getTemperatureC() {
/* Returns temperature in degrees Celcius */
temperatureSensor.requestTemperatures();
return temperatureSensor.getTempCByIndex(0);
}

float getPressureV() {
/* Returns the pressure in Volts */
int sensorValue = analogRead(pinPressure);
return sensorValue * (5.0 / 1023.0);
}

float getFlowHz() {
/* Returns pulse frequency in Herz */
pulseCount = 0;
delay(flowMeasurementDuration);
float frequency = (float)pulseCount * 1000.0 / (flowMeasurementDuration);
return frequency;
}

void setup() {
#if DEBUG
Serial.begin(115200);
#endif

pinMode(pinFlowmeter, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(pinFlowmeter), pulseISR, RISING);

temperatureSensor.begin();
if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) {
DEBUG_PRINTLN("Failed to access Ethernet controller.");
} else {
DEBUG_PRINTLN("Ethernet controller sucessfully accessed.");
}

#if DHCP == 0
ether.staticSetup(myip, gwip);
#else
if (!ether.dhcpSetup()) {
DEBUG_PRINTLN("DHCP failed");
} else {
DEBUG_PRINTIP("DHCP request successful. IP-Adress: ", ether.myip);
DEBUG_PRINT("Lease time: ");
DEBUG_PRINTLN(ether.getLeaseTime());
}
#endif
}

void loop() {
word len = ether.packetReceive();
word pos = ether.packetLoop(len);

if (pos) {
char *data = (char *)Ethernet::buffer + pos;
DEBUG_PRINT("Received data: ");
DEBUG_PRINTLN(data);

float sensorData = 0;
boolean caseFound = false;

char response[10];
memset(response, '\n', sizeof(response));
if (strncmp(data, "temp?", 5) == 0) {
sensorData = getTemperatureC();
dtostrf(sensorData, 4, 4, response);
} else if (strncmp(data, "pres?", 5) == 0) {
sensorData = getPressureV();
dtostrf(sensorData, 4, 4, response);
} else if (strncmp(data, "flow?", 5) == 0) {
sensorData = getFlowHz();
dtostrf(sensorData, 4, 4, response);
} else if (strncmp(data, "test?", 5) == 0) {
strcpy(response, "success");
} else {
strcpy(response, "invalid");
}

DEBUG_PRINT("Sending data: ");
DEBUG_PRINTLN(response);

memcpy(ether.tcpOffset(), response, sizeof(response));
ether.httpServerReply(sizeof(response) - 1);
}

// if DHCP lease is expired or millis wrap over after 49 days
#if DHCP
float leaseStart = ether.getLeaseStart();
if ((millis() - leaseStart >= ether.getLeaseTime()) ||
(millis() < leaseStart)) {
if (!ether.dhcpSetup()) {
DEBUG_PRINTLN("DHCP renew failed");
delay(60000); // wait for one minute before retrying.
} else {
DEBUG_PRINTIP("DHCP renewed. IP-Adress: ", ether.myip);
DEBUG_PRINT("Lease time: ");
DEBUG_PRINTLN(ether.getLeaseTime());
}
}
#endif
}
2 changes: 2 additions & 0 deletions dripline/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@
# Modules in this directory

from .add_auth_spec import *
from .arduino_endpoint import *
from .arduino_service import *
from .thermo_fisher_endpoint import *
from .ethernet_thermo_fisher_service import *
22 changes: 22 additions & 0 deletions dripline/extensions/arduino_endpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from dripline.core import Entity, calibrate, ThrowReply

import logging
logger = logging.getLogger(__name__)

__all__ = []
__all__.append("ArduinoGetEntity")

class ArduinoGetEntity(Entity):
def __init__(self,
get_str=None,
**kwargs):

Entity.__init__(self, **kwargs)
self.get_str = get_str

@calibrate()
def on_get(self):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return self.service.send_to_device([self.get_str])

def on_set(self, value):
raise ThrowReply('message_error_invalid_method', f"endpoint '{self.name}' does not support set")
73 changes: 73 additions & 0 deletions dripline/extensions/arduino_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import re
import socket
import threading

from dripline.core import Service

import logging
logger = logging.getLogger(__name__)

__all__ = []
__all__.append('EthernetArduinoService')

class EthernetArduinoService(Service):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add docstring?

def __init__(self,
socket_info,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given the odd form of socket_info, probably best to define the init args (I know they follow a reduced set of EthernetSCPIService, but since they are independently defined, the docs don't follow)

socket_timeout=10,
**kwargs
):

Service.__init__(self, **kwargs)

if isinstance(socket_info, str):
logger.debug(f"Formatting socket_info: {socket_info}")
re_str = "\([\"'](\S+)[\"'], ?(\d+)\)"
(ip,port) = re.findall(re_str,socket_info)[0]
socket_info = (ip,int(port))

self.alock = threading.Lock()
self.socket = socket.socket()

self.socket_timeout = float(socket_timeout)
self.socket_info = socket_info


def send_to_device(self, commands, **kwargs):

if isinstance(commands, str):
commands = [commands]

self.alock.acquire()
data = []
try:
data = self._send_commands(commands)
except Exception as err:
logger.critical(str(err))
finally:
self.alock.release()
to_return = ';'.join(data)
logger.debug(f"should return:\n{to_return}")
return to_return


def _send_commands(self, commands):
all_data=[]

for command in commands:
try:
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.settimeout(self.socket_timeout)
self.socket.connect(self.socket_info)
self.socket.sendall(bytes(command, encoding="utf-8"))
data = self.socket.recv(1024)
data = data.rstrip(b'\x00\n').decode("utf-8")
except Exception as err:
logger.warning(f"While socket communication we received an error: {err}")
finally:
self.socket.close()

logger.info(f"Received: {data}")

all_data.append(data)

return all_data