├── ADCDMA.cpp ├── ADCDMA.h ├── BQ27441_Definitions.h ├── ClosedCube_SHT31D.cpp ├── ClosedCube_SHT31D.h ├── LICENSE ├── MMA8452-xlero-OSBH.cpp ├── MMA8452-xlero-OSBH.h ├── README.md ├── SparkFunBQ27441.cpp ├── SparkFunBQ27441.h ├── binary.h ├── downsampler.cpp ├── downsampler.h ├── filter.cpp ├── filter.h ├── fw19_OS.bin ├── geolocate.cpp ├── google-maps-device-locator.cpp ├── google-maps-device-locator.h ├── main_beta_comp.cpp ├── params.h ├── particle.ignore └── softap_bb.h /ADCDMA.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | Photon analog input in DMA mode 3 | 4 | ADCDMA.h 5 | ADCDMA Library Header File 6 | Modified by Peter Naylor: Jan 5, 2018 7 | 8 | derived from: Rickkas7 @ Particle.io 9 | https://github.com/rickkas7/photonAudio 10 | 11 | This file prototypes the ADCDMA class, implemented in ADCDMA.cpp. 12 | 13 | Development environment specifics: 14 | ParticleCLI 15 | Particle Photon 16 | 17 | Distributed as-is; no warranty is given. 18 | ******************************************************************************/ 19 | 20 | #include "ADCDMA.h" 21 | 22 | 23 | // Helpful post: 24 | // https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy%2est%2ecom%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex%5fmx%5fstm32%2fstm32f207%20ADC%2bTIMER%2bDMA%20%20Poor%20Peripheral%20Library%20Examples&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B¤tviews=6249 25 | 26 | ADCDMA::ADCDMA(int pin, uint16_t *buf, size_t bufSize) : pin(pin), buf(buf), bufSize(bufSize) { 27 | } 28 | 29 | ADCDMA::~ADCDMA() { 30 | 31 | } 32 | 33 | void ADCDMA::start(size_t freqHZ) { 34 | 35 | // Using Dual ADC Regular Simultaneous DMA Mode 1 36 | 37 | // Using Timer3. To change timers, make sure you edit all of: 38 | // RCC_APB1Periph_TIM3, TIM3, ADC_ExternalTrigConv_T3_TRGO 39 | 40 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); 41 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); 42 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE); 43 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); 44 | 45 | // Set the pin as analog input 46 | // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; 47 | // GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 48 | HAL_Pin_Mode(pin, AN_INPUT); 49 | 50 | // Enable the DMA Stream IRQ Channel 51 | NVIC_InitTypeDef NVIC_InitStructure; 52 | NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn; 53 | NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 54 | NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 55 | NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 56 | NVIC_Init(&NVIC_InitStructure); 57 | 58 | // 60000000UL = 60 MHz Timer Clock = HCLK / 2 59 | // Even low audio rates like 8000 Hz will fit in a 16-bit counter with no prescaler (period = 7500) 60 | TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 61 | TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); 62 | TIM_TimeBaseStructure.TIM_Period = (60000000UL / freqHZ) - 1; 63 | TIM_TimeBaseStructure.TIM_Prescaler = 0; 64 | TIM_TimeBaseStructure.TIM_ClockDivision = 0; 65 | TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 66 | TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 67 | TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update); // ADC_ExternalTrigConv_T3_TRGO 68 | TIM_Cmd(TIM3, ENABLE); 69 | 70 | ADC_CommonInitTypeDef ADC_CommonInitStructure; 71 | ADC_InitTypeDef ADC_InitStructure; 72 | DMA_InitTypeDef DMA_InitStructure; 73 | 74 | // DMA2 Stream0 channel0 configuration 75 | DMA_InitStructure.DMA_Channel = DMA_Channel_0; 76 | DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)buf; 77 | DMA_InitStructure.DMA_PeripheralBaseAddr = 0x40012308; // CDR_ADDRESS; Packed ADC1, ADC2; 78 | DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; 79 | DMA_InitStructure.DMA_BufferSize = bufSize; 80 | DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 81 | DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; 82 | DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; 83 | DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 84 | DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; 85 | DMA_InitStructure.DMA_Priority = DMA_Priority_High; 86 | DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; 87 | DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; 88 | DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; 89 | DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; 90 | DMA_Init(DMA2_Stream0, &DMA_InitStructure); 91 | 92 | // Don't enable DMA Stream Half / Transfer Complete interrupt 93 | // Since we want to write out of loop anyway, there's no real advantage to using the interrupt, and as 94 | // far as I can tell, you can't set the interrupt handler for DMA2_Stream0 without modifying 95 | // system firmware because there's no built-in handler for it. 96 | // DMA_ITConfig(DMA2_Stream0, DMA_IT_TC | DMA_IT_HT, ENABLE); 97 | 98 | DMA_Cmd(DMA2_Stream0, ENABLE); 99 | 100 | // ADC Common Init 101 | ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult; 102 | ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2; 103 | ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1; 104 | ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; 105 | ADC_CommonInit(&ADC_CommonInitStructure); 106 | 107 | // ADC1 configuration 108 | ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; 109 | ADC_InitStructure.ADC_ScanConvMode = DISABLE; 110 | ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; 111 | ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; 112 | ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; 113 | ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Left; 114 | ADC_InitStructure.ADC_NbrOfConversion = 1; 115 | ADC_Init(ADC1, &ADC_InitStructure); 116 | 117 | // ADC2 configuration - same 118 | ADC_Init(ADC2, &ADC_InitStructure); 119 | 120 | // 121 | ADC_RegularChannelConfig(ADC1, PIN_MAP[pin].adc_channel, 1, ADC_SampleTime_15Cycles); 122 | ADC_RegularChannelConfig(ADC2, PIN_MAP[pin].adc_channel, 1, ADC_SampleTime_15Cycles); 123 | 124 | // Enable DMA request after last transfer (Multi-ADC mode) 125 | ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE); 126 | 127 | // Enable ADCs 128 | ADC_Cmd(ADC1, ENABLE); 129 | ADC_Cmd(ADC2, ENABLE); 130 | 131 | ADC_SoftwareStartConv(ADC1); 132 | } 133 | 134 | void ADCDMA::stop() { 135 | // Stop the ADC 136 | ADC_Cmd(ADC1, DISABLE); 137 | ADC_Cmd(ADC2, DISABLE); 138 | 139 | DMA_Cmd(DMA2_Stream0, DISABLE); 140 | 141 | // Stop the timer 142 | TIM_Cmd(TIM3, DISABLE); 143 | } 144 | 145 | 146 | // End ADCDMA 147 | -------------------------------------------------------------------------------- /ADCDMA.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | Photon analog input in DMA mode 3 | 4 | ADCDMA.h 5 | ADCDMA Library Header File 6 | Modified by Peter Naylor: Jan 5, 2018 7 | 8 | derived from: Rickkas7 @ Particle.io 9 | https://github.com/rickkas7/photonAudio 10 | 11 | This file prototypes the ADCDMA class, implemented in ADCDMA.cpp. 12 | 13 | Development environment specifics: 14 | ParticleCLI 15 | Particle Photon 16 | 17 | Distributed as-is; no warranty is given. 18 | ******************************************************************************/ 19 | 20 | #ifndef ADCDMA_h 21 | #define ADCDMA_h 22 | 23 | 24 | #include "Particle.h" 25 | #include "adc_hal.h" 26 | #include "gpio_hal.h" 27 | #include "pinmap_hal.h" 28 | #include "pinmap_impl.h" 29 | 30 | 31 | // ADCDMA - Class to use Photon ADC in DMA Mode 32 | class ADCDMA { 33 | public: 34 | ADCDMA(int pin, uint16_t *buf, size_t bufSize); 35 | virtual ~ADCDMA(); 36 | 37 | void start(size_t freqHZ); 38 | void stop(); 39 | 40 | private: 41 | int pin; 42 | uint16_t *buf; 43 | size_t bufSize; 44 | }; 45 | 46 | #endif -------------------------------------------------------------------------------- /BQ27441_Definitions.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | BQ27441_Definitions.h 3 | BQ27441 LiPo Fuel Gauge Definitions 4 | Jim Lindblom @ SparkFun Electronics 5 | May 9, 2016 6 | https://github.com/sparkfun/SparkFun_BQ27441_Arduino_Library 7 | 8 | BQ27441 hardware constants, register addresses, and bit positions. 9 | 10 | Hardware Resources: 11 | - Arduino Development Board 12 | - SparkFun Battery Babysitter 13 | 14 | Development environment specifics: 15 | Arduino 1.6.7 16 | SparkFun Battery Babysitter v1.0 17 | Arduino Uno (any 'duino should do) 18 | ******************************************************************************/ 19 | #define BQ72441_I2C_ADDRESS 0x55 // Default I2C address of the BQ27441-G1A 20 | 21 | /////////////////////// 22 | // General Constants // 23 | /////////////////////// 24 | #define BQ27441_UNSEAL_KEY 0x8000 // Secret code to unseal the BQ27441-G1A 25 | #define BQ27441_DEVICE_ID 0x0421 // Default device ID 26 | 27 | /////////////////////// 28 | // Standard Commands // 29 | /////////////////////// 30 | // The fuel gauge uses a series of 2-byte standard commands to enable system 31 | // reading and writing of battery information. Each command has an associated 32 | // sequential command-code pair. 33 | #define BQ27441_COMMAND_CONTROL 0x00 // Control() 34 | #define BQ27441_COMMAND_TEMP 0x02 // Temperature() 35 | #define BQ27441_COMMAND_VOLTAGE 0x04 // Voltage() 36 | #define BQ27441_COMMAND_FLAGS 0x06 // Flags() 37 | #define BQ27441_COMMAND_NOM_CAPACITY 0x08 // NominalAvailableCapacity() 38 | #define BQ27441_COMMAND_AVAIL_CAPACITY 0x0A // FullAvailableCapacity() 39 | #define BQ27441_COMMAND_REM_CAPACITY 0x0C // RemainingCapacity() 40 | #define BQ27441_COMMAND_FULL_CAPACITY 0x0E // FullChargeCapacity() 41 | #define BQ27441_COMMAND_AVG_CURRENT 0x10 // AverageCurrent() 42 | #define BQ27441_COMMAND_STDBY_CURRENT 0x12 // StandbyCurrent() 43 | #define BQ27441_COMMAND_MAX_CURRENT 0x14 // MaxLoadCurrent() 44 | #define BQ27441_COMMAND_AVG_POWER 0x18 // AveragePower() 45 | #define BQ27441_COMMAND_SOC 0x1C // StateOfCharge() 46 | #define BQ27441_COMMAND_INT_TEMP 0x1E // InternalTemperature() 47 | #define BQ27441_COMMAND_SOH 0x20 // StateOfHealth() 48 | #define BQ27441_COMMAND_REM_CAP_UNFL 0x28 // RemainingCapacityUnfiltered() 49 | #define BQ27441_COMMAND_REM_CAP_FIL 0x2A // RemainingCapacityFiltered() 50 | #define BQ27441_COMMAND_FULL_CAP_UNFL 0x2C // FullChargeCapacityUnfiltered() 51 | #define BQ27441_COMMAND_FULL_CAP_FIL 0x2E // FullChargeCapacityFiltered() 52 | #define BQ27441_COMMAND_SOC_UNFL 0x30 // StateOfChargeUnfiltered() 53 | 54 | ////////////////////////// 55 | // Control Sub-commands // 56 | ////////////////////////// 57 | // Issuing a Control() command requires a subsequent 2-byte subcommand. These 58 | // additional bytes specify the particular control function desired. The 59 | // Control() command allows the system to control specific features of the fuel 60 | // gauge during normal operation and additional features when the device is in 61 | // different access modes. 62 | #define BQ27441_CONTROL_STATUS 0x00 63 | #define BQ27441_CONTROL_DEVICE_TYPE 0x01 64 | #define BQ27441_CONTROL_FW_VERSION 0x02 65 | #define BQ27441_CONTROL_DM_CODE 0x04 66 | #define BQ27441_CONTROL_PREV_MACWRITE 0x07 67 | #define BQ27441_CONTROL_CHEM_ID 0x08 68 | #define BQ27441_CONTROL_BAT_INSERT 0x0C 69 | #define BQ27441_CONTROL_BAT_REMOVE 0x0D 70 | #define BQ27441_CONTROL_SET_HIBERNATE 0x11 71 | #define BQ27441_CONTROL_CLEAR_HIBERNATE 0x12 72 | #define BQ27441_CONTROL_SET_CFGUPDATE 0x13 73 | #define BQ27441_CONTROL_SHUTDOWN_ENABLE 0x1B 74 | #define BQ27441_CONTROL_SHUTDOWN 0x1C 75 | #define BQ27441_CONTROL_SEALED 0x20 76 | #define BQ27441_CONTROL_PULSE_SOC_INT 0x23 77 | #define BQ27441_CONTROL_RESET 0x41 78 | #define BQ27441_CONTROL_SOFT_RESET 0x42 79 | #define BQ27441_CONTROL_EXIT_CFGUPDATE 0x43 80 | #define BQ27441_CONTROL_EXIT_RESIM 0x44 81 | 82 | /////////////////////////////////////////// 83 | // Control Status Word - Bit Definitions // 84 | /////////////////////////////////////////// 85 | // Bit positions for the 16-bit data of CONTROL_STATUS. 86 | // CONTROL_STATUS instructs the fuel gauge to return status information to 87 | // Control() addresses 0x00 and 0x01. The read-only status word contains status 88 | // bits that are set or cleared either automatically as conditions warrant or 89 | // through using specified subcommands. 90 | #define BQ27441_STATUS_SHUTDOWNEN (1<<15) 91 | #define BQ27441_STATUS_WDRESET (1<<14) 92 | #define BQ27441_STATUS_SS (1<<13) 93 | #define BQ27441_STATUS_CALMODE (1<<12) 94 | #define BQ27441_STATUS_CCA (1<<11) 95 | #define BQ27441_STATUS_BCA (1<<10) 96 | #define BQ27441_STATUS_QMAX_UP (1<<9) 97 | #define BQ27441_STATUS_RES_UP (1<<8) 98 | #define BQ27441_STATUS_INITCOMP (1<<7) 99 | #define BQ27441_STATUS_HIBERNATE (1<<6) 100 | #define BQ27441_STATUS_SLEEP (1<<4) 101 | #define BQ27441_STATUS_LDMD (1<<3) 102 | #define BQ27441_STATUS_RUP_DIS (1<<2) 103 | #define BQ27441_STATUS_VOK (1<<1) 104 | 105 | //////////////////////////////////// 106 | // Flag Command - Bit Definitions // 107 | //////////////////////////////////// 108 | // Bit positions for the 16-bit data of Flags() 109 | // This read-word function returns the contents of the fuel gauging status 110 | // register, depicting the current operating status. 111 | #define BQ27441_FLAG_OT (1<<15) 112 | #define BQ27441_FLAG_UT (1<<14) 113 | #define BQ27441_FLAG_FC (1<<9) 114 | #define BQ27441_FLAG_CHG (1<<8) 115 | #define BQ27441_FLAG_OCVTAKEN (1<<7) 116 | #define BQ27441_FLAG_ITPOR (1<<5) 117 | #define BQ27441_FLAG_CFGUPMODE (1<<4) 118 | #define BQ27441_FLAG_BAT_DET (1<<3) 119 | #define BQ27441_FLAG_SOC1 (1<<2) 120 | #define BQ27441_FLAG_SOCF (1<<1) 121 | #define BQ27441_FLAG_DSG (1<<0) 122 | 123 | //////////////////////////// 124 | // Extended Data Commands // 125 | //////////////////////////// 126 | // Extended data commands offer additional functionality beyond the standard 127 | // set of commands. They are used in the same manner; however, unlike standard 128 | // commands, extended commands are not limited to 2-byte words. 129 | #define BQ27441_EXTENDED_OPCONFIG 0x3A // OpConfig() 130 | #define BQ27441_EXTENDED_CAPACITY 0x3C // DesignCapacity() 131 | #define BQ27441_EXTENDED_DATACLASS 0x3E // DataClass() 132 | #define BQ27441_EXTENDED_DATABLOCK 0x3F // DataBlock() 133 | #define BQ27441_EXTENDED_BLOCKDATA 0x40 // BlockData() 134 | #define BQ27441_EXTENDED_CHECKSUM 0x60 // BlockDataCheckSum() 135 | #define BQ27441_EXTENDED_CONTROL 0x61 // BlockDataControl() 136 | 137 | //////////////////////////////////////// 138 | // Configuration Class, Subclass ID's // 139 | //////////////////////////////////////// 140 | // To access a subclass of the extended data, set the DataClass() function 141 | // with one of these values. 142 | // Configuration Classes 143 | #define BQ27441_ID_SAFETY 2 // Safety 144 | #define BQ27441_ID_CHG_TERMINATION 36 // Charge Termination 145 | #define BQ27441_ID_CONFIG_DATA 48 // Data 146 | #define BQ27441_ID_DISCHARGE 49 // Discharge 147 | #define BQ27441_ID_REGISTERS 64 // Registers 148 | #define BQ27441_ID_POWER 68 // Power 149 | // Gas Gauging Classes 150 | #define BQ27441_ID_IT_CFG 80 // IT Cfg 151 | #define BQ27441_ID_CURRENT_THRESH 81 // Current Thresholds 152 | #define BQ27441_ID_STATE 82 // State 153 | // Ra Tables Classes 154 | #define BQ27441_ID_R_A_RAM 89 // R_a RAM 155 | // Calibration Classes 156 | #define BQ27441_ID_CALIB_DATA 104 // Data 157 | #define BQ27441_ID_CC_CAL 105 // CC Cal 158 | #define BQ27441_ID_CURRENT 107 // Current 159 | // Security Classes 160 | #define BQ27441_ID_CODES 112 // Codes 161 | 162 | ///////////////////////////////////////// 163 | // OpConfig Register - Bit Definitions // 164 | ///////////////////////////////////////// 165 | // Bit positions of the OpConfig Register 166 | #define BQ27441_OPCONFIG_BIE (1<<13) 167 | #define BQ27441_OPCONFIG_BI_PU_EN (1<<12) 168 | #define BQ27441_OPCONFIG_GPIOPOL (1<<11) 169 | #define BQ27441_OPCONFIG_SLEEP (1<<5) 170 | #define BQ27441_OPCONFIG_RMFCC (1<<4) 171 | #define BQ27441_OPCONFIG_BATLOWEN (1<<2) 172 | #define BQ27441_OPCONFIG_TEMPS (1<<0) -------------------------------------------------------------------------------- /ClosedCube_SHT31D.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Particle Photon SHT-3X-DIS Library 4 | 5 | ported from 6 | 7 | Arduino Library for Sensirion SHT3X-DIS Digital Humidity & Temperature Sensors 8 | Written by AA 9 | --- 10 | 11 | The MIT License (MIT) 12 | 13 | Copyright (c) 2015-2016 ClosedCube Limited 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | THE SOFTWARE. 32 | 33 | */ 34 | #include 35 | #include "ClosedCube_SHT31D.h" 36 | 37 | using namespace SHT31D_CC; 38 | 39 | ClosedCube_SHT31D::ClosedCube_SHT31D() 40 | { 41 | } 42 | 43 | SHT31D_ErrorCode ClosedCube_SHT31D::begin(uint8_t address) { 44 | SHT31D_ErrorCode error = NO_ERROR; 45 | _address = address; 46 | Wire.begin(); 47 | 48 | return error; 49 | } 50 | 51 | SHT31D ClosedCube_SHT31D::periodicFetchData() 52 | { 53 | SHT31D_ErrorCode error = writeCommand(CMD_FETCH_DATA); 54 | if (error == NO_ERROR) 55 | return readTemperatureAndHumidity(); 56 | else 57 | returnError(error); 58 | } 59 | 60 | SHT31D_ErrorCode ClosedCube_SHT31D::periodicStop() { 61 | return writeCommand(CMD_STOP_PERIODIC); 62 | } 63 | 64 | SHT31D_ErrorCode ClosedCube_SHT31D::periodicStart(SHT31D_Repeatability repeatability, SHT31D_Frequency frequency) 65 | { 66 | SHT31D_ErrorCode error; 67 | 68 | switch (repeatability) 69 | { 70 | case REPEATABILITY_LOW: 71 | switch (frequency) 72 | { 73 | case FREQUENCY_HZ5: 74 | error = writeCommand(CMD_PERIODIC_HALF_L); 75 | break; 76 | case FREQUENCY_1HZ: 77 | error = writeCommand(CMD_PERIODIC_1_L); 78 | break; 79 | case FREQUENCY_2HZ: 80 | error = writeCommand(CMD_PERIODIC_2_L); 81 | break; 82 | case FREQUENCY_4HZ: 83 | error = writeCommand(CMD_PERIODIC_4_L); 84 | break; 85 | case FREQUENCY_10HZ: 86 | error = writeCommand(CMD_PERIODIC_10_L); 87 | break; 88 | default: 89 | error = PARAM_WRONG_FREQUENCY; 90 | break; 91 | } 92 | break; 93 | case REPEATABILITY_MEDIUM: 94 | switch (frequency) 95 | { 96 | case FREQUENCY_HZ5: 97 | error = writeCommand(CMD_PERIODIC_HALF_M); 98 | break; 99 | case FREQUENCY_1HZ: 100 | error = writeCommand(CMD_PERIODIC_1_M); 101 | break; 102 | case FREQUENCY_2HZ: 103 | error = writeCommand(CMD_PERIODIC_2_M); 104 | break; 105 | case FREQUENCY_4HZ: 106 | error = writeCommand(CMD_PERIODIC_4_M); 107 | break; 108 | case FREQUENCY_10HZ: 109 | error = writeCommand(CMD_PERIODIC_10_M); 110 | break; 111 | default: 112 | error = PARAM_WRONG_FREQUENCY; 113 | break; 114 | } 115 | break; 116 | 117 | case REPEATABILITY_HIGH: 118 | switch (frequency) 119 | { 120 | case FREQUENCY_HZ5: 121 | error = writeCommand(CMD_PERIODIC_HALF_H); 122 | break; 123 | case FREQUENCY_1HZ: 124 | error = writeCommand(CMD_PERIODIC_1_H); 125 | break; 126 | case FREQUENCY_2HZ: 127 | error = writeCommand(CMD_PERIODIC_2_H); 128 | break; 129 | case FREQUENCY_4HZ: 130 | error = writeCommand(CMD_PERIODIC_4_H); 131 | break; 132 | case FREQUENCY_10HZ: 133 | error = writeCommand(CMD_PERIODIC_10_H); 134 | break; 135 | default: 136 | error = PARAM_WRONG_FREQUENCY; 137 | break; 138 | } 139 | break; 140 | default: 141 | error = PARAM_WRONG_REPEATABILITY; 142 | break; 143 | } 144 | 145 | return error; 146 | } 147 | 148 | SHT31D ClosedCube_SHT31D::readTempAndHumidity(SHT31D_Repeatability repeatability, SHT31D_Mode mode, uint8_t timeout) 149 | { 150 | SHT31D result; 151 | 152 | switch (mode) { 153 | case MODE_CLOCK_STRETCH: 154 | result = readTempAndHumidityClockStretch(repeatability); 155 | break; 156 | case MODE_POLLING: 157 | result = readTempAndHumidityPolling(repeatability, timeout); 158 | break; 159 | default: 160 | result = returnError(PARAM_WRONG_MODE); 161 | break; 162 | } 163 | 164 | return result; 165 | } 166 | 167 | 168 | SHT31D ClosedCube_SHT31D::readTempAndHumidityClockStretch(SHT31D_Repeatability repeatability) 169 | { 170 | SHT31D_ErrorCode error = NO_ERROR; 171 | SHT31D_Commands command; 172 | 173 | switch (repeatability) 174 | { 175 | case REPEATABILITY_LOW: 176 | error = writeCommand(CMD_CLOCK_STRETCH_L); 177 | break; 178 | case REPEATABILITY_MEDIUM: 179 | error = writeCommand(CMD_CLOCK_STRETCH_M); 180 | break; 181 | case REPEATABILITY_HIGH: 182 | error = writeCommand(CMD_CLOCK_STRETCH_H); 183 | break; 184 | default: 185 | error = PARAM_WRONG_REPEATABILITY; 186 | break; 187 | } 188 | 189 | if (error == NO_ERROR) { 190 | return readTemperatureAndHumidity(); 191 | } 192 | else { 193 | return returnError(error); 194 | } 195 | 196 | } 197 | 198 | 199 | SHT31D ClosedCube_SHT31D::readTempAndHumidityPolling(SHT31D_Repeatability repeatability, uint8_t timeout) 200 | { 201 | SHT31D_ErrorCode error = NO_ERROR; 202 | SHT31D_Commands command; 203 | 204 | switch (repeatability) 205 | { 206 | case REPEATABILITY_LOW: 207 | error = writeCommand(CMD_POLLING_L); 208 | break; 209 | case REPEATABILITY_MEDIUM: 210 | error = writeCommand(CMD_POLLING_M); 211 | break; 212 | case REPEATABILITY_HIGH: 213 | error = writeCommand(CMD_POLLING_H); 214 | break; 215 | default: 216 | error = PARAM_WRONG_REPEATABILITY; 217 | break; 218 | } 219 | 220 | if (error == NO_ERROR) { 221 | return readTemperatureAndHumidity(); 222 | } 223 | else { 224 | return returnError(error); 225 | } 226 | 227 | } 228 | 229 | SHT31D ClosedCube_SHT31D::readAlertHighSet() { 230 | return readAlertData(CMD_READ_ALR_LIMIT_HS); 231 | } 232 | 233 | SHT31D ClosedCube_SHT31D::readAlertHighClear() { 234 | return readAlertData(CMD_READ_ALR_LIMIT_HC); 235 | } 236 | 237 | SHT31D ClosedCube_SHT31D::readAlertLowSet() { 238 | return readAlertData(CMD_READ_ALR_LIMIT_LS); 239 | } 240 | 241 | SHT31D ClosedCube_SHT31D::readAlertLowClear() { 242 | return readAlertData(CMD_READ_ALR_LIMIT_LC); 243 | } 244 | 245 | 246 | SHT31D_ErrorCode ClosedCube_SHT31D::writeAlertHigh(float temperatureSet, float temperatureClear, float humiditySet, float humidityClear) { 247 | SHT31D_ErrorCode error = writeAlertData(CMD_WRITE_ALR_LIMIT_HS, temperatureSet, humiditySet); 248 | if (error == NO_ERROR) 249 | error = writeAlertData(CMD_WRITE_ALR_LIMIT_HC, temperatureClear, humidityClear); 250 | 251 | return error; 252 | } 253 | 254 | SHT31D_ErrorCode ClosedCube_SHT31D::writeAlertLow(float temperatureClear, float temperatureSet, float humidityClear, float humiditySet) { 255 | SHT31D_ErrorCode error = writeAlertData(CMD_WRITE_ALR_LIMIT_LS, temperatureSet, humiditySet); 256 | if (error == NO_ERROR) 257 | writeAlertData(CMD_WRITE_ALR_LIMIT_LC, temperatureClear, humidityClear); 258 | 259 | return error; 260 | } 261 | 262 | SHT31D_ErrorCode ClosedCube_SHT31D::writeAlertData(SHT31D_Commands command, float temperature, float humidity) 263 | { 264 | SHT31D_ErrorCode error; 265 | 266 | if ((humidity < 0.0) || (humidity > 100.0) || (temperature < -40.0) || (temperature > 125.0)) 267 | { 268 | error = PARAM_WRONG_ALERT; 269 | } 270 | else 271 | { 272 | uint16_t rawTemperature = calculateRaWTemperature(temperature); 273 | uint16_t rawHumidity = calculateRawHumidity(humidity); 274 | uint16_t data = (rawHumidity & 0xFE00) | ((rawTemperature >> 7) & 0x001FF); 275 | 276 | uint8_t buf[2]; 277 | buf[0] = data >> 8; 278 | buf[1] = data & 0xFF; 279 | 280 | uint8_t checksum = calculateCrc(buf); 281 | 282 | Wire.beginTransmission(_address); 283 | Wire.write(command >> 8); 284 | Wire.write(command & 0xFF); 285 | Wire.write(buf[0]); 286 | Wire.write(buf[1]); 287 | Wire.write(checksum); 288 | return (SHT31D_ErrorCode)(-10 * Wire.endTransmission()); 289 | } 290 | 291 | return error; 292 | } 293 | 294 | 295 | SHT31D_ErrorCode ClosedCube_SHT31D::writeCommand(SHT31D_Commands command) 296 | { 297 | Wire.beginTransmission(_address); 298 | Wire.write(command >> 8); 299 | Wire.write(command & 0xFF); 300 | return (SHT31D_ErrorCode)(-10 * Wire.endTransmission()); 301 | } 302 | 303 | SHT31D_ErrorCode ClosedCube_SHT31D::softReset() { 304 | return writeCommand(CMD_SOFT_RESET); 305 | } 306 | 307 | SHT31D_ErrorCode ClosedCube_SHT31D::generalCallReset() { 308 | Wire.beginTransmission(0x0); 309 | Wire.write(0x06); 310 | return (SHT31D_ErrorCode)(-10 * Wire.endTransmission()); 311 | } 312 | 313 | SHT31D_ErrorCode ClosedCube_SHT31D::heaterEnable() { 314 | return writeCommand(CMD_HEATER_ENABLE); 315 | } 316 | 317 | SHT31D_ErrorCode ClosedCube_SHT31D::heaterDisable() { 318 | return writeCommand(CMD_HEATER_DISABLE); 319 | } 320 | 321 | SHT31D_ErrorCode ClosedCube_SHT31D::artEnable() { 322 | return writeCommand(CMD_ART); 323 | } 324 | 325 | 326 | uint32_t ClosedCube_SHT31D::readSerialNumber() 327 | { 328 | uint32_t result = NO_ERROR; 329 | uint16_t buf[2]; 330 | 331 | if (writeCommand(CMD_READ_SERIAL_NUMBER) == NO_ERROR) { 332 | if (read(buf, 2) == NO_ERROR) { 333 | result = (buf[0] << 16) | buf[1]; 334 | } 335 | } 336 | 337 | return result; 338 | } 339 | 340 | SHT31D_RegisterStatus ClosedCube_SHT31D::readStatusRegister() 341 | { 342 | SHT31D_RegisterStatus result; 343 | 344 | SHT31D_ErrorCode error = writeCommand(CMD_READ_STATUS); 345 | if (error == NO_ERROR) 346 | error = read(&result.rawData, 1); 347 | 348 | return result; 349 | } 350 | 351 | SHT31D_ErrorCode ClosedCube_SHT31D::clearAll() { 352 | return writeCommand(CMD_CLEAR_STATUS); 353 | } 354 | 355 | 356 | SHT31D ClosedCube_SHT31D::readTemperatureAndHumidity() 357 | { 358 | SHT31D result; 359 | 360 | result.t = 0; 361 | result.rh = 0; 362 | 363 | SHT31D_ErrorCode error; 364 | uint16_t buf[2]; 365 | 366 | if (error == NO_ERROR) 367 | error = read(buf, 2); 368 | 369 | if (error == NO_ERROR) { 370 | result.t = calculateTemperature(buf[0]); 371 | result.rh = calculateHumidity(buf[1]); 372 | } 373 | result.error = error; 374 | 375 | return result; 376 | } 377 | 378 | SHT31D ClosedCube_SHT31D::readAlertData(SHT31D_Commands command) 379 | { 380 | SHT31D result; 381 | 382 | result.t = 0; 383 | result.rh = 0; 384 | 385 | SHT31D_ErrorCode error; 386 | uint16_t buf[1]; 387 | 388 | error = writeCommand(command); 389 | 390 | if (error == NO_ERROR) 391 | error = read(buf, 1); 392 | 393 | if (error == NO_ERROR) { 394 | result.rh = calculateHumidity(buf[0] << 7); 395 | result.t = calculateTemperature(buf[0] & 0xFE00); 396 | } 397 | 398 | result.error = error; 399 | 400 | return result; 401 | } 402 | 403 | SHT31D_ErrorCode ClosedCube_SHT31D::read(uint16_t* data, uint8_t numOfPair) 404 | { 405 | uint8_t buf[2]; 406 | uint8_t checksum; 407 | 408 | const uint8_t numOfBytes = numOfPair * 3; 409 | 410 | Wire.beginTransmission(_address); 411 | Wire.requestFrom(_address, numOfBytes); 412 | 413 | int counter = 0; 414 | while (Wire.available() < numOfBytes) 415 | { 416 | counter++; 417 | delay(50); 418 | if (counter > 100) 419 | return TIMEOUT_ERROR; 420 | } 421 | 422 | for (counter = 0; counter < numOfPair; counter++) { 423 | Wire.readBytes((char *)buf, (uint8_t)2); 424 | checksum = Wire.read(); 425 | 426 | if (checkCrc(buf, checksum) != 0) 427 | return CRC_ERROR; 428 | 429 | data[counter] = (buf[0] << 8) | buf[1]; 430 | } 431 | 432 | Wire.endTransmission(); 433 | 434 | return NO_ERROR; 435 | } 436 | 437 | 438 | uint8_t ClosedCube_SHT31D::checkCrc(uint8_t data[], uint8_t checksum) 439 | { 440 | return calculateCrc(data) != checksum; 441 | } 442 | 443 | float ClosedCube_SHT31D::calculateTemperature(uint16_t rawValue) 444 | { 445 | return 175.0f * (float)rawValue / 65535.0f - 45.0f; 446 | } 447 | 448 | 449 | float ClosedCube_SHT31D::calculateHumidity(uint16_t rawValue) 450 | { 451 | return 100.0f * rawValue / 65535.0f; 452 | } 453 | 454 | uint16_t ClosedCube_SHT31D::calculateRaWTemperature(float value) 455 | { 456 | return (value + 45.0f) / 175.0f * 65535.0f; 457 | } 458 | 459 | uint16_t ClosedCube_SHT31D::calculateRawHumidity(float value) 460 | { 461 | return value / 100.0f * 65535.0f; 462 | } 463 | 464 | uint8_t ClosedCube_SHT31D::calculateCrc(uint8_t data[]) 465 | { 466 | uint8_t bit; 467 | uint8_t crc = 0xFF; 468 | uint8_t dataCounter = 0; 469 | 470 | for (; dataCounter < 2; dataCounter++) 471 | { 472 | crc ^= (data[dataCounter]); 473 | for (bit = 8; bit > 0; --bit) 474 | { 475 | if (crc & 0x80) 476 | crc = (crc << 1) ^ 0x131; 477 | else 478 | crc = (crc << 1); 479 | } 480 | } 481 | 482 | return crc; 483 | } 484 | 485 | SHT31D ClosedCube_SHT31D::returnError(SHT31D_ErrorCode error) { 486 | SHT31D result; 487 | result.t = 0; 488 | result.rh = 0; 489 | result.error = error; 490 | return result; 491 | } 492 | -------------------------------------------------------------------------------- /ClosedCube_SHT31D.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Particle Photon SHT-3X-DIS Library 4 | 5 | ported from 6 | 7 | Arduino Library for Sensirion SHT3X-DIS Digital Humidity & Temperature Sensors 8 | Written by AA 9 | --- 10 | 11 | The MIT License (MIT) 12 | 13 | Copyright (c) 2015-2016 ClosedCube Limited 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | THE SOFTWARE. 32 | 33 | */ 34 | 35 | #ifndef CLOSEDCUBE_SHT31D 36 | #define CLOSEDCUBE_SHT31D 37 | 38 | #include "application.h" 39 | 40 | namespace SHT31D_CC { 41 | 42 | typedef enum { 43 | CMD_READ_SERIAL_NUMBER = 0x3780, 44 | 45 | CMD_READ_STATUS = 0xF32D, 46 | CMD_CLEAR_STATUS = 0x3041, 47 | 48 | CMD_HEATER_ENABLE = 0x306D, 49 | CMD_HEATER_DISABLE = 0x3066, 50 | 51 | CMD_SOFT_RESET = 0x30A2, 52 | 53 | CMD_CLOCK_STRETCH_H = 0x2C06, 54 | CMD_CLOCK_STRETCH_M = 0x2C0D, 55 | CMD_CLOCK_STRETCH_L = 0x2C10, 56 | 57 | CMD_POLLING_H = 0x2400, 58 | CMD_POLLING_M = 0x240B, 59 | CMD_POLLING_L = 0x2416, 60 | 61 | CMD_ART = 0x2B32, 62 | 63 | CMD_PERIODIC_HALF_H = 0x2032, 64 | CMD_PERIODIC_HALF_M = 0x2024, 65 | CMD_PERIODIC_HALF_L = 0x202F, 66 | CMD_PERIODIC_1_H = 0x2130, 67 | CMD_PERIODIC_1_M = 0x2126, 68 | CMD_PERIODIC_1_L = 0x212D, 69 | CMD_PERIODIC_2_H = 0x2236, 70 | CMD_PERIODIC_2_M = 0x2220, 71 | CMD_PERIODIC_2_L = 0x222B, 72 | CMD_PERIODIC_4_H = 0x2334, 73 | CMD_PERIODIC_4_M = 0x2322, 74 | CMD_PERIODIC_4_L = 0x2329, 75 | CMD_PERIODIC_10_H = 0x2737, 76 | CMD_PERIODIC_10_M = 0x2721, 77 | CMD_PERIODIC_10_L = 0x272A, 78 | 79 | CMD_FETCH_DATA = 0xE000, 80 | CMD_STOP_PERIODIC = 0x3093, 81 | 82 | CMD_READ_ALR_LIMIT_LS = 0xE102, 83 | CMD_READ_ALR_LIMIT_LC = 0xE109, 84 | CMD_READ_ALR_LIMIT_HS = 0xE11F, 85 | CMD_READ_ALR_LIMIT_HC = 0xE114, 86 | 87 | CMD_WRITE_ALR_LIMIT_HS = 0x611D, 88 | CMD_WRITE_ALR_LIMIT_HC = 0x6116, 89 | CMD_WRITE_ALR_LIMIT_LC = 0x610B, 90 | CMD_WRITE_ALR_LIMIT_LS = 0x6100, 91 | 92 | CMD_NO_SLEEP = 0x303E, 93 | } SHT31D_Commands; 94 | 95 | 96 | typedef enum { 97 | REPEATABILITY_HIGH, 98 | REPEATABILITY_MEDIUM, 99 | REPEATABILITY_LOW, 100 | } SHT31D_Repeatability; 101 | 102 | typedef enum { 103 | MODE_CLOCK_STRETCH, 104 | MODE_POLLING, 105 | } SHT31D_Mode; 106 | 107 | typedef enum { 108 | FREQUENCY_HZ5, 109 | FREQUENCY_1HZ, 110 | FREQUENCY_2HZ, 111 | FREQUENCY_4HZ, 112 | FREQUENCY_10HZ 113 | } SHT31D_Frequency; 114 | 115 | typedef enum { 116 | NO_ERROR = 0, 117 | 118 | CRC_ERROR = -101, 119 | TIMEOUT_ERROR = -102, 120 | 121 | PARAM_WRONG_MODE = -501, 122 | PARAM_WRONG_REPEATABILITY = -502, 123 | PARAM_WRONG_FREQUENCY = -503, 124 | PARAM_WRONG_ALERT = -504, 125 | 126 | // Wire I2C translated error codes 127 | WIRE_I2C_DATA_TOO_LOG = -10, 128 | WIRE_I2C_RECEIVED_NACK_ON_ADDRESS = -20, 129 | WIRE_I2C_RECEIVED_NACK_ON_DATA = -30, 130 | WIRE_I2C_UNKNOW_ERROR = -40 131 | } SHT31D_ErrorCode; 132 | 133 | typedef union { 134 | uint16_t rawData; 135 | struct { 136 | uint8_t WriteDataChecksumStatus : 1; 137 | uint8_t CommandStatus : 1; 138 | uint8_t Reserved0 : 2; 139 | uint8_t SystemResetDetected : 1; 140 | uint8_t Reserved1 : 5; 141 | uint8_t T_TrackingAlert : 1; 142 | uint8_t RH_TrackingAlert : 1; 143 | uint8_t Reserved2 : 1; 144 | uint8_t HeaterStatus : 1; 145 | uint8_t Reserved3 : 1; 146 | uint8_t AlertPending : 1; 147 | }; 148 | } SHT31D_RegisterStatus; 149 | 150 | struct SHT31D { 151 | float t; 152 | float rh; 153 | SHT31D_ErrorCode error; 154 | }; 155 | 156 | class ClosedCube_SHT31D { 157 | public: 158 | ClosedCube_SHT31D(); 159 | 160 | SHT31D_ErrorCode begin(uint8_t address); 161 | SHT31D_ErrorCode clearAll(); 162 | SHT31D_RegisterStatus readStatusRegister(); 163 | 164 | SHT31D_ErrorCode heaterEnable(); 165 | SHT31D_ErrorCode heaterDisable(); 166 | 167 | SHT31D_ErrorCode softReset(); 168 | SHT31D_ErrorCode generalCallReset(); 169 | 170 | SHT31D_ErrorCode artEnable(); 171 | 172 | uint32_t readSerialNumber(); 173 | 174 | SHT31D readTempAndHumidity(SHT31D_Repeatability repeatability, SHT31D_Mode mode, uint8_t timeout); 175 | SHT31D readTempAndHumidityClockStretch(SHT31D_Repeatability repeatability); 176 | SHT31D readTempAndHumidityPolling(SHT31D_Repeatability repeatability, uint8_t timeout); 177 | 178 | SHT31D_ErrorCode periodicStart(SHT31D_Repeatability repeatability, SHT31D_Frequency frequency); 179 | SHT31D periodicFetchData(); 180 | SHT31D_ErrorCode periodicStop(); 181 | 182 | SHT31D_ErrorCode writeAlertHigh(float temperatureSet, float temperatureClear, float humiditySet, float humidityClear); 183 | SHT31D readAlertHighSet(); 184 | SHT31D readAlertHighClear(); 185 | 186 | SHT31D_ErrorCode writeAlertLow(float temperatureClear, float temperatureSet, float humidityClear, float humiditySet); 187 | SHT31D readAlertLowSet(); 188 | SHT31D readAlertLowClear(); 189 | 190 | 191 | private: 192 | uint8_t _address; 193 | SHT31D_RegisterStatus _status; 194 | 195 | SHT31D_ErrorCode writeCommand(SHT31D_Commands command); 196 | SHT31D_ErrorCode writeAlertData(SHT31D_Commands command, float temperature, float humidity); 197 | 198 | uint8_t checkCrc(uint8_t data[], uint8_t checksum); 199 | uint8_t calculateCrc(uint8_t data[]); 200 | 201 | float calculateHumidity(uint16_t rawValue); 202 | float calculateTemperature(uint16_t rawValue); 203 | 204 | uint16_t calculateRawHumidity(float value); 205 | uint16_t calculateRaWTemperature(float value); 206 | 207 | SHT31D readTemperatureAndHumidity(); 208 | SHT31D readAlertData(SHT31D_Commands command); 209 | SHT31D_ErrorCode read(uint16_t* data, uint8_t numOfPair); 210 | 211 | SHT31D returnError(SHT31D_ErrorCode command); 212 | }; 213 | } 214 | 215 | #endif 216 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 OSBeehives, Inc. 4 | 5 | Audio portions: Copyright (c) 2016 rickkas7 6 | MMA8452 portions: credit to 2014 Jim Lindblom @ SparkFun Electronics 7 | Battery Babysitter portions: Copyright (c) 2016 SparkFun Electronics 8 | ClosedCube_SHT31D portions: Copyright (c) 2015-2016 ClosedCube Limited 9 | 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | -------------------------------------------------------------------------------- /MMA8452-xlero-OSBH.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | MMA8452-Accelerometer-Library-Spark-Core.cpp 3 | MMA8452-Accelerometer-Library-Spark-Core Library Source File 4 | Jim Lindblom @ SparkFun Electronics 5 | Original Creation Date: June 3, 2014 6 | Modified by Peter Naylor: Jan 5, 2018 7 | Original Source: 8 | https://github.com/sparkfun/MMA8452_Accelerometer 9 | 10 | This file implements all functions of the MMA8452Q class. Functions here range 11 | from higher level stuff, like reading/writing MMA8452Q registers to low-level, 12 | hardware I2C reads and writes. 13 | 14 | This code is beerware; if you see me (or any other SparkFun employee) at the 15 | local, and you've found our code helpful, please buy us a round! 16 | 17 | Distributed as-is; no warranty is given. 18 | ******************************************************************************/ 19 | 20 | #include "MMA8452-xlero-OSBH.h" 21 | 22 | // CONSTRUCTUR 23 | // This function, called when you initialize the class will simply write the 24 | // supplied address into a private variable for future use. 25 | // The variable addr should be either 0x1C or 0x1D, depending on which voltage 26 | // the SA0 pin is tied to (GND or 3.3V respectively). 27 | MMA8452Q::MMA8452Q(byte addr) 28 | { 29 | address = addr; // Store address into private variable 30 | } 31 | 32 | // INITIALIZATION 33 | // This function initializes the MMA8452Q. It sets up the scale (either 2, 4, 34 | // or 8g), output data rate, portrait/landscape detection and tap detection. 35 | // It also checks the WHO_AM_I register to make sure we can communicate with 36 | // the sensor. Returns a 0 if communication failed, 1 if successful. 37 | byte MMA8452Q::init(MMA8452Q_Scale fsr, MMA8452Q_ODR odr) 38 | { 39 | scale = fsr; // Haul fsr into our class variable, scale 40 | 41 | Wire.begin(); // Initialize I2C 42 | 43 | byte c = readRegister(WHO_AM_I); // Read WHO_AM_I register 44 | 45 | if (c != 0x2A) // WHO_AM_I should always be 0x2A 46 | { 47 | return 0; 48 | } 49 | 50 | standby(); // Must be in standby to change registers 51 | 52 | setScale(scale); // Set up accelerometer scale 53 | setODR(odr); // Set up output data rate 54 | writeRegister(CTRL_REG2, 0x11); // Set MODS and SMODS to low noise low power mode (8uA) 55 | // Multiply parameter by 0.0625g to calculate threshold. 56 | setupTap(0x10, 0x10, 0x10); // Enable x, y, z set to 0.5g 57 | setupINT(); // setup interrupt INT1 for PULSE 58 | active(); // Set to active to start reading 59 | 60 | return 1; 61 | } 62 | 63 | // READ ACCELERATION DATA 64 | // This function will read the acceleration values from the MMA8452Q. After 65 | // reading, it will update two triplets of variables: 66 | // * int's x, y, and z will store the signed 12-bit values read out 67 | // of the acceleromter. 68 | // * floats cx, cy, and cz will store the calculated acceleration from 69 | // those 12-bit values. These variables are in units of g's. 70 | void MMA8452Q::read() 71 | { 72 | byte rawData[6]; // x/y/z accel register data stored here 73 | 74 | readRegisters(OUT_X_MSB, rawData, 6); // Read the six raw data registers into data array 75 | 76 | x = (rawData[0]<<8 | rawData[1]) >> 4; 77 | y = (rawData[2]<<8 | rawData[3]) >> 4; 78 | z = (rawData[4]<<8 | rawData[5]) >> 4; 79 | cx = (float) x / (float)(1<<11) * (float)(scale); 80 | cy = (float) y / (float)(1<<11) * (float)(scale); 81 | cz = (float) z / (float)(1<<11) * (float)(scale); 82 | } 83 | 84 | // CHECK IF NEW DATA IS AVAILABLE 85 | // This function checks the status of the MMA8452Q to see if new data is availble. 86 | // returns 0 if no new data is present, or a 1 if new data is available. 87 | byte MMA8452Q::available() 88 | { 89 | return (readRegister(STATUS) & 0x08) >> 3; 90 | } 91 | 92 | // SET FULL-SCALE RANGE 93 | // This function sets the full-scale range of the x, y, and z axis accelerometers. 94 | // Possible values for the fsr variable are SCALE_2G, SCALE_4G, or SCALE_8G. 95 | void MMA8452Q::setScale(MMA8452Q_Scale fsr) 96 | { 97 | // Must be in standby mode to make changes!!! 98 | byte cfg = readRegister(XYZ_DATA_CFG); 99 | cfg &= 0xFC; // Mask out scale bits 100 | cfg |= (fsr >> 2); // Neat trick, see page 22. 00 = 2G, 01 = 4A, 10 = 8G 101 | writeRegister(XYZ_DATA_CFG, cfg); 102 | } 103 | 104 | // SET THE OUTPUT DATA RATE 105 | // This function sets the output data rate of the MMA8452Q. 106 | // Possible values for the odr parameter are: ODR_800, ODR_400, ODR_200, 107 | // ODR_100, ODR_50, ODR_12, ODR_6, or ODR_1 108 | void MMA8452Q::setODR(MMA8452Q_ODR odr) 109 | { 110 | // Must be in standby mode to make changes!!! 111 | byte ctrl = readRegister(CTRL_REG1); 112 | ctrl &= 0xCF; // Mask out data rate bits 113 | ctrl |= (odr << 3); 114 | writeRegister(CTRL_REG1, ctrl); 115 | } 116 | 117 | // SET UP TAP DETECTION 118 | // This function can set up tap detection on the x, y, and/or z axes. 119 | // The xThs, yThs, and zThs parameters serve two functions: 120 | // 1. Enable tap detection on an axis. If the 7th bit is SET (0x80) 121 | // tap detection on that axis will be DISABLED. 122 | // 2. Set tap g's threshold. The lower 7 bits will set the tap threshold 123 | // on that axis. 124 | void MMA8452Q::setupTap(byte xThs, byte yThs, byte zThs) 125 | { 126 | // Set up single and double tap - 5 steps: 127 | // for more info check out this app note: 128 | // http://cache.freescale.com/files/sensors/doc/app_note/AN4072.pdf 129 | // Set the threshold - minimum required acceleration to cause a tap. 130 | byte temp = 0; 131 | if (!(xThs & 0x80)) // If top bit ISN'T set 132 | { 133 | temp |= 0x3; // Enable taps on x 134 | writeRegister(PULSE_THSX, xThs); // x thresh 135 | } 136 | if (!(yThs & 0x80)) 137 | { 138 | temp |= 0xC; // Enable taps on y 139 | writeRegister(PULSE_THSY, yThs); // y thresh 140 | } 141 | if (!(zThs & 0x80)) 142 | { 143 | temp |= 0x30; // Enable taps on z 144 | writeRegister(PULSE_THSZ, zThs); // z thresh 145 | } 146 | // Set up single and/or double tap detection on each axis individually. 147 | writeRegister(PULSE_CFG, temp | 0xFF); 148 | // Set the time limit - the maximum time that a tap can be above the thresh 149 | writeRegister(PULSE_TMLT, 0x30); // 30ms time limit at 800Hz odr 150 | // Set the pulse latency - the minimum required time between pulses 151 | writeRegister(PULSE_LTCY, 0x40); // 200ms (at 800Hz odr) between taps min 152 | // Set the second pulse window - maximum allowed time between end of 153 | // latency and start of second pulse 154 | writeRegister(PULSE_WIND, 0xFF); // 5. 318ms (max value) between taps max 155 | } 156 | 157 | // READ TAP STATUS 158 | // This function returns any taps read by the MMA8452Q. If the function 159 | // returns no new taps were detected. Otherwise the function will return the 160 | // lower 7 bits of the PULSE_SRC register. Clears interrupt flag in INT_SOURCE. 161 | byte MMA8452Q::readTap() 162 | { 163 | byte tapStat = readRegister(PULSE_SRC); 164 | if (tapStat & 0x80) // Read EA bit to check if a interrupt was generated 165 | { 166 | return tapStat & 0x7F; 167 | } 168 | else 169 | return 0; 170 | } 171 | 172 | // SET UP INTERRUPT on INT1 173 | // This function sets up portrait and landscape detection. 174 | void MMA8452Q::setupINT() 175 | { 176 | // Must be in standby mode to make changes!!! 177 | // For more info check out this app note: 178 | // http://cache.freescale.com/files/sensors/doc/app_note/AN4068.pdf 179 | // 1. Enable SRC_PULSE 180 | writeRegister(CTRL_REG3, 0x02); // Set IPOL (enable), active HIGH for INT pin 181 | writeRegister(CTRL_REG4, 0x08); // Set SRC_PULSE (enable), cleared when PULSE_SRC is read 182 | writeRegister(CTRL_REG5, 0x08); // Set INT_CFG_PULSE (enable), interrupt source is directed to INT1 pin 183 | } 184 | 185 | // SET STANDBY MODE 186 | // Sets the MMA8452 to standby mode. It must be in standby to change most register settings 187 | void MMA8452Q::standby() 188 | { 189 | byte c = readRegister(CTRL_REG1); 190 | writeRegister(CTRL_REG1, c & ~(0x01)); //Clear the active bit to go into standby 191 | } 192 | 193 | // SET ACTIVE MODE 194 | // Sets the MMA8452 to active mode. Needs to be in this mode to output data 195 | void MMA8452Q::active() 196 | { 197 | byte c = readRegister(CTRL_REG1); 198 | writeRegister(CTRL_REG1, c | 0x01); //Set the active bit to begin detection 199 | } 200 | 201 | // WRITE A SINGLE REGISTER 202 | // Write a single byte of data to a register in the MMA8452Q. 203 | void MMA8452Q::writeRegister(MMA8452Q_Register reg, byte data) 204 | { 205 | writeRegisters(reg, &data, 1); 206 | } 207 | 208 | // WRITE MULTIPLE REGISTERS 209 | // Write an array of "len" bytes ("buffer"), starting at register "reg", and 210 | // auto-incrmenting to the next. 211 | void MMA8452Q::writeRegisters(MMA8452Q_Register reg, byte *buffer, byte len) 212 | { 213 | Wire.beginTransmission(address); 214 | Wire.write(reg); 215 | for (int x = 0; x < len; x++) 216 | Wire.write(buffer[x]); 217 | Wire.endTransmission(); //Stop transmitting 218 | } 219 | 220 | // READ A SINGLE REGISTER 221 | // Read a byte from the MMA8452Q register "reg". 222 | byte MMA8452Q::readRegister(MMA8452Q_Register reg) 223 | { 224 | Wire.beginTransmission(address); 225 | Wire.write(reg); 226 | Wire.endTransmission(false); //endTransmission but keep the connection active 227 | 228 | Wire.requestFrom(address, (byte) 1); //Ask for 1 byte, once done, bus is released by default 229 | 230 | while(!Wire.available()) ; //Wait for the data to come back 231 | 232 | return Wire.read(); //Return this one byte 233 | } 234 | 235 | // READ MULTIPLE REGISTERS 236 | // Read "len" bytes from the MMA8452Q, starting at register "reg". Bytes are stored 237 | // in "buffer" on exit. 238 | void MMA8452Q::readRegisters(MMA8452Q_Register reg, byte *buffer, byte len) 239 | { 240 | Wire.beginTransmission(address); 241 | Wire.write(reg); 242 | Wire.endTransmission(false); //endTransmission but keep the connection active 243 | 244 | Wire.requestFrom(address, len); //Ask for bytes, once done, bus is released by default 245 | 246 | while(Wire.available() < len); //Hang out until we get the # of bytes we expect 247 | 248 | for(int x = 0 ; x < len ; x++) 249 | buffer[x] = Wire.read(); 250 | } -------------------------------------------------------------------------------- /MMA8452-xlero-OSBH.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | MMA8452-Accelerometer-Library-Spark-Core.h 3 | MMA8452-Accelerometer-Library-Spark-Core Library Header File 4 | Jim Lindblom @ SparkFun Electronics 5 | Original Creation Date: June 3, 2014 6 | Modified by Peter Naylor: Jan 5, 2018 7 | Original Source: 8 | https://github.com/sparkfun/MMA8452_Accelerometer 9 | 10 | This file prototypes the MMA8452Q class, implemented in SFE_MMA8452Q.cpp. In 11 | addition, it defines every register in the MMA8452Q. 12 | 13 | Development environment specifics: 14 | IDE: Arduino 1.0.5 15 | Hardware Platform: Arduino Uno 16 | 17 | This code is beerware; if you see me (or any other SparkFun employee) at the 18 | local, and you've found our code helpful, please buy us a round! 19 | 20 | Distributed as-is; no warranty is given. 21 | ******************************************************************************/ 22 | 23 | #ifndef MMA8452-xlero_OSBH_h 24 | #define MMA8452-xlero_OSBH_h 25 | 26 | #include "application.h" // Needed for Spark Core 27 | 28 | 29 | /////////////////////////////////// 30 | // MMA8452Q Register Definitions // 31 | /////////////////////////////////// 32 | enum MMA8452Q_Register { 33 | STATUS = 0x00, 34 | OUT_X_MSB = 0x01, 35 | OUT_X_LSB = 0x02, 36 | OUT_Y_MSB = 0x03, 37 | OUT_Y_LSB = 0x04, 38 | OUT_Z_MSB = 0x05, 39 | OUT_Z_LSB = 0x06, 40 | SYSMOD = 0x0B, 41 | INT_SOURCE = 0x0C, 42 | WHO_AM_I = 0x0D, 43 | XYZ_DATA_CFG = 0x0E, 44 | HP_FILTER_CUTOFF = 0x0F, 45 | PL_STATUS = 0x10, 46 | PL_CFG = 0x11, 47 | PL_COUNT = 0x12, 48 | PL_BF_ZCOMP = 0x13, 49 | P_L_THS_REG = 0x14, 50 | FF_MT_CFG = 0x15, 51 | FF_MT_SRC = 0x16, 52 | FF_MT_THS = 0x17, 53 | FF_MT_COUNT = 0x18, 54 | TRANSIENT_CFG = 0x1D, 55 | TRANSIENT_SRC = 0x1E, 56 | TRANSIENT_THS = 0x1F, 57 | TRANSIENT_COUNT = 0x20, 58 | PULSE_CFG = 0x21, 59 | PULSE_SRC = 0x22, 60 | PULSE_THSX = 0x23, 61 | PULSE_THSY = 0x24, 62 | PULSE_THSZ = 0x25, 63 | PULSE_TMLT = 0x26, 64 | PULSE_LTCY = 0x27, 65 | PULSE_WIND = 0x28, 66 | ASLP_COUNT = 0x29, 67 | CTRL_REG1 = 0x2A, 68 | CTRL_REG2 = 0x2B, 69 | CTRL_REG3 = 0x2C, 70 | CTRL_REG4 = 0x2D, 71 | CTRL_REG5 = 0x2E, 72 | OFF_X = 0x2F, 73 | OFF_Y = 0x30, 74 | OFF_Z = 0x31 75 | }; 76 | 77 | //////////////////////////////// 78 | // MMA8452Q Misc Declarations // 79 | //////////////////////////////// 80 | enum MMA8452Q_Scale {SCALE_2G = 2, SCALE_4G = 4, SCALE_8G = 8}; // Possible full-scale settings 81 | enum MMA8452Q_ODR {ODR_800, ODR_400, ODR_200, ODR_100, ODR_50, ODR_12, ODR_6, ODR_1}; // possible data rates 82 | // Possible portrait/landscape settings 83 | #define PORTRAIT_U 0 84 | #define PORTRAIT_D 1 85 | #define LANDSCAPE_R 2 86 | #define LANDSCAPE_L 3 87 | #define LOCKOUT 0x40 88 | 89 | //////////////////////////////// 90 | // MMA8452Q Class Declaration // 91 | //////////////////////////////// 92 | class MMA8452Q 93 | { 94 | public: 95 | MMA8452Q(byte addr = 0x1C); // Constructor 96 | 97 | byte init(MMA8452Q_Scale fsr = SCALE_2G, MMA8452Q_ODR odr = ODR_800); 98 | void read(); 99 | byte available(); 100 | byte readTap(); 101 | 102 | int x, y, z; 103 | float cx, cy, cz; 104 | private: 105 | byte address; 106 | MMA8452Q_Scale scale; 107 | 108 | void standby(); 109 | void active(); 110 | void setupINT(); 111 | void setupTap(byte xThs, byte yThs, byte zThs); 112 | void setScale(MMA8452Q_Scale fsr); 113 | void setODR(MMA8452Q_ODR odr); 114 | void writeRegister(MMA8452Q_Register reg, byte data); 115 | void writeRegisters(MMA8452Q_Register reg, byte *buffer, byte len); 116 | byte readRegister(MMA8452Q_Register reg); 117 | void readRegisters(MMA8452Q_Register reg, byte *buffer, byte len); 118 | }; 119 | 120 | #endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BzBoard_BETA 2 | beta-release of development firmware for BuzzzBoard PCBA 3 | 4 | 5 | ## Use 6 | 7 | See license file. 8 | 9 | 10 | ## Change Log 11 | 12 | ### v2 13 | - updated audio server URL 14 | ### v1 15 | - Initial release 16 | 17 | 18 | 19 | ## Local Build 20 | 21 | - Save to new folder. 22 | 23 | - Build using Particle CLI command 'particle compile photon . --saveTo firmware.bin' 24 | 25 | - Upload 'firmware.bin' to Photon using USB cable and DFU mode: 26 | 27 | - hold SETUP button on RESET until blinking YELLOW 28 | 29 | - Send 'particle flash --usb firmware.bin' 30 | 31 | -------------------------------------------------------------------------------- /SparkFunBQ27441.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | SparkFunBQ27441.cpp 3 | BQ27441 Arduino Library Main Source File 4 | Jim Lindblom @ SparkFun Electronics 5 | May 9, 2016 6 | https://github.com/sparkfun/SparkFun_BQ27441_Arduino_Library 7 | 8 | Implementation of all features of the BQ27441 LiPo Fuel Gauge. 9 | 10 | Particle Port: Peter Naylor 11 | 12 | Hardware Resources: 13 | - Arduino Development Board 14 | - SparkFun Battery Babysitter 15 | 16 | Development environment specifics: 17 | Arduino 1.6.7 18 | SparkFun Battery Babysitter v1.0 19 | Arduino Uno (any 'duino should do) 20 | ******************************************************************************/ 21 | 22 | #include "application.h" 23 | #include "SparkFunBQ27441.h" 24 | #include "BQ27441_Definitions.h" 25 | 26 | /***************************************************************************** 27 | ************************** Initialization Functions ************************* 28 | *****************************************************************************/ 29 | // Initializes class variables 30 | BQ27441::BQ27441() : _deviceAddress(BQ72441_I2C_ADDRESS), _sealFlag(false), _userConfigControl(false) 31 | { 32 | } 33 | 34 | // Initializes I2C and verifies communication with the BQ27441. 35 | bool BQ27441::begin(void) 36 | { 37 | uint16_t deviceID = 0; 38 | 39 | Wire.begin(); // Initialize I2C master 40 | 41 | deviceID = deviceType(); // Read deviceType from BQ27441 42 | 43 | if (deviceID == BQ27441_DEVICE_ID) 44 | { 45 | return true; // If device ID is valid, return true 46 | } 47 | 48 | return false; // Otherwise return false 49 | } 50 | 51 | // Configures the design capacity of the connected battery. 52 | bool BQ27441::setCapacity(uint16_t capacity) 53 | { 54 | // Write to STATE subclass (82) of BQ27441 extended memory. 55 | // Offset 0x0A (10) 56 | // Design capacity is a 2-byte piece of data - MSB first 57 | uint8_t capMSB = capacity >> 8; 58 | uint8_t capLSB = capacity & 0x00FF; 59 | uint8_t capacityData[2] = {capMSB, capLSB}; 60 | return writeExtendedData(BQ27441_ID_STATE, 10, capacityData, 2); 61 | } 62 | 63 | /***************************************************************************** 64 | ********************** Battery Characteristics Functions ******************** 65 | *****************************************************************************/ 66 | 67 | // Reads and returns the battery voltage 68 | uint16_t BQ27441::voltage(void) 69 | { 70 | return readWord(BQ27441_COMMAND_VOLTAGE); 71 | } 72 | 73 | // Reads and returns the specified current measurement 74 | int16_t BQ27441::current(current_measure type) 75 | { 76 | int16_t current = 0; 77 | switch (type) 78 | { 79 | case AVG: 80 | current = (int16_t) readWord(BQ27441_COMMAND_AVG_CURRENT); 81 | break; 82 | case STBY: 83 | current = (int16_t) readWord(BQ27441_COMMAND_STDBY_CURRENT); 84 | break; 85 | case MAX: 86 | current = (int16_t) readWord(BQ27441_COMMAND_MAX_CURRENT); 87 | break; 88 | } 89 | 90 | return current; 91 | } 92 | 93 | // Reads and returns the specified capacity measurement 94 | uint16_t BQ27441::capacity(capacity_measure type) 95 | { 96 | uint16_t capacity = 0; 97 | switch (type) 98 | { 99 | case REMAIN: 100 | return readWord(BQ27441_COMMAND_REM_CAPACITY); 101 | break; 102 | case FULL: 103 | return readWord(BQ27441_COMMAND_FULL_CAPACITY); 104 | break; 105 | case AVAIL: 106 | capacity = readWord(BQ27441_COMMAND_NOM_CAPACITY); 107 | break; 108 | case AVAIL_FULL: 109 | capacity = readWord(BQ27441_COMMAND_AVAIL_CAPACITY); 110 | break; 111 | case REMAIN_F: 112 | capacity = readWord(BQ27441_COMMAND_REM_CAP_FIL); 113 | break; 114 | case REMAIN_UF: 115 | capacity = readWord(BQ27441_COMMAND_REM_CAP_UNFL); 116 | break; 117 | case FULL_F: 118 | capacity = readWord(BQ27441_COMMAND_FULL_CAP_FIL); 119 | break; 120 | case FULL_UF: 121 | capacity = readWord(BQ27441_COMMAND_FULL_CAP_UNFL); 122 | break; 123 | case DESIGN: 124 | capacity = readWord(BQ27441_EXTENDED_CAPACITY); 125 | } 126 | 127 | return capacity; 128 | } 129 | 130 | // Reads and returns measured average power 131 | int16_t BQ27441::power(void) 132 | { 133 | return (int16_t) readWord(BQ27441_COMMAND_AVG_POWER); 134 | } 135 | 136 | // Reads and returns specified state of charge measurement 137 | uint16_t BQ27441::soc(soc_measure type) 138 | { 139 | uint16_t socRet = 0; 140 | switch (type) 141 | { 142 | case FILTERED: 143 | socRet = readWord(BQ27441_COMMAND_SOC); 144 | break; 145 | case UNFILTERED: 146 | socRet = readWord(BQ27441_COMMAND_SOC_UNFL); 147 | break; 148 | } 149 | 150 | return socRet; 151 | } 152 | 153 | // Reads and returns specified state of health measurement 154 | uint8_t BQ27441::soh(soh_measure type) 155 | { 156 | uint16_t sohRaw = readWord(BQ27441_COMMAND_SOH); 157 | uint8_t sohStatus = sohRaw >> 8; 158 | uint8_t sohPercent = sohRaw & 0x00FF; 159 | 160 | if (type == PERCENT) 161 | return sohPercent; 162 | else 163 | return sohStatus; 164 | } 165 | 166 | // Reads and returns specified temperature measurement 167 | uint16_t BQ27441::temperature(temp_measure type) 168 | { 169 | uint16_t temp = 0; 170 | switch (type) 171 | { 172 | case BATTERY: 173 | temp = readWord(BQ27441_COMMAND_TEMP); 174 | break; 175 | case INTERNAL_TEMP: 176 | temp = readWord(BQ27441_COMMAND_INT_TEMP); 177 | break; 178 | } 179 | return temp; 180 | } 181 | 182 | /***************************************************************************** 183 | ************************** GPOUT Control Functions ************************** 184 | *****************************************************************************/ 185 | // Get GPOUT polarity setting (active-high or active-low) 186 | bool BQ27441::GPOUTPolarity(void) 187 | { 188 | uint16_t opConfigRegister = opConfig(); 189 | 190 | return (opConfigRegister & BQ27441_OPCONFIG_GPIOPOL); 191 | } 192 | 193 | // Set GPOUT polarity to active-high or active-low 194 | bool BQ27441::setGPOUTPolarity(bool activeHigh) 195 | { 196 | uint16_t oldOpConfig = opConfig(); 197 | 198 | // Check to see if we need to update opConfig: 199 | if ((activeHigh && (oldOpConfig & BQ27441_OPCONFIG_GPIOPOL)) || 200 | (!activeHigh && !(oldOpConfig & BQ27441_OPCONFIG_GPIOPOL))) 201 | return true; 202 | 203 | uint16_t newOpConfig = oldOpConfig; 204 | if (activeHigh) 205 | newOpConfig |= BQ27441_OPCONFIG_GPIOPOL; 206 | else 207 | newOpConfig &= ~(BQ27441_OPCONFIG_GPIOPOL); 208 | 209 | return writeOpConfig(newOpConfig); 210 | } 211 | 212 | // Get GPOUT function (BAT_LOW or SOC_INT) 213 | bool BQ27441::GPOUTFunction(void) 214 | { 215 | uint16_t opConfigRegister = opConfig(); 216 | 217 | return (opConfigRegister & BQ27441_OPCONFIG_BATLOWEN); 218 | } 219 | 220 | // Set GPOUT function to BAT_LOW or SOC_INT 221 | bool BQ27441::setGPOUTFunction(gpout_function function) 222 | { 223 | uint16_t oldOpConfig = opConfig(); 224 | 225 | // Check to see if we need to update opConfig: 226 | if ((function && (oldOpConfig & BQ27441_OPCONFIG_BATLOWEN)) || 227 | (!function && !(oldOpConfig & BQ27441_OPCONFIG_BATLOWEN))) 228 | return true; 229 | 230 | // Modify BATLOWN_EN bit of opConfig: 231 | uint16_t newOpConfig = oldOpConfig; 232 | if (function) 233 | newOpConfig |= BQ27441_OPCONFIG_BATLOWEN; 234 | else 235 | newOpConfig &= ~(BQ27441_OPCONFIG_BATLOWEN); 236 | 237 | // Write new opConfig 238 | return writeOpConfig(newOpConfig); 239 | } 240 | 241 | // Get SOC1_Set Threshold - threshold to set the alert flag 242 | uint8_t BQ27441::SOC1SetThreshold(void) 243 | { 244 | return readExtendedData(BQ27441_ID_DISCHARGE, 0); 245 | } 246 | 247 | // Get SOC1_Clear Threshold - threshold to clear the alert flag 248 | uint8_t BQ27441::SOC1ClearThreshold(void) 249 | { 250 | return readExtendedData(BQ27441_ID_DISCHARGE, 1); 251 | } 252 | 253 | // Set the SOC1 set and clear thresholds to a percentage 254 | bool BQ27441::setSOC1Thresholds(uint8_t set, uint8_t clear) 255 | { 256 | uint8_t thresholds[2]; 257 | thresholds[0] = constrain(set, 0, 100); 258 | thresholds[1] = constrain(clear, 0, 100); 259 | return writeExtendedData(BQ27441_ID_DISCHARGE, 0, thresholds, 2); 260 | } 261 | 262 | // Get SOCF_Set Threshold - threshold to set the alert flag 263 | uint8_t BQ27441::SOCFSetThreshold(void) 264 | { 265 | return readExtendedData(BQ27441_ID_DISCHARGE, 2); 266 | } 267 | 268 | // Get SOCF_Clear Threshold - threshold to clear the alert flag 269 | uint8_t BQ27441::SOCFClearThreshold(void) 270 | { 271 | return readExtendedData(BQ27441_ID_DISCHARGE, 3); 272 | } 273 | 274 | // Set the SOCF set and clear thresholds to a percentage 275 | bool BQ27441::setSOCFThresholds(uint8_t set, uint8_t clear) 276 | { 277 | uint8_t thresholds[2]; 278 | thresholds[0] = constrain(set, 0, 100); 279 | thresholds[1] = constrain(clear, 0, 100); 280 | return writeExtendedData(BQ27441_ID_DISCHARGE, 2, thresholds, 2); 281 | } 282 | 283 | // Check if the SOC1 flag is set 284 | bool BQ27441::socFlag(void) 285 | { 286 | uint16_t flagState = flags(); 287 | 288 | return flagState & BQ27441_FLAG_SOC1; 289 | } 290 | 291 | // Check if the SOCF flag is set 292 | bool BQ27441::socfFlag(void) 293 | { 294 | uint16_t flagState = flags(); 295 | 296 | return flagState & BQ27441_FLAG_SOCF; 297 | 298 | } 299 | 300 | // Get the SOC_INT interval delta 301 | uint8_t BQ27441::sociDelta(void) 302 | { 303 | return readExtendedData(BQ27441_ID_STATE, 26); 304 | } 305 | 306 | // Set the SOC_INT interval delta to a value between 1 and 100 307 | bool BQ27441::setSOCIDelta(uint8_t delta) 308 | { 309 | uint8_t soci = constrain(delta, 0, 100); 310 | return writeExtendedData(BQ27441_ID_STATE, 26, &soci, 1); 311 | } 312 | 313 | // Pulse the GPOUT pin - must be in SOC_INT mode 314 | bool BQ27441::pulseGPOUT(void) 315 | { 316 | return executeControlWord(BQ27441_CONTROL_PULSE_SOC_INT); 317 | } 318 | 319 | /***************************************************************************** 320 | *************************** Control Sub-Commands **************************** 321 | *****************************************************************************/ 322 | 323 | // Read the device type - should be 0x0421 324 | uint16_t BQ27441::deviceType(void) 325 | { 326 | return readControlWord(BQ27441_CONTROL_DEVICE_TYPE); 327 | } 328 | 329 | // Enter configuration mode - set userControl if calling from an Arduino sketch 330 | // and you want control over when to exitConfig 331 | bool BQ27441::enterConfig(bool userControl) 332 | { 333 | if (userControl) _userConfigControl = true; 334 | 335 | if (sealed()) 336 | { 337 | _sealFlag = true; 338 | unseal(); // Must be unsealed before making changes 339 | } 340 | 341 | if (executeControlWord(BQ27441_CONTROL_SET_CFGUPDATE)) 342 | { 343 | int16_t timeout = BQ72441_I2C_TIMEOUT; 344 | while ((timeout--) && (!(status() & BQ27441_FLAG_CFGUPMODE))) 345 | delay(1); 346 | 347 | if (timeout > 0) 348 | return true; 349 | } 350 | 351 | return false; 352 | } 353 | 354 | // Exit configuration mode with the option to perform a resimulation 355 | bool BQ27441::exitConfig(bool resim) 356 | { 357 | // There are two methods for exiting config mode: 358 | // 1. Execute the EXIT_CFGUPDATE command 359 | // 2. Execute the SOFT_RESET command 360 | // EXIT_CFGUPDATE exits config mode _without_ an OCV (open-circuit voltage) 361 | // measurement, and without resimulating to update unfiltered-SoC and SoC. 362 | // If a new OCV measurement or resimulation is desired, SOFT_RESET or 363 | // EXIT_RESIM should be used to exit config mode. 364 | if (resim) 365 | { 366 | if (softReset()) 367 | { 368 | int16_t timeout = BQ72441_I2C_TIMEOUT; 369 | while ((timeout--) && ((flags() & BQ27441_FLAG_CFGUPMODE))) 370 | delay(1); 371 | if (timeout > 0) 372 | { 373 | if (_sealFlag) seal(); // Seal back up if we IC was sealed coming in 374 | return true; 375 | } 376 | } 377 | return false; 378 | } 379 | else 380 | { 381 | return executeControlWord(BQ27441_CONTROL_EXIT_CFGUPDATE); 382 | } 383 | } 384 | 385 | // Read the flags() command 386 | uint16_t BQ27441::flags(void) 387 | { 388 | return readWord(BQ27441_COMMAND_FLAGS); 389 | } 390 | 391 | // Read the CONTROL_STATUS subcommand of control() 392 | uint16_t BQ27441::status(void) 393 | { 394 | return readControlWord(BQ27441_CONTROL_STATUS); 395 | } 396 | 397 | /***************************** Private Functions *****************************/ 398 | 399 | // Check if the BQ27441-G1A is sealed or not. 400 | bool BQ27441::sealed(void) 401 | { 402 | uint16_t stat = status(); 403 | return stat & BQ27441_STATUS_SS; 404 | } 405 | 406 | // Seal the BQ27441-G1A 407 | bool BQ27441::seal(void) 408 | { 409 | return readControlWord(BQ27441_CONTROL_SEALED); 410 | } 411 | 412 | // UNseal the BQ27441-G1A 413 | bool BQ27441::unseal(void) 414 | { 415 | // To unseal the BQ27441, write the key to the control 416 | // command. Then immediately write the same key to control again. 417 | if (readControlWord(BQ27441_UNSEAL_KEY)) 418 | { 419 | return readControlWord(BQ27441_UNSEAL_KEY); 420 | } 421 | return false; 422 | } 423 | 424 | // Read the 16-bit opConfig register from extended data 425 | uint16_t BQ27441::opConfig(void) 426 | { 427 | return readWord(BQ27441_EXTENDED_OPCONFIG); 428 | } 429 | 430 | // Write the 16-bit opConfig register in extended data 431 | bool BQ27441::writeOpConfig(uint16_t value) 432 | { 433 | uint8_t opConfigMSB = value >> 8; 434 | uint8_t opConfigLSB = value & 0x00FF; 435 | uint8_t opConfigData[2] = {opConfigMSB, opConfigLSB}; 436 | 437 | // OpConfig register location: BQ27441_ID_REGISTERS id, offset 0 438 | return writeExtendedData(BQ27441_ID_REGISTERS, 0, opConfigData, 2); 439 | } 440 | 441 | // Issue a soft-reset to the BQ27441-G1A 442 | bool BQ27441::softReset(void) 443 | { 444 | return executeControlWord(BQ27441_CONTROL_SOFT_RESET); 445 | } 446 | 447 | // Read a 16-bit command word from the BQ27441-G1A 448 | uint16_t BQ27441::readWord(uint16_t subAddress) 449 | { 450 | uint8_t data[2]; 451 | i2cReadBytes(subAddress, data, 2); 452 | return ((uint16_t) data[1] << 8) | data[0]; 453 | } 454 | 455 | // Read a 16-bit subcommand() from the BQ27441-G1A's control() 456 | uint16_t BQ27441::readControlWord(uint16_t function) 457 | { 458 | uint8_t subCommandMSB = (function >> 8); 459 | uint8_t subCommandLSB = (function & 0x00FF); 460 | uint8_t command[2] = {subCommandLSB, subCommandMSB}; 461 | uint8_t data[2] = {0, 0}; 462 | 463 | i2cWriteBytes((uint8_t) 0, command, 2); 464 | 465 | if (i2cReadBytes((uint8_t) 0, data, 2)) 466 | { 467 | return ((uint16_t)data[1] << 8) | data[0]; 468 | } 469 | 470 | return false; 471 | } 472 | 473 | // Execute a subcommand() from the BQ27441-G1A's control() 474 | bool BQ27441::executeControlWord(uint16_t function) 475 | { 476 | uint8_t subCommandMSB = (function >> 8); 477 | uint8_t subCommandLSB = (function & 0x00FF); 478 | uint8_t command[2] = {subCommandLSB, subCommandMSB}; 479 | uint8_t data[2] = {0, 0}; 480 | 481 | if (i2cWriteBytes((uint8_t) 0, command, 2)) 482 | return true; 483 | 484 | return false; 485 | } 486 | 487 | /***************************************************************************** 488 | ************************** Extended Data Commands *************************** 489 | *****************************************************************************/ 490 | 491 | // Issue a BlockDataControl() command to enable BlockData access 492 | bool BQ27441::blockDataControl(void) 493 | { 494 | uint8_t enableByte = 0x00; 495 | return i2cWriteBytes(BQ27441_EXTENDED_CONTROL, &enableByte, 1); 496 | } 497 | 498 | // Issue a DataClass() command to set the data class to be accessed 499 | bool BQ27441::blockDataClass(uint8_t id) 500 | { 501 | return i2cWriteBytes(BQ27441_EXTENDED_DATACLASS, &id, 1); 502 | } 503 | 504 | // Issue a DataBlock() command to set the data block to be accessed 505 | bool BQ27441::blockDataOffset(uint8_t offset) 506 | { 507 | return i2cWriteBytes(BQ27441_EXTENDED_DATABLOCK, &offset, 1); 508 | } 509 | 510 | // Read the current checksum using BlockDataCheckSum() 511 | uint8_t BQ27441::blockDataChecksum(void) 512 | { 513 | uint8_t csum; 514 | i2cReadBytes(BQ27441_EXTENDED_CHECKSUM, &csum, 1); 515 | return csum; 516 | } 517 | 518 | // Use BlockData() to read a byte from the loaded extended data 519 | uint8_t BQ27441::readBlockData(uint8_t offset) 520 | { 521 | uint8_t ret; 522 | uint8_t address = offset + BQ27441_EXTENDED_BLOCKDATA; 523 | i2cReadBytes(address, &ret, 1); 524 | return ret; 525 | } 526 | 527 | // Use BlockData() to write a byte to an offset of the loaded data 528 | bool BQ27441::writeBlockData(uint8_t offset, uint8_t data) 529 | { 530 | uint8_t address = offset + BQ27441_EXTENDED_BLOCKDATA; 531 | return i2cWriteBytes(address, &data, 1); 532 | } 533 | 534 | // Read all 32 bytes of the loaded extended data and compute a 535 | // checksum based on the values. 536 | uint8_t BQ27441::computeBlockChecksum(void) 537 | { 538 | uint8_t data[32]; 539 | i2cReadBytes(BQ27441_EXTENDED_BLOCKDATA, data, 32); 540 | 541 | uint8_t csum = 0; 542 | for (int i=0; i<32; i++) 543 | { 544 | csum += data[i]; 545 | } 546 | csum = 255 - csum; 547 | 548 | return csum; 549 | } 550 | 551 | // Use the BlockDataCheckSum() command to write a checksum value 552 | bool BQ27441::writeBlockChecksum(uint8_t csum) 553 | { 554 | return i2cWriteBytes(BQ27441_EXTENDED_CHECKSUM, &csum, 1); 555 | } 556 | 557 | // Read a byte from extended data specifying a class ID and position offset 558 | uint8_t BQ27441::readExtendedData(uint8_t classID, uint8_t offset) 559 | { 560 | uint8_t retData = 0; 561 | if (!_userConfigControl) enterConfig(false); 562 | 563 | if (!blockDataControl()) // // enable block data memory control 564 | return false; // Return false if enable fails 565 | if (!blockDataClass(classID)) // Write class ID using DataBlockClass() 566 | return false; 567 | 568 | blockDataOffset(offset / 32); // Write 32-bit block offset (usually 0) 569 | 570 | computeBlockChecksum(); // Compute checksum going in 571 | uint8_t oldCsum = blockDataChecksum(); 572 | /*for (int i=0; i<32; i++) 573 | Serial.print(String(readBlockData(i)) + " ");*/ 574 | retData = readBlockData(offset % 32); // Read from offset (limit to 0-31) 575 | 576 | if (!_userConfigControl) exitConfig(); 577 | 578 | return retData; 579 | } 580 | 581 | // Write a specified number of bytes to extended data specifying a 582 | // class ID, position offset. 583 | bool BQ27441::writeExtendedData(uint8_t classID, uint8_t offset, uint8_t * data, uint8_t len) 584 | { 585 | if (len > 32) 586 | return false; 587 | 588 | if (!_userConfigControl) enterConfig(false); 589 | 590 | if (!blockDataControl()) // // enable block data memory control 591 | return false; // Return false if enable fails 592 | if (!blockDataClass(classID)) // Write class ID using DataBlockClass() 593 | return false; 594 | 595 | blockDataOffset(offset / 32); // Write 32-bit block offset (usually 0) 596 | computeBlockChecksum(); // Compute checksum going in 597 | uint8_t oldCsum = blockDataChecksum(); 598 | 599 | // Write data bytes: 600 | for (int i = 0; i < len; i++) 601 | { 602 | // Write to offset, mod 32 if offset is greater than 32 603 | // The blockDataOffset above sets the 32-bit block 604 | writeBlockData((offset % 32) + i, data[i]); 605 | } 606 | 607 | // Write new checksum using BlockDataChecksum (0x60) 608 | uint8_t newCsum = computeBlockChecksum(); // Compute the new checksum 609 | writeBlockChecksum(newCsum); 610 | 611 | if (!_userConfigControl) exitConfig(); 612 | 613 | return true; 614 | } 615 | 616 | /***************************************************************************** 617 | ************************ I2C Read and Write Routines ************************ 618 | *****************************************************************************/ 619 | 620 | // Read a specified number of bytes over I2C at a given subAddress 621 | int16_t BQ27441::i2cReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count) 622 | { 623 | int16_t timeout = BQ72441_I2C_TIMEOUT; 624 | Wire.beginTransmission(_deviceAddress); 625 | Wire.write(subAddress); 626 | Wire.endTransmission(true); 627 | 628 | Wire.requestFrom(_deviceAddress, count); 629 | while ((Wire.available() < count) && timeout--) 630 | delay(1); 631 | if (timeout) 632 | { 633 | for (int i=0; i0 indicates charging. 114 | */ 115 | int16_t current(current_measure type = AVG); 116 | 117 | /** 118 | Reads and returns the specified capacity measurement 119 | 120 | @param capacity_measure enum specifying capacity value to be read 121 | @return specified capacity measurement in mAh. 122 | */ 123 | uint16_t capacity(capacity_measure type = REMAIN); 124 | 125 | /** 126 | Reads and returns measured average power 127 | 128 | @return average power in mAh. >0 indicates charging. 129 | */ 130 | int16_t power(void); 131 | 132 | /** 133 | Reads and returns specified state of charge measurement 134 | 135 | @param soc_measure enum specifying filtered or unfiltered measurement 136 | @return specified state of charge measurement in % 137 | */ 138 | uint16_t soc(soc_measure type = FILTERED); 139 | 140 | /** 141 | Reads and returns specified state of health measurement 142 | 143 | @param soh_measure enum specifying filtered or unfiltered measurement 144 | @return specified state of health measurement in %, or status bits 145 | */ 146 | uint8_t soh(soh_measure type = PERCENT); 147 | 148 | /** 149 | Reads and returns specified temperature measurement 150 | 151 | @param temp_measure enum specifying internal or battery measurement 152 | @return specified temperature measurement in degrees C 153 | */ 154 | uint16_t temperature(temp_measure type = BATTERY); 155 | 156 | //////////////////////////// 157 | // GPOUT Control Commands // 158 | //////////////////////////// 159 | /** 160 | Get GPOUT polarity setting (active-high or active-low) 161 | 162 | @return true if active-high, false if active-low 163 | */ 164 | bool GPOUTPolarity(void); 165 | 166 | /** 167 | Set GPOUT polarity to active-high or active-low 168 | 169 | @param activeHigh is true if active-high, false if active-low 170 | @return true on success 171 | */ 172 | bool setGPOUTPolarity(bool activeHigh); 173 | 174 | /** 175 | Get GPOUT function (BAT_LOW or SOC_INT) 176 | 177 | @return true if BAT_LOW or false if SOC_INT 178 | */ 179 | bool GPOUTFunction(void); 180 | 181 | /** 182 | Set GPOUT function to BAT_LOW or SOC_INT 183 | 184 | @param function should be either BAT_LOW or SOC_INT 185 | @return true on success 186 | */ 187 | bool setGPOUTFunction(gpout_function function); 188 | 189 | /** 190 | Get SOC1_Set Threshold - threshold to set the alert flag 191 | 192 | @return state of charge value between 0 and 100% 193 | */ 194 | uint8_t SOC1SetThreshold(void); 195 | 196 | /** 197 | Get SOC1_Clear Threshold - threshold to clear the alert flag 198 | 199 | @return state of charge value between 0 and 100% 200 | */ 201 | uint8_t SOC1ClearThreshold(void); 202 | 203 | /** 204 | Set the SOC1 set and clear thresholds to a percentage 205 | 206 | @param set and clear percentages between 0 and 100. clear > set. 207 | @return true on success 208 | */ 209 | bool setSOC1Thresholds(uint8_t set, uint8_t clear); 210 | 211 | /** 212 | Get SOCF_Set Threshold - threshold to set the alert flag 213 | 214 | @return state of charge value between 0 and 100% 215 | */ 216 | uint8_t SOCFSetThreshold(void); 217 | 218 | /** 219 | Get SOCF_Clear Threshold - threshold to clear the alert flag 220 | 221 | @return state of charge value between 0 and 100% 222 | */ 223 | uint8_t SOCFClearThreshold(void); 224 | 225 | /** 226 | Set the SOCF set and clear thresholds to a percentage 227 | 228 | @param set and clear percentages between 0 and 100. clear > set. 229 | @return true on success 230 | */ 231 | bool setSOCFThresholds(uint8_t set, uint8_t clear); 232 | 233 | /** 234 | Check if the SOC1 flag is set in flags() 235 | 236 | @return true if flag is set 237 | */ 238 | bool socFlag(void); 239 | 240 | /** 241 | Check if the SOCF flag is set in flags() 242 | 243 | @return true if flag is set 244 | */ 245 | bool socfFlag(void); 246 | 247 | /** 248 | Get the SOC_INT interval delta 249 | 250 | @return interval percentage value between 1 and 100 251 | */ 252 | uint8_t sociDelta(void); 253 | 254 | /** 255 | Set the SOC_INT interval delta to a value between 1 and 100 256 | 257 | @param interval percentage value between 1 and 100 258 | @return true on success 259 | */ 260 | bool setSOCIDelta(uint8_t delta); 261 | 262 | /** 263 | Pulse the GPOUT pin - must be in SOC_INT mode 264 | 265 | @return true on success 266 | */ 267 | bool pulseGPOUT(void); 268 | 269 | ////////////////////////// 270 | // Control Sub-commands // 271 | ////////////////////////// 272 | 273 | /** 274 | Read the device type - should be 0x0421 275 | 276 | @return 16-bit value read from DEVICE_TYPE subcommand 277 | */ 278 | uint16_t deviceType(void); 279 | 280 | /** 281 | Enter configuration mode - set userControl if calling from an Arduino 282 | sketch and you want control over when to exitConfig. 283 | 284 | @param userControl is true if the Arduino sketch is handling entering 285 | and exiting config mode (should be false in library calls). 286 | @return true on success 287 | */ 288 | bool enterConfig(bool userControl = true); 289 | 290 | /** 291 | Exit configuration mode with the option to perform a resimulation 292 | 293 | @param resim is true if resimulation should be performed after exiting 294 | @return true on success 295 | */ 296 | bool exitConfig(bool resim = true); 297 | 298 | /** 299 | Read the flags() command 300 | 301 | @return 16-bit representation of flags() command register 302 | */ 303 | uint16_t flags(void); 304 | 305 | /** 306 | Read the CONTROL_STATUS subcommand of control() 307 | 308 | @return 16-bit representation of CONTROL_STATUS subcommand 309 | */ 310 | uint16_t status(void); 311 | 312 | private: 313 | uint8_t _deviceAddress; // Stores the BQ27441-G1A's I2C address 314 | bool _sealFlag; // Global to identify that IC was previously sealed 315 | bool _userConfigControl; // Global to identify that user has control over 316 | // entering/exiting config 317 | 318 | /** 319 | Check if the BQ27441-G1A is sealed or not. 320 | 321 | @return true if the chip is sealed 322 | */ 323 | bool sealed(void); 324 | 325 | /** 326 | Seal the BQ27441-G1A 327 | 328 | @return true on success 329 | */ 330 | bool seal(void); 331 | 332 | /** 333 | UNseal the BQ27441-G1A 334 | 335 | @return true on success 336 | */ 337 | bool unseal(void); 338 | 339 | /** 340 | Read the 16-bit opConfig register from extended data 341 | 342 | @return opConfig register contents 343 | */ 344 | uint16_t opConfig(void); 345 | 346 | /** 347 | Write the 16-bit opConfig register in extended data 348 | 349 | @param New 16-bit value for opConfig 350 | @return true on success 351 | */ 352 | bool writeOpConfig(uint16_t value); 353 | 354 | /** 355 | Issue a soft-reset to the BQ27441-G1A 356 | 357 | @return true on success 358 | */ 359 | bool softReset(void); 360 | 361 | /** 362 | Read a 16-bit command word from the BQ27441-G1A 363 | 364 | @param subAddress is the command to be read from 365 | @return 16-bit value of the command's contents 366 | */ 367 | uint16_t readWord(uint16_t subAddress); 368 | 369 | /** 370 | Read a 16-bit subcommand() from the BQ27441-G1A's control() 371 | 372 | @param function is the subcommand of control() to be read 373 | @return 16-bit value of the subcommand's contents 374 | */ 375 | uint16_t readControlWord(uint16_t function); 376 | 377 | /** 378 | Execute a subcommand() from the BQ27441-G1A's control() 379 | 380 | @param function is the subcommand of control() to be executed 381 | @return true on success 382 | */ 383 | bool executeControlWord(uint16_t function); 384 | 385 | //////////////////////////// 386 | // Extended Data Commands // 387 | //////////////////////////// 388 | /** 389 | Issue a BlockDataControl() command to enable BlockData access 390 | 391 | @return true on success 392 | */ 393 | bool blockDataControl(void); 394 | 395 | /** 396 | Issue a DataClass() command to set the data class to be accessed 397 | 398 | @param id is the id number of the class 399 | @return true on success 400 | */ 401 | bool blockDataClass(uint8_t id); 402 | 403 | /** 404 | Issue a DataBlock() command to set the data block to be accessed 405 | 406 | @param offset of the data block 407 | @return true on success 408 | */ 409 | bool blockDataOffset(uint8_t offset); 410 | 411 | /** 412 | Read the current checksum using BlockDataCheckSum() 413 | 414 | @return true on success 415 | */ 416 | uint8_t blockDataChecksum(void); 417 | 418 | /** 419 | Use BlockData() to read a byte from the loaded extended data 420 | 421 | @param offset of data block byte to be read 422 | @return true on success 423 | */ 424 | uint8_t readBlockData(uint8_t offset); 425 | 426 | /** 427 | Use BlockData() to write a byte to an offset of the loaded data 428 | 429 | @param offset is the position of the byte to be written 430 | data is the value to be written 431 | @return true on success 432 | */ 433 | bool writeBlockData(uint8_t offset, uint8_t data); 434 | 435 | /** 436 | Read all 32 bytes of the loaded extended data and compute a 437 | checksum based on the values. 438 | 439 | @return 8-bit checksum value calculated based on loaded data 440 | */ 441 | uint8_t computeBlockChecksum(void); 442 | 443 | /** 444 | Use the BlockDataCheckSum() command to write a checksum value 445 | 446 | @param csum is the 8-bit checksum to be written 447 | @return true on success 448 | */ 449 | bool writeBlockChecksum(uint8_t csum); 450 | 451 | /** 452 | Read a byte from extended data specifying a class ID and position offset 453 | 454 | @param classID is the id of the class to be read from 455 | offset is the byte position of the byte to be read 456 | @return 8-bit value of specified data 457 | */ 458 | uint8_t readExtendedData(uint8_t classID, uint8_t offset); 459 | 460 | /** 461 | Write a specified number of bytes to extended data specifying a 462 | class ID, position offset. 463 | 464 | @param classID is the id of the class to be read from 465 | offset is the byte position of the byte to be read 466 | data is the data buffer to be written 467 | len is the number of bytes to be written 468 | @return true on success 469 | */ 470 | bool writeExtendedData(uint8_t classID, uint8_t offset, uint8_t * data, uint8_t len); 471 | 472 | ///////////////////////////////// 473 | // I2C Read and Write Routines // 474 | ///////////////////////////////// 475 | 476 | /** 477 | Read a specified number of bytes over I2C at a given subAddress 478 | 479 | @param subAddress is the 8-bit address of the data to be read 480 | dest is the data buffer to be written to 481 | count is the number of bytes to be read 482 | @return true on success 483 | */ 484 | int16_t i2cReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count); 485 | 486 | /** 487 | Write a specified number of bytes over I2C to a given subAddress 488 | 489 | @param subAddress is the 8-bit address of the data to be written to 490 | src is the data buffer to be written 491 | count is the number of bytes to be written 492 | @return true on success 493 | */ 494 | uint16_t i2cWriteBytes(uint8_t subAddress, uint8_t * src, uint8_t count); 495 | }; 496 | 497 | extern BQ27441 lipo; // Use lipo.[] to interact with the library in an Arduino sketch 498 | // Thanks for reading! 499 | 500 | #endif 501 | -------------------------------------------------------------------------------- /binary.h: -------------------------------------------------------------------------------- 1 | //Original code from: http://code.google.com/p/arduino/source/browse/trunk/hardware/arduino/cores/arduino/binary.h 2 | 3 | #ifndef Binary_h 4 | #define Binary_h 5 | 6 | #define B0 0 7 | #define B00 0 8 | #define B000 0 9 | #define B0000 0 10 | #define B00000 0 11 | #define B000000 0 12 | #define B0000000 0 13 | #define B00000000 0 14 | #define B1 1 15 | #define B01 1 16 | #define B001 1 17 | #define B0001 1 18 | #define B00001 1 19 | #define B000001 1 20 | #define B0000001 1 21 | #define B00000001 1 22 | #define B10 2 23 | #define B010 2 24 | #define B0010 2 25 | #define B00010 2 26 | #define B000010 2 27 | #define B0000010 2 28 | #define B00000010 2 29 | #define B11 3 30 | #define B011 3 31 | #define B0011 3 32 | #define B00011 3 33 | #define B000011 3 34 | #define B0000011 3 35 | #define B00000011 3 36 | #define B100 4 37 | #define B0100 4 38 | #define B00100 4 39 | #define B000100 4 40 | #define B0000100 4 41 | #define B00000100 4 42 | #define B101 5 43 | #define B0101 5 44 | #define B00101 5 45 | #define B000101 5 46 | #define B0000101 5 47 | #define B00000101 5 48 | #define B110 6 49 | #define B0110 6 50 | #define B00110 6 51 | #define B000110 6 52 | #define B0000110 6 53 | #define B00000110 6 54 | #define B111 7 55 | #define B0111 7 56 | #define B00111 7 57 | #define B000111 7 58 | #define B0000111 7 59 | #define B00000111 7 60 | #define B1000 8 61 | #define B01000 8 62 | #define B001000 8 63 | #define B0001000 8 64 | #define B00001000 8 65 | #define B1001 9 66 | #define B01001 9 67 | #define B001001 9 68 | #define B0001001 9 69 | #define B00001001 9 70 | #define B1010 10 71 | #define B01010 10 72 | #define B001010 10 73 | #define B0001010 10 74 | #define B00001010 10 75 | #define B1011 11 76 | #define B01011 11 77 | #define B001011 11 78 | #define B0001011 11 79 | #define B00001011 11 80 | #define B1100 12 81 | #define B01100 12 82 | #define B001100 12 83 | #define B0001100 12 84 | #define B00001100 12 85 | #define B1101 13 86 | #define B01101 13 87 | #define B001101 13 88 | #define B0001101 13 89 | #define B00001101 13 90 | #define B1110 14 91 | #define B01110 14 92 | #define B001110 14 93 | #define B0001110 14 94 | #define B00001110 14 95 | #define B1111 15 96 | #define B01111 15 97 | #define B001111 15 98 | #define B0001111 15 99 | #define B00001111 15 100 | #define B10000 16 101 | #define B010000 16 102 | #define B0010000 16 103 | #define B00010000 16 104 | #define B10001 17 105 | #define B010001 17 106 | #define B0010001 17 107 | #define B00010001 17 108 | #define B10010 18 109 | #define B010010 18 110 | #define B0010010 18 111 | #define B00010010 18 112 | #define B10011 19 113 | #define B010011 19 114 | #define B0010011 19 115 | #define B00010011 19 116 | #define B10100 20 117 | #define B010100 20 118 | #define B0010100 20 119 | #define B00010100 20 120 | #define B10101 21 121 | #define B010101 21 122 | #define B0010101 21 123 | #define B00010101 21 124 | #define B10110 22 125 | #define B010110 22 126 | #define B0010110 22 127 | #define B00010110 22 128 | #define B10111 23 129 | #define B010111 23 130 | #define B0010111 23 131 | #define B00010111 23 132 | #define B11000 24 133 | #define B011000 24 134 | #define B0011000 24 135 | #define B00011000 24 136 | #define B11001 25 137 | #define B011001 25 138 | #define B0011001 25 139 | #define B00011001 25 140 | #define B11010 26 141 | #define B011010 26 142 | #define B0011010 26 143 | #define B00011010 26 144 | #define B11011 27 145 | #define B011011 27 146 | #define B0011011 27 147 | #define B00011011 27 148 | #define B11100 28 149 | #define B011100 28 150 | #define B0011100 28 151 | #define B00011100 28 152 | #define B11101 29 153 | #define B011101 29 154 | #define B0011101 29 155 | #define B00011101 29 156 | #define B11110 30 157 | #define B011110 30 158 | #define B0011110 30 159 | #define B00011110 30 160 | #define B11111 31 161 | #define B011111 31 162 | #define B0011111 31 163 | #define B00011111 31 164 | #define B100000 32 165 | #define B0100000 32 166 | #define B00100000 32 167 | #define B100001 33 168 | #define B0100001 33 169 | #define B00100001 33 170 | #define B100010 34 171 | #define B0100010 34 172 | #define B00100010 34 173 | #define B100011 35 174 | #define B0100011 35 175 | #define B00100011 35 176 | #define B100100 36 177 | #define B0100100 36 178 | #define B00100100 36 179 | #define B100101 37 180 | #define B0100101 37 181 | #define B00100101 37 182 | #define B100110 38 183 | #define B0100110 38 184 | #define B00100110 38 185 | #define B100111 39 186 | #define B0100111 39 187 | #define B00100111 39 188 | #define B101000 40 189 | #define B0101000 40 190 | #define B00101000 40 191 | #define B101001 41 192 | #define B0101001 41 193 | #define B00101001 41 194 | #define B101010 42 195 | #define B0101010 42 196 | #define B00101010 42 197 | #define B101011 43 198 | #define B0101011 43 199 | #define B00101011 43 200 | #define B101100 44 201 | #define B0101100 44 202 | #define B00101100 44 203 | #define B101101 45 204 | #define B0101101 45 205 | #define B00101101 45 206 | #define B101110 46 207 | #define B0101110 46 208 | #define B00101110 46 209 | #define B101111 47 210 | #define B0101111 47 211 | #define B00101111 47 212 | #define B110000 48 213 | #define B0110000 48 214 | #define B00110000 48 215 | #define B110001 49 216 | #define B0110001 49 217 | #define B00110001 49 218 | #define B110010 50 219 | #define B0110010 50 220 | #define B00110010 50 221 | #define B110011 51 222 | #define B0110011 51 223 | #define B00110011 51 224 | #define B110100 52 225 | #define B0110100 52 226 | #define B00110100 52 227 | #define B110101 53 228 | #define B0110101 53 229 | #define B00110101 53 230 | #define B110110 54 231 | #define B0110110 54 232 | #define B00110110 54 233 | #define B110111 55 234 | #define B0110111 55 235 | #define B00110111 55 236 | #define B111000 56 237 | #define B0111000 56 238 | #define B00111000 56 239 | #define B111001 57 240 | #define B0111001 57 241 | #define B00111001 57 242 | #define B111010 58 243 | #define B0111010 58 244 | #define B00111010 58 245 | #define B111011 59 246 | #define B0111011 59 247 | #define B00111011 59 248 | #define B111100 60 249 | #define B0111100 60 250 | #define B00111100 60 251 | #define B111101 61 252 | #define B0111101 61 253 | #define B00111101 61 254 | #define B111110 62 255 | #define B0111110 62 256 | #define B00111110 62 257 | #define B111111 63 258 | #define B0111111 63 259 | #define B00111111 63 260 | #define B1000000 64 261 | #define B01000000 64 262 | #define B1000001 65 263 | #define B01000001 65 264 | #define B1000010 66 265 | #define B01000010 66 266 | #define B1000011 67 267 | #define B01000011 67 268 | #define B1000100 68 269 | #define B01000100 68 270 | #define B1000101 69 271 | #define B01000101 69 272 | #define B1000110 70 273 | #define B01000110 70 274 | #define B1000111 71 275 | #define B01000111 71 276 | #define B1001000 72 277 | #define B01001000 72 278 | #define B1001001 73 279 | #define B01001001 73 280 | #define B1001010 74 281 | #define B01001010 74 282 | #define B1001011 75 283 | #define B01001011 75 284 | #define B1001100 76 285 | #define B01001100 76 286 | #define B1001101 77 287 | #define B01001101 77 288 | #define B1001110 78 289 | #define B01001110 78 290 | #define B1001111 79 291 | #define B01001111 79 292 | #define B1010000 80 293 | #define B01010000 80 294 | #define B1010001 81 295 | #define B01010001 81 296 | #define B1010010 82 297 | #define B01010010 82 298 | #define B1010011 83 299 | #define B01010011 83 300 | #define B1010100 84 301 | #define B01010100 84 302 | #define B1010101 85 303 | #define B01010101 85 304 | #define B1010110 86 305 | #define B01010110 86 306 | #define B1010111 87 307 | #define B01010111 87 308 | #define B1011000 88 309 | #define B01011000 88 310 | #define B1011001 89 311 | #define B01011001 89 312 | #define B1011010 90 313 | #define B01011010 90 314 | #define B1011011 91 315 | #define B01011011 91 316 | #define B1011100 92 317 | #define B01011100 92 318 | #define B1011101 93 319 | #define B01011101 93 320 | #define B1011110 94 321 | #define B01011110 94 322 | #define B1011111 95 323 | #define B01011111 95 324 | #define B1100000 96 325 | #define B01100000 96 326 | #define B1100001 97 327 | #define B01100001 97 328 | #define B1100010 98 329 | #define B01100010 98 330 | #define B1100011 99 331 | #define B01100011 99 332 | #define B1100100 100 333 | #define B01100100 100 334 | #define B1100101 101 335 | #define B01100101 101 336 | #define B1100110 102 337 | #define B01100110 102 338 | #define B1100111 103 339 | #define B01100111 103 340 | #define B1101000 104 341 | #define B01101000 104 342 | #define B1101001 105 343 | #define B01101001 105 344 | #define B1101010 106 345 | #define B01101010 106 346 | #define B1101011 107 347 | #define B01101011 107 348 | #define B1101100 108 349 | #define B01101100 108 350 | #define B1101101 109 351 | #define B01101101 109 352 | #define B1101110 110 353 | #define B01101110 110 354 | #define B1101111 111 355 | #define B01101111 111 356 | #define B1110000 112 357 | #define B01110000 112 358 | #define B1110001 113 359 | #define B01110001 113 360 | #define B1110010 114 361 | #define B01110010 114 362 | #define B1110011 115 363 | #define B01110011 115 364 | #define B1110100 116 365 | #define B01110100 116 366 | #define B1110101 117 367 | #define B01110101 117 368 | #define B1110110 118 369 | #define B01110110 118 370 | #define B1110111 119 371 | #define B01110111 119 372 | #define B1111000 120 373 | #define B01111000 120 374 | #define B1111001 121 375 | #define B01111001 121 376 | #define B1111010 122 377 | #define B01111010 122 378 | #define B1111011 123 379 | #define B01111011 123 380 | #define B1111100 124 381 | #define B01111100 124 382 | #define B1111101 125 383 | #define B01111101 125 384 | #define B1111110 126 385 | #define B01111110 126 386 | #define B1111111 127 387 | #define B01111111 127 388 | #define B10000000 128 389 | #define B10000001 129 390 | #define B10000010 130 391 | #define B10000011 131 392 | #define B10000100 132 393 | #define B10000101 133 394 | #define B10000110 134 395 | #define B10000111 135 396 | #define B10001000 136 397 | #define B10001001 137 398 | #define B10001010 138 399 | #define B10001011 139 400 | #define B10001100 140 401 | #define B10001101 141 402 | #define B10001110 142 403 | #define B10001111 143 404 | #define B10010000 144 405 | #define B10010001 145 406 | #define B10010010 146 407 | #define B10010011 147 408 | #define B10010100 148 409 | #define B10010101 149 410 | #define B10010110 150 411 | #define B10010111 151 412 | #define B10011000 152 413 | #define B10011001 153 414 | #define B10011010 154 415 | #define B10011011 155 416 | #define B10011100 156 417 | #define B10011101 157 418 | #define B10011110 158 419 | #define B10011111 159 420 | #define B10100000 160 421 | #define B10100001 161 422 | #define B10100010 162 423 | #define B10100011 163 424 | #define B10100100 164 425 | #define B10100101 165 426 | #define B10100110 166 427 | #define B10100111 167 428 | #define B10101000 168 429 | #define B10101001 169 430 | #define B10101010 170 431 | #define B10101011 171 432 | #define B10101100 172 433 | #define B10101101 173 434 | #define B10101110 174 435 | #define B10101111 175 436 | #define B10110000 176 437 | #define B10110001 177 438 | #define B10110010 178 439 | #define B10110011 179 440 | #define B10110100 180 441 | #define B10110101 181 442 | #define B10110110 182 443 | #define B10110111 183 444 | #define B10111000 184 445 | #define B10111001 185 446 | #define B10111010 186 447 | #define B10111011 187 448 | #define B10111100 188 449 | #define B10111101 189 450 | #define B10111110 190 451 | #define B10111111 191 452 | #define B11000000 192 453 | #define B11000001 193 454 | #define B11000010 194 455 | #define B11000011 195 456 | #define B11000100 196 457 | #define B11000101 197 458 | #define B11000110 198 459 | #define B11000111 199 460 | #define B11001000 200 461 | #define B11001001 201 462 | #define B11001010 202 463 | #define B11001011 203 464 | #define B11001100 204 465 | #define B11001101 205 466 | #define B11001110 206 467 | #define B11001111 207 468 | #define B11010000 208 469 | #define B11010001 209 470 | #define B11010010 210 471 | #define B11010011 211 472 | #define B11010100 212 473 | #define B11010101 213 474 | #define B11010110 214 475 | #define B11010111 215 476 | #define B11011000 216 477 | #define B11011001 217 478 | #define B11011010 218 479 | #define B11011011 219 480 | #define B11011100 220 481 | #define B11011101 221 482 | #define B11011110 222 483 | #define B11011111 223 484 | #define B11100000 224 485 | #define B11100001 225 486 | #define B11100010 226 487 | #define B11100011 227 488 | #define B11100100 228 489 | #define B11100101 229 490 | #define B11100110 230 491 | #define B11100111 231 492 | #define B11101000 232 493 | #define B11101001 233 494 | #define B11101010 234 495 | #define B11101011 235 496 | #define B11101100 236 497 | #define B11101101 237 498 | #define B11101110 238 499 | #define B11101111 239 500 | #define B11110000 240 501 | #define B11110001 241 502 | #define B11110010 242 503 | #define B11110011 243 504 | #define B11110100 244 505 | #define B11110101 245 506 | #define B11110110 246 507 | #define B11110111 247 508 | #define B11111000 248 509 | #define B11111001 249 510 | #define B11111010 250 511 | #define B11111011 251 512 | #define B11111100 252 513 | #define B11111101 253 514 | #define B11111110 254 515 | #define B11111111 255 516 | 517 | #endif -------------------------------------------------------------------------------- /downsampler.cpp: -------------------------------------------------------------------------------- 1 | #include "downsampler.h" 2 | #include 3 | 4 | //Constructor 5 | Downsampler::Downsampler(int ratio) 6 | { 7 | //Float vectors for storing coefficients 8 | vector> coeffs; 9 | vector sos(6); 10 | 11 | if (ratio==5) 12 | { 13 | sos={(float)0.24523728, 0.24523728, 0.000000e+00, 1.000000e+00, -0.50952545, 0.000000e+00}; 14 | } 15 | else if(ratio==10){ 16 | sos={(float)0.02008337, 0.04016673, 0.02008337, 1.0, -1.56101808,0.64135154}; 17 | } 18 | else if(ratio==20){ 19 | sos={(float)0.00554272, 0.01108543, 0.00554272, 1.0, -1.77863178,0.80080265}; 20 | } 21 | else if(ratio==50){ 22 | sos={(float)9.44691844e-04, 1.88938369e-03, 9.44691844e-04, 1.00000000e+00, -1.91119707e+00, 9.14975835e-01}; 23 | } 24 | else if(ratio==100){ 25 | sos={(float)2.41359049e-04, 4.82718098e-04, 2.41359049e-04,1.00000000e+00, -1.95557824e+00, 9.56543677e-01}; 26 | } 27 | 28 | coeffs.push_back(sos); 29 | f= new Filter(coeffs, 1.0);; 30 | this->ratio=ratio; 31 | this->ready=false; 32 | this->lastSample=0; 33 | this->sampleCount=ratio-2; 34 | } 35 | 36 | //Update feature extractor with new sample 37 | 38 | void Downsampler::update (float value) 39 | { 40 | lastSample=f->filter(value); 41 | 42 | sampleCount+=1; 43 | //If sample count has reached window length, normalize, set flag as ready and reset sample count 44 | if(sampleCount>=ratio) 45 | { 46 | ready=true; 47 | sampleCount=0; 48 | } 49 | 50 | } 51 | 52 | //Return ready state 53 | bool Downsampler::isReady() 54 | { 55 | return ready; 56 | } 57 | 58 | 59 | //Return sample 60 | float Downsampler::getSample() 61 | { 62 | ready=false; 63 | return lastSample; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /downsampler.h: -------------------------------------------------------------------------------- 1 | //#include 2 | #include 3 | #include "filter.h" 4 | using namespace std; 5 | 6 | //feature Extractor class. Computes energy for each stored filter 7 | 8 | class Downsampler { 9 | 10 | //Filter 11 | Filter* f; 12 | 13 | //downsampling ratio 14 | int ratio; 15 | 16 | //updated sample count 17 | int sampleCount; 18 | 19 | //ready flag 20 | bool ready; 21 | 22 | //last sample 23 | float lastSample; 24 | 25 | public: 26 | Downsampler (int ratio); 27 | void update(float value); 28 | bool isReady(); 29 | float getSample(); 30 | 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /filter.cpp: -------------------------------------------------------------------------------- 1 | #include "filter.h" 2 | 3 | 4 | //Constructor 5 | Filter::Filter (vector > coeffs, float g){ 6 | this->g=g; 7 | this->coeffs=coeffs; 8 | this->size=coeffs.size(); 9 | this->z.resize(size); 10 | 11 | //Set z to 0 12 | for (int i=0; iz[i].begin(), this->z[i].end(), 0); 16 | } 17 | } 18 | 19 | 20 | //Filter function based on sequential 2nd order section filtering (Direct Form II) 21 | //coeffs structure: 22 | //b01 b11 b21 1 a11 a21 23 | //b02 b12 b22 1 a12 a22 24 | //... 25 | float Filter::filter (float x) 26 | { 27 | float y=g*x; 28 | 29 | float v=0; 30 | 31 | //for each 2nd order section 32 | for (int f=0; f 2 | #include 3 | using namespace std; 4 | 5 | #ifndef _FILTER_ 6 | #define _FILTER_ 7 | 8 | 9 | //Simple bandpass filter 10 | 11 | class Filter 12 | { 13 | 14 | //Number of second order sections 15 | int size; 16 | 17 | 18 | //Filter coefficients, organized in second order sections: 19 | //b01 b11 b21 1 a11 a21 20 | //b02 b12 b22 1 a12 a22 21 | vector > coeffs; 22 | 23 | //Filter gain 24 | float g; 25 | 26 | //Buffers 27 | vector > z; 28 | 29 | public: 30 | Filter (vector > coeffs, float g); 31 | float filter (float); 32 | 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /fw19_OS.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensourcebeehives/BuzzBoard_firmware/bd5be0be936579e5d149e35586315f8e1a2da116/fw19_OS.bin -------------------------------------------------------------------------------- /geolocate.cpp: -------------------------------------------------------------------------------- 1 | #include "Particle.h" 2 | #include "google-maps-device-locator.h" 3 | 4 | void locationCallback(float lat, float lon, float accuracy); 5 | 6 | SerialLogHandler logHandler; 7 | GoogleMapsDeviceLocator locator; 8 | 9 | SYSTEM_THREAD(ENABLED); 10 | 11 | 12 | void setup() { 13 | Serial.begin(9600); 14 | // locator.withLocatePeriodic(60).withEventName("geoLocate").withSubscribe(locationCallback); 15 | locator.withEventName("geoLocate").withSubscribe(locationCallback); 16 | } 17 | 18 | 19 | void loop() { 20 | locator.loop(); 21 | 22 | delay(3000); 23 | locator.publishLocation(); 24 | delay(1000); 25 | } 26 | 27 | void locationCallback(float lat, float lon, float accuracy) { 28 | Serial.printlnf("lat=%f lon=%f accuracy=%f", lat, lon, accuracy); 29 | } 30 | -------------------------------------------------------------------------------- /google-maps-device-locator.cpp: -------------------------------------------------------------------------------- 1 | #include "Particle.h" 2 | #include "google-maps-device-locator.h" 3 | 4 | #if Wiring_Cellular 5 | # include "CellularHelper.h" 6 | #endif 7 | 8 | static char requestBuf[256]; 9 | static char *requestCur; 10 | static int numAdded = 0; 11 | 12 | GoogleMapsDeviceLocator::GoogleMapsDeviceLocator() : locatorMode(LOCATOR_MODE_MANUAL), periodMs(10000), eventName("deviceLocator"), 13 | stateTime(0), state(CONNECT_WAIT_STATE), callback(NULL), waitAfterConnect(8000) { 14 | 15 | } 16 | 17 | GoogleMapsDeviceLocator::~GoogleMapsDeviceLocator() { 18 | 19 | } 20 | 21 | GoogleMapsDeviceLocator &GoogleMapsDeviceLocator::withLocateOnce() { 22 | locatorMode = LOCATOR_MODE_ONCE; 23 | return *this; 24 | } 25 | 26 | GoogleMapsDeviceLocator &GoogleMapsDeviceLocator::withLocatePeriodic(unsigned long secondsPeriodic) { 27 | locatorMode = LOCATOR_MODE_PERIODIC; 28 | if (secondsPeriodic < 5) { 29 | secondsPeriodic = 5; 30 | } 31 | periodMs = secondsPeriodic * 1000; 32 | return *this; 33 | } 34 | 35 | GoogleMapsDeviceLocator &GoogleMapsDeviceLocator::withEventName(const char *name) { 36 | this->eventName = name; 37 | return *this; 38 | } 39 | 40 | GoogleMapsDeviceLocator &GoogleMapsDeviceLocator::withSubscribe(GoogleMapsDeviceLocatorSubscriptionCallback callback) { 41 | this->callback = callback; 42 | 43 | snprintf(requestBuf, sizeof(requestBuf), "hook-response/%s/%s", eventName.c_str(), System.deviceID().c_str()); 44 | 45 | Particle.subscribe(requestBuf, &GoogleMapsDeviceLocator::subscriptionHandler, this, MY_DEVICES); 46 | 47 | return *this; 48 | } 49 | 50 | void GoogleMapsDeviceLocator::loop() { 51 | switch(state) { 52 | case CONNECT_WAIT_STATE: 53 | if (Particle.connected()) { 54 | state = CONNECTED_WAIT_STATE; 55 | stateTime = millis(); 56 | } 57 | break; 58 | 59 | case CONNECTED_WAIT_STATE: 60 | if (millis() - stateTime >= waitAfterConnect) { 61 | // Wait several seconds after connecting before doing the location 62 | if (locatorMode == LOCATOR_MODE_ONCE) { 63 | publishLocation(); 64 | 65 | state = IDLE_STATE; 66 | } 67 | else 68 | if (locatorMode == LOCATOR_MODE_MANUAL) { 69 | state = IDLE_STATE; 70 | } 71 | else { 72 | state = CONNECTED_STATE; 73 | stateTime = millis() - periodMs; 74 | } 75 | } 76 | break; 77 | 78 | case CONNECTED_STATE: 79 | if (Particle.connected()) { 80 | if (millis() - stateTime >= periodMs) { 81 | stateTime = millis(); 82 | publishLocation(); 83 | } 84 | } 85 | else { 86 | // We have disconnected, rec 87 | state = CONNECT_WAIT_STATE; 88 | } 89 | break; 90 | 91 | 92 | case IDLE_STATE: 93 | // Just hang out here forever (entered only on LOCATOR_MODE_ONCE) 94 | break; 95 | } 96 | 97 | } 98 | 99 | const char *GoogleMapsDeviceLocator::scan() { 100 | #if Wiring_WiFi 101 | return wifiScan(); 102 | #endif 103 | #if Wiring_Cellular 104 | return cellularScan(); 105 | #endif 106 | } 107 | 108 | 109 | void GoogleMapsDeviceLocator::publishLocation() { 110 | 111 | Serial.println("publishLocation"); 112 | 113 | const char *scanData = scan(); 114 | 115 | Serial.printlnf("scanData=%s", scanData); 116 | 117 | if (scanData[0]) { 118 | 119 | if (Particle.connected()) { 120 | Particle.publish(eventName, scanData, PRIVATE); 121 | } 122 | } 123 | } 124 | 125 | void GoogleMapsDeviceLocator::subscriptionHandler(const char *event, const char *data) { 126 | // event: hook-response/deviceLocator//0 127 | 128 | if (callback) { 129 | // float lat, float lon, float accuracy 130 | char *mutableCopy = strdup(data); 131 | char *part, *end; 132 | float lat, lon, accuracy; 133 | 134 | part = strtok_r(mutableCopy, ",", &end); 135 | if (part) { 136 | lat = atof(part); 137 | part = strtok_r(NULL, ",", &end); 138 | if (part) { 139 | lon = atof(part); 140 | part = strtok_r(NULL, ",", &end); 141 | if (part) { 142 | accuracy = atof(part); 143 | 144 | (*callback)(lat, lon, accuracy); 145 | } 146 | } 147 | } 148 | 149 | free(mutableCopy); 150 | } 151 | } 152 | 153 | 154 | 155 | #if Wiring_WiFi 156 | 157 | static void wifiScanCallback(WiFiAccessPoint* wap, void* data) { 158 | // The - 3 factor here to leave room for the closing JSON array ] object }} and the trailing null 159 | size_t spaceLeft = &requestBuf[sizeof(requestBuf) - 3] - requestCur; 160 | if (spaceLeft < 30) { 161 | return; 162 | } 163 | 164 | int sizeNeeded = snprintf(requestCur, spaceLeft, 165 | "%s{\"m\":\"%02x:%02x:%02x:%02x:%02x:%02x\",\"s\":%d,\"c\":%d}", 166 | (requestCur[-1] == '[' ? "" : ","), 167 | wap->bssid[0], wap->bssid[1], wap->bssid[2], wap->bssid[3], wap->bssid[4], wap->bssid[5], 168 | wap->rssi, wap->channel); 169 | if (sizeNeeded > 0 && sizeNeeded < (int)spaceLeft) { 170 | // There is enough space to store the whole entry, so save it 171 | requestCur += sizeNeeded; 172 | numAdded++; 173 | } 174 | } 175 | 176 | 177 | const char *GoogleMapsDeviceLocator::wifiScan() { 178 | 179 | requestCur = requestBuf; 180 | numAdded = 0; 181 | 182 | requestCur += sprintf(requestCur, "{\"w\":{\"a\":"); 183 | *requestCur++ = '['; 184 | 185 | WiFi.scan(wifiScanCallback); 186 | 187 | *requestCur++ = ']'; 188 | *requestCur++ = '}'; 189 | *requestCur++ = '}'; 190 | *requestCur++ = 0; 191 | 192 | if (numAdded == 0) { 193 | requestBuf[0] = 0; 194 | } 195 | 196 | return requestBuf; 197 | } 198 | 199 | #endif /* Wiring_WiFi */ 200 | 201 | 202 | #if Wiring_Cellular 203 | 204 | static void cellularAddTower(const CellularHelperEnvironmentCellData *cellData) { 205 | // The - 4 factor here to leave room for the closing JSON array ], object }}, and the trailing null 206 | size_t spaceLeft = &requestBuf[sizeof(requestBuf) - 4] - requestCur; 207 | 208 | int sizeNeeded = snprintf(requestCur, spaceLeft, 209 | "%s{\"i\":%d,\"l\":%u,\"c\":%d,\"n\":%d}", 210 | (requestCur[-1] == '[' ? "" : ","), 211 | cellData->ci, cellData->lac, cellData->mcc, cellData->mnc); 212 | 213 | if (sizeNeeded > 0 && sizeNeeded < (int)spaceLeft && cellData->lac != 0 && cellData->lac != 65535 && cellData->mcc != 65535 && cellData->mnc != 65535) { 214 | // There is enough space to store the whole entry, so save it 215 | requestCur += sizeNeeded; 216 | numAdded++; 217 | } 218 | 219 | } 220 | 221 | const char *GoogleMapsDeviceLocator::cellularScan() { 222 | 223 | // First try to get info on neighboring cells. This doesn't work for me using the U260 224 | CellularHelperEnvironmentResponseStatic<4> envResp; 225 | 226 | CellularHelper.getEnvironment(5, envResp); 227 | 228 | if (envResp.resp != RESP_OK) { 229 | // We couldn't get neighboring cells, so try just the receiving cell 230 | CellularHelper.getEnvironment(3, envResp); 231 | } 232 | // envResp.serialDebug(); 233 | 234 | 235 | requestCur = requestBuf; 236 | numAdded = 0; 237 | 238 | // We know these things fit, so just using sprintf instead of snprintf here 239 | requestCur += sprintf(requestCur, "{\"c\":{\"o\":\"%s\",", 240 | CellularHelper.getOperatorName().c_str()); 241 | 242 | requestCur += sprintf(requestCur, "\"a\":["); 243 | 244 | cellularAddTower(&envResp.service); 245 | 246 | for(size_t ii = 0; ii < envResp.getNumNeighbors(); ii++) { 247 | cellularAddTower(&envResp.neighbors[ii]); 248 | } 249 | 250 | *requestCur++ = ']'; 251 | *requestCur++ = '}'; 252 | *requestCur++ = '}'; 253 | *requestCur++ = 0; 254 | 255 | if (numAdded == 0) { 256 | requestBuf[0] = 0; 257 | } 258 | 259 | return requestBuf; 260 | } 261 | 262 | 263 | #endif /* Wiring_Cellular */ 264 | 265 | 266 | 267 | 268 | 269 | -------------------------------------------------------------------------------- /google-maps-device-locator.h: -------------------------------------------------------------------------------- 1 | #ifndef __GOOGLEMAPSDEVICELOCATOR_H 2 | #define __GOOGLEMAPSDEVICELOCATOR_H 3 | 4 | 5 | #include "Particle.h" 6 | 7 | /** 8 | * This is the callback function prototype for the callback if you use the 9 | * withSubscribe() method to be notified of your own location. 10 | * 11 | * lat is the latitude in degrees 12 | * lon is the longitude in degrees 13 | * accuracy is the accuracy radius in meters 14 | */ 15 | typedef void (*GoogleMapsDeviceLocatorSubscriptionCallback)(float lat, float lon, float accuracy); 16 | 17 | class GoogleMapsDeviceLocator { 18 | public: 19 | GoogleMapsDeviceLocator(); 20 | virtual ~GoogleMapsDeviceLocator(); 21 | 22 | /** 23 | * If you use withLocateOnce() then the location will be updated once after you've 24 | * connected to the cloud. 25 | */ 26 | GoogleMapsDeviceLocator &withLocateOnce(); 27 | 28 | /** 29 | * If you use withLocatePeriod() then the location will be updated this often when 30 | * connected to the cloud. 31 | * 32 | * If you use neither withLocateOnce() nor withLocatePeriodic() you can check manually 33 | * using publishLocation() instead 34 | */ 35 | GoogleMapsDeviceLocator &withLocatePeriodic(unsigned long secondsPeriodic); 36 | 37 | /** 38 | * The default event name is "deviceLocator". Use this method to change it. Note that this 39 | * name is also configured in your Google location integration and must be changed in both 40 | * locations or location detection will not work 41 | */ 42 | GoogleMapsDeviceLocator &withEventName(const char *name); 43 | 44 | /** 45 | * Use this method to register a callback to be notified when your location has been found. 46 | * 47 | * The prototype for the function is: 48 | * 49 | * void locationCallback(float lat, float lon, float accuracy) 50 | */ 51 | GoogleMapsDeviceLocator &withSubscribe(GoogleMapsDeviceLocatorSubscriptionCallback callback); 52 | 53 | /** 54 | * You should call this from loop() to give the code time to process things in the background. 55 | * This is really only needed if you use withLocateOnce() or withLocatePeriodic() but it doesn't 56 | * hurt to call it always from loop. It executes quickly. 57 | */ 58 | void loop(); 59 | 60 | /** 61 | * You can use this to manually publish your location. It finds the Wi-Fi or cellular location 62 | * using scan() and then publishes it as an event 63 | */ 64 | void publishLocation(); 65 | 66 | /** 67 | * Queries the location information (Wi-Fi or cellular) and returns a JSON block of data to 68 | * put in the event data. This returns a static buffer pointer and is not reentrant. 69 | */ 70 | const char *scan(); 71 | 72 | protected: 73 | void subscriptionHandler(const char *event, const char *data); 74 | 75 | #if Wiring_WiFi 76 | const char *wifiScan(); 77 | #endif 78 | 79 | #if Wiring_Cellular 80 | const char *cellularScan(); 81 | #endif 82 | 83 | static const int CONNECT_WAIT_STATE = 0; 84 | static const int CONNECTED_WAIT_STATE = 2; 85 | static const int CONNECTED_STATE = 3; 86 | static const int IDLE_STATE = 4; 87 | 88 | static const int LOCATOR_MODE_MANUAL = 0; 89 | static const int LOCATOR_MODE_ONCE = 1; 90 | static const int LOCATOR_MODE_PERIODIC = 2; 91 | 92 | int locatorMode; 93 | unsigned long periodMs; 94 | String eventName; 95 | unsigned long stateTime; 96 | int state; 97 | GoogleMapsDeviceLocatorSubscriptionCallback callback; 98 | unsigned long waitAfterConnect; 99 | }; 100 | 101 | #endif /* __GOOGLEMAPSDEVICELOCATOR_H */ 102 | 103 | -------------------------------------------------------------------------------- /main_beta_comp.cpp: -------------------------------------------------------------------------------- 1 | // BzBoard Firmware Application - June 2018 2 | // OSBeehives, Inc. Development 3 | // Created by: Peter Naylor, Javier Abors 4 | // 5 | // 6 | // LOTS of help and reference from Particle.io community: 7 | // ricckas7, ScruffR, peekay123, kennethlimcp, etc. 8 | // 9 | // 10 | // Libraries can be found here: 11 | // https://github.com/rickkas7/photonAudio 12 | // https://github.com/sparkfun/Battery_Babysitter 13 | // 14 | // TODO: 15 | // - Deep sleep 16 | // - Memory card backup for audio samples 17 | // - Sleep mode for peripheralz 18 | 19 | 20 | #include "Particle.h" 21 | #include "softap_bb.h" 22 | #include "google-maps-device-locator.h" 23 | #include "ClosedCube_SHT31D.h" 24 | #include "SparkFunBQ27441.h" 25 | #include "MMA8452-xlero-OSBH.h" 26 | #include "adc_hal.h" 27 | #include "gpio_hal.h" 28 | #include "pinmap_hal.h" 29 | #include "pinmap_impl.h" 30 | #include 31 | #include "params.h" 32 | #include "downsampler.h" 33 | 34 | using namespace std; 35 | 36 | const int FW_VERSION = 21; 37 | 38 | 39 | // System threading is required for this project 40 | SYSTEM_THREAD(ENABLED); 41 | SYSTEM_MODE(SEMI_AUTOMATIC); 42 | 43 | // Class instances 44 | MMA8452Q xlero; 45 | SHT31D_CC::ClosedCube_SHT31D sht31d; 46 | TCPClient client; 47 | Downsampler d(10); 48 | GoogleMapsDeviceLocator locator; 49 | 50 | const unsigned int BATTERY_CAPACITY = 850; // e.g. 850mAh battery 51 | 52 | // This is the pin connected to the INT1 pin on the MMA8452 chip 53 | const int ACCEL_INT_PIN = D2; 54 | 55 | // Various timing constants 56 | const unsigned long MAX_RECORDING_LENGTH_MS = 10000; // audio sample time 57 | const unsigned long MAX_TIME_TO_PUBLISH_MS = 30000; // Only stay awake for 20 seconds trying to connect to the cloud and publish 58 | const unsigned long TIME_AFTER_PUBLISH_MS = 1000; // After publish, wait 4 seconds for data to go out 59 | const unsigned long TIME_AFTER_BOOT_MS = 1000; // At boot, wait 1 seconds before going to publish 60 | const unsigned long TIME_PUBLISH_BATTERY_SEC = 20 * 60; // every 10 minutes, send an update 61 | const unsigned long TIME_PUBLISH_BATTERY_SEC_LP = 60 * 60; // states) 326 | { 327 | //Count all states 328 | std::array count={0,0,0,0,0,0,0,0,0,0,0}; 329 | for( int i = 0; i < states.size(); i++ ) 330 | { 331 | count[states[i]]++; 332 | } 333 | 334 | //Determine most repeated state 335 | int indexWinner = 1; 336 | for( int i = 1; i < count.size(); i++ ) 337 | { 338 | if( count[i] > count[indexWinner] ) 339 | { 340 | indexWinner = i; 341 | } 342 | } 343 | return indexWinner; 344 | } 345 | 346 | void printResult(String text, SHT31D_CC::SHT31D result) { 347 | if (result.error == SHT31D_CC::NO_ERROR) { 348 | Serial.print(text); 349 | Serial.print(": T="); 350 | Serial.print(result.t); 351 | Serial.print("C, RH="); 352 | Serial.print(result.rh); 353 | Serial.println("%"); 354 | tempC_in = result.t; 355 | RH_in = result.rh; 356 | } 357 | else { 358 | Serial.print(text); 359 | Serial.print(": [ERROR] Code #"); 360 | Serial.println(result.error); 361 | } 362 | } 363 | 364 | 365 | void update_params(void){ 366 | Serial.println("update params..."); 367 | printResult("SHT31", sht31d.readTempAndHumidity(SHT31D_CC::REPEATABILITY_LOW, SHT31D_CC::MODE_CLOCK_STRETCH, 50)); 368 | 369 | // BQ27441 Battery Manager calls 370 | soc = lipo.soc(); // Read state-of-charge (in %) 371 | volts = lipo.voltage(); // Read voltage (in mV) 372 | current = lipo.current(AVG); // Read average current (in mA) 373 | power = lipo.power(); // Read power consumption (in mW) 374 | soh = lipo.soh(); // Read state-of-health (in %) 375 | 376 | } 377 | 378 | void free_mem(void){ 379 | freemem = System.freeMemory(); 380 | Serial.print(" ----- free memory: "); 381 | Serial.println(freemem); 382 | } 383 | 384 | void buff_in_mem(){ 385 | //uint16_t samplez[MAX_BUF][SAMPLE_BUF_SIZE/4]; // MAX_BUF number of samples 386 | samplez = (uint8_t **) malloc (sizeof(uint8_t *)*MAX_BUF); 387 | Serial.printlnf("size: %i", sizeof(uint8_t *)*MAX_BUF); 388 | for (int r = 0; r < MAX_BUF; r++) { 389 | samplez[r] = (uint8_t *) malloc(sizeof(uint8_t)*SAMPLE_BUF_SIZE/2); 390 | Serial.printlnf("size %i: %i", r, sizeof(uint8_t)*SAMPLE_BUF_SIZE/2); 391 | } 392 | } 393 | 394 | void buff_out_mem(){ 395 | for (int r = 0; r < MAX_BUF; r++) { 396 | free(samplez[r]); 397 | Serial.printlnf("row: %i", r); 398 | free_mem(); 399 | } 400 | free(samplez); 401 | } 402 | 403 | void otaHandler(){ 404 | Serial.println("ota update"); 405 | stateTime = millis(); 406 | state = SETUP_LOOP; 407 | ota_active = true; 408 | Particle.process(); 409 | } 410 | 411 | void button_click_handler(system_event_t event, int param) 412 | { 413 | times = times + system_button_clicks(param); 414 | if(times%2){ 415 | Serial.println("===test mode==="); 416 | state = RSSI_TEST_LOOP_PRE; 417 | } 418 | else{ 419 | Serial.println("===run mode==="); 420 | state = BOOT_WAIT_STATE; 421 | } 422 | } 423 | 424 | void rssi_report(){ 425 | rssi = WiFi.RSSI(); 426 | wifi_rat = (float)(wifi_goo + 1) / (wifi_timeout + wifi_goo + 1); 427 | aud_rat = (float)(good_count + 1) / (total_count + 1); 428 | Serial.printlnf("good: %i total: %i", good_count, total_count); 429 | Serial.printlnf("RSSI: %i, NG/OK: %i/%i, wifi_rat: %.2f, A_NG/OK: %i/%i, aud_rat: %.2f", rssi, wifi_timeout, wifi_goo, wifi_rat, (total_count - good_count), total_count, aud_rat ); 430 | 431 | } 432 | 433 | void batt_report(){ 434 | Serial.print("State of Charge/Health: "); 435 | Serial.print(soc); 436 | Serial.print(", "); 437 | Serial.println(soh); 438 | int16_t l = -600, h = -400; 439 | for(int j = 0; j <= 5 ; j++){ 440 | if((power >= l && power < h) || j ==5){ 441 | chg = j; 442 | break; 443 | } 444 | l = l + 200; 445 | h = l + 200; 446 | } 447 | Serial.printlnf("chg: %i, mA: %i ,mW: %i", chg, current, power); 448 | } 449 | 450 | bool WiFi_Listen_Check(){ // checks for WiFi listening mode on Photon 451 | if(WiFi.listening() && !setup_once){ 452 | state = SETUP_LOOP; 453 | stateTime = millis(); 454 | Serial.println("SETUP LOOP"); 455 | setup_once = true; 456 | // buff_out_mem(); 457 | return true; 458 | } 459 | else return false; 460 | } 461 | 462 | int8_t ALaw_Encode(int16_t number) 463 | { 464 | const uint16_t ALAW_MAX = 0xFFF; 465 | uint16_t mask = 0x800; 466 | uint8_t sign = 0; 467 | uint8_t position = 11; 468 | uint8_t lsb = 0; 469 | if (number < 0) 470 | { 471 | number = -number; 472 | sign = 0x80; 473 | } 474 | if (number > ALAW_MAX) 475 | { 476 | number = ALAW_MAX; 477 | } 478 | for (; ((number & mask) != mask && position >= 5); mask >>= 1, position--); 479 | lsb = (number >> ((position == 4) ? (1) : (position - 4))) & 0x0f; 480 | return (sign | ((position - 4) << 4) | lsb) ^ 0x55; 481 | } 482 | 483 | ApplicationWatchdog wd(30000, System.reset, 256); // declare watchdog 484 | 485 | 486 | void setup() { 487 | 488 | Serial.begin(57600); 489 | WiFi.useDynamicIP(); 490 | delay(1000); 491 | 492 | Serial.printlnf("System firmware version: %s", System.version().c_str()); 493 | Serial.printlnf("BuzzBoard FW version: %i", FW_VERSION); 494 | Serial.println("Setup"); 495 | free_mem(); 496 | 497 | String myID; 498 | myID = System.deviceID(); 499 | myID.toCharArray(myIDtest, sizeof(myIDtest)); 500 | 501 | Serial.printlnf("DeviceID: %s", myIDtest); 502 | 503 | WiFi.selectAntenna(ANT_EXTERNAL); 504 | 505 | sht31d.begin(0x44); 506 | Serial.print("SHT31D Serial #"); 507 | Serial.println(sht31d.readSerialNumber()); 508 | 509 | // Initialization for the battery monitor 510 | if (!lipo.begin()) 511 | { 512 | Serial.println("Error: Unable to communicate with BQ27441."); 513 | Serial.println("Check wiring and try again."); 514 | Serial.println("(Battery must be plugged into Battery Babysitter!)"); 515 | } 516 | 517 | Serial.println("Connected to BQ27441!"); 518 | lipo.setCapacity(BATTERY_CAPACITY); // Configure BQ27441 to assume a 1000 mAh battery 519 | 520 | // Initialization for accelerometer 521 | Serial.println("MMA xlerometer init"); 522 | xlero.init(SCALE_2G, ODR_12); // 2g scale, sampled at 12 Hz 523 | 524 | pinMode(D7, OUTPUT); 525 | free_mem(); 526 | System.on(firmware_update_pending, otaHandler); 527 | System.on(button_click, button_click_handler); 528 | 529 | Serial.println("Setup COMPLETE"); 530 | } 531 | 532 | 533 | void loop() { 534 | 535 | uint16_t *sendBuf = NULL; 536 | 537 | if(millis() >= debug_timer){ 538 | if(state != 5){ 539 | Serial.printlnf("State: %d", state); 540 | } 541 | wd.checkin(); // reset watchdog timer 542 | Particle.process(); 543 | WiFi_Listen_Check(); 544 | free_mem(); 545 | locator.loop(); 546 | debug_timer = millis() + 1000; /// try this 547 | if(xlero.readTap() != 0){ 548 | Serial.println("xlrometer disturbance~!"); 549 | } 550 | } 551 | 552 | 553 | switch(state) { 554 | 555 | case BOOT_WAIT_STATE: 556 | if (millis() - stateTime >= TIME_AFTER_BOOT_MS) { 557 | // To publish the battery stats after boot, set state to PUBLISH_STATE 558 | // To go to sleep immediately, set state to SLEEP_STATE 559 | Particle.connect(); 560 | p_once = false; 561 | state = PUBLISH_STATE; 562 | } 563 | break; 564 | 565 | case LP_CHECK: // Li-Ion battery check 566 | delay(250); 567 | 568 | // check to see if reason for waking was xlrometer disturbance, or timer 569 | if(xlero.readTap() != 0){ 570 | Serial.println("xlrometer disturbance~!"); 571 | accel_awake = true; 572 | } 573 | else{ 574 | accel_awake = false; 575 | } 576 | 577 | update_params(); 578 | if(soc < LP_SHUTDOWN_PERCENTAGE || soc > LP_FULL_CHARGE){ 579 | Serial.println("Low Battery, system will shutdown until charged"); 580 | batt_report(); 581 | // WiFi.off(); 582 | state = SLEEP_STATE; // go to sleep 583 | } 584 | if(soc >= LP_SHUTDOWN_PERCENTAGE && soc <= LP_FULL_CHARGE){ 585 | state = BOOT_WAIT_STATE; // continue normal operation 586 | WiFi.useDynamicIP(); 587 | WiFi.on(); 588 | } 589 | if(soc < LP_SLOWDOWN){ // set low-power update time 590 | publish_time = TIME_PUBLISH_BATTERY_SEC_LP; 591 | } 592 | if(soc >= LP_SLOWDOWN && soc <= LP_FULL_CHARGE){ // set normal update time 593 | publish_time = TIME_PUBLISH_BATTERY_SEC; 594 | } 595 | 596 | break; 597 | 598 | case PUBLISH_STATE: 599 | 600 | if (!p_once) { 601 | // BME280 reads 602 | update_params(); 603 | // free_mem(); 604 | rssi_report(); 605 | batt_report(); 606 | 607 | // Debug printouts 608 | Serial.println("SHT30: "); 609 | Serial.print("temp (C): "); 610 | Serial.print(tempC_in); 611 | Serial.print("RH (%): "); 612 | Serial.println(RH_in); 613 | 614 | p_once = true; 615 | } 616 | 617 | if(WiFi_Listen_Check()) break; 618 | 619 | if (Particle.connected()) { 620 | 621 | locator.withEventName("geoLocate").withSubscribe(locationCallback); 622 | locator.publishLocation(); 623 | // delay(500); 624 | 625 | rssi_report(); 626 | 627 | if(soc >= LP_SHUTDOWN_PERCENTAGE && soc < LP_SLOWDOWN){ 628 | Particle.publish("Low Battery Warning"); 629 | Serial.println("Low Battery Warning"); 630 | } 631 | 632 | Serial.println("publish"); 633 | if(accel_awake){ 634 | Particle.publish("disturbance"); 635 | } 636 | 637 | update_params(); 638 | batt_report(); 639 | 640 | char data[512]; 641 | snprintf(data, sizeof(data), "{\"T_in\": %.01f, \"RH_i\": %d, \"soc\": %d, \"soh\": %d, \"chg\": %d, \"I\": %d, \"P\": %d, \"RSSI\": %i, \"w_r\": %.2f, \"a_r\": %.2f, \"acc\": %f, \"lon\": %f, \"lat\": %f}", tempC_in, RH_in, soc, soh, chg, current, power, rssi, wifi_rat, aud_rat, accuracy, longitude, latitude); 642 | Particle.publish("measurements", data, PRIVATE); 643 | 644 | // Wait for the publish to go out 645 | stateTime = millis(); 646 | wifi_goo++; 647 | state = PUBLISH_WAIT_STATE; 648 | } 649 | else { 650 | // Haven't come online yet 651 | if (millis() - stateTime >= MAX_TIME_TO_PUBLISH_MS) { 652 | // Took too long to publish, just go to sleep 653 | state = SLEEP_STATE; // {{PETER}} SLEEP_STATE 654 | wifi_timeout++; 655 | } 656 | } 657 | break; 658 | 659 | case PUBLISH_WAIT_STATE: 660 | if (millis() - stateTime >= TIME_AFTER_PUBLISH_MS) { 661 | state = AWS_CONNECT; //AWS_CONNECT 662 | } 663 | break; 664 | 665 | case AWS_CONNECT: 666 | // Ready to connect to the server via TCP 667 | if (client.connect(serverAddr, serverPort)) { 668 | // Connected 669 | 670 | Serial.println("stream starting"); 671 | // Serial.println("packet #,ok?,good,re-try,failed"); 672 | free_mem(); 673 | 674 | char influxDbMessage[256]; 675 | snprintf(influxDbMessage, sizeof(influxDbMessage), "%s%s%s%s%d%s%s", postHeader1test, myIDtest, postHeader2test, postHeader4test, FW_VERSION, postHeader5test, postHeaderAudiotest); 676 | 677 | int count = client.write(influxDbMessage); 678 | 679 | if(count == strlen(influxDbMessage)){ 680 | Serial.println(influxDbMessage); 681 | } 682 | free_mem(); 683 | 684 | buff_in_mem(); // aalocate audio buffer in memory 685 | free_mem(); 686 | 687 | Serial.printlnf("sizeof samplez: %i, %i", sizeof(samplez), sizeof(samplez[0])); 688 | 689 | adcDMA.start(SAMPLE_RATE); 690 | recordingStart = millis(); 691 | digitalWrite(D7, HIGH); 692 | stateTime = millis(); 693 | state = AWS_STREAM; 694 | } 695 | else { 696 | Serial.println("failed to connect to server"); 697 | delay(500); 698 | state = SLEEP_STATE; //{{PETER}} SLEEP_STATE, AWS_CONNECT 699 | } 700 | break; 701 | 702 | case HEADER_WAIT_STATE: 703 | if (millis() - stateTime >= TIME_AFTER_PUBLISH_MS) { 704 | adcDMA.start(SAMPLE_RATE); 705 | recordingStart = millis(); 706 | digitalWrite(D7, HIGH); 707 | state = AWS_STREAM; // 708 | } 709 | break; 710 | 711 | 712 | case AWS_STREAM: 713 | bool success; 714 | 715 | if (DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_HTIF0)) { // first half 716 | DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_HTIF0); 717 | sendBuf = samples; 718 | } 719 | if (DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_TCIF0)) { // second half 720 | DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_TCIF0); 721 | sendBuf = &samples[SAMPLE_BUF_SIZE / 2]; 722 | } 723 | 724 | if (sendBuf != NULL) { // process 32kHz samples pass to downsampler 725 | // There is a sample buffer to send 726 | // Average the pairs of samples and format them 727 | for(size_t ii = 0, jj = 0; ii < SAMPLE_BUF_SIZE / 2; ii += 2, jj++) { 728 | uint32_t sum = (uint32_t)sendBuf[ii] + (uint32_t)sendBuf[ii + 1]; 729 | sendBuf[jj] = (uint16_t)(sum / 2); 730 | 731 | x = (((float)sendBuf[jj] - 32768)/65536); 732 | d.update(x); 733 | 734 | if(d.isReady()){ // downsample ready 735 | x = d.getSample(); 736 | re_sample_i16 = (int16_t)(x * 65536); // convert to uint16 for audio stream 737 | re_sample_i8 = ALaw_Encode(re_sample_i16); 738 | samplez[aud_select][sample_num] = re_sample_i8; //fill send-stream buffer 739 | sample_num++; 740 | 741 | if(sample_num >= (SAMPLE_BUF_SIZE / 2)){ // send-stream sample ready /// /2 742 | total_count++; // total number of TCP buffers filled 743 | sample_num = 0; 744 | aud_select++; 745 | retry_timer = 0; 746 | bo_once = false; 747 | if(aud_select > MAX_BUF - 1) aud_select = 0; 748 | } 749 | 750 | } 751 | } 752 | } 753 | 754 | 755 | if(aud_select != send_select && millis() >= retry_timer){ // send sample packet 756 | 757 | Serial.println("-----"); 758 | Serial.printlnf("aud_/send_select: %i / %i, millis(%lu)", aud_select, send_select, millis()); 759 | //Send buffer to server 760 | int count = client.write((uint8_t *)samplez[send_select], SAMPLE_BUF_SIZE / 2); 761 | 762 | if (count == SAMPLE_BUF_SIZE / 2) { 763 | // Success 764 | good_count++; 765 | success = true; 766 | 767 | send_select++; 768 | if(send_select > MAX_BUF - 1) send_select = 0; 769 | 770 | Serial.printlnf("success! %d, %d", good_count, count); 771 | } 772 | 773 | else 774 | if (count == -16) { 775 | // TCP Buffer full 776 | error_count++; 777 | success = false; 778 | retry_timer = millis() + RETRY_TIME; 779 | Serial.printlnf("TCP buffer full"); 780 | } 781 | else { 782 | // Error 783 | Serial.printlnf("error writing %d", count); 784 | pass_count = 0; 785 | 786 | adcDMA.stop(); 787 | digitalWrite(D7, LOW); 788 | client.stop(); 789 | 790 | accel_awake = false; 791 | stateTime = millis(); 792 | 793 | state = AWS_FINISH; 794 | } 795 | } 796 | if((aud_select == send_select) && !success && !bo_once){ // error, aud_buffer has looped around and reached send_buffer 797 | Serial.printlnf("BUFFER OVERWRITE: aud: %i, send: %i", aud_select, send_select); 798 | bo_once = true; 799 | overflow_count++; 800 | } 801 | 802 | if (millis() - recordingStart >= MAX_RECORDING_LENGTH_MS) { // recording time reached 803 | state = AWS_FINISH; 804 | } 805 | break; 806 | 807 | case AWS_FINISH: 808 | adcDMA.stop(); 809 | buff_out_mem(); 810 | digitalWrite(D7, LOW); 811 | Serial.println("aws stopping"); 812 | Serial.println("testnum entries ; good/fails/overflows (x16) // total packets: "); 813 | Serial.printlnf("%lu ; %d/%d/%d // %d", pass_count, good_count, error_count, overflow_count, total_count); 814 | 815 | client.stop(); 816 | 817 | //If feature extractor is ready 818 | if (client.connect(serverAddr, serverPort)) { 819 | // Connected 820 | 821 | Serial.println("OBC send"); 822 | 823 | char influxDbMessage[256]; 824 | snprintf(influxDbMessage, sizeof(influxDbMessage), "%s%s%s%s%s%s%s\r\n\r\n", postHeader1test, myIDtest, postHeader2test, postHeader3test, postHeader5test, postHeaderStatetest, hive_state_msg_test); 825 | 826 | int count = client.write(influxDbMessage); 827 | if(count == strlen(influxDbMessage)){ 828 | Serial.println(influxDbMessage); 829 | } 830 | client.stop(); 831 | } 832 | else { 833 | Serial.println("failed to connect to server"); 834 | state = SLEEP_STATE; 835 | } 836 | 837 | //print On-Board-Classification State 838 | state = SLEEP_STATE; 839 | break; 840 | 841 | case SLEEP_STATE: // Sleep 842 | 843 | Serial.print("Go to sleep..."); 844 | 845 | pass_count = 0; 846 | aud_select = 0; 847 | send_select = 0; 848 | 849 | if (WiFi_Listen_Check()) break; 850 | 851 | state = GO_TO_SLEEP; 852 | break; 853 | 854 | 855 | case GO_TO_SLEEP: 856 | 857 | Serial.println(" zzz"); 858 | xlero.readTap(); 859 | Particle.disconnect(); 860 | WiFi.off(); 861 | System.sleep(ACCEL_INT_PIN, RISING, publish_time); // slow to sleep after waking, only when no wifi connection 862 | state = LP_CHECK; 863 | stateTime = millis(); 864 | break; 865 | 866 | case SETUP_LOOP: 867 | if (millis() - stateTime >= 300000) { 868 | state = GO_TO_SLEEP; //AWS_CONNECT 869 | } 870 | if(!WiFi.listening() && !ota_active){ 871 | stateTime = millis(); 872 | state = BOOT_WAIT_STATE; 873 | setup_once = false; 874 | } 875 | 876 | break; 877 | 878 | 879 | case RSSI_TEST_LOOP_PRE: 880 | 881 | pass_count = 0; 882 | adcDMA.stop(); 883 | digitalWrite(D7, LOW); 884 | client.stop(); 885 | 886 | accel_awake = false; 887 | 888 | state = RSSI_TEST_LOOP; 889 | break; 890 | 891 | 892 | case RSSI_TEST_LOOP: 893 | if(millis() - stateTime >= RSSI_DELAY){ 894 | 895 | rssi_report(); 896 | update_params(); 897 | batt_report(); 898 | 899 | stateTime = millis(); 900 | } 901 | break; 902 | 903 | } 904 | 905 | } 906 | 907 | 908 | 909 | 910 | // all samples working!! 911 | -------------------------------------------------------------------------------- /params.h: -------------------------------------------------------------------------------- 1 | //Window Length in s 2 | float windowLength=2; 3 | 4 | //Sampling Rate in Hz 5 | float samplingRate=6300; 6 | 7 | -------------------------------------------------------------------------------- /particle.ignore: -------------------------------------------------------------------------------- 1 | # particle.ignore 2 | .ds_store 3 | wake_adxl.cpp 4 | blink.ino 5 | main_beta_mem_SHT.cpp 6 | main_beta_mem_MMA.cpp 7 | geolocate.cpp 8 | PCBA-test.cpp 9 | ADCDMA.cpp 10 | ADCDMA.h 11 | -------------------------------------------------------------------------------- /softap_bb.h: -------------------------------------------------------------------------------- 1 | #include "Particle.h" 2 | #include "softap_http.h" 3 | 4 | struct Page 5 | { 6 | const char* url; 7 | const char* mime_type; 8 | const char* data; 9 | }; 10 | 11 | const char index_html[] = "Setup your Buzzbox

Setup your Buzzbox

My device ID:

Scan for visible WiFi networks

"; 12 | const char rsa_js[] = "function parseBigInt(a,b){return new BigInteger(a,b);}function linebrk(a,b){var c='';var d=0;while(d+b=0&&b>0){var e=a.charCodeAt(d--);if(e<128)c[--b]=e;else if((e>127)&&(e<2048)){c[--b]=(e&63)|128;c[--b]=(e>>6)|192;}else{c[--b]=(e&63)|128;c[--b]=((e>>6)&63)|128;c[--b]=(e>>12)|224;}}c[--b]=0;var f=new SecureRandom();var g=new Array();while(b>2){g[0]=0;while(g[0]==0)f.nextBytes(g);c[--b]=g[0];}c[--b]=2;c[--b]=0;return new BigInteger(c);}function RSAKey(){this.n=null;this.e=0;this.d=null;this.p=null;this.q=null;this.dmp1=null;this.dmq1=null;this.coeff=null;}function RSASetPublic(a,b){if(a!=null&&b!=null&&a.length>0&&b.length>0){this.n=parseBigInt(a,16);this.e=parseInt(b,16);}else alert('Invalid RSA public key');}function RSADoPublic(a){return a.modPowInt(this.e,this.n);}function RSAEncrypt(a){var b=pkcs1pad2(a,(this.n.bitLength()+7)>>3);if(b==null)return null;var c=this.doPublic(b);if(c==null)return null;var d=c.toString(16);if((d.length&1)==0)return d;else return '0'+d;}RSAKey.prototype.doPublic=RSADoPublic;RSAKey.prototype.setPublic=RSASetPublic;RSAKey.prototype.encrypt=RSAEncrypt;"; 13 | const char style_css[] = "html{height:100%;margin:auto;background-color:white}body{box-sizing:border-box;min-height:100%;padding:20px;background-color:#FFD042;font-family:'Lucida Sans Unicode','Lucida Grande',sans-serif;font-weight:normal;color:#8F6526;margin-top:0;margin-left:auto;margin-right:auto;margin-bottom:0;max-width:400px;text-align:center;border:1px solid #6e6e70;border-radius:4px}div{margin-top:25px;margin-bottom:25px}h1{margin-top:25px;margin-bottom:25px}button{border-color:#D4A037;background-color:#D4A037;color:white;border-radius:5px;height:30px;font-size:15px;font-weight:bold}button.input-helper{background-color:#bebebe;border-color:#bebebe;color:#6e6e70;margin-left:3px}button:disabled{background-color:#bebebe;border-color:#bebebe;color:white}input[type='text'],input[type='password']{background-color:white;color:#6e6e70;border-color:white;border-radius:5px;height:25px;text-align:center;font-size:15px}input:disabled{background-color:#bebebe;border-color:#bebebe}input[type='radio']{position:relative;bottom:-0.33em;margin:0;border:0;height:1.5em;width:15%}label{padding-top:7px;padding-bottom:7px;padding-left:5%;display:inline-block;width:80%;text-align:left}input[type='radio']:checked+label{font-weight:bold;color:#D4A037}.scanning-error{font-weight:bold;text-align:center}.radio-div{box-sizing:border-box;margin:2px;margin-left:auto;margin-right:auto;background-color:white;color:#6e6e70;border:1px solid #6e6e70;border-radius:3px;width:100%;padding:5px}#networks-div{margin-left:auto;margin-right:auto;text-align:left}#device-id{text-align:center}#scan-button{min-width:100px}#connect-button{display:block;min-width:100px;margin-top:10px;margin-left:auto;margin-right:auto;margin-bottom:20px}#password{margin-top:20px;margin-bottom:10px}"; 14 | const char rng_js[] = "var rng_state;var rng_pool;var rng_pptr;function rng_seed_int(a){rng_pool[rng_pptr++]^=a&255;rng_pool[rng_pptr++]^=(a>>8)&255;rng_pool[rng_pptr++]^=(a>>16)&255;rng_pool[rng_pptr++]^=(a>>24)&255;if(rng_pptr>=rng_psize)rng_pptr-=rng_psize;}function rng_seed_time(){rng_seed_int(new Date().getTime());}if(rng_pool==null){rng_pool=new Array();rng_pptr=0;var t;if(window.crypto&&window.crypto.getRandomValues){var ua=new Uint8Array(32);window.crypto.getRandomValues(ua);for(t=0;t<32;++t)rng_pool[rng_pptr++]=ua[t];}if(navigator.appName=='Netscape'&&navigator.appVersion<'5'&&window.crypto){var z=window.crypto.random(32);for(t=0;t>>8;rng_pool[rng_pptr++]=t&255;}rng_pptr=0;rng_seed_time();}function rng_get_byte(){if(rng_state==null){rng_seed_time();rng_state=prng_newstate();rng_state.init(rng_pool);for(rng_pptr=0;rng_pptrwrite(myPages[idx].data); 61 | } 62 | } 63 | 64 | STARTUP(softap_set_application_page_handler(myPage, nullptr)); 65 | 66 | --------------------------------------------------------------------------------