Compare commits

..

No commits in common. "master" and "arduinoide" have entirely different histories.

42 changed files with 316 additions and 5434 deletions

7
.gitignore vendored
View file

@ -1,6 +1 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
src/config.h
config.h

View file

@ -1,10 +0,0 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

24
LICENSE
View file

@ -1,24 +0,0 @@
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,8 +1,8 @@
# ATTNode v3 Firmware
# ATTNode v3 Firmware (WiP)
This is the code repository for ATTNode v3 compatible firmware. At the moment it supports LoRa communication using OTAA and a growing number of sensor, like the BME280 and SHT21 for temperature, humidity and atmospheric pressure, MH-Z19C and SCD30 for CO2 or HM330x for Particulate Matter. It also uses deep sleep to save energy between Measurements.
## Disclaimer
The firmware is developed using [PlatformIO](https://platformio.org/). At least version 5.1.0 is needed for ATTiny3216 support.
THIS IS STILL WORK IN PROGRESS!
## Documentation
@ -10,30 +10,21 @@ The firmware is developed using [PlatformIO](https://platformio.org/). At least
## Configuration and Programming
To set the fuses for clock speed, BOD levels etc., use the "Set Fuses" operation in PlatformIO. This has to be done once for a "fresh" Node or when the Board Config in platformio.ini was changed. Afterwards it is enough to use the normal "Upload" function for Code or config.h changes.
This is the Work in Progress Repository for ATTNode v3 compatible firmware. At the moment it supports LoRa communication using OTAA and a BME280 or SHT21 sensor, as well as deep sleep between measurements.
Before programming a node, copy src/config.h.example to src/config.h and set the used sensor, LoRaWAN keys and other options as needed.
As there is no PlatformIO Support for the ATTiny3216 yet, it is (for now) developed using Arduino IDE and the [MegaTinyCore](https://github.com/SpenceKonde/megaTinyCore). You also need to set the correct Settings for programming the ATTiny3216 in ArduionIDE. Here is a screenshot of the settings I use:
Programming is done using a [MicroUPDI Programmer](https://github.com/MCUdude/microUPDI), settings in platformio.ini are set to use it. For other pogrammer options see the PlatformIO Documentation
![ArduinoIDE Settings](ide_settings.png)
## Debugging
You also need to install the MCCI Arduino LMIC Library form the IDEs Library Manager or from https://github.com/mcci-catena/arduino-lmic
The firmware has the ability to produce some debug output via the Serial UART as long as there is no sensor using the UART. To enable it, uncomment the line -D DEBUG in platformio.ini. This will produce some debug output showing the state of the node. The following macros are available as a replacement for the normal Serial.print and Serial.println functions:
Before Compiling and Flashing make sure to copy config.h.example to config.h and set your LoRa OTAA Keys there. You can also set the Sending Interval and used Sensors there.
DEBUG_PRINT("Debug Output");
DEBUG_PRINTLN("Debug Output with Linebreak");
These will only produce additional code in the firmware when the DEBUG-Flag is enabled, and will be entirely removed in the ouput binary if not. The macros work like the normal Serial.print* statements form the standard arduino functions.
## Payload Decoder
You need to specify a Payload Decoder fitting for your configured Sensors for a Node. See payload/index.html in this repository. Open it in your Browser of Choice and select the enabled sensors. It will generate the Payload Decoder fitting for the choosen Sensors. You can also use the Online Version at [attno.de](https://www.attno.de/payload-generator)
Programming is done using a [MicroUPDI Programmer](https://github.com/MCUdude/microUPDI) - for other pogramming variants see the MegaTinyCore documentation.
## Configuring via Downlink
It is possible to change the sending interval via Downlink-Packets at runtime. Downlinks for setting the transmit interval have to be sent on port 1. The time between Transmits is specified in minutes (or more exactly, 64 Second intervals) and as a 2 byte value, which will be interpreted as an uint. So for example 0x0001 means 1 Minute, 0x0002 means 2 Minutes and so on. Sending 0xFFFF resets the value to the compiled in default.
Sending an arbitrary value on port 2 will activate the calibration sequence for sensors that support it (like some of the CO2-Sensors).
It is possible to change the sending interval via Downlink-Packets at runtime. The time between Transmits is specified in minutes (or more exactly, 64 Second intervals) and has to be sent as a 2 byte value, which will be interpreted as an uint. so for example 0x0001 means 1 Minute, 0x0002 means 2 Minutes and so on. Sending 0xFFFF resets the value to the compiled in default.
## Acknowledgements

View file

@ -1,11 +1,11 @@
#include <Arduino.h>
#include <stdint.h>
#include <Wire.h>
#include "BME280.h"
BME280::BME280() {}
void BME280::getCalData() {
DEBUG_PRINTLN("BME280::getCalData");
dig_T1 = read16_LE(0x88);
dig_T2 = readS16_LE(0x8A);
dig_T3 = readS16_LE(0x8C);
@ -78,13 +78,11 @@ int32_t BME280::compensate_h(int32_t adc_H)
return (uint32_t)((v_x1_u32r>>12)/10);
}
uint8_t BME280::getSensorData(char *payload, uint8_t startbyte) {
void BME280::getData(int32_t *t, int32_t *p, int32_t *h) {
int32_t UP, UT, UH;
int32_t rawP, rawT;
DEBUG_PRINTLN("BME280::geSensorData");
// Trigger Measurement
// Set Sensor Config
write8(0xF2, 0b00000001); // 1x Oversampling for Humidity
@ -107,14 +105,10 @@ uint8_t BME280::getSensorData(char *payload, uint8_t startbyte) {
// Read Humidity
UH = read16(0xFD);
// Temperature
int32ToPayload(compensate_t(UT), payload, startbyte);
// Humidity
int32ToPayload(compensate_h(UH), payload, startbyte+4);
// Pressure
int32ToPayload(compensate_p(UP), payload, startbyte+8);
return startbyte+12;
// Compensate Values and Return
*t = compensate_t(UT);
*p = compensate_p(UP);
*h = compensate_h(UH);
}
uint8_t BME280::read8(uint8_t addr) {
@ -153,4 +147,4 @@ void BME280::write8(uint8_t addr, uint8_t data) {
Wire.write(addr);
Wire.write(data);
Wire.endTransmission();
}
}

View file

@ -1,12 +1,12 @@
#ifndef BME280_H
#define BME280_H
#include "../../include/attsensor.h"
#include "../../include/debug.h"
#include <stdint.h>
#define BME280_I2CADDR 0x76
class BME280 : public AttSensor {
class BME280
{
private:
// Variables for Calibration Values
uint8_t dig_H1, dig_H3;
@ -29,13 +29,14 @@ private:
int16_t readS16(uint8_t addr);
int16_t readS16_LE(uint8_t addr);
void write8(uint8_t addr, uint8_t data);
void getCalData(void);
public:
BME280(void);
uint8_t getSensorData(char *payload, uint8_t startbyte);
void calibrate(void) {};
void initialize(void) {getCalData();};
uint8_t numBytes(void) {return 12;};
// Get Calibration Data from Sensor
void getCalData(void);
// Read Pressure From Sensor
void getData(int32_t *t, int32_t *p, int32_t *h);
};
#endif

View file

@ -52,13 +52,10 @@ uint16_t SHT21::sensorRead(uint8_t command) {
return result;
}
uint8_t SHT21::getSensorData(char *payload, uint8_t startbyte) {
DEBUG_PRINTLN("SHT21::getSensorData");
// Temperature
int32ToPayload((int32_t)((-46.85 + 175.72 / 65536.0 * (float)(sensorRead(SHT21_TEMPHOLD)))*100), payload, startbyte);
// Humidity
int32ToPayload((int32_t)((-6.0 + 125.0 / 65536.0 * (float)(sensorRead(SHT21_HUMIHOLD)))*100), payload, startbyte+4);
return startbyte+8;
}
float SHT21::getTemperature(void) {
return (-46.85 + 175.72 / 65536.0 * (float)(sensorRead(SHT21_TEMPHOLD)));
}
float SHT21::getHumidity(void) {
return (-6.0 + 125.0 / 65536.0 * (float)(sensorRead(SHT21_HUMIHOLD)));
}

View file

@ -27,8 +27,7 @@
#ifndef SHT21_H
#define SHT21_H
#include "../../include/attsensor.h"
#include "../../include/debug.h"
#include <inttypes.h>
#define SHT21_I2CADDR 0x40
@ -38,16 +37,16 @@
#define SHT21_HUMINOHOLD 0xF5
#define SHT21_SOFTRESET 0xFE
class SHT21 : public AttSensor {
class SHT21
{
private:
uint16_t sensorRead(uint8_t command);
public:
SHT21(void);
uint8_t getSensorData(char *payload, uint8_t startbyte);
void calibrate(void) {};
void initialize(void) {};
uint8_t numBytes(void) {return 8;};
float getTemperature(void);
float getHumidity(void);
};
#endif
#endif

32
firmware/config.h.example Normal file
View file

@ -0,0 +1,32 @@
// ATTNode v3 Onboard LED is on PIN_PA7
#define LED_PIN PIN_PA7
// Enable Serial Debugging. Parameters for the Serial Port are 115200
// #define DEBUG
// Define which Sensor is installed
#define HAS_BME280
// #define HAS_SHT21
// #define HAS_NO_SENSOR
// How many minutes to sleep between Measuring/Sending
// Since this is a 2-byte value internally, intervals between 1 and 65536 are possible
// This is the default interval to use, which can be overwritten via DownLink. If an interval
// is set via DownLink it will be saved in EEPROM and the time specified here will no longer be used.
// Actual Sleep Time is SLEEP_TIME*2*32 Seconds due to the 32s sleep intervals of the ATTiny3216
uint16_t sleep_time = 10;
// Specify TTN EU Bandplan and LoRa Chip for the LMIC as well as Disabling some unused functions
// See LMIC documentation / project_config.h for more options
#define CFG_eu868 1
#define CFG_sx1276_radio 1
#define DISABLE_PING
#define DISABLE_BEACONS
// Keys for OTAA Mode
// APPEUI and DEVEUI from TTN, LSB!
static const u1_t PROGMEM APPEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static const u1_t PROGMEM DEVEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
// APPKey from TTN, MSB!
static const u1_t PROGMEM APPKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

View file

@ -8,11 +8,9 @@ DebugUtils.h - Simple debugging utilities.
#ifdef DEBUG
#define DEBUG_PRINT(...) Serial.print(__VA_ARGS__); Serial.flush();
#define DEBUG_PRINTLN(...) Serial.println(__VA_ARGS__); Serial.flush();
#define DEBUG_PRINTARR(...) for (uint8_t i=0; i<(sizeof(__VA_ARGS__)/sizeof(__VA_ARGS__[0])); i++) { Serial.print(__VA_ARGS__[i], HEX); Serial.print(" ");}; Serial.flush();
#else
#define DEBUG_PRINT(...)
#define DEBUG_PRINTLN(...)
#define DEBUG_PRINTARR(...)
#endif
#endif
#endif

242
firmware/firmware.ino Normal file
View file

@ -0,0 +1,242 @@
#include <Arduino.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <SPI.h>
#include <Wire.h>
#include <EEPROM.h>
// Keep Track of used EEPROM Addresses
#define ADDR_SLP 0 // Sleep Interval, 2 Bytes
// Use the local config.h for LMIC Configuration
#define ARDUINO_LMIC_PROJECT_CONFIG_H config.h
#include <lmic.h>
#include <hal/hal.h>
#include "config.h"
#include "debug.h"
// define the blink function and BLINK_LED Macro depending
// on the definition of LED_PIN
#ifdef LED_PIN
void blink(uint8_t num) {
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, 0);
for (uint8_t i = 0; i < num * 2; i++) {
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
delay(100);
}
digitalWrite(LED_PIN, 0);
}
#define BLINK_LED(COUNT) blink(COUNT);
#else
#define BLINK_LED(COUNT)
#endif
#ifdef HAS_BME280
#include "BME280.h"
BME280 sensor;
#endif
#ifdef HAS_SHT21
#include "SHT21.h"
SHT21 sensor;
#endif
// Define some LMIC Callbacks and Variables
void os_getArtEui (u1_t* buf) {
memcpy_P(buf, APPEUI, 8);
}
void os_getDevEui (u1_t* buf) {
memcpy_P(buf, DEVEUI, 8);
}
void os_getDevKey (u1_t* buf) {
memcpy_P(buf, APPKEY, 16);
}
static osjob_t sendjob;
// Pin-Mapping for ATTNode v3
const lmic_pinmap lmic_pins = {
.nss = PIN_PA5,
.rxtx = LMIC_UNUSED_PIN,
.rst = LMIC_UNUSED_PIN,
.dio = {PIN_PA4, PIN_PA6, LMIC_UNUSED_PIN},
};
// List of unused Pins - will be disabled for Power Saving
#ifdef DEBUG
const int disabledPins[] = {PIN_PB5, PIN_PB4, PIN_PB1, PIN_PB0, PIN_PC3, PIN_PC2, PIN_PC1, PIN_PC0};
#else
const int disabledPins[] = {PIN_PB5, PIN_PB4, PIN_PB3, PIN_PB2, PIN_PB1, PIN_PB0, PIN_PC3, PIN_PC2, PIN_PC1, PIN_PC0};
#endif
// ISR Routine for Sleep
ISR(RTC_PIT_vect)
{
/* Clear interrupt flag by writing '1' (required) */
RTC.PITINTFLAGS = RTC_PI_bm;
}
// Sleep Routine
void sleep_32s() {
cli();
while (RTC.PITSTATUS > 0) {}
RTC.PITINTCTRL = RTC_PI_bm;
// 32 Sekunden
RTC.PITCTRLA = RTC_PERIOD_CYC32768_gc | RTC_PITEN_bm;
while (RTC.PITSTATUS & RTC_CTRLBUSY_bm) {}
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sei();
sleep_cpu();
sleep_disable();
sei();
}
// LMIC Callback Handling
void onEvent(ev_t ev) {
switch (ev) {
case EV_JOINED:
// Disable LinkCheck
LMIC_setLinkCheckMode(0);
BLINK_LED(2);
DEBUG_PRINTLN("OTAA Join Succeeded");
break;
case EV_TXCOMPLETE:
// Check for Downlink
DEBUG_PRINTLN("LoRa Packet Sent");
if ((int)LMIC.dataLen == 2) {
// We got a Packet with the right size - lets assemble it into a uint16_t
DEBUG_PRINTLN("Received Downlink")
uint16_t tmpslp = (LMIC.frame[LMIC.dataBeg] << 8) | LMIC.frame[LMIC.dataBeg+1];
DEBUG_PRINT("Setting Sleep Time to: ");
DEBUG_PRINTLN(tmpslp);
sleep_time = tmpslp;
EEPROM.put(ADDR_SLP, tmpslp);
}
// Got to sleep for specified Time
DEBUG_PRINTLN("Going to Sleep");
for (uint16_t i = 0; i < sleep_time*2; i++)
sleep_32s();
// Schedule Next Transmit
do_send(&sendjob);
break;
}
}
// Get Battery Voltage
uint16_t readSupplyVoltage() { //returns value in millivolts to avoid floating point
uint16_t temp = 0;
analogReference(VDD);
VREF.CTRLA = VREF_ADC0REFSEL_1V5_gc;
ADC0.CTRLD = ADC_INITDLY_DLY256_gc;
ADC0_CTRLB = ADC_SAMPNUM_ACC64_gc;
uint16_t reading = analogRead(ADC_INTREF);
temp = reading / 64;
uint32_t intermediate = 1534500;
reading = 0;
reading = intermediate / temp;
return reading;
}
// Read Sensors and Send Data
// All Sensor Code and Data Preparation goes here
void do_send(osjob_t* j) {
// Prepare LoRa Data Packet
#ifdef HAS_NO_SENSOR
struct lora_data {
uint8_t bat;
} __attribute ((packed)) data;
#elif defined HAS_SHT21
struct lora_data {
uint8_t bat;
int32_t temperature;
int32_t humidity;
} __attribute__ ((packed)) data;
#elif defined HAS_BME280
struct lora_data {
uint8_t bat;
int32_t temperature;
int32_t humidity;
int32_t pressure;
} __attribute__ ((packed)) data;
#endif
if (LMIC.opmode & OP_TXRXPEND) {
delay(1);
} else {
// Add Battery Voltage (0.2V Accuracy stored in 1 byte)
uint32_t batv = readSupplyVoltage();
data.bat = (uint8_t)(batv / 20);
if (batv % 20 > 9)
data.bat += 1;
// Take Measurements depending on Sensor
#ifdef HAS_SHT21
data.temperature = (int32_t)(sensor.getTemperature()*100);
data.humidity = (int32_t)(sensor.getHumidity()*100);
#elif defined HAS_BME280
sensor.getData(&data.temperature, &data.pressure, &data.humidity);
#endif
// Queue Packet for Sending
DEBUG_PRINTLN("LoRa-Packet Queued");
LMIC_setTxData2(1, (unsigned char *)&data, sizeof(data), 0);
}
}
void setup()
{
#ifdef DEBUG
Serial.begin(115200);
#endif
// Initialize SPI and I2C
Wire.begin();
SPI.begin();
// Disable unused Pins (for power saving)
for (int i = 0; i < (sizeof(disabledPins) / sizeof(disabledPins[0])) - 1; i++)
pinMode(disabledPins[i], INPUT_PULLUP);
// Set RTC
while (RTC.STATUS > 0) {}
RTC.CLKSEL = RTC_CLKSEL_INT1K_gc;
while (RTC.PITSTATUS > 0) {}
// Initialize Sensor(s)
#ifdef HAS_BME280
sensor.getCalData();
#endif
// Setup LMIC
DEBUG_PRINT("Initializing LMIC...")
os_init();
LMIC_reset(); // Reset LMIC state and cancel all queued transmissions
LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100); // Compensate for Clock Skew
LMIC.dn2Dr = DR_SF9; // Downlink Band
LMIC_setDrTxpow(DR_SF7, 14); // Default to SF7
DEBUG_PRINTLN("Done");
// Check if Sending Interval is set in EEPROM
// if we get 65535 (0xFFFF) EEPROM was not written
uint16_t tmpsleep = 0;
EEPROM.get(ADDR_SLP, tmpsleep);
if (tmpsleep < 65535) {
DEBUG_PRINT("Setting Sleep Time from EEPROM to ");
DEBUG_PRINTLN(tmpsleep);
sleep_time = tmpsleep;
}
DEBUG_PRINTLN("Setup Finished");
// Schedule First Send (Triggers OTAA Join as well)
do_send(&sendjob);
}
void loop()
{
// Only Run the LMIC loop here. Actual Sending Code is in do_send()
os_runloop_once();
}

BIN
ide_settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

View file

@ -1,38 +0,0 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

View file

@ -1,63 +0,0 @@
/*
attsensor.h - Define the Base Sensor Interface Class
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ATTSENSOR_H
#define ATTSENSOR_H
#include <inttypes.h>
class AttSensor {
public:
// Put the Data into the Payload Array starting at <startbyte>
// Return the next startbyte when done
virtual uint8_t getSensorData(char *payload, uint8_t startbyte) = 0;
// Called in Setup, Do any Necessary Initialization
virtual void initialize(void) = 0;
// Return the number of Bytes added to the Payload
virtual uint8_t numBytes(void) = 0;
// Calibrate a Sensor. Needs to be Implemented in the Child Class
virtual void calibrate(void) = 0;
// Helper Functions to Put Values Into the Payload Array
static void int32ToPayload(int32_t value, char *payload, uint8_t startbyte) {
payload[startbyte] = (value) & 0XFF;
payload[startbyte+1] = (value >> 8) & 0XFF;
payload[startbyte+2] = (value >> 16) & 0XFF;
payload[startbyte+3] = (value >> 24) & 0XFF;
}
static void int16ToPayload(int16_t value, char *payload, uint8_t startbyte) {
payload[startbyte] = (value) & 0XFF;
payload[startbyte+1] = (value >> 8) & 0XFF;
}
static void uint16ToPayload(uint16_t value, char *payload, uint8_t startbyte) {
payload[startbyte] = (value) & 0XFF;
payload[startbyte+1] = (value >> 8) & 0XFF;
}
};
#endif

View file

@ -1,57 +0,0 @@
/*
.cpp - Brightness Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <Arduino.h>
#include "Brightness.h"
// Constructor - Inititalize Hardware UART
Brightness::Brightness(uint8_t a, uint8_t c) {
this->anode = a;
this->cathode = c;
}
uint8_t Brightness::getSensorData(char *payload, uint8_t startbyte) {
uint16_t counter;
pinMode(this->anode, OUTPUT);
digitalWrite(this->anode, LOW);
pinMode(this->cathode, OUTPUT);
digitalWrite(this->cathode, HIGH);
delayMicroseconds(4);
pinMode(this->cathode, INPUT);
digitalWrite(this->cathode, LOW);
for ( counter = 0; counter < 65000; counter++) {
if (digitalRead(this->cathode)==0) break;
delayMicroseconds(5);
}
uint16ToPayload(65000-counter, payload, startbyte);
return startbyte+2;
}

View file

@ -1,46 +0,0 @@
/*
Brightness.h - Brightness Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef Brightness_H
#define Brightness_H
#include "../../include/attsensor.h"
#include "../../include/debug.h"
class Brightness : public AttSensor {
private:
uint8_t anode, cathode;
public:
Brightness(uint8_t a, uint8_t c);
uint8_t getSensorData(char *payload, uint8_t startbyte);
void calibrate(void) {};
void initialize(void) {};
uint8_t numBytes(void) { return 2; };
};
#endif

View file

@ -1,140 +0,0 @@
/*
DS18B20.cpp - Dallas 1-Wire Temperature Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "DS18B20.h"
DS18B20::DS18B20(uint8_t owpin, uint8_t resbits = 12, bool para = false) {
onewire = new OneWire(owpin);
parasite = para;
switch(resbits) {
case 9 :
resolution = DS18B20_9_BIT;
convtime = 300;
break;
case 10 :
resolution = DS18B20_10_BIT;
convtime = 600;
break;
case 11 :
resolution = DS18B20_11_BIT;
convtime = 900;
break;
default :
resolution = DS18B20_12_BIT;
convtime = 1200;
}
}
void DS18B20::initialize(void){
DEBUG_PRINTLN("DS18B20::initialize");
onewire->reset();
onewire->reset_search();
sensorcount = 0;
while (onewire->search(addr)) {
if (OneWire::crc8( addr, 7) == addr[7] && addr[0] != 0x00) {
sensorcount++;
setResolution();
}
}
delay(500);
}
uint8_t DS18B20::numBytes(void){
return sensorcount * 2;
}
void DS18B20::setResolution(void) {
// Write Scratchpad
onewire->reset();
onewire->select(addr);
onewire->write(0x4E);
// Dummy Alarm Values (not used)
onewire->write(0);
onewire->write(100);
// Write Resolution
onewire->write(resolution);
onewire->reset();
}
uint8_t DS18B20::getSensorData(char *payload, uint8_t startbyte){
uint8_t data[9];
int16_t value;
DEBUG_PRINTLN("DS18B20::getSensorData");
// Start Conversation on all Sensors
onewire->reset();
onewire->skip();
onewire->write(0x44,parasite);
// Wait for Conversion to Finish
delay(convtime);
// Read Temperature from all Sensors
onewire->reset_search();
while (onewire->search(addr)) {
if (OneWire::crc8(addr, 7) == addr[7] && addr[0] != 0x00) {
// Get Data
onewire->reset();
onewire->select(addr);
onewire->write(0xBE);
for (uint8_t i = 0; i < 9; i++)
data[i] = onewire->read();
onewire->reset();
if (OneWire::crc8(data, 8) == data[8]) {
int16_t rawTemp = (((int16_t)data[1]) << 8) | data[0];
byte cfg = (data[4] & 0x60);
// at lower res, the low bits are undefined, so let's zero them
switch (cfg) {
case 0x00 :
rawTemp = rawTemp & ~7;
break;
case 0x20 :
rawTemp = rawTemp & ~3;
break;
case 0x40 :
rawTemp = rawTemp & ~1;
break;
}
value = ((float)rawTemp/16.0)*100;
} else {
value = -85;
}
// Add to Payload
int16ToPayload(value, payload, startbyte);
// Set next Startbyte
startbyte += 2;
delay(50);
}
}
onewire->depower();
return startbyte;
}

View file

@ -1,59 +0,0 @@
/*
DS18B20.h - Dallas 1-Wire Temperature Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef DS18B20_H
#define DS18B20_H
#include "../../include/attsensor.h"
#include "../../include/debug.h"
#include <OneWire.h>
// Sensor Resolutions
#define DS18B20_9_BIT 0x1F
#define DS18B20_10_BIT 0x3F
#define DS18B20_11_BIT 0x5F
#define DS18B20_12_BIT 0x7F
class DS18B20 : public AttSensor {
private:
OneWire *onewire;
uint8_t addr[8];
uint8_t sensorcount = 0;
uint8_t resolution = DS18B20_12_BIT;
uint8_t convtime = 750;
bool parasite;
void setResolution(void);
public:
DS18B20(uint8_t owpin, uint8_t resbits = 12, bool para = false);
uint8_t getSensorData(char *payload, uint8_t startbyte);
void calibrate(void) {};
void initialize(void);
uint8_t numBytes(void);
};
#endif

View file

@ -1,152 +0,0 @@
/*
HM330x.cpp - Seeed HM330x Particle-Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <Arduino.h>
#include <inttypes.h>
#include <Wire.h>
#include "HM330x.h"
// Default Constructor
HM330x::HM330x(uint8_t sp = 0) {
sleep_pin = sp;
};
// Initialize the Sensor
void HM330x::initialize(void) {
uint8_t retryCount = 0;
DEBUG_PRINTLN("HM330x::initialize");
// Enable Sleep Mode if Pin is Configured
if (sleep_pin > 0) {
pinMode(sleep_pin, OUTPUT);
digitalWrite(sleep_pin, HIGH);
}
// Wait for Sensor to get Ready
if (sleep_pin == 0) {
DEBUG_PRINTLN("HM330x::initialize Waiting for Sensor Startup");
delay(30000);
}
DEBUG_PRINTLN("HM330x::initialize Trying to send Select");
// Send select command
while (!sendCmd(HM330x_SELECT)) {
if (retryCount++ >= 10) {
DEBUG_PRINTLN("HM330x::initialize Select Failed!");
return;
} else {
delay(500);
DEBUG_PRINTLN("HM330x::initialize Retrying Select...");
}
}
}
// Read Data From Sensor and Put Them Into the Payload Array
uint8_t HM330x::getSensorData(char *payload, uint8_t startbyte) {
uint8_t data[HM330x_DATA_LEN] = {0};
uint16_t values[3] = { 0xEEEE, 0xEEEE, 0xEEEE };
DEBUG_PRINTLN("HM330x::getSensorData");
// Enable Sensor and Wait for it to Settle
if (sleep_pin > 0) {
digitalWrite(sleep_pin, HIGH);
DEBUG_PRINTLN("HM330x::getSensorData Waiting for Stable Values after Sleep");
delay(30000);
sendCmd(HM330x_SELECT);
}
// Initialize Payload with 0s
for (uint8_t i=startbyte; i < startbyte+6; i++)
payload[i] = 0xFF;
bool dataok = false;
uint8_t tries = 5;
while (!dataok && tries > 0) {
DEBUG_PRINT("HM330x::getSensorData reading I2C Try ");
DEBUG_PRINTLN(6-tries);
// Get Data from the Sensors
Wire.requestFrom(HM330x_ADDR, HM330x_DATA_LEN);
if (Wire.available()) {
DEBUG_PRINT("HM330x::getSensorData I2C data: 0x")
for (uint8_t i = 0; i<HM330x_DATA_LEN; i++){
data[i] = Wire.read();
DEBUG_PRINT(data[i], HEX);
DEBUG_PRINT(" ");
}
DEBUG_PRINTLN("");
if (calcSum(data) == data[HM330x_DATA_LEN-1]) {
for (uint8_t pos = 5; pos<8; pos++) {
values[pos-5]= bytesToUint16(data, pos);
}
dataok = true;
} else {
DEBUG_PRINTLN("HM330x::getSensorData Checksum Error")
delay(2000);
tries--;
}
} else {
DEBUG_PRINTLN("HM330x::getSensorData I2C Not Ready")
delay(2000);
tries--;
}
}
uint16ToPayload(values[0], payload, startbyte);
uint16ToPayload(values[1], payload, startbyte+2);
uint16ToPayload(values[2], payload, startbyte+4);
if (sleep_pin > 0) {
digitalWrite(sleep_pin, LOW);
}
return startbyte+6;
}
// Send only a Command
bool HM330x::sendCmd(uint8_t cmd) {
DEBUG_PRINT("HM330x::sendCmd: 0x")
DEBUG_PRINTLN(cmd, HEX);
Wire.beginTransmission(HM330x_ADDR);
Wire.write(cmd);
return Wire.endTransmission();
}
// Calculate the CheckSum
uint8_t HM330x::calcSum(uint8_t bytes[]) {
uint8_t checksum = 0;
for (int i = 0; i < HM330x_DATA_LEN - 1; i++) {
checksum += bytes[i];
}
DEBUG_PRINT("HM330x::calcSum: 0x");
DEBUG_PRINTLN(checksum, HEX);
return checksum;
}
// Get Value from Sensor Bytes Array
uint16_t HM330x::bytesToUint16(uint8_t bytes[], uint8_t pos){
return bytes[pos * 2] * 0x100 + bytes[pos * 2 + 1];
}

View file

@ -1,54 +0,0 @@
/*
HM330x.h - Seeed HM330x Particle-Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef HM330x_H
#define HM330x_H
#include "../../include/attsensor.h"
#include "../../include/debug.h"
// Sensor I2C Address
#define HM330x_ADDR 0x40
#define HM330x_SELECT 0x88
#define HM330x_DATA_LEN 29
class HM330x : public AttSensor {
private:
uint8_t sleep_pin = 0;
bool sendCmd(uint8_t cmd);
uint8_t calcSum(uint8_t bytes[]);
uint16_t bytesToUint16(uint8_t bytes[], uint8_t pos);
public:
HM330x(uint8_t sp = 0);
void initialize(void);
void calibrate(void) {};
uint8_t numBytes(void) {return 6;};
uint8_t getSensorData(char *payload, uint8_t startbyte);
};
#endif

View file

@ -1,128 +0,0 @@
/*
MHZ19C.cpp - MHZ19C Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <Arduino.h>
#include "MHZ19C.h"
// Constructor - Inititalize Hardware UART
MHZ19C::MHZ19C(void) {
Serial.begin(9600);
Serial.setTimeout(MHZ19C_READ_TIMEOUT);
}
MHZ19C::MHZ19C(pin_size_t calpin) {
this->calpin = calpin;
Serial.begin(9600);
Serial.setTimeout(MHZ19C_READ_TIMEOUT);
}
void MHZ19C::initialize(void) {
#ifdef MHZ19C_ENABLE_AUTOCAL
setSelfCalibration(1);
#else
setSelfCalibration(0);
#endif
}
void MHZ19C::calibrate(void) {
pinMode(calpin, OUTPUT);
digitalWrite(calpin, LOW);
delay(7500);
digitalWrite(calpin, HIGH);
pinMode(calpin, INPUT_PULLUP);
}
uint8_t MHZ19C::getSensorData(char * payload, uint8_t startbyte) {
write(MHZ19C_CMD_GET_PPM, 0x00);
delay(50);
uint8_t readBytes = read();
payload[startbyte] = 0x00;
payload[startbyte+1] = 0x00;
if (readBytes > 0) {
switch(buffer[1]) {
case 0x86:
uint16ToPayload((buffer[2]*256) + buffer[3], payload, startbyte);
break;
}
}
return startbyte+2;
}
// Turn Self Calibration Routine On or Off
void MHZ19C::setSelfCalibration(bool state) {
if (state) {
write(0x79, 0xA0);
} else {
write(0x79, 0x00);
}
}
// Write a Command to the Sensor
void MHZ19C::write(uint8_t cmd, uint8_t arg) {
uint8_t _cmd[9] = {0xFF, 0x01, cmd, arg, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t crc = crc8(_cmd);
_cmd[8] = crc;
while (Serial.available() > 0) Serial.read();
Serial.write(_cmd, 9);
Serial.flush();
}
// Read a Sensor Response
uint8_t MHZ19C::read() {
uint8_t ret = 0;
zeroBuffer();
// Read Available Bytes
if (Serial.available() > 0) {
ret = Serial.readBytes(buffer, MHZ19C_SER_BUF_LEN);
}
// Check Sync Bit and CRC
if (buffer[0] != 0xFF || buffer [8] != crc8(buffer))
return 0;
// Return Read Bytes
return ret;
}
// Fill the Internal Buffer with Zeroes
void MHZ19C::zeroBuffer() {
for (int i=0; i < MHZ19C_SER_BUF_LEN; i++)
buffer[i] = 0x00;
}
// Calculate 8Bit CRC of Messages and Commands
uint8_t MHZ19C::crc8(uint8_t *paket){
uint8_t i, checksum = 0x00;
for( i = 1; i < 8; i++)
checksum += paket[i];
checksum = 0xff - checksum;
checksum += 1;
return checksum;
}

View file

@ -1,59 +0,0 @@
/*
MHZ19C.h - MHZ19C Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MHZ19C_H
#define MHZ19C_H
#include "../../include/attsensor.h"
#define MHZ19C_READ_TIMEOUT 500 // Timeout for Serial Communication
#define MHZ19C_SER_BUF_LEN 9 // Length of the Internal Serial Message Buffer
#define MHZ19C_CMD_SET_AUTOCAL 0x79 // Turn Self Calibration on/off
#define MHZ19C_CMD_GET_PPM 0x86 // Get Current PPM Reading
class MHZ19C : public AttSensor {
private:
uint8_t buffer[MHZ19C_SER_BUF_LEN];
pin_size_t calpin = PIN_PB4; // PB4 is the Calibration Pin on the Addon PCB
void write(byte cmd, byte arg);
uint8_t read();
void zeroBuffer(void);
uint8_t crc8(uint8_t *paket);
uint16_t getPPM(void);
public:
MHZ19C(void);
MHZ19C(pin_size_t calpin);
void initialize(void);
void calibrate(void);
uint8_t numBytes(void) {return 2;};
uint8_t getSensorData(char *payload, uint8_t startbyte);
void setSelfCalibration(bool state);
};
#endif

View file

@ -1,580 +0,0 @@
/*
Copyright (c) 2007, Jim Studt (original old version - many contributors since)
The latest version of this library may be found at:
http://www.pjrc.com/teensy/td_libs_OneWire.html
OneWire has been maintained by Paul Stoffregen (paul@pjrc.com) since
January 2010.
DO NOT EMAIL for technical support, especially not for ESP chips!
All project support questions must be posted on public forums
relevant to the board or chips used. If using Arduino, post on
Arduino's forum. If using ESP, post on the ESP community forums.
There is ABSOLUTELY NO TECH SUPPORT BY PRIVATE EMAIL!
Github's issue tracker for OneWire should be used only to report
specific bugs. DO NOT request project support via Github. All
project and tech support questions must be posted on forums, not
github issues. If you experience a problem and you are not
absolutely sure it's an issue with the library, ask on a forum
first. Only use github to report issues after experts have
confirmed the issue is with OneWire rather than your project.
Back in 2010, OneWire was in need of many bug fixes, but had
been abandoned the original author (Jim Studt). None of the known
contributors were interested in maintaining OneWire. Paul typically
works on OneWire every 6 to 12 months. Patches usually wait that
long. If anyone is interested in more actively maintaining OneWire,
please contact Paul (this is pretty much the only reason to use
private email about OneWire).
OneWire is now very mature code. No changes other than adding
definitions for newer hardware support are anticipated.
Version 2.3:
Unknown chip fallback mode, Roger Clark
Teensy-LC compatibility, Paul Stoffregen
Search bug fix, Love Nystrom
Version 2.2:
Teensy 3.0 compatibility, Paul Stoffregen, paul@pjrc.com
Arduino Due compatibility, http://arduino.cc/forum/index.php?topic=141030
Fix DS18B20 example negative temperature
Fix DS18B20 example's low res modes, Ken Butcher
Improve reset timing, Mark Tillotson
Add const qualifiers, Bertrik Sikken
Add initial value input to crc16, Bertrik Sikken
Add target_search() function, Scott Roberts
Version 2.1:
Arduino 1.0 compatibility, Paul Stoffregen
Improve temperature example, Paul Stoffregen
DS250x_PROM example, Guillermo Lovato
PIC32 (chipKit) compatibility, Jason Dangel, dangel.jason AT gmail.com
Improvements from Glenn Trewitt:
- crc16() now works
- check_crc16() does all of calculation/checking work.
- Added read_bytes() and write_bytes(), to reduce tedious loops.
- Added ds2408 example.
Delete very old, out-of-date readme file (info is here)
Version 2.0: Modifications by Paul Stoffregen, January 2010:
http://www.pjrc.com/teensy/td_libs_OneWire.html
Search fix from Robin James
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27
Use direct optimized I/O in all cases
Disable interrupts during timing critical sections
(this solves many random communication errors)
Disable interrupts during read-modify-write I/O
Reduce RAM consumption by eliminating unnecessary
variables and trimming many to 8 bits
Optimize both crc8 - table version moved to flash
Modified to work with larger numbers of devices - avoids loop.
Tested in Arduino 11 alpha with 12 sensors.
26 Sept 2008 -- Robin James
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27
Updated to work with arduino-0008 and to include skip() as of
2007/07/06. --RJL20
Modified to calculate the 8-bit CRC directly, avoiding the need for
the 256-byte lookup table to be loaded in RAM. Tested in arduino-0010
-- Tom Pollard, Jan 23, 2008
Jim Studt's original library was modified by Josh Larios.
Tom Pollard, pollard@alum.mit.edu, contributed around May 20, 2008
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.
Much of the code was inspired by Derek Yerger's code, though I don't
think much of that remains. In any event that was..
(copyleft) 2006 by Derek Yerger - Free to distribute freely.
The CRC code was excerpted and inspired by the Dallas Semiconductor
sample code bearing this copyright.
//---------------------------------------------------------------------------
// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved.
//
// 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 DALLAS SEMICONDUCTOR 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.
//
// Except as contained in this notice, the name of Dallas Semiconductor
// shall not be used except as stated in the Dallas Semiconductor
// Branding Policy.
//--------------------------------------------------------------------------
*/
#include <Arduino.h>
#include "OneWire.h"
#include "util/OneWire_direct_gpio.h"
void OneWire::begin(uint8_t pin)
{
pinMode(pin, INPUT);
bitmask = PIN_TO_BITMASK(pin);
baseReg = PIN_TO_BASEREG(pin);
#if ONEWIRE_SEARCH
reset_search();
#endif
}
// Perform the onewire reset function. We will wait up to 250uS for
// the bus to come high, if it doesn't then it is broken or shorted
// and we return a 0;
//
// Returns 1 if a device asserted a presence pulse, 0 otherwise.
//
uint8_t OneWire::reset(void)
{
IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask;
volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg;
uint8_t r;
uint8_t retries = 125;
noInterrupts();
DIRECT_MODE_INPUT(reg, mask);
interrupts();
// wait until the wire is high... just in case
do {
if (--retries == 0) return 0;
delayMicroseconds(2);
} while ( !DIRECT_READ(reg, mask));
noInterrupts();
DIRECT_WRITE_LOW(reg, mask);
DIRECT_MODE_OUTPUT(reg, mask); // drive output low
interrupts();
delayMicroseconds(480);
noInterrupts();
DIRECT_MODE_INPUT(reg, mask); // allow it to float
delayMicroseconds(70);
r = !DIRECT_READ(reg, mask);
interrupts();
delayMicroseconds(410);
return r;
}
//
// Write a bit. Port and bit is used to cut lookup time and provide
// more certain timing.
//
void OneWire::write_bit(uint8_t v)
{
IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask;
volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg;
if (v & 1) {
noInterrupts();
DIRECT_WRITE_LOW(reg, mask);
DIRECT_MODE_OUTPUT(reg, mask); // drive output low
delayMicroseconds(10);
DIRECT_WRITE_HIGH(reg, mask); // drive output high
interrupts();
delayMicroseconds(55);
} else {
noInterrupts();
DIRECT_WRITE_LOW(reg, mask);
DIRECT_MODE_OUTPUT(reg, mask); // drive output low
delayMicroseconds(65);
DIRECT_WRITE_HIGH(reg, mask); // drive output high
interrupts();
delayMicroseconds(5);
}
}
//
// Read a bit. Port and bit is used to cut lookup time and provide
// more certain timing.
//
uint8_t OneWire::read_bit(void)
{
IO_REG_TYPE mask IO_REG_MASK_ATTR = bitmask;
volatile IO_REG_TYPE *reg IO_REG_BASE_ATTR = baseReg;
uint8_t r;
noInterrupts();
DIRECT_MODE_OUTPUT(reg, mask);
DIRECT_WRITE_LOW(reg, mask);
delayMicroseconds(3);
DIRECT_MODE_INPUT(reg, mask); // let pin float, pull up will raise
delayMicroseconds(10);
r = DIRECT_READ(reg, mask);
interrupts();
delayMicroseconds(53);
return r;
}
//
// Write a byte. The writing code uses the active drivers to raise the
// pin high, if you need power after the write (e.g. DS18S20 in
// parasite power mode) then set 'power' to 1, otherwise the pin will
// go tri-state at the end of the write to avoid heating in a short or
// other mishap.
//
void OneWire::write(uint8_t v, uint8_t power /* = 0 */) {
uint8_t bitMask;
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
OneWire::write_bit( (bitMask & v)?1:0);
}
if ( !power) {
noInterrupts();
DIRECT_MODE_INPUT(baseReg, bitmask);
DIRECT_WRITE_LOW(baseReg, bitmask);
interrupts();
}
}
void OneWire::write_bytes(const uint8_t *buf, uint16_t count, bool power /* = 0 */) {
for (uint16_t i = 0 ; i < count ; i++)
write(buf[i]);
if (!power) {
noInterrupts();
DIRECT_MODE_INPUT(baseReg, bitmask);
DIRECT_WRITE_LOW(baseReg, bitmask);
interrupts();
}
}
//
// Read a byte
//
uint8_t OneWire::read() {
uint8_t bitMask;
uint8_t r = 0;
for (bitMask = 0x01; bitMask; bitMask <<= 1) {
if ( OneWire::read_bit()) r |= bitMask;
}
return r;
}
void OneWire::read_bytes(uint8_t *buf, uint16_t count) {
for (uint16_t i = 0 ; i < count ; i++)
buf[i] = read();
}
//
// Do a ROM select
//
void OneWire::select(const uint8_t rom[8])
{
uint8_t i;
write(0x55); // Choose ROM
for (i = 0; i < 8; i++) write(rom[i]);
}
//
// Do a ROM skip
//
void OneWire::skip()
{
write(0xCC); // Skip ROM
}
void OneWire::depower()
{
noInterrupts();
DIRECT_MODE_INPUT(baseReg, bitmask);
interrupts();
}
#if ONEWIRE_SEARCH
//
// You need to use this function to start a search again from the beginning.
// You do not need to do it for the first search, though you could.
//
void OneWire::reset_search()
{
// reset the search state
LastDiscrepancy = 0;
LastDeviceFlag = false;
LastFamilyDiscrepancy = 0;
for(int i = 7; ; i--) {
ROM_NO[i] = 0;
if ( i == 0) break;
}
}
// Setup the search to find the device type 'family_code' on the next call
// to search(*newAddr) if it is present.
//
void OneWire::target_search(uint8_t family_code)
{
// set the search state to find SearchFamily type devices
ROM_NO[0] = family_code;
for (uint8_t i = 1; i < 8; i++)
ROM_NO[i] = 0;
LastDiscrepancy = 64;
LastFamilyDiscrepancy = 0;
LastDeviceFlag = false;
}
//
// Perform a search. If this function returns a '1' then it has
// enumerated the next device and you may retrieve the ROM from the
// OneWire::address variable. If there are no devices, no further
// devices, or something horrible happens in the middle of the
// enumeration then a 0 is returned. If a new device is found then
// its address is copied to newAddr. Use OneWire::reset_search() to
// start over.
//
// --- Replaced by the one from the Dallas Semiconductor web site ---
//--------------------------------------------------------------------------
// Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing
// search state.
// Return TRUE : device found, ROM number in ROM_NO buffer
// FALSE : device not found, end of search
//
bool OneWire::search(uint8_t *newAddr, bool search_mode /* = true */)
{
uint8_t id_bit_number;
uint8_t last_zero, rom_byte_number;
bool search_result;
uint8_t id_bit, cmp_id_bit;
unsigned char rom_byte_mask, search_direction;
// initialize for search
id_bit_number = 1;
last_zero = 0;
rom_byte_number = 0;
rom_byte_mask = 1;
search_result = false;
// if the last call was not the last one
if (!LastDeviceFlag) {
// 1-Wire reset
if (!reset()) {
// reset the search
LastDiscrepancy = 0;
LastDeviceFlag = false;
LastFamilyDiscrepancy = 0;
return false;
}
// issue the search command
if (search_mode == true) {
write(0xF0); // NORMAL SEARCH
} else {
write(0xEC); // CONDITIONAL SEARCH
}
// loop to do the search
do
{
// read a bit and its complement
id_bit = read_bit();
cmp_id_bit = read_bit();
// check for no devices on 1-wire
if ((id_bit == 1) && (cmp_id_bit == 1)) {
break;
} else {
// all devices coupled have 0 or 1
if (id_bit != cmp_id_bit) {
search_direction = id_bit; // bit write value for search
} else {
// if this discrepancy if before the Last Discrepancy
// on a previous next then pick the same as last time
if (id_bit_number < LastDiscrepancy) {
search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0);
} else {
// if equal to last pick 1, if not then pick 0
search_direction = (id_bit_number == LastDiscrepancy);
}
// if 0 was picked then record its position in LastZero
if (search_direction == 0) {
last_zero = id_bit_number;
// check for Last discrepancy in family
if (last_zero < 9)
LastFamilyDiscrepancy = last_zero;
}
}
// set or clear the bit in the ROM byte rom_byte_number
// with mask rom_byte_mask
if (search_direction == 1)
ROM_NO[rom_byte_number] |= rom_byte_mask;
else
ROM_NO[rom_byte_number] &= ~rom_byte_mask;
// serial number search direction write bit
write_bit(search_direction);
// increment the byte counter id_bit_number
// and shift the mask rom_byte_mask
id_bit_number++;
rom_byte_mask <<= 1;
// if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
if (rom_byte_mask == 0) {
rom_byte_number++;
rom_byte_mask = 1;
}
}
}
while(rom_byte_number < 8); // loop until through all ROM bytes 0-7
// if the search was successful then
if (!(id_bit_number < 65)) {
// search successful so set LastDiscrepancy,LastDeviceFlag,search_result
LastDiscrepancy = last_zero;
// check for last device
if (LastDiscrepancy == 0) {
LastDeviceFlag = true;
}
search_result = true;
}
}
// if no device found then reset counters so next 'search' will be like a first
if (!search_result || !ROM_NO[0]) {
LastDiscrepancy = 0;
LastDeviceFlag = false;
LastFamilyDiscrepancy = 0;
search_result = false;
} else {
for (int i = 0; i < 8; i++) newAddr[i] = ROM_NO[i];
}
return search_result;
}
#endif
#if ONEWIRE_CRC
// The 1-Wire CRC scheme is described in Maxim Application Note 27:
// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products"
//
#if ONEWIRE_CRC8_TABLE
// Dow-CRC using polynomial X^8 + X^5 + X^4 + X^0
// Tiny 2x16 entry CRC table created by Arjen Lentz
// See http://lentz.com.au/blog/calculating-crc-with-a-tiny-32-entry-lookup-table
static const uint8_t PROGMEM dscrc2x16_table[] = {
0x00, 0x5E, 0xBC, 0xE2, 0x61, 0x3F, 0xDD, 0x83,
0xC2, 0x9C, 0x7E, 0x20, 0xA3, 0xFD, 0x1F, 0x41,
0x00, 0x9D, 0x23, 0xBE, 0x46, 0xDB, 0x65, 0xF8,
0x8C, 0x11, 0xAF, 0x32, 0xCA, 0x57, 0xE9, 0x74
};
// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM
// and the registers. (Use tiny 2x16 entry CRC table)
uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len)
{
uint8_t crc = 0;
while (len--) {
crc = *addr++ ^ crc; // just re-using crc as intermediate
crc = pgm_read_byte(dscrc2x16_table + (crc & 0x0f)) ^
pgm_read_byte(dscrc2x16_table + 16 + ((crc >> 4) & 0x0f));
}
return crc;
}
#else
//
// Compute a Dallas Semiconductor 8 bit CRC directly.
// this is much slower, but a little smaller, than the lookup table.
//
uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len)
{
uint8_t crc = 0;
while (len--) {
#if defined(__AVR__)
crc = _crc_ibutton_update(crc, *addr++);
#else
uint8_t inbyte = *addr++;
for (uint8_t i = 8; i; i--) {
uint8_t mix = (crc ^ inbyte) & 0x01;
crc >>= 1;
if (mix) crc ^= 0x8C;
inbyte >>= 1;
}
#endif
}
return crc;
}
#endif
#if ONEWIRE_CRC16
bool OneWire::check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc)
{
crc = ~crc16(input, len, crc);
return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1];
}
uint16_t OneWire::crc16(const uint8_t* input, uint16_t len, uint16_t crc)
{
#if defined(__AVR__)
for (uint16_t i = 0 ; i < len ; i++) {
crc = _crc16_update(crc, input[i]);
}
#else
static const uint8_t oddparity[16] =
{ 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 };
for (uint16_t i = 0 ; i < len ; i++) {
// Even though we're just copying a byte from the input,
// we'll be doing 16-bit computation with it.
uint16_t cdata = input[i];
cdata = (cdata ^ crc) & 0xff;
crc >>= 8;
if (oddparity[cdata & 0x0F] ^ oddparity[cdata >> 4])
crc ^= 0xC001;
cdata <<= 6;
crc ^= cdata;
cdata <<= 1;
crc ^= cdata;
}
#endif
return crc;
}
#endif
#endif

View file

@ -1,182 +0,0 @@
#ifndef OneWire_h
#define OneWire_h
#ifdef __cplusplus
#include <stdint.h>
#if defined(__AVR__)
#include <util/crc16.h>
#endif
#if ARDUINO >= 100
#include <Arduino.h> // for delayMicroseconds, digitalPinToBitMask, etc
#else
#include "WProgram.h" // for delayMicroseconds
#include "pins_arduino.h" // for digitalPinToBitMask, etc
#endif
// You can exclude certain features from OneWire. In theory, this
// might save some space. In practice, the compiler automatically
// removes unused code (technically, the linker, using -fdata-sections
// and -ffunction-sections when compiling, and Wl,--gc-sections
// when linking), so most of these will not result in any code size
// reduction. Well, unless you try to use the missing features
// and redesign your program to not need them! ONEWIRE_CRC8_TABLE
// is the exception, because it selects a fast but large algorithm
// or a small but slow algorithm.
// you can exclude onewire_search by defining that to 0
#ifndef ONEWIRE_SEARCH
#define ONEWIRE_SEARCH 1
#endif
// You can exclude CRC checks altogether by defining this to 0
#ifndef ONEWIRE_CRC
#define ONEWIRE_CRC 1
#endif
// Select the table-lookup method of computing the 8-bit CRC
// by setting this to 1. The lookup table enlarges code size by
// about 250 bytes. It does NOT consume RAM (but did in very
// old versions of OneWire). If you disable this, a slower
// but very compact algorithm is used.
#ifndef ONEWIRE_CRC8_TABLE
#define ONEWIRE_CRC8_TABLE 1
#endif
// You can allow 16-bit CRC checks by defining this to 1
// (Note that ONEWIRE_CRC must also be 1.)
#ifndef ONEWIRE_CRC16
#define ONEWIRE_CRC16 1
#endif
// Board-specific macros for direct GPIO
#include "util/OneWire_direct_regtype.h"
class OneWire
{
private:
IO_REG_TYPE bitmask;
volatile IO_REG_TYPE *baseReg;
#if ONEWIRE_SEARCH
// global search state
unsigned char ROM_NO[8];
uint8_t LastDiscrepancy;
uint8_t LastFamilyDiscrepancy;
bool LastDeviceFlag;
#endif
public:
OneWire() { }
OneWire(uint8_t pin) { begin(pin); }
void begin(uint8_t pin);
// Perform a 1-Wire reset cycle. Returns 1 if a device responds
// with a presence pulse. Returns 0 if there is no device or the
// bus is shorted or otherwise held low for more than 250uS
uint8_t reset(void);
// Issue a 1-Wire rom select command, you do the reset first.
void select(const uint8_t rom[8]);
// Issue a 1-Wire rom skip command, to address all on bus.
void skip(void);
// Write a byte. If 'power' is one then the wire is held high at
// the end for parasitically powered devices. You are responsible
// for eventually depowering it by calling depower() or doing
// another read or write.
void write(uint8_t v, uint8_t power = 0);
void write_bytes(const uint8_t *buf, uint16_t count, bool power = 0);
// Read a byte.
uint8_t read(void);
void read_bytes(uint8_t *buf, uint16_t count);
// Write a bit. The bus is always left powered at the end, see
// note in write() about that.
void write_bit(uint8_t v);
// Read a bit.
uint8_t read_bit(void);
// Stop forcing power onto the bus. You only need to do this if
// you used the 'power' flag to write() or used a write_bit() call
// and aren't about to do another read or write. You would rather
// not leave this powered if you don't have to, just in case
// someone shorts your bus.
void depower(void);
#if ONEWIRE_SEARCH
// Clear the search state so that if will start from the beginning again.
void reset_search();
// Setup the search to find the device type 'family_code' on the next call
// to search(*newAddr) if it is present.
void target_search(uint8_t family_code);
// Look for the next device. Returns 1 if a new address has been
// returned. A zero might mean that the bus is shorted, there are
// no devices, or you have already retrieved all of them. It
// might be a good idea to check the CRC to make sure you didn't
// get garbage. The order is deterministic. You will always get
// the same devices in the same order.
bool search(uint8_t *newAddr, bool search_mode = true);
#endif
#if ONEWIRE_CRC
// Compute a Dallas Semiconductor 8 bit CRC, these are used in the
// ROM and scratchpad registers.
static uint8_t crc8(const uint8_t *addr, uint8_t len);
#if ONEWIRE_CRC16
// Compute the 1-Wire CRC16 and compare it against the received CRC.
// Example usage (reading a DS2408):
// // Put everything in a buffer so we can compute the CRC easily.
// uint8_t buf[13];
// buf[0] = 0xF0; // Read PIO Registers
// buf[1] = 0x88; // LSB address
// buf[2] = 0x00; // MSB address
// WriteBytes(net, buf, 3); // Write 3 cmd bytes
// ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16
// if (!CheckCRC16(buf, 11, &buf[11])) {
// // Handle error.
// }
//
// @param input - Array of bytes to checksum.
// @param len - How many bytes to use.
// @param inverted_crc - The two CRC16 bytes in the received data.
// This should just point into the received data,
// *not* at a 16-bit integer.
// @param crc - The crc starting value (optional)
// @return True, iff the CRC matches.
static bool check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc = 0);
// Compute a Dallas Semiconductor 16 bit CRC. This is required to check
// the integrity of data received from many 1-Wire devices. Note that the
// CRC computed here is *not* what you'll get from the 1-Wire network,
// for two reasons:
// 1) The CRC is transmitted bitwise inverted.
// 2) Depending on the endian-ness of your processor, the binary
// representation of the two-byte return value may have a different
// byte order than the two bytes you get from 1-Wire.
// @param input - Array of bytes to checksum.
// @param len - How many bytes to use.
// @param crc - The crc starting value (optional)
// @return The CRC16, as defined by Dallas Semiconductor.
static uint16_t crc16(const uint8_t* input, uint16_t len, uint16_t crc = 0);
#endif
#endif
};
// Prevent this name from leaking into Arduino sketches
#ifdef IO_REG_TYPE
#undef IO_REG_TYPE
#endif
#endif // __cplusplus
#endif // OneWire_h

View file

@ -1,422 +0,0 @@
#ifndef OneWire_Direct_GPIO_h
#define OneWire_Direct_GPIO_h
// This header should ONLY be included by OneWire.cpp. These defines are
// meant to be private, used within OneWire.cpp, but not exposed to Arduino
// sketches or other libraries which may include OneWire.h.
#include <stdint.h>
// Platform specific I/O definitions
#if defined(__AVR__)
#define PIN_TO_BASEREG(pin) (portInputRegister(digitalPinToPort(pin)))
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
#define IO_REG_TYPE uint8_t
#define IO_REG_BASE_ATTR asm("r30")
#define IO_REG_MASK_ATTR
#if defined(__AVR_ATmega4809__)
#define DIRECT_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0)
#define DIRECT_MODE_INPUT(base, mask) ((*((base)-8)) &= ~(mask))
#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)-8)) |= (mask))
#define DIRECT_WRITE_LOW(base, mask) ((*((base)-4)) &= ~(mask))
#define DIRECT_WRITE_HIGH(base, mask) ((*((base)-4)) |= (mask))
#else
#define DIRECT_READ(base, mask) (((*((base))) & (mask)) ? 1 : 0)
#define DIRECT_MODE_INPUT(base, mask) ((*((base)-6)) = (mask))
#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)-7)) = (mask))
#define DIRECT_WRITE_LOW(base, mask) ((*((base)-2)) = (mask))
#define DIRECT_WRITE_HIGH(base, mask) ((*((base)-3)) = (mask))
#endif
#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
#define PIN_TO_BASEREG(pin) (portOutputRegister(pin))
#define PIN_TO_BITMASK(pin) (1)
#define IO_REG_TYPE uint8_t
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR __attribute__ ((unused))
#define DIRECT_READ(base, mask) (*((base)+512))
#define DIRECT_MODE_INPUT(base, mask) (*((base)+640) = 0)
#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+640) = 1)
#define DIRECT_WRITE_LOW(base, mask) (*((base)+256) = 1)
#define DIRECT_WRITE_HIGH(base, mask) (*((base)+128) = 1)
#elif defined(__MKL26Z64__)
#define PIN_TO_BASEREG(pin) (portOutputRegister(pin))
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
#define IO_REG_TYPE uint8_t
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR
#define DIRECT_READ(base, mask) ((*((base)+16) & (mask)) ? 1 : 0)
#define DIRECT_MODE_INPUT(base, mask) (*((base)+20) &= ~(mask))
#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+20) |= (mask))
#define DIRECT_WRITE_LOW(base, mask) (*((base)+8) = (mask))
#define DIRECT_WRITE_HIGH(base, mask) (*((base)+4) = (mask))
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
#define PIN_TO_BASEREG(pin) (portOutputRegister(pin))
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
#define IO_REG_TYPE uint32_t
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR
#define DIRECT_READ(base, mask) ((*((base)+2) & (mask)) ? 1 : 0)
#define DIRECT_MODE_INPUT(base, mask) (*((base)+1) &= ~(mask))
#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+1) |= (mask))
#define DIRECT_WRITE_LOW(base, mask) (*((base)+34) = (mask))
#define DIRECT_WRITE_HIGH(base, mask) (*((base)+33) = (mask))
#elif defined(__SAM3X8E__) || defined(__SAM3A8C__) || defined(__SAM3A4C__)
// Arduino 1.5.1 may have a bug in delayMicroseconds() on Arduino Due.
// http://arduino.cc/forum/index.php/topic,141030.msg1076268.html#msg1076268
// If you have trouble with OneWire on Arduino Due, please check the
// status of delayMicroseconds() before reporting a bug in OneWire!
#define PIN_TO_BASEREG(pin) (&(digitalPinToPort(pin)->PIO_PER))
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
#define IO_REG_TYPE uint32_t
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR
#define DIRECT_READ(base, mask) (((*((base)+15)) & (mask)) ? 1 : 0)
#define DIRECT_MODE_INPUT(base, mask) ((*((base)+5)) = (mask))
#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+4)) = (mask))
#define DIRECT_WRITE_LOW(base, mask) ((*((base)+13)) = (mask))
#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+12)) = (mask))
#ifndef PROGMEM
#define PROGMEM
#endif
#ifndef pgm_read_byte
#define pgm_read_byte(addr) (*(const uint8_t *)(addr))
#endif
#elif defined(__PIC32MX__)
#define PIN_TO_BASEREG(pin) (portModeRegister(digitalPinToPort(pin)))
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
#define IO_REG_TYPE uint32_t
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR
#define DIRECT_READ(base, mask) (((*(base+4)) & (mask)) ? 1 : 0) //PORTX + 0x10
#define DIRECT_MODE_INPUT(base, mask) ((*(base+2)) = (mask)) //TRISXSET + 0x08
#define DIRECT_MODE_OUTPUT(base, mask) ((*(base+1)) = (mask)) //TRISXCLR + 0x04
#define DIRECT_WRITE_LOW(base, mask) ((*(base+8+1)) = (mask)) //LATXCLR + 0x24
#define DIRECT_WRITE_HIGH(base, mask) ((*(base+8+2)) = (mask)) //LATXSET + 0x28
#elif defined(ARDUINO_ARCH_ESP8266)
// Special note: I depend on the ESP community to maintain these definitions and
// submit good pull requests. I can not answer any ESP questions or help you
// resolve any problems related to ESP chips. Please do not contact me and please
// DO NOT CREATE GITHUB ISSUES for ESP support. All ESP questions must be asked
// on ESP community forums.
#define PIN_TO_BASEREG(pin) ((volatile uint32_t*) GPO)
#define PIN_TO_BITMASK(pin) (1 << pin)
#define IO_REG_TYPE uint32_t
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR
#define DIRECT_READ(base, mask) ((GPI & (mask)) ? 1 : 0) //GPIO_IN_ADDRESS
#define DIRECT_MODE_INPUT(base, mask) (GPE &= ~(mask)) //GPIO_ENABLE_W1TC_ADDRESS
#define DIRECT_MODE_OUTPUT(base, mask) (GPE |= (mask)) //GPIO_ENABLE_W1TS_ADDRESS
#define DIRECT_WRITE_LOW(base, mask) (GPOC = (mask)) //GPIO_OUT_W1TC_ADDRESS
#define DIRECT_WRITE_HIGH(base, mask) (GPOS = (mask)) //GPIO_OUT_W1TS_ADDRESS
#elif defined(ARDUINO_ARCH_ESP32)
#include <driver/rtc_io.h>
#define PIN_TO_BASEREG(pin) (0)
#define PIN_TO_BITMASK(pin) (pin)
#define IO_REG_TYPE uint32_t
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR
static inline __attribute__((always_inline))
IO_REG_TYPE directRead(IO_REG_TYPE pin)
{
if ( pin < 32 )
return (GPIO.in >> pin) & 0x1;
else if ( pin < 40 )
return (GPIO.in1.val >> (pin - 32)) & 0x1;
return 0;
}
static inline __attribute__((always_inline))
void directWriteLow(IO_REG_TYPE pin)
{
if ( pin < 32 )
GPIO.out_w1tc = ((uint32_t)1 << pin);
else if ( pin < 34 )
GPIO.out1_w1tc.val = ((uint32_t)1 << (pin - 32));
}
static inline __attribute__((always_inline))
void directWriteHigh(IO_REG_TYPE pin)
{
if ( pin < 32 )
GPIO.out_w1ts = ((uint32_t)1 << pin);
else if ( pin < 34 )
GPIO.out1_w1ts.val = ((uint32_t)1 << (pin - 32));
}
static inline __attribute__((always_inline))
void directModeInput(IO_REG_TYPE pin)
{
if ( digitalPinIsValid(pin) )
{
uint32_t rtc_reg(rtc_gpio_desc[pin].reg);
if ( rtc_reg ) // RTC pins PULL settings
{
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux);
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown);
}
if ( pin < 32 )
GPIO.enable_w1tc = ((uint32_t)1 << pin);
else
GPIO.enable1_w1tc.val = ((uint32_t)1 << (pin - 32));
uint32_t pinFunction((uint32_t)2 << FUN_DRV_S); // what are the drivers?
pinFunction |= FUN_IE; // input enable but required for output as well?
pinFunction |= ((uint32_t)2 << MCU_SEL_S);
ESP_REG(DR_REG_IO_MUX_BASE + esp32_gpioMux[pin].reg) = pinFunction;
GPIO.pin[pin].val = 0;
}
}
static inline __attribute__((always_inline))
void directModeOutput(IO_REG_TYPE pin)
{
if ( digitalPinIsValid(pin) && pin <= 33 ) // pins above 33 can be only inputs
{
uint32_t rtc_reg(rtc_gpio_desc[pin].reg);
if ( rtc_reg ) // RTC pins PULL settings
{
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux);
ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown);
}
if ( pin < 32 )
GPIO.enable_w1ts = ((uint32_t)1 << pin);
else // already validated to pins <= 33
GPIO.enable1_w1ts.val = ((uint32_t)1 << (pin - 32));
uint32_t pinFunction((uint32_t)2 << FUN_DRV_S); // what are the drivers?
pinFunction |= FUN_IE; // input enable but required for output as well?
pinFunction |= ((uint32_t)2 << MCU_SEL_S);
ESP_REG(DR_REG_IO_MUX_BASE + esp32_gpioMux[pin].reg) = pinFunction;
GPIO.pin[pin].val = 0;
}
}
#define DIRECT_READ(base, pin) directRead(pin)
#define DIRECT_WRITE_LOW(base, pin) directWriteLow(pin)
#define DIRECT_WRITE_HIGH(base, pin) directWriteHigh(pin)
#define DIRECT_MODE_INPUT(base, pin) directModeInput(pin)
#define DIRECT_MODE_OUTPUT(base, pin) directModeOutput(pin)
// https://github.com/PaulStoffregen/OneWire/pull/47
// https://github.com/stickbreaker/OneWire/commit/6eb7fc1c11a15b6ac8c60e5671cf36eb6829f82c
#ifdef interrupts
#undef interrupts
#endif
#ifdef noInterrupts
#undef noInterrupts
#endif
#define noInterrupts() {portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;portENTER_CRITICAL(&mux)
#define interrupts() portEXIT_CRITICAL(&mux);}
//#warning "ESP32 OneWire testing"
#elif defined(ARDUINO_ARCH_STM32)
#define PIN_TO_BASEREG(pin) (0)
#define PIN_TO_BITMASK(pin) ((uint32_t)digitalPinToPinName(pin))
#define IO_REG_TYPE uint32_t
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR
#define DIRECT_READ(base, pin) digitalReadFast((PinName)pin)
#define DIRECT_WRITE_LOW(base, pin) digitalWriteFast((PinName)pin, LOW)
#define DIRECT_WRITE_HIGH(base, pin) digitalWriteFast((PinName)pin, HIGH)
#define DIRECT_MODE_INPUT(base, pin) pin_function((PinName)pin, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0))
#define DIRECT_MODE_OUTPUT(base, pin) pin_function((PinName)pin, STM_PIN_DATA(STM_MODE_OUTPUT_PP, GPIO_NOPULL, 0))
#elif defined(__SAMD21G18A__)
#define PIN_TO_BASEREG(pin) portModeRegister(digitalPinToPort(pin))
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
#define IO_REG_TYPE uint32_t
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR
#define DIRECT_READ(base, mask) (((*((base)+8)) & (mask)) ? 1 : 0)
#define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) = (mask))
#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+2)) = (mask))
#define DIRECT_WRITE_LOW(base, mask) ((*((base)+5)) = (mask))
#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+6)) = (mask))
#elif defined(RBL_NRF51822)
#define PIN_TO_BASEREG(pin) (0)
#define PIN_TO_BITMASK(pin) (pin)
#define IO_REG_TYPE uint32_t
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR
#define DIRECT_READ(base, pin) nrf_gpio_pin_read(pin)
#define DIRECT_WRITE_LOW(base, pin) nrf_gpio_pin_clear(pin)
#define DIRECT_WRITE_HIGH(base, pin) nrf_gpio_pin_set(pin)
#define DIRECT_MODE_INPUT(base, pin) nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_NOPULL)
#define DIRECT_MODE_OUTPUT(base, pin) nrf_gpio_cfg_output(pin)
#elif defined(__arc__) /* Arduino101/Genuino101 specifics */
#include "scss_registers.h"
#include "portable.h"
#include "avr/pgmspace.h"
#define GPIO_ID(pin) (g_APinDescription[pin].ulGPIOId)
#define GPIO_TYPE(pin) (g_APinDescription[pin].ulGPIOType)
#define GPIO_BASE(pin) (g_APinDescription[pin].ulGPIOBase)
#define DIR_OFFSET_SS 0x01
#define DIR_OFFSET_SOC 0x04
#define EXT_PORT_OFFSET_SS 0x0A
#define EXT_PORT_OFFSET_SOC 0x50
/* GPIO registers base address */
#define PIN_TO_BASEREG(pin) ((volatile uint32_t *)g_APinDescription[pin].ulGPIOBase)
#define PIN_TO_BITMASK(pin) pin
#define IO_REG_TYPE uint32_t
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR
static inline __attribute__((always_inline))
IO_REG_TYPE directRead(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
{
IO_REG_TYPE ret;
if (SS_GPIO == GPIO_TYPE(pin)) {
ret = READ_ARC_REG(((IO_REG_TYPE)base + EXT_PORT_OFFSET_SS));
} else {
ret = MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, EXT_PORT_OFFSET_SOC);
}
return ((ret >> GPIO_ID(pin)) & 0x01);
}
static inline __attribute__((always_inline))
void directModeInput(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
{
if (SS_GPIO == GPIO_TYPE(pin)) {
WRITE_ARC_REG(READ_ARC_REG((((IO_REG_TYPE)base) + DIR_OFFSET_SS)) & ~(0x01 << GPIO_ID(pin)),
((IO_REG_TYPE)(base) + DIR_OFFSET_SS));
} else {
MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) &= ~(0x01 << GPIO_ID(pin));
}
}
static inline __attribute__((always_inline))
void directModeOutput(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
{
if (SS_GPIO == GPIO_TYPE(pin)) {
WRITE_ARC_REG(READ_ARC_REG(((IO_REG_TYPE)(base) + DIR_OFFSET_SS)) | (0x01 << GPIO_ID(pin)),
((IO_REG_TYPE)(base) + DIR_OFFSET_SS));
} else {
MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) |= (0x01 << GPIO_ID(pin));
}
}
static inline __attribute__((always_inline))
void directWriteLow(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
{
if (SS_GPIO == GPIO_TYPE(pin)) {
WRITE_ARC_REG(READ_ARC_REG(base) & ~(0x01 << GPIO_ID(pin)), base);
} else {
MMIO_REG_VAL(base) &= ~(0x01 << GPIO_ID(pin));
}
}
static inline __attribute__((always_inline))
void directWriteHigh(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
{
if (SS_GPIO == GPIO_TYPE(pin)) {
WRITE_ARC_REG(READ_ARC_REG(base) | (0x01 << GPIO_ID(pin)), base);
} else {
MMIO_REG_VAL(base) |= (0x01 << GPIO_ID(pin));
}
}
#define DIRECT_READ(base, pin) directRead(base, pin)
#define DIRECT_MODE_INPUT(base, pin) directModeInput(base, pin)
#define DIRECT_MODE_OUTPUT(base, pin) directModeOutput(base, pin)
#define DIRECT_WRITE_LOW(base, pin) directWriteLow(base, pin)
#define DIRECT_WRITE_HIGH(base, pin) directWriteHigh(base, pin)
#elif defined(__riscv)
/*
* Tested on highfive1
*
* Stable results are achieved operating in the
* two high speed modes of the highfive1. It
* seems to be less reliable in slow mode.
*/
#define PIN_TO_BASEREG(pin) (0)
#define PIN_TO_BITMASK(pin) digitalPinToBitMask(pin)
#define IO_REG_TYPE uint32_t
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR
static inline __attribute__((always_inline))
IO_REG_TYPE directRead(IO_REG_TYPE mask)
{
return ((GPIO_REG(GPIO_INPUT_VAL) & mask) != 0) ? 1 : 0;
}
static inline __attribute__((always_inline))
void directModeInput(IO_REG_TYPE mask)
{
GPIO_REG(GPIO_OUTPUT_XOR) &= ~mask;
GPIO_REG(GPIO_IOF_EN) &= ~mask;
GPIO_REG(GPIO_INPUT_EN) |= mask;
GPIO_REG(GPIO_OUTPUT_EN) &= ~mask;
}
static inline __attribute__((always_inline))
void directModeOutput(IO_REG_TYPE mask)
{
GPIO_REG(GPIO_OUTPUT_XOR) &= ~mask;
GPIO_REG(GPIO_IOF_EN) &= ~mask;
GPIO_REG(GPIO_INPUT_EN) &= ~mask;
GPIO_REG(GPIO_OUTPUT_EN) |= mask;
}
static inline __attribute__((always_inline))
void directWriteLow(IO_REG_TYPE mask)
{
GPIO_REG(GPIO_OUTPUT_VAL) &= ~mask;
}
static inline __attribute__((always_inline))
void directWriteHigh(IO_REG_TYPE mask)
{
GPIO_REG(GPIO_OUTPUT_VAL) |= mask;
}
#define DIRECT_READ(base, mask) directRead(mask)
#define DIRECT_WRITE_LOW(base, mask) directWriteLow(mask)
#define DIRECT_WRITE_HIGH(base, mask) directWriteHigh(mask)
#define DIRECT_MODE_INPUT(base, mask) directModeInput(mask)
#define DIRECT_MODE_OUTPUT(base, mask) directModeOutput(mask)
#else
#define PIN_TO_BASEREG(pin) (0)
#define PIN_TO_BITMASK(pin) (pin)
#define IO_REG_TYPE unsigned int
#define IO_REG_BASE_ATTR
#define IO_REG_MASK_ATTR
#define DIRECT_READ(base, pin) digitalRead(pin)
#define DIRECT_WRITE_LOW(base, pin) digitalWrite(pin, LOW)
#define DIRECT_WRITE_HIGH(base, pin) digitalWrite(pin, HIGH)
#define DIRECT_MODE_INPUT(base, pin) pinMode(pin,INPUT)
#define DIRECT_MODE_OUTPUT(base, pin) pinMode(pin,OUTPUT)
#warning "OneWire. Fallback mode. Using API calls for pinMode,digitalRead and digitalWrite. Operation of this library is not guaranteed on this architecture."
#endif
#endif

View file

@ -1,52 +0,0 @@
#ifndef OneWire_Direct_RegType_h
#define OneWire_Direct_RegType_h
#include <stdint.h>
// Platform specific I/O register type
#if defined(__AVR__)
#define IO_REG_TYPE uint8_t
#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
#define IO_REG_TYPE uint8_t
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
#define IO_REG_TYPE uint32_t
#elif defined(__MKL26Z64__)
#define IO_REG_TYPE uint8_t
#elif defined(__SAM3X8E__) || defined(__SAM3A8C__) || defined(__SAM3A4C__)
#define IO_REG_TYPE uint32_t
#elif defined(__PIC32MX__)
#define IO_REG_TYPE uint32_t
#elif defined(ARDUINO_ARCH_ESP8266)
#define IO_REG_TYPE uint32_t
#elif defined(ARDUINO_ARCH_ESP32)
#define IO_REG_TYPE uint32_t
#define IO_REG_MASK_ATTR
#elif defined(ARDUINO_ARCH_STM32)
#define IO_REG_TYPE uint32_t
#elif defined(__SAMD21G18A__)
#define IO_REG_TYPE uint32_t
#elif defined(RBL_NRF51822)
#define IO_REG_TYPE uint32_t
#elif defined(__arc__) /* Arduino101/Genuino101 specifics */
#define IO_REG_TYPE uint32_t
#elif defined(__riscv)
#define IO_REG_TYPE uint32_t
#else
#define IO_REG_TYPE unsigned int
#endif
#endif

View file

@ -1,46 +0,0 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View file

@ -1,160 +0,0 @@
/*
SCD30.cpp - Sensirion SCD30 CO2-Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <Arduino.h>
#include <inttypes.h>
#include <Wire.h>
#include "SCD30.h"
// Default Constructor
SCD30::SCD30() {};
// Constructor with Measuring Interval and Autocalibration Status
SCD30::SCD30(uint8_t interval, bool selfcalib) {
this->interval = interval;
this->selfcalib = selfcalib;
}
// Initialize the Sensor, Enable Constant Measurement, Set Autocalibration and Interval
void SCD30::initialize(void) {
uint8_t fw[3];
DEBUG_PRINTLN("SCD30::initialize");
// Soft Reset
reset();
// Send Get Firmware, Workaround to Avoid First Real Command to be Ignored
getBytes(SCD30_GET_FW_VER, fw, 3);
delay(10);
// Start Continous Measurement
sendCmd(SCD30_CONT_MEASURE, 0);
// Enable/Disable Autocalibration
if (selfcalib) {
sendCmd(SCD30_AUTOCAL, 1);
} else {
sendCmd(SCD30_AUTOCAL, 0);
}
delay(10);
// Measurement Interval
sendCmd(SCD30_SET_INTERVAL, interval);
}
// Read Data From Sensor and Put Them Into the Payload Array
uint8_t SCD30::getSensorData(char *payload, uint8_t startbyte) {
uint8_t ready[2] = {0};
uint8_t data[18] = {0};
DEBUG_PRINTLN("SCD30::getSensorData");
// Check if Sensor Data is Available
getBytes(SCD30_DATA_READY, ready, 2);
if (ready[1] == 1){
// Get All Measurements (18 Bytes)
getBytes(SCD30_GET_MEASURE, data, 18);
// CO2 PPM
int16_t value = (int16_t)(byteToFloat(data[0], data[1], data[3], data[4]));
int16ToPayload(value, payload, startbyte);
// Temperature
value = (int16_t)(byteToFloat(data[6], data[7], data[9], data[10])*100);
int16ToPayload(value, payload, startbyte+2);
// Humidity
value = (int16_t)(byteToFloat(data[12], data[13], data[15], data[16])*100);
int16ToPayload(value, payload, startbyte+4);
}
return startbyte+6;
}
// Calibrate the Sensor to 400ppm (Outside Level)
void SCD30::calibrate(void) {
sendCmd(SCD30_SET_RECALIB, 400);
}
// Sensor Soft Reset
void SCD30::reset(void) {
sendCmd(SCD30_RESET);
delay(2000); // Bootup Time
}
// Send a Command with Argument
void SCD30::sendCmd(uint16_t cmd, uint16_t arg) {
uint8_t args[2], crc;
args[0] = (arg >> 8);
args[1] = (arg & 0xFF);
crc = calcCrc(args, 2);
Wire.beginTransmission(SCD30_ADDR);
Wire.write(cmd >> 8);
Wire.write(cmd & 0xFF);
Wire.write(args[0]);
Wire.write(args[1]);
Wire.write(crc);
Wire.endTransmission();
}
// Send only a Command
void SCD30::sendCmd(uint16_t cmd) {
Wire.beginTransmission(SCD30_ADDR);
Wire.write(cmd >> 8);
Wire.write(cmd & 0xFF);
Wire.endTransmission();
}
// Read len Number of Bytes from Register reg into Array bytes
void SCD30::getBytes(uint16_t reg, uint8_t bytes[], uint8_t len) {
sendCmd(reg);
delay(3);
Wire.requestFrom((uint8_t)SCD30_ADDR, (uint8_t)len);
if (Wire.available()) {
for (uint8_t i = 0; i<len; i++)
bytes[i] = Wire.read();
}
}
// Calculate CRC8
uint8_t SCD30::calcCrc(uint8_t bytes[], uint8_t len) {
uint8_t crc8 = 0xFF;
for (uint8_t i = 0; i < len; i++)
{
crc8 ^= bytes[i];
for (uint8_t j = 0; j < 8; j++)
{
if (crc8 & 0x80)
crc8 = (uint8_t)((crc8 << 1) ^ 0x31);
else
crc8 <<= 1;
}
}
return crc8;
}
// Convert 4 Bytes from the Measurement to a Float Value
float SCD30::byteToFloat(uint8_t mmsb, uint8_t mlsb, uint8_t lmsb, uint8_t llsb) {
uint32_t iValue = (uint32_t)((((uint32_t)mmsb) << 24) | (((uint32_t)mlsb) << 16) | (((uint32_t)lmsb) << 8) | ((uint32_t)llsb));
float fValue = *(float*)&iValue;
return fValue;
}

View file

@ -1,71 +0,0 @@
/*
SCD30.h - Sensirion SCD30 CO2-Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SCD30_H
#define SCD30_H
#include "../../include/attsensor.h"
#include "../../include/debug.h"
// Sensor I2C Address
#define SCD30_ADDR 0x61
// Commands and Registers
#define SCD30_CONT_MEASURE 0x0010
#define SCD30_SET_INTERVAL 0x4600
#define SCD30_DATA_READY 0x0202
#define SCD30_GET_MEASURE 0x0300
#define SCD30_AUTOCAL 0x5306
#define SCD30_SET_RECALIB 0x5204
#define SCD30_SET_TEMPOFFSET 0x5403
#define SCD30_SET_ALTCOMP 0x5102
#define SCD30_RESET 0xD304
#define SCD30_STOP_MEAS 0x0104
#define SCD30_GET_FW_VER 0xD100
class SCD30 : public AttSensor {
private:
bool selfcalib = false;
uint16_t interval = 2;
void reset(void);
void sendCmd(uint16_t cmd, uint16_t arg);
void sendCmd(uint16_t cmd);
void getBytes(uint16_t register, uint8_t bytes[], uint8_t len);
uint8_t calcCrc(uint8_t bytes[], uint8_t len);
float byteToFloat(uint8_t mmsb, uint8_t mlsb, uint8_t lmsb, uint8_t llsb);
public:
SCD30();
SCD30(uint8_t interval, bool selfcalib);
void initialize(void);
void calibrate(void);
uint8_t numBytes(void) {return 6;};
uint8_t getSensorData(char *payload, uint8_t startbyte);
};
#endif

View file

@ -1,86 +0,0 @@
/*
.cpp - SENSAIRS8 Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <Arduino.h>
#include "SENSAIRS8.h"
// Constructor - Inititalize Hardware UART
SENSAIRS8::SENSAIRS8(void) {
Serial.begin(9600);
Serial.setTimeout(READ_TIMEOUT);
}
uint8_t SENSAIRS8::getSensorData(char *payload, uint8_t startbyte) {
uint8_t _cmd[7] = {0xFE, 0x44, 0x00, 0x08, 0x02, 0x9F, 0x25};
while (Serial.available() > 0) Serial.read();
Serial.write(_cmd, 7);
delay(1000);
uint8_t readBytes = read();
payload[startbyte] = 0x00;
payload[startbyte+1] = 0x00;
if (readBytes > 0) {
uint16ToPayload((buffer[3]*256) + buffer[4], payload, startbyte);
}
return startbyte+2;
}
void SENSAIRS8::calibrate(void) {
pinMode(calpin, OUTPUT);
digitalWrite(calpin, LOW);
delay(6000);
digitalWrite(calpin, HIGH);
pinMode(calpin, INPUT_PULLUP);
}
void SENSAIRS8::initialize(void) {
// Disable Auto Background Calibration
uint8_t _cmd[8] = {0xFE, 0x06, 0x00, 0x01F, 0x00, 0x00, 0xAC, 0x03};
while (Serial.available() > 0) Serial.read();
Serial.write(_cmd, 8);
Serial.flush();
}
// Read a Sensor Response
uint8_t SENSAIRS8::read() {
// Number of returned Bytes
uint8_t ret = 0;
// Clear Internal Buffer
zeroBuffer();
// Read Available Bytes
if (Serial.available() > 0) {
ret = Serial.readBytes(buffer, SER_BUF_LEN);
}
return ret;
}
// Fill the Internal Buffer with Zeroes
void SENSAIRS8::zeroBuffer() {
for (int i=0; i < SER_BUF_LEN; i++)
buffer[i] = 0x00;
}

View file

@ -1,52 +0,0 @@
/*
SENSAIRS8.h - SENSAIRS8 Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SENSAIRS8_H
#define SENSAIRS8_H
#include "../../include/attsensor.h"
#define READ_TIMEOUT 500 // Timeout for Serial Communication
#define SER_BUF_LEN 7 // Length of the Internal Serial Message Buffer
class SENSAIRS8 : public AttSensor {
private:
uint8_t buffer[SER_BUF_LEN];
pin_size_t calpin = PIN_PB4; // PB4 is the Calibration Pin on the Addon PCB
uint8_t read();
void zeroBuffer(void);
uint16_t getPPM(void);
public:
SENSAIRS8(void);
uint8_t getSensorData(char *payload, uint8_t startbyte);
void calibrate(void);
void initialize(void);
uint8_t numBytes(void) {return 2;};
};
#endif

View file

@ -1,117 +0,0 @@
/*
SG112A.cpp - SG112A Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <Arduino.h>
#include "SG112A.h"
// Constructor - Inititalize Hardware UART
SG112A::SG112A(void) {
Serial.begin(9600);
Serial.setTimeout(READ_TIMEOUT);
}
uint8_t SG112A::getSensorData(char *payload, uint8_t startbyte) {
write(CMD_GET_PPM);
delay(50);
uint8_t readBytes = read();
payload[startbyte] = 0x00;
payload[startbyte+1] = 0x00;
if (readBytes > 0) {
switch(buffer[2]) {
case 0x15:
uint16ToPayload((buffer[3]*256) + buffer[4], payload, startbyte);
break;
}
}
return startbyte+2;
}
// Write a Command to the Sensor
void SG112A::write(byte cmd) {
uint8_t _cmd[6] = {0xAA, 0x55, cmd, 0x00, 0x00, 0x00};
uint16_t crc = crc16(_cmd, 4);
_cmd[4] = (uint8_t)(crc & 0xFF);
_cmd[5] = (uint8_t)(crc >> 8);
while (Serial.available() > 0) Serial.read();
Serial.write(_cmd, 6);
Serial.flush();
}
// Read a Sensor Response
uint8_t SG112A::read() {
// Number of returned Bytes
uint8_t ret = 0;
// Clear Internal Buffer
zeroBuffer();
// Read Available Bytes
if (Serial.available() > 0) {
ret = Serial.readBytes(buffer, SER_BUF_LEN);
}
// Check Sync Bytes
if (buffer[0] != 0xBB || buffer[1] != 0x66)
return 0;
// Check CRC of the Returned Messages
uint16_t crc = crc16(buffer, ret-2);
if (buffer[ret-1] != (uint8_t)(crc >> 8) || buffer[ret-2] != (uint8_t)(crc & 0xFF))
return 0;
return ret;
}
// Fill the Internal Buffer with Zeroes
void SG112A::zeroBuffer() {
for (int i=0; i < SER_BUF_LEN; i++)
buffer[i] = 0x00;
}
// Calculate 16Bit CRC of Messages and Commands
uint16_t SG112A::crc16(uint8_t *cmd, int len){
uint16_t ret = 0xffff;
uint16_t polynomial = 0xa001;
int shift = 0x0;
int i = 0;
for (i = len - 1; i >= 0 ; i-- ){
uint16_t code = ( uint16_t )( cmd [ len -1 - i ] & 0xff );
ret = ret^code;
shift = 0x0;
while ( shift <= 7 ){
if ( ret & 0x1 ) {
ret = ret >> 1;
ret = ret^polynomial ;
} else {
ret = ret >> 1;
}
shift++;
}
}
return ret;
}

View file

@ -1,57 +0,0 @@
/*
SG112A.h - SG112A Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SG112A_H
#define SG112A_H
#include "../../include/attsensor.h"
#define READ_TIMEOUT 500 // Timeout for Serial Communication
#define SER_BUF_LEN 16 // Length of the Internal Serial Message Buffer
#define CMD_GET_VER 0x10 // Get Sensor Version
#define CMD_GET_SER 0x12 // Get Sensor Serial
#define CMD_GET_PPM 0x14 // Get Current PPM Reading
class SG112A : public AttSensor {
private:
uint8_t buffer[SER_BUF_LEN];
void write(byte cmd);
uint8_t read();
void zeroBuffer(void);
uint16_t crc16(uint8_t *cmd, int len);
uint16_t getPPM(void);
public:
SG112A(void);
uint8_t getSensorData(char *payload, uint8_t startbyte);
void calibrate(void) {};
void initialize(void) {};
uint8_t numBytes(void) {return 2;};
};
#endif

View file

@ -1,181 +0,0 @@
/*
.cpp - SPS30 Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <Arduino.h>
#include "SPS30.h"
// Read Measurements from Sensor and put them into the Paylaod
uint8_t SPS30::getSensorData(char *payload, uint8_t startbyte) {
// Buffer to hold the raw I2C Return
uint8_t data[30];
// Array with calculated Values.
// Predefined with 0xEEEE to recognize Read Errors in the Payload
uint16_t values[5] = {0xEEEE, 0xEEEE, 0xEEEE, 0xEEEE, 0xEEEE};
// Wakeup Sensor from Sleep
DEBUG_PRINTLN("SPS30::getSensorData - Wake Up Sensor");
Wire.beginTransmission(SPS30_I2C_ADDRESS);
Wire.endTransmission();
delay(50);
write(SPS30_WAKEUP);
delay(50);
// Start Measuring, Integer Output
DEBUG_PRINTLN("SPS30::getSensorData - StartMeasurement");
write(SPS30_START_MEASUREMENT, 0x0500);
// Wait for Stable Values
DEBUG_PRINTLN("SPS30::getSensorData - WaitForStabilize");
delay(SPS30_STABILIZE_TIME);
bool dataok = false;
uint8_t tries = 5;
// Retry up to 5 times to get a Valid Measurement from the Sensor
while (!dataok && tries > 0) {
DEBUG_PRINT("SPS30::getSensorData - Reading Sensor Data, Try ");
DEBUG_PRINTLN(6-tries);
if (hasData()) {
write(SPS30_READ_MEASUREMENT);
Wire.requestFrom(SPS30_I2C_ADDRESS, 30);
if (Wire.available() != 0) {
Wire.readBytes(data, 30);
DEBUG_PRINT("SPS30::getSensorData - I2C-Data: ");
DEBUG_PRINTARR(data);
DEBUG_PRINTLN("");
// Get Values for PM1.0, 2.5, 4 and 10 in Order
dataok = true;
for (uint8_t i=0; i<4; i++)
if (data[(i*3)+2] == calcCRC(data+(i*3), 2)) {
values[i] = data[i*3] << 8 | data[(i*3)+1]; // Create uint16_t from Bytes
} else {
dataok = false; // Set false on Checksum Error
}
// Get Typical Particle size
if (data[29] == calcCRC(data+27, 2)) {
values[4] = data[27] << 8 | data[28]; // Create uint16_t from Bytes
} else {
dataok = false; // Set false on Checksum Error
}
if (!dataok) {
// If a Checksum error occured
DEBUG_PRINTLN("SPS30::getSensorData - CheckSum Error");
tries--;
delay(2000);
}
} else {
// No I2C Data Available
DEBUG_PRINTLN("SPS30::getSensorData - Error, no I2C Data available");
tries--;
delay(2000);
}
} else {
// No Data Ready from Sensor
DEBUG_PRINTLN("SPS30::getSensorData - Error, Sensor Data not Ready");
tries--;
delay(2000);
}
}
DEBUG_PRINTLN("SPS30::getSensorData - StopMeasurement");
write(SPS30_STOP_MEASUREMENT);
delay(50);
DEBUG_PRINTLN("SPS30::getSensorData - SensorSleep");
write(SPS30_SLEEP);
// Put the Values into the Payload Array
for (uint8_t i=0; i<5; i++)
uint16ToPayload(values[i], payload, startbyte+(i*2));
}
// Check if Sensor has Data
bool SPS30::hasData(void) {
uint16_t resp = readReg(SPS30_GET_DATA_READY);
return (resp == 1);
}
// Read a 16Bit Register
uint16_t SPS30::readReg(uint16_t regAddr) {
Wire.beginTransmission(SPS30_I2C_ADDRESS);
Wire.write(regAddr >> 8);
Wire.write(regAddr & 0xFF);
Wire.endTransmission();
Wire.requestFrom(SPS30_I2C_ADDRESS, 2);
if (Wire.available() != 0) {
uint8_t msb = Wire.read();
uint8_t lsb = Wire.read();
return ((uint16_t)msb << 8 | lsb);
}
return(0);
}
// Write a 16 Bit Command without Parameters
bool SPS30::write(uint16_t cmd) {
Wire.beginTransmission(SPS30_I2C_ADDRESS);
Wire.write(cmd >> 8);
Wire.write(cmd & 0xFF);
if (Wire.endTransmission() != 0)
return(false);
return(true);
}
// Write a 16Bit Command with 16Bit Parameters
bool SPS30::write(uint16_t cmd, uint16_t arg){
uint8_t crcdata[2];
crcdata[0] = arg >> 8;
crcdata[1] = arg & 0xFF;
uint8_t csum = calcCRC(crcdata, 2);
Wire.beginTransmission(SPS30_I2C_ADDRESS);
Wire.write(cmd >> 8);
Wire.write(cmd & 0xFF);
Wire.write(arg >> 8);
Wire.write(arg & 0xFF);
Wire.write(csum);
if (Wire.endTransmission() != 0)
return(false);
return(true);
}
// Calculate the Checksum
uint8_t SPS30::calcCRC(uint8_t data[], uint8_t len) {
uint8_t csum = 0xFF;
for (uint8_t x = 0; x < len; x++)
{
csum ^= data[x];
for (uint8_t i = 0; i < 8; i++)
{
if ((csum & 0x80) != 0)
csum = (uint8_t)((csum << 1) ^ 0x31);
else
csum <<= 1;
}
}
return csum;
}

View file

@ -1,67 +0,0 @@
/*
SPS30.h - SPS30 Sensor Library
Copyright (c) 2020-2021, Stefan Brand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SPS30_H
#define SPS30_H
#include <Wire.h>
#include "../../include/attsensor.h"
#include "../../include/debug.h"
// I2C Address
#define SPS30_I2C_ADDRESS 0x69
#define SPS30_STABILIZE_TIME 30000
// I2C Commands and Registers
#define SPS30_START_MEASUREMENT 0x0010
#define SPS30_STOP_MEASUREMENT 0x0104
#define SPS30_GET_DATA_READY 0x0202
#define SPS30_READ_MEASUREMENT 0x0300
#define SPS30_AUTO_CLEAN_INTERVAL 0x8004
#define SPS30_START_FAN_CLEANING 0x5607
#define SPS30_READ_ARTICLE_CODE 0xD025
#define SPS30_READ_SERIAL_NUMBER 0xD033
#define SPS30_DEVICE_RESET 0xD304
#define SPS30_SLEEP 0x1001
#define SPS30_WAKEUP 0x1103
class SPS30 : public AttSensor {
private:
bool hasData(void);
uint8_t calcCRC(uint8_t data[], uint8_t len);
uint16_t readReg(uint16_t regAddr);
bool write(uint16_t cmd);
bool write(uint16_t cmd, uint16_t arg);
public:
SPS30() {};
uint8_t getSensorData(char *payload, uint8_t startbyte) override;
void calibrate(void) override {};
void initialize(void) override {} ;
uint8_t numBytes(void) override { return 10; };
};
#endif

View file

@ -1,165 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

File diff suppressed because it is too large Load diff

View file

@ -1,327 +0,0 @@
/*--------------------------------------------------------------------
This file is part of the tinyNeoPixel library, derived from
Adafruit_NeoPixel.
NeoPixel is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
--------------------------------------------------------------------*/
// *INDENT-OFF* astyle hates this file
// *PAD-OFF* and destroys the lookup tables!
#ifndef TINYNEOPIXEL_H
#define TINYNEOPIXEL_H
#include <Arduino.h>
#if (__AVR_ARCH__ < 100)
#error "This version of the library only supports AVRxt parts (tinyAVR 0/1/2-series, megaAVR 0-series and the AVR DA/DB/DD parts. For tinyNeoPixel, for classic AVR, get from ATTinyCore package"
#endif
// The order of primary colors in the NeoPixel data stream can vary
// among device types, manufacturers and even different revisions of
// the same item. The third parameter to the Adafruit_NeoPixel
// constructor encodes the per-pixel byte offsets of the red, green
// and blue primaries (plus white, if present) in the data stream --
// the following #defines provide an easier-to-use named version for
// each permutation. e.g. NEO_GRB indicates a NeoPixel-compatible
// device expecting three bytes per pixel, with the first byte
// containing the green value, second containing red and third
// containing blue. The in-memory representation of a chain of
// NeoPixels is the same as the data-stream order; no re-ordering of
// bytes is required when issuing data to the chain.
// Bits 5,4 of this value are the offset (0-3) from the first byte of
// a pixel to the location of the red color byte. Bits 3,2 are the
// green offset and 1,0 are the blue offset. If it is an RGBW-type
// device (supporting a white primary in addition to R,G,B), bits 7,6
// are the offset to the white byte...otherwise, bits 7,6 are set to
// the same value as 5,4 (red) to indicate an RGB (not RGBW) device.
// i.e. binary representation:
// 0bWWRRGGBB for RGBW devices
// 0bRRRRGGBB for RGB
// RGB NeoPixel permutations; white and red offsets are always same
// Offset: W R G B
#define NEO_RGB ((0 << 6) | (0 << 4) | (1 << 2) | (2))
#define NEO_RBG ((0 << 6) | (0 << 4) | (2 << 2) | (1))
#define NEO_GRB ((1 << 6) | (1 << 4) | (0 << 2) | (2))
#define NEO_GBR ((2 << 6) | (2 << 4) | (0 << 2) | (1))
#define NEO_BRG ((1 << 6) | (1 << 4) | (2 << 2) | (0))
#define NEO_BGR ((2 << 6) | (2 << 4) | (1 << 2) | (0))
// RGBW NeoPixel permutations; all 4 offsets are distinct
// Offset: W R G B
#define NEO_WRGB ((0 << 6) | (1 << 4) | (2 << 2) | (3))
#define NEO_WRBG ((0 << 6) | (1 << 4) | (3 << 2) | (2))
#define NEO_WGRB ((0 << 6) | (2 << 4) | (1 << 2) | (3))
#define NEO_WGBR ((0 << 6) | (3 << 4) | (1 << 2) | (2))
#define NEO_WBRG ((0 << 6) | (2 << 4) | (3 << 2) | (1))
#define NEO_WBGR ((0 << 6) | (3 << 4) | (2 << 2) | (1))
#define NEO_RWGB ((1 << 6) | (0 << 4) | (2 << 2) | (3))
#define NEO_RWBG ((1 << 6) | (0 << 4) | (3 << 2) | (2))
#define NEO_RGWB ((2 << 6) | (0 << 4) | (1 << 2) | (3))
#define NEO_RGBW ((3 << 6) | (0 << 4) | (1 << 2) | (2))
#define NEO_RBWG ((2 << 6) | (0 << 4) | (3 << 2) | (1))
#define NEO_RBGW ((3 << 6) | (0 << 4) | (2 << 2) | (1))
#define NEO_GWRB ((1 << 6) | (2 << 4) | (0 << 2) | (3))
#define NEO_GWBR ((1 << 6) | (3 << 4) | (0 << 2) | (2))
#define NEO_GRWB ((2 << 6) | (1 << 4) | (0 << 2) | (3))
#define NEO_GRBW ((3 << 6) | (1 << 4) | (0 << 2) | (2))
#define NEO_GBWR ((2 << 6) | (3 << 4) | (0 << 2) | (1))
#define NEO_GBRW ((3 << 6) | (2 << 4) | (0 << 2) | (1))
#define NEO_BWRG ((1 << 6) | (2 << 4) | (3 << 2) | (0))
#define NEO_BWGR ((1 << 6) | (3 << 4) | (2 << 2) | (0))
#define NEO_BRWG ((2 << 6) | (1 << 4) | (3 << 2) | (0))
#define NEO_BRGW ((3 << 6) | (1 << 4) | (2 << 2) | (0))
#define NEO_BGWR ((2 << 6) | (3 << 4) | (1 << 2) | (0))
#define NEO_BGRW ((3 << 6) | (2 << 4) | (1 << 2) | (0))
#define NEO_KHZ800 0x0000 ///< 800 KHz data transmission
// 400 kHz neopixels are virtually absent from the market today
// They are not supported.
// These two tables are declared outside the Adafruit_NeoPixel class
// because some boards may require oldschool compilers that don't
// handle the C++11 constexpr keyword.
/* A pre-calculated 8-bit sine look-up table stored in flash for use
with the sine8() function. This is apparently of use in some animation
algorithms. If __AVR_ARCH__==103, then all of the flash is memory
mapped, and we can simply declare it const, access it like a
normal variable, and it won't be copied to RAM.
AVRxt devices with too much flash for all of it to be mapped
which includes the AVR64Dx and AVR128Dx parts. DxCore defines a
.section for the area of PROGMEM that is mapped by default, and
a MAPPED_PROGMEM macro. A variable declared const MAPPED_PROGMEM can
be accessed normally, but will be stored in the flash and not copied to RAM.
Finally, if neither of those are an option - it gets declared with PROGMEM
Copy & paste this snippet into a Python REPL to regenerate:
import math
for x in range(256):
print("{:3},".format(int((math.sin(x/128.0*math.pi)+1.0)*127.5+0.5))),
if x&15 == 15: print
*/
#if (__AVR_ARCH__==103)
// All out flash is mapped - yay!
static const uint8_t _NeoPixelSineTable[256] = {
#elif defined(MAPPED_PROGMEM)
// Some of it is - but we can put stuff there - yay!
static const uint8_t MAPPED_PROGMEM _NeoPixelSineTable[256] = {
#else
// Back to progmem...
static const uint8_t PROGMEM _NeoPixelSineTable[256] = {
#endif
128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173,
176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215,
218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244,
245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255,
255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246,
245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220,
218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179,
176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131,
128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82,
79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40,
37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11,
10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9,
10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35,
37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124};
/* Similar to above, but for an 8-bit gamma-correction table.
Copy & paste this snippet into a Python REPL to regenerate:
import math
gamma=2.6
for x in range(256):
print("{:3},".format(int(math.pow((x)/255.0,gamma)*255.0+0.5))),
if x&15 == 15: print
*/
#if (__AVR_ARCH__==103)
// All our flash is mapped - yay!
static const uint8_t _NeoPixelGammaTable[256] = {
#elif defined(MAPPED_PROGMEM)
// Some of it is - but we can put stuff there - yay!
static const uint8_t MAPPED_PROGMEM _NeoPixelGammaTable[256] = {
#else
// Back to progmem...
static const uint8_t PROGMEM _NeoPixelGammaTable[256] = {
#endif
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7,
7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12,
13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20,
20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29,
30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75,
76, 77, 78, 80, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96,
97, 99,100,102,103,105,106,108,109,111,112,114,115,117,119,120,
122,124,125,127,129,130,132,134,136,137,139,141,143,145,146,148,
150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,
182,184,186,188,191,193,195,197,199,202,204,206,209,211,213,215,
218,220,223,225,227,230,232,235,237,240,242,245,247,250,252,255};
typedef uint8_t neoPixelType;
class tinyNeoPixel {
public:
// Constructor: number of LEDs, pin number, LED type
tinyNeoPixel(uint16_t n, uint8_t p, neoPixelType t, uint8_t *pxl);
~tinyNeoPixel();
void
show(void),
setPin(uint8_t p),
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b),
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w),
setPixelColor(uint16_t n, uint32_t c),
fill(uint32_t c=0, uint16_t first=0, uint16_t count=0),
setBrightness(uint8_t b),
clear();
uint8_t
*getPixels(void) const,
getBrightness(void) const;
uint16_t
numPixels(void) const;
uint32_t
getPixelColor(uint16_t n) const;
uint8_t getPin(void) { return pin; }
void begin(void) {return;}
/*!
@brief An 8-bit integer sine wave function, not directly compatible
with standard trigonometric units like radians or degrees.
@param x Input angle, 0-255; 256 would loop back to zero, completing
the circle (equivalent to 360 degrees or 2 pi radians).
One can therefore use an unsigned 8-bit variable and simply
add or subtract, allowing it to overflow/underflow and it
still does the expected contiguous thing.
@return Sine result, 0 to 255, or -128 to +127 if type-converted to
a signed int8_t, but you'll most likely want unsigned as this
output is often used for pixel brightness in animation effects.
*/
static uint8_t sine8(uint8_t x) { // 0-255 in, 0-255 out
#if (__AVR_ARCH__==103 || defined(MAPPED_PROGMEM))
return _NeoPixelSineTable[x];
#else // We had to put it in PROGMEM, and that's how we get it out
return pgm_read_byte(&_NeoPixelSineTable[x]); // 0-255 in, 0-255 out
#endif
}
/*!
@brief An 8-bit gamma-correction function for basic pixel brightness
adjustment. Makes color transitions appear more perceptially
correct.
@param x Input brightness, 0 (minimum or off/black) to 255 (maximum).
@return Gamma-adjusted brightness, can then be passed to one of the
setPixelColor() functions. This uses a fixed gamma correction
exponent of 2.6, which seems reasonably okay for average
NeoPixels in average tasks. If you need finer control you'll
need to provide your own gamma-correction function instead.
*/
static uint8_t gamma8(uint8_t x) {
#if (__AVR_ARCH__==103 || defined(MAPPED_PROGMEM))
return _NeoPixelGammaTable[x];
#else
return pgm_read_byte(&_NeoPixelGammaTable[x]);
#endif
}
/*!
@brief Convert separate red, green and blue values into a single
"packed" 32-bit RGB color.
@param r Red brightness, 0 to 255.
@param g Green brightness, 0 to 255.
@param b Blue brightness, 0 to 255.
@return 32-bit packed RGB value, which can then be assigned to a
variable for later use or passed to the setPixelColor()
function. Packed RGB format is predictable, regardless of
LED strand color order.
*/
static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) {
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
/*!
@brief Convert separate red, green, blue and white values into a
single "packed" 32-bit WRGB color.
@param r Red brightness, 0 to 255.
@param g Green brightness, 0 to 255.
@param b Blue brightness, 0 to 255.
@param w White brightness, 0 to 255.
@return 32-bit packed WRGB value, which can then be assigned to a
variable for later use or passed to the setPixelColor()
function. Packed WRGB format is predictable, regardless of
LED strand color order.
*/
static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
static uint32_t ColorHSV(uint16_t hue, uint8_t sat=255, uint8_t val=255);
/*!
@brief A gamma-correction function for 32-bit packed RGB or WRGB
colors. Makes color transitions appear more perceptially
correct.
@param x 32-bit packed RGB or WRGB color.
@return Gamma-adjusted packed color, can then be passed in one of the
setPixelColor() functions. Like gamma8(), this uses a fixed
gamma correction exponent of 2.6, which seems reasonably okay
for average NeoPixels in average tasks. If you need finer
control you'll need to provide your own gamma-correction
function instead.
*/
static uint32_t gamma32(uint32_t x);
#if (!defined(DISABLEMILLIS) && !defined(MILLIS_USE_TIMERRTC) && !defined(MILLIS_USE_TIMERRTC_XTAL) && !defined(MILLIS_USE_TIMERRTC_XOSC))
inline bool canShow(void) { return (micros() - endTime) >= 50L; }
#else
inline bool canShow(void) {return 1;} //we don't have micros here;
#endif
private:
uint16_t
numLEDs, // Number of RGB LEDs in strip
numBytes; // Size of 'pixels' buffer below (3 or 4 bytes/pixel)
int8_t
pin; // Output pin number (-1 if not yet set)
uint8_t
brightness,
*pixels, // Holds LED color values (3 or 4 bytes each)
rOffset, // Index of red byte within each 3- or 4-byte pixel
gOffset, // Index of green byte
bOffset, // Index of blue byte
wOffset; // Index of white byte (same as rOffset if no white)
uint32_t
endTime; // Latch timing reference
volatile uint8_t
*port; // Output PORT register
uint8_t
pinMask; // Output PORT bitmask
};
#endif // TINYNEOPIXEL_H

View file

@ -1,202 +0,0 @@
<!doctype html>
<html>
<head>
<title>AttNode v3 Payload Decoder Generator</title>
<meta name="description" content="AttNode v3 Payload Decoder Generator">
<meta name="author" content="Stefan Brand">
<style>
body {
background-color: #e9eaec;
font-family: Roboto, sans-serif;
font-size: 1em;
padding: 24px;
}
div {
background: #f8f8f8;
border: 1px solid #ccc;
padding: 1em 2em;
margin-bottom: 40px;
}
pre {
border: 1px solid black;
font-family: monospace, monospace;
font-size: 1em;
padding: 4px;
}
</style>
</head>
<body>
<div class="title">
<h1>AttNode v3 Payload Decoder Generator</h1>
</div>
<div class="header">
<p>Please choose the Sensors that you have enabled in the Firmware. The Payload Decoder code below will change accordingly.</p>
<p>Notes:</p>
<ul>
<li>You can set arbitary combinations of Sensors here. Not all of them are useable / technically possible</li>
<li>Enable only the sensors in the decoder, which are enabled in the firmware for a particular device. The Decoder does not detect which sensors are present in the LoRa paket. You can set a payload decoder per device in TTN v3 to have nodes with different sensors in one application</li>
<li>The Payload Decoder shown here is for TTN v3 / The Things Stack v3. If you use other LoRa Stacks you may have to change the <b>decodeUplink</b> function and its return value.</li>
</ul>
</div>
<div class="chooser">
<h3>Sensors:</h3>
<form id="chooserForm">
<label>
<input type="checkbox" value="mhz19c" onchange="updateDecoder();">
MH-Z19C
</label>
<label>
<input type="checkbox" value="sg112a" onchange="updateDecoder();">
SG112A
</label>
<label>
<input type="checkbox" value="sensairs8" onchange="updateDecoder();">
Sensair S8
</label>
<label>
<input type="checkbox" value="scd30" onchange="updateDecoder();">
SCD30
</label>
<label>
<input type="checkbox" value="bme280" onchange="updateDecoder();">
BME280
</label>
<label>
<input type="checkbox" value="sht21" onchange="updateDecoder();">
SHT21
</label>
<label>
<input type="checkbox" value="sps30" onchange="updateDecoder();">
SPS30
</label>
<label>
<input type="checkbox" value="hm330x" onchange="updateDecoder();">
HM330x
</label>
<label>
<input type="checkbox" value="brightness" onchange="updateDecoder();">
Brightness
</label>
<label>
<input type="checkbox" value="ds18b20" onchange="updateDecoder();">
DS18B20
</label>
</form>
<h3>Payload Decoder:</h3>
<pre id="pd">
</pre>
</div>
<a href="https://www.attno.de/">Back to attno.de Main Page</a>
<script>
var sensors = {
"mhz19c": [
{ "name": "co2ppm", "type": "UInt16", "factor": 1, "multi": false },
],
"sg112a": [
{ "name": "co2ppm", "type": "UInt16", "factor": 1, "multi": false },
],
"sensairs8": [
{ "name": "co2ppm", "type": "UInt16", "factor": 1, "multi": false },
],
"scd30": [
{ "name": "co2ppm", "type": "UInt16", "factor": 1, "multi": false },
{ "name": "temperature", "type": "Int16", "factor": 100, "multi": false },
{ "name": "humidity", "type": "UInt16", "factor": 100, "multi": false },
],
"bme280": [
{ "name": "temperature", "type": "Int32", "factor": 100, "multi": false },
{ "name": "humidity", "type": "Int32", "factor": 100, "multi": false },
{ "name": "pressure", "type": "Int32", "factor": 100, "multi": false },
],
"sht21": [
{ "name": "temperature", "type": "Int32", "factor": 100, "multi": false },
{ "name": "humidity", "type": "Int32", "factor": 100, "multi": false },
],
"sps30": [
{ "name": "pm1", "type": "UInt16", "factor": 1, "multi": false },
{ "name": "pm2_5", "type": "UInt16", "factor": 1, "multi": false },
{ "name": "pm4", "type": "UInt16", "factor": 1, "multi": false },
{ "name": "pm10", "type": "UInt16", "factor": 1, "multi": false },
{ "name": "typPMsize", "type": "UInt16", "factor": 1, "multi": false },
],
"hm330x": [
{ "name": "pm1", "type": "UInt16", "factor": 1, "multi": false },
{ "name": "pm2_5", "type": "UInt16", "factor": 1, "multi": false },
{ "name": "pm10", "type": "UInt16", "factor": 1, "multi": false },
],
"brightness": [
{ "name": "brightness", "type": "UInt16", "factor": 1, "multi": false },
],
"ds18b20": [
{ "name": "t", "type": "Int16", "factor": 100, "multi": true },
]
};
var typelen = {"Int32": "4", "Int16": "2", "UInt16": "2"};
function updateDecoder() {
var output = document.getElementById("pd");
output.innerHTML = "function bytesToInt16(bytes, start) {\n";
output.innerHTML += " var out = ((bytes[start]) | (bytes[start+1] << 8 ));\n";
output.innerHTML += " var sign = bytes[start+1] & (1 << 7);\n";
output.innerHTML += " if (sign)\n";
output.innerHTML += " out = 0xFFFF0000 | out;\n";
output.innerHTML += " return out;\n";
output.innerHTML += "}\n\n";
output.innerHTML += "function bytesToUInt16(bytes, start) {\n";
output.innerHTML += " return ((bytes[start]) | (bytes[start+1] << 8 ));\n";
output.innerHTML += "}\n\n";
output.innerHTML += "function bytesToInt32(bytes, start) {\n";
output.innerHTML += " return ((bytes[start]) | (bytes[start+1] << 8) | (bytes[start+2] << 16) | (bytes[start+3] << 24));\n";
output.innerHTML += "}\n\n";
output.innerHTML += "function decodeUplink(input) {\n";
output.innerHTML += " var decoded = {};\n";
output.innerHTML += " /* Battery Voltage, always enabled */\n";
output.innerHTML += " decoded.v = (input.bytes[0] * 20) / 1000.0;\n\n";
output.innerHTML += " var i = 1;\n";
var elements = document.getElementById("chooserForm").elements;
for (var i = 0, element; element = elements[i++];) {
if (element.type === "checkbox")
if (element.checked) {
sensors[element.value].forEach(sensor => {
if (sensor.multi) {
output.innerHTML += " var n = 1;\n";
output.innerHTML += " for (var j = i; j < input.bytes.length-1; j+=2) {\n";
if (sensor.factor > 1) {
output.innerHTML += " decoded['" + sensor.name + "' + n] = bytesTo" + sensor.type + " (input.bytes, j);\n";
} else {
output.innerHTML += " decoded['" + sensor.name + "' + n] = bytesTo" + sensor.type + " (input.bytes, j)/" + sensor.factor + ";\n";
}
output.innerHTML += " n++;\n";
output.innerHTML += " }\n";
} else {
if (sensor.factor > 1) {
output.innerHTML += " decoded." + sensor.name + " = bytesTo" + sensor.type + "(input.bytes, i)/" + sensor.factor + ";\n";
} else {
output.innerHTML += " decoded." + sensor.name + " = bytesTo" + sensor.type + "(input.bytes, i);\n";
}
output.innerHTML += " i += " + typelen[sensor.type] + ";\n";
}
});
}
}
output.innerHTML += "\n\n return {\n";
output.innerHTML += " data: decoded,\n";
output.innerHTML += " warnings: [],\n";
output.innerHTML += " errors: []\n";
output.innerHTML += " };\n";
output.innerHTML += "}\n";
}
</script>
</body>
</html>

View file

@ -1,52 +0,0 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:ATtiny3216]
platform = atmelmegaavr
board = ATtiny3216
framework = arduino
## Board Config ##
# You might want to set f_cpu to 5MHz (5000000L) to allow operation at lower Battery Voltage - Use "Burn Fuses" after changing f_cpu
# Be aware that some Functions (like WS2812B LED Support) will not work at 5 HMz
board_build.f_cpu = 8000000L
board_hardware.oscillator = internal
board_hardware.bod = disabled
## Debug Port Config ##
monitor_speed = 115200
monitor_port = /dev/ttyACM1
## LMIC Config and Debug Mode via Build Flags ##
#**************************************************************************************************************************
# Uncomment -D DEBUG to enable Serial Debugging. Parameters for the Serial Port are 115200 Baud 8n1
# By default the Firmware will output some messages about LoRa-Status and indicate sending
# The Macros DEBUG_PRINT() and DEBUG_PRINTLN() can be used to produce debug output depending on this define
# Please be aware that this will not work when a Serial Sensor like the SG112A/MH-Z19C or Sensair S8 ist used
#*************************************************************************************************************************/
build_flags =
-D CFG_eu868
-D CFG_sx1276_radio
-D DISABLE_PING
-D DISABLE_BEACONS
-D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS
# -D DEBUG
## Programmer Config (MicroUPDI) ##
upload_port = usb
upload_protocol = xplainedmini_updi
upload_flags =
-p$BOARD_MCU
-P$UPLOAD_PORT
-c$UPLOAD_PROTOCOL
## External Libraries ##
lib_deps =
mcci-catena/MCCI LoRaWAN LMIC library @ ^3.3.0

View file

@ -1,109 +0,0 @@
#ifndef CONFIG_H
#define CONFIG_H
// ALL EDITS BELOW THIS LINE!
/**************************************************************************************************************************
* Use a Single Color LED for Status Signaling
* At Startup the LED will blink twice to signal a sucessfull OTAA Join
*************************************************************************************************************************/
#define LED_PIN PIN_PA7
/**************************************************************************************************************************
* Enable WS2812B RGB LED Support for the CO2 Addon Board
* * First LED shows CO2-Level (green: <1000, yellow: 1000-1800, red: >=1800)
* * Second LED shows LoRa Status (yellow: Joining, green 1s: Joined, green 100ms: Sending, blue 250ms: Received Downlink)
* WS2812B_BRIGHT can be set to the desired brightness value for the LEDs (0=off, 255=brightest)
* Uncomment the 3 following lines to get the default behaviour
*************************************************************************************************************************/
// #define WS2812B_PIN PIN_PC1
// #define WS2812B_NUM 2
// #define WS2812B_BRIGHT 32
/**************************************************************************************************************************
* Enable Sending on Button Press, as well as CO2 Background Level Calibration by Pressing Button for 4s with MH-Z19C Addon
* The Button has to be connected to a pin that is capable of Fully Asynchronus Interrupts.
* For The ATTNode this means Pin PC2 if you don't want to block any other Interfaces
*************************************************************************************************************************/
// #define BTN_PIN PIN_PC2
/**************************************************************************************************************************
* External Battery Monitoring - If EXT_BAT_PIN is set to an analog pin of the ATTiny (e.g. PB4 or PB5), it will be used
* to read the Battery Voltage instead of the internal supply voltage. This can e.g. be used to monitor the voltage of an
* LiPo/LiIon cell connected to the Node via the PowerPack. The Devider should not put out more than 2.5V at the
* measurement point. Choose high resistor values (e.g. 2x 1MOhm for LiPo/LiIon) to minimize current drain through the
* resistors. The Send voltage will be measured voltage at the pin times EXT_BAT_RATIO.
*************************************************************************************************************************/
// #define EXT_BAT_PIN PIN_PB5
// #define EXT_BAT_RATIO 2
/**************************************************************************************************************************
* Number of active Sensors (used as long as HAS_NO_SENSOR is not enabled)
* Set to the correct number of enabled sensors below
* Not doing so will lead to unpredictable results or not work at all
* The default payload decoder will NOT WORK if more than one sensor is enabled, so you MUST define a specific
* decoder for this case. See the README.md for details
*************************************************************************************************************************/
#define NUM_SENSORS 1
/**************************************************************************************************************************
* Define which Sensors are installed
* Not all sensors can be used at the same time
* For example you can only have one sensor using serial UART
* If HAS_NO_SENSOR is selected all other activated sensor will be ignored
*************************************************************************************************************************/
#define HAS_NO_SENSOR
// #define HAS_MHZ19C
// #define HAS_SG112A
// #define HAS_SENSAIRS8
// #define HAS_SCD30
// #define HAS_BME280
// #define HAS_SHT21
// #define HAS_SPS30
// #define HAS_HM330x
// #define HAS_BRIGHTNESS
// #define HAS_DS18B20
/**************************************************************************************************************************
* Sensor Configuration
* Change according to your Needs
*************************************************************************************************************************/
// Pin for the OneWire Bus
#define DS18B20_PIN PIN_PC3
// A/D Resolution of the DS18B20 (9-12 Bit)
#define DS18B20_RES 12
// Connect a free pin on the ATTNode to the SET-Pin on the HM3301-Board
// Set HM330x_SLLEP_PIN to the choosen pin to enable the Sleep-Mode of the HM3301.
// Setting it to 0 disables Sleep Mode
#define HM330x_SLEEP_PIN 0
// LED connected between 2 pins, switched in reverse direction for brightness measurement
// BRIGHTNESS_LED_A -> LED Anode
// BRIGHTNESS_LED_C -> LED Cathode
#define BRIGHTNESS_LED_A PIN_PC0
#define BRIGHTNESS_LED_C PIN_PC1
/**************************************************************************************************************************
* How many minutes to sleep between Measuring/Sending
* Since this is a 2-byte value internally, intervals between 1 and 65536 are possible
* This is the default interval to use, which can be overwritten via DownLink. If an interval
* is set via DownLink it will be saved in EEPROM and the time specified here will no longer be used.
* Actual sleep Time is sleep_time*2*32 seconds due to the 32s sleep intervals of the ATTiny3216
*************************************************************************************************************************/
uint16_t sleep_time = 10;
/**************************************************************************************************************************
* LoRa Keys for OTAA Mode
* Please make sure to use the correct byte order when copying these from the TTN-Console!
* APPEUI and DEVEUI need to be inserted in LSB order
* APPKEY needs to be inserted in MSB order
* in some cases there is no APPEUI, for example when using Chirpstack. leave it set to all 0x00 in these cases
*************************************************************************************************************************/
static const u1_t PROGMEM APPEUI[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static const u1_t PROGMEM DEVEUI[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static const u1_t PROGMEM APPKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
// ALL EDITS ABOVE THIS LINE!
#endif

View file

@ -1,458 +0,0 @@
#include <Arduino.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <SPI.h>
#include <Wire.h>
#include <EEPROM.h>
#include <lmic.h>
#include <hal/hal.h>
// Keep Track of used EEPROM Addresses
#define ADDR_SLP 0 // Sleep Interval, 2 Bytes
// Include Config and Helpers
#include "config.h"
#include <debug.h>
#include <attsensor.h>
// Include All Sensors Activated in config.h
#ifndef HAS_NO_SENSOR
#ifdef HAS_MHZ19C
#include <MHZ19C.h>
#endif
#ifdef HAS_SG112A
#include <SG112A.h>
#endif
#ifdef HAS_SENSAIRS8
#include <SENSAIRS8.h>
#endif
#ifdef HAS_SCD30
#include <SCD30.h>
#endif
#ifdef HAS_BME280
#include <BME280.h>
#endif
#ifdef HAS_SHT21
#include <SHT21.h>
#endif
#ifdef HAS_SPS30
#include <SPS30.h>
#endif
#ifdef HAS_HM330x
#include <HM330x.h>
#endif
#ifdef HAS_BRIGHTNESS
#include <Brightness.h>
#endif
#ifdef HAS_DS18B20
#include <DS18B20.h>
#endif
#endif
// Define the blink function and BLINK_LED Macro depending
// on the definition of LED_PIN
#ifdef LED_PIN
void blink(uint8_t num) {
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, 0);
for (uint8_t i = 0; i < num * 2; i++) {
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
delay(100);
}
digitalWrite(LED_PIN, 0);
}
#define BLINK_LED(COUNT) blink(COUNT);
#else
#define BLINK_LED(COUNT)
#endif
// WS2812B RGB LEDs on the CO2 Addon Board
// Defines the Macro Function WS2812B_SETLED so we don't need #ifdefs everywhere
#ifdef WS2812B_PIN
#include <tinyNeoPixel_Static.h>
byte pixels[WS2812B_NUM * 3];
tinyNeoPixel leds = tinyNeoPixel(WS2812B_NUM, WS2812B_PIN, NEO_GRB, pixels);
#define WS2812B_SETLED(led,r,g,b) leds.setPixelColor(led,r,g,b); leds.show()
#define WS2812B_BLINK(led,r,g,b,ms) leds.setPixelColor(led,r,g,b); leds.show(); delay(ms); leds.setPixelColor(led,0,0,0); leds.show()
#else
#define WS2812B_SETLED(led,r,g,b)
#define WS2812B_BLINK(led,r,g,b,ms)
#endif
// Create Array for the Sensor Objects
#ifndef HAS_NO_SENSORS
AttSensor* sensors[NUM_SENSORS];
#endif
// Track Length of Payload (Depends on Active Sensors)
int payloadBytes = 1;
// Define some LMIC Callbacks and Variables
void os_getArtEui (u1_t* buf) {
memcpy_P(buf, APPEUI, 8);
}
void os_getDevEui (u1_t* buf) {
memcpy_P(buf, DEVEUI, 8);
}
void os_getDevKey (u1_t* buf) {
memcpy_P(buf, APPKEY, 16);
}
static osjob_t sendjob;
void do_send(osjob_t* j);
// Pin-Mapping for ATTNode v3
const lmic_pinmap lmic_pins = {
.nss = PIN_PA5,
.rxtx = LMIC_UNUSED_PIN,
.rst = LMIC_UNUSED_PIN,
.dio = {PIN_PA4, PIN_PA6, LMIC_UNUSED_PIN},
};
// List of unused Pins - will be disabled for Power Saving
#if defined DEBUG || defined HAS_SG112A || defined HAS_MHZ19C || defined HAS_SENSAIRS8
const int disabledPins[] = {PIN_PB5, PIN_PB4, PIN_PB1, PIN_PB0, PIN_PC3, PIN_PC2, PIN_PC1, PIN_PC0};
#else
const int disabledPins[] = {PIN_PB5, PIN_PB4, PIN_PB3, PIN_PB2, PIN_PB1, PIN_PB0, PIN_PC3, PIN_PC2, PIN_PC1, PIN_PC0};
#endif
// Helper variables and Interrupt Routine for Button
#ifdef BTN_PIN
volatile bool btn_pressed = 0;
volatile unsigned long btn_millis = 0;
// ISR Routine for Button
void btn_press() {
btn_pressed = 1;
btn_millis = millis();
delayMicroseconds(250000);
}
#endif
// Interrupt Routine for Sleep
ISR(RTC_PIT_vect)
{
/* Clear interrupt flag by writing '1' (required) */
RTC.PITINTFLAGS = RTC_PI_bm;
}
// Sleep Routine, Sleep for 32 Seconds
void sleep_32s() {
cli();
while (RTC.PITSTATUS > 0) {}
RTC.PITINTCTRL = RTC_PI_bm;
// 32 Seconds
RTC.PITCTRLA = RTC_PERIOD_CYC32768_gc | RTC_PITEN_bm;
while (RTC.PITSTATUS & RTC_CTRLBUSY_bm) {}
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sei();
sleep_cpu();
sleep_disable();
sei();
}
// LMIC Callback Handling
void onEvent(ev_t ev) {
switch (ev) {
case EV_JOINED:
// Disable LinkCheck
LMIC_setLinkCheckMode(0);
BLINK_LED(2);
#if WS2812B_NUM > 1
WS2812B_BLINK(1,0,127,0,1000);
#endif
DEBUG_PRINTLN("OTAA Join Succeeded");
break;
case EV_TXCOMPLETE:
// Check for Downlink
DEBUG_PRINTLN("LoRa Packet Sent");
#if WS2812B_NUM > 1
WS2812B_BLINK(1,0,127,0,1000);
#endif
if ((int)LMIC.dataLen > 0) {
// Check for Downlinks
// Function based in Ports:
// Port 1
// Set Sending Interval
// Port 2
// Do Calibration
switch((uint8_t)LMIC.frame[LMIC.dataBeg-1]) {
case 1:
if ((int)LMIC.dataLen == 2) {
// We got a Packet with the right size - lets assemble it into a uint16_t
DEBUG_PRINTLN("Received Downlink")
uint16_t tmpslp = (LMIC.frame[LMIC.dataBeg] << 8) | LMIC.frame[LMIC.dataBeg+1];
DEBUG_PRINT("Setting Sleep Time to: ");
DEBUG_PRINTLN(tmpslp);
sleep_time = tmpslp;
EEPROM.put(ADDR_SLP, tmpslp);
#if WS2812B_NUM > 1
WS2812B_BLINK(1,0,0,127,250);
#endif
}
break;
case 2:
for (uint8_t i=0; i<NUM_SENSORS; i++)
sensors[i]->calibrate();
BLINK_LED(3);
break;
}
}
// Got to sleep for specified Time
DEBUG_PRINT("Going to Sleep for ");
DEBUG_PRINT(sleep_time*64);
DEBUG_PRINTLN(" Seconds");
for (uint16_t i = 0; i < sleep_time*2; i++) {
// Cancel sleep Cycle if Button was Pressed
#ifdef BTN_PIN
if (btn_pressed && digitalRead(BTN_PIN) == HIGH) {
DEBUG_PRINTLN("Button Pressed, waking up");
i = sleep_time*2;
btn_pressed = 0;
} else {
#endif
sleep_32s();
#ifdef BTN_PIN
}
#endif
}
// Schedule Next Transmit
do_send(&sendjob);
break;
}
}
// Get Battery Voltage
#ifdef EXT_BAT_PIN
uint16_t readSupplyVoltage() {
pinMode(EXT_BAT_PIN, INPUT);
analogReference(INTERNAL2V5);
ADC0.CTRLD = ADC_INITDLY_DLY256_gc;
ADC0_CTRLB = ADC_SAMPNUM_ACC64_gc;
uint16_t reading = analogRead(EXT_BAT_PIN);
reading = reading/64;
DEBUG_PRINT("ADC Reading: ");
DEBUG_PRINTLN(reading);
reading = (int16_t)(reading * (2500/1024.0) * EXT_BAT_RATIO);
return reading;
}
#else
uint16_t readSupplyVoltage() { //returns value in millivolts to avoid floating point
uint16_t temp = 0;
analogReference(VDD);
VREF.CTRLA = VREF_ADC0REFSEL_1V5_gc;
ADC0.CTRLD = ADC_INITDLY_DLY256_gc;
ADC0_CTRLB = ADC_SAMPNUM_ACC64_gc;
uint16_t reading = analogRead(ADC_INTREF);
temp = reading / 64;
uint32_t intermediate = 1534500;
reading = 0;
reading = intermediate / temp;
return reading;
}
#endif
// Read Sensors and Send Data
void do_send(osjob_t* j) {
// Array of Bytes for the Payload
// Length is defined by the Enabled Sensors
char payload[payloadBytes];
if (LMIC.opmode & OP_TXRXPEND) {
// Wayt if LMIC is busy
delay(1);
} else {
// Track Current Position in Payload Array
uint8_t curByte = 0;
// Add Battery Voltage (0.2V Accuracy stored in 1 byte)
uint32_t batv = readSupplyVoltage();
payload[curByte] = (uint8_t)(batv / 20);
if (batv % 20 > 9)
payload[curByte] += 1;
curByte++;
#ifndef HAS_NO_SENSOR
// Put Sensor Readings into the Payload Array
for (int i=0; i < NUM_SENSORS; i++)
curByte = sensors[i]->getSensorData(payload, curByte);
// If CO2 Addon Boards with RGB-LEDS is installed, set LED according to the current CO2 Reading
#if defined WS2812B_PIN && (defined HAS_SG112A || defined HAS_MHZ19C || defined HAS_SENSAIRS8)
// CO2 PPM Levels and LED Colors
// < 1000 ppm green
// < 1800 ppm yellow
// > 1000 ppm red
// Get PPM from Payload:
uint16_t ppm = word(payload[2], payload[1]);
// Set WS2812B-LED accodring to PPM Value
if (ppm > 0 && ppm <= 1000) {
WS2812B_SETLED(0,0,127,0);
} else if (ppm > 1000 && ppm <= 1800) {
WS2812B_SETLED(0,127,127,0);
} else if (ppm > 1800) {
WS2812B_SETLED(0,127,0,0);
} else {
WS2812B_SETLED(0,0,0,0);
}
#endif // WS2812B
#endif // HAS_NO_SENSOR
// Queue Packet for Sending
DEBUG_PRINTLN("LoRa-Packet Queued");
LMIC_setTxData2(1, payload, sizeof(payload), 0);
}
}
void setup()
{
// Initialize Serial if Debug is enabled
#ifdef DEBUG
Serial.begin(115200);
// Wait 2 seconds for Monitor to connect
delay(2000);
#endif
// Initialize SPI and I2C
Wire.begin();
SPI.begin();
// Disable unused Pins (for power saving)
for (int i = 0; i < (sizeof(disabledPins) / sizeof(disabledPins[0])) - 1; i++)
pinMode(disabledPins[i], INPUT_PULLUP);
#ifdef EXT_BAT_PIN
#endif
// Setup WS2812B LEDs
#ifdef WS2812B_PIN
pinMode(WS2812B_PIN, OUTPUT);
leds.setBrightness(WS2812B_BRIGHT);
#endif
// Setup Button Interrupt
#ifdef BTN_PIN
pinMode(BTN_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BTN_PIN), btn_press, FALLING);
#endif
// Setup all Sensors and Calculate the Payload Length
// Order of the Sensors here is Order in the Payload
#ifndef HAS_NO_SENSOR
uint8_t i = 0;
#ifdef HAS_MHZ19C
sensors[i] = new MHZ19C();
i++;
#endif
#ifdef HAS_SG112A
sensors[i] = new SG112A();
i++;
#endif
#ifdef HAS_SENSAIRS8
sensors[i] = new SENSAIRS8();
i++;
#endif
#ifdef HAS_SCD30
sensors[i] = new SCD30();
i++;
#endif
#ifdef HAS_BME280
sensors[i] = new BME280();
i++;
#endif
#ifdef HAS_SHT21
sensors[i] = new SHT21();
i++;
#endif
#ifdef HAS_SPS30
sensors[i] = new SPS30();
i++;
#endif
#ifdef HAS_HM330x
sensors[i] = new HM330x(HM330x_SLEEP_PIN);
i++;
#endif
#ifdef HAS_BRIGHTNESS
sensors[i] = new Brightness(BRIGHTNESS_LED_A, BRIGHTNESS_LED_C);
i++;
#endif
#ifdef HAS_DS18B20
sensors[i] = new DS18B20(DS18B20_PIN, DS18B20_RES);
i++;
#endif
// Initialize all Sensors
for (i = 0; i < NUM_SENSORS; i++) {
sensors[i]->initialize();
payloadBytes += sensors[i]->numBytes();
}
#endif
// Setup RTC
while (RTC.STATUS > 0) {}
RTC.CLKSEL = RTC_CLKSEL_INT1K_gc;
while (RTC.PITSTATUS > 0) {}
// Setup LMIC
DEBUG_PRINT("Initializing LMIC...")
os_init();
LMIC_reset(); // Reset LMIC state and cancel all queued transmissions
LMIC_setClockError(MAX_CLOCK_ERROR * 10 / 100); // Compensate for Clock Skew
LMIC.dn2Dr = DR_SF9; // Downlink Band
LMIC_setDrTxpow(DR_SF7, 14); // Default to SF7
DEBUG_PRINTLN("Done");
// Check if Sending Interval is set in EEPROM
// if we get 65535 (0xFFFF) EEPROM was not written
uint16_t tmpsleep = 0;
EEPROM.get(ADDR_SLP, tmpsleep);
if (tmpsleep < 65535) {
DEBUG_PRINT("Setting Sleep Time from EEPROM to ");
DEBUG_PRINTLN(tmpsleep);
sleep_time = tmpsleep;
}
DEBUG_PRINTLN("Setup Finished");
// Set WS2812B to Yellow for "Joining" (if enabled)
#if WS2812B_NUM > 1
WS2812B_SETLED(1,127,127,0);
#endif
// Schedule First Send (Triggers OTAA Join as well)
do_send(&sendjob);
}
void loop()
{
// Handle long Button Press for Calibration with MH-Z19C Sensor
#ifdef BTN_PIN
if (digitalRead(BTN_PIN) == LOW) {
// Press Button longer than 4 Seconds -> Start Sensor Calibration Routine (if applicable)
unsigned long loop_millis = millis();
if ((unsigned long)(loop_millis - btn_millis) >= 4000) {
WS2812B_SETLED(0,153,0,153);
BLINK_LED(3);
delay(1000);
for (uint8_t i=0; i<NUM_SENSORS; i++)
sensors[i]->calibrate();
BLINK_LED(1);
WS2812B_SETLED(0,0,0,0);
} else {
delay(500);
}
} else {
#endif
// Only Run the LMIC loop here. Actual Sending Code is in do_send()
os_runloop_once();
#ifdef BTN_PIN
}
#endif
}