Browse Source

Merge pull request #1301 from ruimarinho/feature/atlas-scientific-ph-probe

Add support for EZO™ pH Circuit
alexa
Xose Pérez 6 years ago
committed by GitHub
parent
commit
fe2d238a1b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 265 additions and 11 deletions
  1. +1
    -0
      code/espurna/config/arduino.h
  2. +8
    -3
      code/espurna/config/progmem.h
  3. +29
    -5
      code/espurna/config/sensors.h
  4. +3
    -1
      code/espurna/config/types.h
  5. +9
    -0
      code/espurna/sensor.ino
  6. +212
    -0
      code/espurna/sensors/EZOPHSensor.h
  7. +3
    -2
      code/html/custom.js

+ 1
- 0
code/espurna/config/arduino.h View File

@ -179,3 +179,4 @@
//#define TMP3X_SUPPORT 1
//#define V9261F_SUPPORT 1
//#define VL53L1X_SUPPORT 1
//#define EZOPH_SUPPORT 1

+ 8
- 3
code/espurna/config/progmem.h View File

@ -232,6 +232,9 @@ PROGMEM const char espurna_sensors[] =
#if VL53L1X_SUPPORT
"VL53L1X "
#endif
#if EZOPH_SUPPORT
"EZOPH "
#endif
"";
@ -246,7 +249,7 @@ PROGMEM const unsigned char magnitude_decimals[] = {
3, 0,
4, 4, // Geiger Counter decimals
0,
0, 0, 0 // NO2, CO, Ohms
0, 0, 0, 3 // NO2, CO, Ohms, pH
};
PROGMEM const char magnitude_unknown_topic[] = "unknown";
@ -280,6 +283,7 @@ PROGMEM const char magnitude_count_topic[] = "count";
PROGMEM const char magnitude_no2_topic[] = "no2";
PROGMEM const char magnitude_co_topic[] = "co";
PROGMEM const char magnitude_resistance_topic[] = "resistance";
PROGMEM const char magnitude_ph_topic[] = "ph";
PROGMEM const char* const magnitude_topics[] = {
magnitude_unknown_topic, magnitude_temperature_topic, magnitude_humidity_topic,
@ -293,7 +297,7 @@ PROGMEM const char* const magnitude_topics[] = {
magnitude_distance_topic, magnitude_hcho_topic,
magnitude_geiger_cpm_topic, magnitude_geiger_sv_topic,
magnitude_count_topic,
magnitude_no2_topic, magnitude_co_topic, magnitude_resistance_topic
magnitude_no2_topic, magnitude_co_topic, magnitude_resistance_topic, magnitude_ph_topic
};
PROGMEM const char magnitude_empty[] = "";
@ -330,7 +334,8 @@ PROGMEM const char* const magnitude_units[] = {
magnitude_geiger_cpm, magnitude_geiger_sv, // Geiger counter units
magnitude_empty, //
magnitude_ppm, magnitude_ppm, // NO2 & CO2
magnitude_resistance
magnitude_resistance,
magnitude_empty // pH
};
#endif

+ 29
- 5
code/espurna/config/sensors.h View File

@ -817,6 +817,25 @@
#define VEML6075_DYNAMIC_MODE VEML6075::DYNAMIC_NORMAL // The dynamic mode can either be normal or high. In high
#endif // dynamic mode, the resolution increases by about two
// times.
// EZOPH pH meter
// Enable support by passing EZOPH_SUPPORT=1 build flag
//------------------------------------------------------------------------------
#ifndef EZOPH_SUPPORT
#define EZOPH_SUPPORT 0
#endif
#ifndef EZOPH_RX_PIN
#define EZOPH_RX_PIN 13 // Software serial RX GPIO
#endif
#ifndef EZOPH_TX_PIN
#define EZOPH_TX_PIN 15 // Software serial TX GPIO
#endif
#ifndef EZOPH_SYNC_INTERVAL
#define EZOPH_SYNC_INTERVAL 1000 // Amount of time (in ms) sync new readings.
#endif
// =============================================================================
// Sensor helpers configuration - can't move to dependencies.h
@ -855,7 +874,8 @@
TMP3X_SUPPORT || \
V9261F_SUPPORT || \
VEML6075_SUPPORT || \
VL53L1X_SUPPORT \
VL53L1X_SUPPORT || \
EZOPH_SUPPORT \
)
#endif
@ -949,6 +969,10 @@
#include "../sensors/EventSensor.h"
#endif
#if EZOPH_SUPPORT
#include "../sensors/EZOPHSensor.h"
#endif
#if GEIGER_SUPPORT
#include "../sensors/GeigerSensor.h"
#endif
@ -961,6 +985,10 @@
#include "../sensors/HLW8012Sensor.h"
#endif
#if MAX6675_SUPPORT
#include "../sensors/MAX6675.h"
#endif
#if MHZ19_SUPPORT
#include "../sensors/MHZ19Sensor.h"
#endif
@ -1025,8 +1053,4 @@
#include "../sensors/VL53L1XSensor.h"
#endif
#if MAX6675_SUPPORT
#include "../sensors/MAX6675.h"
#endif
#endif // SENSOR_SUPPORT

+ 3
- 1
code/espurna/config/types.h View File

@ -282,6 +282,7 @@
#define SENSOR_PULSEMETER_ID 30
#define SENSOR_VEML6075_ID 31
#define SENSOR_VL53L1X_ID 32
#define SENSOR_EZOPH_ID 33
//--------------------------------------------------------------------------------
// Magnitudes
@ -319,5 +320,6 @@
#define MAGNITUDE_NO2 28
#define MAGNITUDE_CO 29
#define MAGNITUDE_RESISTANCE 30
#define MAGNITUDE_PH 31
#define MAGNITUDE_MAX 31
#define MAGNITUDE_MAX 32

+ 9
- 0
code/espurna/sensor.ino View File

@ -754,6 +754,15 @@ void _sensorLoad() {
_sensors.push_back(sensor);
}
#endif
#if EZOPH_SUPPORT
{
EZOPHSensor * sensor = new EZOPHSensor();
sensor->setRX(EZOPH_RX_PIN);
sensor->setTX(EZOPH_TX_PIN);
_sensors.push_back(sensor);
}
#endif
}
void _sensorCallback(unsigned char i, unsigned char type, double value) {


+ 212
- 0
code/espurna/sensors/EZOPHSensor.h View File

@ -0,0 +1,212 @@
// -----------------------------------------------------------------------------
// EZO pH Circuit from Atlas Scientific
//
// Uses SoftwareSerial library
// Copyright (C) 2018 by Rui Marinho <ruipmarinho at gmail dot com>
// -----------------------------------------------------------------------------
#if SENSOR_SUPPORT && EZOPH_SUPPORT
#pragma once
#include "Arduino.h"
#include "BaseSensor.h"
#include <SoftwareSerial.h>
class EZOPHSensor : public BaseSensor {
public:
// ---------------------------------------------------------------------
// Public
// ---------------------------------------------------------------------
EZOPHSensor(): BaseSensor() {
_count = 1;
_sensor_id = SENSOR_EZOPH_ID;
}
~EZOPHSensor() {
if (_serial) delete _serial;
}
// ---------------------------------------------------------------------
void setRX(unsigned char pin_rx) {
if (_pin_rx == pin_rx) return;
_pin_rx = pin_rx;
_dirty = true;
}
void setTX(unsigned char pin_tx) {
if (_pin_tx == pin_tx) return;
_pin_tx = pin_tx;
_dirty = true;
}
// ---------------------------------------------------------------------
unsigned char getRX() {
return _pin_rx;
}
unsigned char getTX() {
return _pin_tx;
}
// ---------------------------------------------------------------------
// Sensor API
// ---------------------------------------------------------------------
// Initialization method, must be idempotent
void begin() {
if (!_dirty) return;
if (_serial) delete _serial;
_serial = new SoftwareSerial(_pin_rx, _pin_tx);
_serial->enableIntTx(false);
_serial->begin(9600);
_ready = true;
_dirty = false;
}
// Descriptive name of the sensor
String description() {
char buffer[28];
snprintf(buffer, sizeof(buffer), "EZOPH @ SwSerial(%u,%u)", _pin_rx, _pin_tx);
return String(buffer);
}
// Descriptive name of the slot # index
String slot(unsigned char index) {
return description();
};
// Address of the sensor (it could be the GPIO or I2C address)
String address(unsigned char index) {
char buffer[6];
snprintf(buffer, sizeof(buffer), "%u:%u", _pin_rx, _pin_tx);
return String(buffer);
}
// Type for slot # index
unsigned char type(unsigned char index) {
if (index == 0) return MAGNITUDE_PH;
return MAGNITUDE_NONE;
}
void tick() {
_setup();
_read();
}
// Current value for slot # index
double value(unsigned char index) {
if (index == 0) return _ph;
return 0;
}
protected:
// ---------------------------------------------------------------------
// Protected
// ---------------------------------------------------------------------
void _setup() {
if (_sync_responded) {
return;
}
_error = SENSOR_ERROR_WARM_UP;
String sync_serial = "";
sync_serial.reserve(30);
if (!_sync_requested) {
_serial->write(67); // C
_serial->write(44); // ,
_serial->write(63); // ?
_serial->write(13); // \r
_serial->flush();
_sync_requested = true;
}
while ((_serial->available() > 0)) {
char sync_char = (char)_serial->read();
sync_serial += sync_char;
if (sync_char == '\r') {
break;
}
}
if (sync_serial.startsWith("?C,")) {
_sync_interval = sync_serial.substring(sync_serial.indexOf(",") + 1).toInt() * 1000;
if (_sync_interval == 0) {
_error = SENSOR_ERROR_OTHER;
return;
}
}
if (sync_serial.startsWith("*OK")) {
_sync_responded = true;
}
if (!_sync_responded) {
return;
}
_error = SENSOR_ERROR_OK;
}
void _read() {
if (_error != SENSOR_ERROR_OK) {
return;
}
if (millis() - _ts <= _sync_interval) {
return;
}
_ts = millis();
String ph_serial = "";
ph_serial.reserve(30);
while ((_serial->available() > 0)) {
char ph_char = (char)_serial->read();
ph_serial += ph_char;
if (ph_char == '\r') {
break;
}
}
if (ph_serial == "*ER") {
_error = SENSOR_ERROR_OTHER;
return;
}
_ph = ph_serial.toFloat();
_error = SENSOR_ERROR_OK;
}
bool _sync_requested = false;
bool _sync_responded = false;
unsigned long _sync_interval = 100000; // Maximum continuous reading interval allowed is 99000 milliseconds.
unsigned long _ts = 0;
double _ph = 0;
unsigned int _pin_rx;
unsigned int _pin_tx;
SoftwareSerial * _serial = NULL;
};
#endif // SENSOR_SUPPORT && EZOPH_SUPPORT

+ 3
- 2
code/html/custom.js View File

@ -47,7 +47,8 @@ function sensorName(id) {
"Events", "PMSX003", "BMX280", "MHZ19", "SI7021",
"SHT3X I2C", "BH1750", "PZEM004T", "AM2320 I2C", "GUVAS12SD",
"TMP3X", "Sonar", "SenseAir", "GeigerTicks", "GeigerCPM",
"NTC", "SDS011", "MICS2710", "MICS5525", "VL53L1X", "VEML6075"
"NTC", "SDS011", "MICS2710", "MICS5525", "VL53L1X", "VEML6075",
"EZOPH"
];
if (1 <= id && id <= names.length) {
return names[id - 1];
@ -64,7 +65,7 @@ function magnitudeType(type) {
"PM1.0", "PM2.5", "PM10", "CO2", "Lux", "UVA", "UVB", "UV Index", "Distance" , "HCHO",
"Local Dose Rate", "Local Dose Rate",
"Count",
"NO2", "CO", "Resistance"
"NO2", "CO", "Resistance", "pH"
];
if (1 <= type && type <= types.length) {
return types[type - 1];


Loading…
Cancel
Save