Compare commits
No commits in common. "master" and "arduinoide" have entirely different histories.
master
...
arduinoide
42 changed files with 316 additions and 5434 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,6 +1 @@
|
|||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
src/config.h
|
||||
config.h
|
||||
|
|
10
.vscode/extensions.json
vendored
10
.vscode/extensions.json
vendored
|
@ -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
24
LICENSE
|
@ -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.
|
29
README.md
29
README.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
|
@ -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
|
|
@ -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)));
|
||||
}
|
|
@ -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
|
32
firmware/config.h.example
Normal file
32
firmware/config.h.example
Normal 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 };
|
|
@ -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
|
242
firmware/firmware.ino
Normal file
242
firmware/firmware.ino
Normal 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
BIN
ide_settings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 155 KiB |
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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];
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
46
lib/README
46
lib/README
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
458
src/main.cpp
458
src/main.cpp
|
@ -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
|
||||
}
|
Loading…
Reference in a new issue