Hardware components | ||||||
| × | 4 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 4 | ||||
| × | 4 | ||||
| × | 4 | ||||
| × | 4 | ||||
| × | 4 | ||||
| × | 4 | ||||
| × | 4 | ||||
Software apps and online services | ||||||
| ||||||
| ||||||
| ||||||
| ||||||
| ||||||
| ||||||
| ||||||
| ||||||
| ||||||
| ||||||
| ||||||
Hand tools and fabrication machines | ||||||
| ||||||
| ||||||
|
The project runs on the general basis of overall improving the average home user’s sprinkle system. We observed that there is much room for improvement over a basic mechanical sprinkler timer. Our design will be an autonomous sprinkler system capable of monitoring outdoor conditions and adjusting sprinkler settings accordingly. The system will measure soil humidity, weather data, and watering schedules to determine which sprinklers in our network should run. Using local network connections, TI's CC1310 Launchpads, a Beagle Bone Black, Amazon Web Services, and the OpenWeatherMap weather API, we will communicate moisture data, weather data, and user preferences to determine when to sprinklers on and off. The system will run both indoors and outdoors and will be able to remain running with little to no physical maintenance. Devices running outside will be solar powered and switch on to transmit information periodically and minimize power usage. Using a cloud hosted web interface, a user will be able to input desired watering options and view live data of their outdoor conditions. The web interface will also allow the user to schedule watering and override computer settings. The current system status will also be provided. Using our on-board program for the hub, the system will save water by not allowing the system to run when the soil is saturated, when it is currently raining, or when it is predicted to rain soon.
Our system is divided into 4 major components. Each component serves a crucial purpose in ensuring the integrity of the system.
TI CC1310 Launchpad
The CC1310 Launchpads provide an efficient, low power communication that will allow us to wirelessly send information back and forth between outdoors and our hub over a large distance. This will benefit us in that our system will typically operate over distances from the backyard to the garage. The ultra-low power mode of the launchpad will also help sustain the system outdoors for long periods of time with minimal power. This will help ensure a longer system uptime for our design.
Beagle Bone Black
With the hub portion of our project resides the Beagle Bone Black. The Beagle Bone Black provides complete access to a Linux terminal which we will access to run higher level access scripts to bridge our website to the CC1310. This will allow us to perform HTTPS queries between our operated website using Python as well as generate and parse API requests to OpenWeatherMap to retrieve local weather information. In additon, we will also utilize Python to talk with the hub’s CC1310 through a direct UART connection. Python will also serve us in parsing information and maintaining the overall program logic of the system.
Capacitive Moisture Sensor
The capacitive moisture sensor provides a robust way to obtain an analog representative value corresponding the moisture level in the surrounding soil. Rather than a simple 0/1 representation of wet vs dry, we are able to read a relative amount of water saturation in the soil with values scaling from 0-100. Additionally, because the sensor is capacitive (unlike its resistive alternatives), it is corrosive resistant and not as likely to deteriorate over a long period of time.
User ControlsThe user controls service to give an individual access to their sprinkler system anytime and anywhere. These controls can be accessed on any web-capable device.
Cloud Hosted Service
The cloud hosted service we provide is the gateway between the user and the system. Using a basic UI we allow the user to see a status of the system as it is currently operating. The user will know if the system is on and which sprinklers are running. The weather is provided for convenience. Also provided are a manual override button and the schedule preferences. The user can toggle the override button to enable the sprinklers in the system. The user can also input a scheduled start and end time for the system to designate all possible times the system may run. All of this information gets stored in a database on our server and later gets parsed by the Beagle Bone Black. The website used for our installation is at http://www.dnedesign.us.to
Transmitter
For the transmitter, a charging circuit was connected to a battery and the micro-controller. A capacitive moisture sensor was read using SensorController Studio and processed with Code Composer Studio to minimize power consumption. This data gets transmitted to the receiver acting as the sprinkler hub. This assembly is capable of lasting 3.7 years on ultra low power mode from full charge without any extra power from the solar panel. With the solar panel, this setup is capable of lasting for the life of the parts.
Receiver
For the assembly, all the relays were soldered to a protoboard with inputs from a transistor circuit triggered by the CC1310 digital pins. These relays switched to a 12V 2A DC source used to power the sprinkler solenoids.
For the installation of the wireless sensor nodes, they only require outdoor placement and for the moisture sensor to be grounded where desired. The enclosures will protect all electronics from the elements through a weather tight seal.
To install the hub, remove any old components from the old sprinkler box. Each of the remaining wires can be connected to the relay board. After the connections are placed, plug the system in using a USB power adapter and the AC to DC power adapter. The system can then be tested using the website's scheduling system and manual override buttons. An example is available at http://www.dnedesign.us.to
Top and bottom of the moisture sensor enclosure. This was used to keep the electronics sealed and weather proof, permitting a long lasting installation.
sender.c
C/C++// Frequency: 869.52501 MHz @625 bps, Legacy Long Range (10 kchips/s, 2-FSK, conv. FEC r=1/2 K=7, DSSS SF=8, Tx dev.: 5 kHz, RX BW: 40 kHz
int sensorID=0;
#include <stdio.h>
#include <xdc/std.h>
#include <stdlib.h>
#include <xdc/runtime/System.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Clock.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/drivers/PIN.h>
#include <ti/sysbios/knl/Swi.h>
#include <ti/drivers/rf/RF.h>
#include "Board.h"
#include "smartrf_settings.h"
#include "scif.h"
#define BV(x) (1 << (x))
#define TASKSTACKSIZE 512
#define TX_TASK_STACK_SIZE 1024
#define TX_TASK_PRIORITY 2
#define PAYLOAD_LENGTH 30
#define PACKET_INTERVAL (uint32_t)(4000000*0.5f) /* Set packet interval to 500ms */
static void senderFxn(UArg arg0, UArg arg1);
/***** Variable declarations *****/
static Task_Params txTaskParams;
Task_Struct txTask; /* not static so you can see in ROV */
static uint8_t txTaskStack[TX_TASK_STACK_SIZE];
Task_Struct task0Struct;
Char task0Stack[TASKSTACKSIZE];
uint32_t capSensorMoisture1 = 0;
static RF_Object rfObject;
static RF_Handle rfHandle;
static PIN_Handle ledPinHandle;
static PIN_State ledPinState;
static Task_Params txTaskParams;
Task_Struct txTask; /* not static so you can see in ROV */
static uint8_t txTaskStack[TX_TASK_STACK_SIZE];
uint32_t time;
static uint8_t packet[PAYLOAD_LENGTH];
static uint16_t seqNumber;
uint32_t time;
uint8_t errorState=0;
static PIN_Handle pinHandle;
PIN_Config pinTable[] =
{
Board_LED0 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
Board_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
PIN_TERMINATE
};
void scCtrlReadyCallback(void)
{
}
void scTaskAlertCallback(void)
{
processTaskAlert();
}
void processTaskAlert(void)
{
// Clear the ALERT interrupt source
scifClearAlertIntSource();
// Acknowledge the ALERT event
scifAckAlertEvents();
}
void TxTask_init()
{
Task_Params_init(&txTaskParams);
txTaskParams.stackSize = TX_TASK_STACK_SIZE;
txTaskParams.priority = TX_TASK_PRIORITY;
txTaskParams.stack = &txTaskStack;
txTaskParams.arg0 = (UInt)1000000;
Task_construct(&txTask, senderFxn, &txTaskParams, NULL);
}
Void senderFxn(UArg arg0, UArg arg1)
{
uint32_t time;
RF_Params rfParams;
RF_Params_init(&rfParams);
RF_cmdPropTx.pktLen = PAYLOAD_LENGTH;
RF_cmdPropTx.pPkt = packet;
RF_cmdPropTx.startTrigger.triggerType = TRIG_ABSTIME;
RF_cmdPropTx.startTrigger.pastTrig = 1;
RF_cmdPropTx.startTime = 0;
uint32_t standbyDurationUs = 0;
// Initialize the Sensor Controller
scifOsalInit();
scifOsalRegisterCtrlReadyCallback(scCtrlReadyCallback);
scifOsalRegisterTaskAlertCallback(scTaskAlertCallback);
scifInit(&scifDriverSetup);
// Set the Sensor Controller task tick interval to 1 second
uint32_t rtc_Hz = 1; // 1Hz RTC
scifStartRtcTicksNow(0x00010000 / rtc_Hz);
// Start Sensor Controller task
scifStartTasksNbl(BV(SCIF_ADC_LEVEL_TRIGGER_TASK_ID));
while(1)
{
//Small delay to allow reset
standbyDurationUs = 0.5*1000000;
Task_sleep(standbyDurationUs / Clock_tickPeriod);
//Reset Variables
capSensorMoisture1 = 0;
// Sensor Value Manipulation
capSensorMoisture1 = scifTaskData.adcLevelTrigger.output.adcValue;
capSensorMoisture1 = capSensorMoisture1/5; //Wet=89, Dry=207
capSensorMoisture1 = 207-capSensorMoisture1; //Wet=118, Dry=0
capSensorMoisture1 = capSensorMoisture1*0.834; //Dry=0, Wet=99
//Error Check (2.8V Reg.)
if(capSensorMoisture1 > 150 || capSensorMoisture1 < -50){
errorState=1;
}else{
errorState=0;
}
//Clipping (2.8V Reg.)
if(capSensorMoisture1 > 99){
capSensorMoisture1 = 99;
}
if(capSensorMoisture1 < 0){
capSensorMoisture1 = 0;
}
/* Request access to the radio */
rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);
/* Set the frequency */
RF_postCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL, 0);
/* Get current time */
time = RF_getCurrentTime();
/* Create packet with incrementing sequence number and random payload */
packet[0] = (uint8_t)(seqNumber >> 8);
packet[1] = (uint8_t)(seqNumber++);
packet[2] = sensorID;
packet[3] = errorState;
uint8_t i;
for (i = 4; i < PAYLOAD_LENGTH; i++)
{
packet[i] = capSensorMoisture1;
}
/* Set absolute TX time to utilize automatic power management */
time += PACKET_INTERVAL;
RF_cmdPropTx.startTime = time;
/* Send packet */
RF_EventMask result = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTx, RF_PriorityNormal, NULL, 0);
if (!(result & RF_EventLastCmdDone))
{
/* Error */
while(1);
}
//Make sure all LED's are off before going into low power mode
PIN_setOutputValue(pinHandle, Board_LED0, 0);
PIN_setOutputValue(pinHandle, Board_LED1, 0);
//Close RF Communication to reduce current consumption
RF_close(rfHandle);
// Stop task to reduce current consumption
while (scifWaitOnNbl(0) != SCIF_SUCCESS);
scifStopTasksNbl(BV(SCIF_ADC_LEVEL_TRIGGER_TASK_ID));
scifStopRtcTicks();
standbyDurationUs = (1800)*1000000; //seconds******************1800s for 30 min
Task_sleep(standbyDurationUs / Clock_tickPeriod);
// Restart task
scifResetTaskStructs(BV(SCIF_ADC_LEVEL_TRIGGER_TASK_ID), 0);
scifStartRtcTicksNow(0x00010000 / rtc_Hz);
scifStartTasksNbl(BV(SCIF_ADC_LEVEL_TRIGGER_TASK_ID));
}
}
/*
* ======== main ========
*/
int main(void)
{
Board_initGeneral();
TxTask_init();
/* Start BIOS */
BIOS_start();
return (0);
}
// Frequency: 869.52501 MHz @625 bps, Legacy Long Range (10 kchips/s, 2-FSK, conv. FEC r=1/2 K=7, DSSS SF=8, Tx dev.: 5 kHz, RX BW: 40 kHz
#include <stdlib.h>
#include <xdc/std.h>
#include <xdc/cfg/global.h>
#include <xdc/runtime/System.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/drivers/rf/RF.h>
#include <ti/drivers/PIN.h>
#include <ti/drivers/UART.h>
#include <driverlib/rf_prop_mailbox.h>
#include "Board.h"
#include "smartrf_settings.h"
#include "RFQueue.h"
#include "smartrf_settings.h"
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
static PIN_Handle ledPinHandle;
static PIN_State ledPinState;
PIN_Config pinTable[] =
{
Board_LED0 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
Board_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
Board_DIO8 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, //TL
Board_DIO9 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, //TR
Board_DIO13 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, //Not used
Board_DIO14 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, //BL
Board_DIO15 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, //BR
PIN_TERMINATE
};
#define TASKSTACKSIZE 768
Task_Struct task0Struct;
Char task0Stack[TASKSTACKSIZE];
#define RX_TASK_STACK_SIZE 1024
#define RX_TASK_PRIORITY 2
#define SOLENOID_TASK_STACK_SIZE 1024
#define SOLENOID_TASK_PRIORITY 3
#define DATA_ENTRY_HEADER_SIZE 8 /* Constant header size of a Generic Data Entry */
#define MAX_LENGTH 30 /* Max length byte the radio will accept */
#define NUM_DATA_ENTRIES 2 /* NOTE: Only two data entries supported at the moment */
#define NUM_APPENDED_BYTES 2 /* The Data Entries data field will contain:
* 1 Header byte (RF_cmdPropRx.rxConf.bIncludeHdr = 0x1)
* Max 30 payload bytes
* 1 status byte (RF_cmdPropRx.rxConf.bAppendStatus = 0x1) */
static void rxTaskFunction(UArg arg0, UArg arg1);
static void solenoidTaskFunction(UArg arg0, UArg arg1);
static void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e);
static Task_Params rxTaskParams;
static Task_Params solenoidTaskParams;
Task_Struct rxTask; /* not static so you can see in ROV */
Task_Struct solenoidTask;
static uint8_t rxTaskStack[RX_TASK_STACK_SIZE];
static uint8_t solenoidTaskStack[SOLENOID_TASK_STACK_SIZE];
static RF_Object rfObject;
static RF_Handle rfHandle;
#if defined(__TI_COMPILER_VERSION__)
#pragma DATA_ALIGN (rxDataEntryBuffer, 4);
static uint8_t rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
MAX_LENGTH,
NUM_APPENDED_BYTES)];
#elif defined(__IAR_SYSTEMS_ICC__)
#pragma data_alignment = 4
static uint8_t rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
MAX_LENGTH,
NUM_APPENDED_BYTES)];
#elif defined(__GNUC__)
static uint8_t rxDataEntryBuffer [RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
MAX_LENGTH, NUM_APPENDED_BYTES)] __attribute__ ((aligned (4)));
#else
#error This compiler is not supported.
#endif
static dataQueue_t dataQueue;
static rfc_dataEntryGeneral_t* currentDataEntry;
static uint8_t packetLength;
static uint8_t* packetDataPointer;
static PIN_Handle pinHandle;
static uint8_t packet[MAX_LENGTH + NUM_APPENDED_BYTES - 1]; /* The length byte is stored in a separate variable */
void RxTask_init(PIN_Handle ledPinHandle) {
pinHandle = ledPinHandle;
Task_Params_init(&rxTaskParams);
rxTaskParams.stackSize = RX_TASK_STACK_SIZE;
rxTaskParams.priority = RX_TASK_PRIORITY;
rxTaskParams.stack = &rxTaskStack;
rxTaskParams.arg0 = (UInt)1000000;
Task_Params_init(&solenoidTaskParams);
solenoidTaskParams.stackSize = SOLENOID_TASK_STACK_SIZE;
solenoidTaskParams.priority = SOLENOID_TASK_PRIORITY;
solenoidTaskParams.stack = &solenoidTaskStack;
solenoidTaskParams.arg0 = (UInt)1000000;
Task_construct(&rxTask, rxTaskFunction, &rxTaskParams, NULL);
Task_construct(&solenoidTask, solenoidTaskFunction, &solenoidTaskParams, NULL);
}
int sensorID=0;
int packetData0=0;
int packetData1=0;
int packetData2=0;
int packetData3=0;
int senderErrorState=0;
int receiverErrorState=0;
int solenoid0=0;
int solenoid1=0;
int solenoid2=0;
int solenoid3=0;
int i=0;
static void solenoidTaskFunction(UArg arg0, UArg arg1){
//UART SETUP
char input;
char value[5];
char output[4];
UART_Handle uart;
UART_Params uartParams;
UART_Params_init(&uartParams);
uartParams.writeDataMode = UART_DATA_BINARY;
uartParams.readDataMode = UART_DATA_BINARY;
uartParams.readReturnMode = UART_RETURN_FULL;
uartParams.readEcho = UART_ECHO_OFF;
uartParams.baudRate = 9600;
uart = UART_open(Board_UART0, &uartParams);
if(uart == NULL){
System_abort("Error opening the UART");
}
while(1){
//Wait for serial command
for(i=0; i<5; i++){
UART_read(uart, &input, 1);
value[i]=input;
if(input == '9')
i=0;
if((value[i]!='0' && value[i]!='1' && value[i]!='9') || ((input!='9' && i==0))){
i=-1;
}
}
//Distribute
solenoid0=value[1]-'0';
solenoid1=value[2]-'0';
solenoid2=value[3]-'0';
solenoid3=value[4]-'0';
PIN_setOutputValue(pinHandle, Board_DIO8, solenoid0);
PIN_setOutputValue(pinHandle, Board_DIO9, solenoid1);
PIN_setOutputValue(pinHandle, Board_DIO14, solenoid2);
PIN_setOutputValue(pinHandle, Board_DIO15, solenoid3);
//Response
if(packetData0 < 10){
sprintf(output, "z00%d", packetData0);
UART_write(uart, output, sizeof(output));
}
else{
sprintf(output, "z0%d", packetData0);
UART_write(uart, output, sizeof(output));
}
if(packetData1 < 10){
sprintf(output, "z10%d", packetData1);
UART_write(uart, output, sizeof(output));
}
else{
sprintf(output, "z1%d", packetData1);
UART_write(uart, output, sizeof(output));
}
if(packetData2 < 10){
sprintf(output, "z20%d", packetData2);
UART_write(uart, output, sizeof(output));
}
else{
sprintf(output, "z2%d", packetData2);
UART_write(uart, output, sizeof(output));
}
if(packetData3 < 10){
sprintf(output, "z30%d", packetData3);
UART_write(uart, output, sizeof(output));
}
else{
sprintf(output, "z3%d", packetData3);
UART_write(uart, output, sizeof(output));
}
}//endwhile
}
static void rxTaskFunction(UArg arg0, UArg arg1)
{
RF_Params rfParams;
RF_Params_init(&rfParams);
if( RFQueue_defineQueue(&dataQueue,
rxDataEntryBuffer,
sizeof(rxDataEntryBuffer),
NUM_DATA_ENTRIES,
MAX_LENGTH + NUM_APPENDED_BYTES))
{
/* Failed to allocate space for all data entries */
while(1);
}
/* Modify CMD_PROP_RX command for application needs */
RF_cmdPropRx.pQueue = &dataQueue; /* Set the Data Entity queue for received data */
RF_cmdPropRx.rxConf.bAutoFlushIgnored = 1; /* Discard ignored packets from Rx queue */
RF_cmdPropRx.rxConf.bAutoFlushCrcErr = 1; /* Discard packets with CRC error from Rx queue */
RF_cmdPropRx.maxPktLen = MAX_LENGTH; /* Implement packet length filtering to avoid PROP_ERROR_RXBUF */
RF_cmdPropRx.pktConf.bRepeatOk = 1;
RF_cmdPropRx.pktConf.bRepeatNok = 1;
/* Request access to the radio */
rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);
/* Set the frequency */
RF_postCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL, 0);
/* Enter RX mode and stay forever in RX */
RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx, RF_PriorityNormal, &callback, IRQ_RX_ENTRY_DONE);
while(1);
}
void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
{
if (e & RF_EventRxEntryDone)
{
currentDataEntry = RFQueue_getDataEntry();
packetLength = *(uint8_t*)(¤tDataEntry->data);
packetDataPointer = (uint8_t*)(¤tDataEntry->data + 1);
memcpy(packet, packetDataPointer, (packetLength + 1));
RFQueue_nextEntry();
//Remove bad data
int maxCount=0;
int index=-1; //sentinels
int i,j;
for(i=4; i<MAX_LENGTH; i++){
int count=0;
for(j=4; j<MAX_LENGTH; j++){
if(packet[i] == packet[j])
count++;
}
if(count > maxCount){
maxCount=count;
index=i;
}
}//End Sort
//Report sender error
if(packet[3] != 0){
senderErrorState=1;
}
//Report receiver error
if(sensorID != 0 && sensorID != 1 && sensorID != 2 && sensorID != 3){
receiverErrorState=1;
}
if(receiverErrorState == 0){
//Distribute Data to the Correct Sensor IDs
sensorID=packet[2];
if(sensorID == 0){
packetData0=packet[index];
}
if(sensorID == 1){
packetData1=packet[index];
}
if(sensorID == 2){
packetData2=packet[index];
}
if(sensorID == 3){
packetData3=packet[index];
}
}
//Green LED on if moisture > 20% detected
PIN_setOutputValue(pinHandle, Board_LED1, packet[index]>20);
//Turn on Red LED if sender or receiver error is generated
PIN_setOutputValue(pinHandle, Board_LED0, receiverErrorState==1 || senderErrorState==1);
}
}
/*
* ======== main ========
*/
int main(void)
{
Board_initGeneral();
/* Open pins */
ledPinHandle = PIN_open(&ledPinState, pinTable);
if(!ledPinHandle)
{
System_abort("Error initializing board LED pins\n");
}
/* Initialize task */
RxTask_init(ledPinHandle);
/* Start BIOS */
BIOS_start();
return (0);
}
smartrf_settings.c
C/C++//*********************************************************************************
// Generated by SmartRF Studio version 2.12.1 (build#160)
// The applied template is compatible with CC13x0 SDK 2.30.xx.xx
// Device: CC1310 Rev. B (2.1)
//
//*********************************************************************************
//*********************************************************************************
// Parameter summary
// RX Address0: 0xAA
// RX Address1: 0xBB
// RX Address Mode: No address check
// Frequency: 869.52501 MHz
// Data Format: Serial mode disable
// Deviation: 5.000 kHz
// Packet Length Config: Variable
// Max Packet Length: 255
// Packet Length: 20
// Packet Data: 255
// RX Filter BW: 39 kHz
// Symbol Rate: 10.00061 kBaud
// Sync Word Length: 32 Bits
// TX Power: 14 dBm (requires define CCFG_FORCE_VDDR_HH = 1 in ccfg.c, see CC13xx/CC26xx Technical Reference Manual)
// Whitening: No whitening
#include <ti/devices/DeviceFamily.h>
#include DeviceFamily_constructPath(driverlib/rf_mailbox.h)
#include DeviceFamily_constructPath(driverlib/rf_common_cmd.h)
#include DeviceFamily_constructPath(driverlib/rf_prop_cmd.h)
#include <ti/drivers/rf/RF.h>
#include DeviceFamily_constructPath(rf_patches/rf_patch_cpe_lrm.h)
#include DeviceFamily_constructPath(rf_patches/rf_patch_rfe_lrm.h)
#include "smartrf_settings.h"
// TI-RTOS RF Mode Object
RF_Mode RF_prop =
{
.rfMode = RF_MODE_PROPRIETARY_SUB_1,
.cpePatchFxn = &rf_patch_cpe_lrm,
.mcePatchFxn = 0,
.rfePatchFxn = &rf_patch_rfe_lrm
};
// Overrides for CMD_PROP_RADIO_DIV_SETUP
uint32_t pOverrides[] =
{
// override_use_patch_prop_lrm.xml
// PHY: Use MCE ROM bank 3, RFE RAM patch
MCE_RFE_OVERRIDE(0,3,0,1,0,0),
// override_synth_prop_863_930_div5.xml
// Synth: Set recommended RTRIM to 7
HW_REG_OVERRIDE(0x4038,0x0037),
// Synth: Set Fref to 4 MHz
(uint32_t)0x000684A3,
// Synth: Configure fine calibration setting
HW_REG_OVERRIDE(0x4020,0x7F00),
// Synth: Configure fine calibration setting
HW_REG_OVERRIDE(0x4064,0x0040),
// Synth: Configure fine calibration setting
(uint32_t)0xB1070503,
// Synth: Configure fine calibration setting
(uint32_t)0x05330523,
// Synth: Set loop bandwidth after lock to 20 kHz
(uint32_t)0x0A480583,
// Synth: Set loop bandwidth after lock to 20 kHz
(uint32_t)0x7AB80603,
// Synth: Configure VCO LDO (in ADI1, set VCOLDOCFG=0x9F to use voltage input reference)
ADI_REG_OVERRIDE(1,4,0x9F),
// Synth: Configure synth LDO (in ADI1, set SLDOCTL0.COMP_CAP=1)
ADI_HALFREG_OVERRIDE(1,7,0x4,0x4),
// Synth: Use 24 MHz XOSC as synth clock, enable extra PLL filtering
(uint32_t)0x02010403,
// Synth: Configure extra PLL filtering
(uint32_t)0x00108463,
// Synth: Increase synth programming timeout (0x04B0 RAT ticks = 300 us)
(uint32_t)0x04B00243,
// override_phy_rx_aaf_bw_0xd.xml
// Rx: Set anti-aliasing filter bandwidth to 0xD (in ADI0, set IFAMPCTL3[7:4]=0xD)
ADI_HALFREG_OVERRIDE(0,61,0xF,0xD),
// override_phy_gfsk_rx.xml
// Rx: Set LNA bias current trim offset to 3
(uint32_t)0x00038883,
// Rx: Freeze RSSI on sync found event
HW_REG_OVERRIDE(0x6084,0x35F1),
// override_phy_gfsk_pa_ramp_agc_reflevel_0x1a.xml
// Tx: Configure PA ramping setting (0x41). Rx: Set AGC reference level to 0x1A.
HW_REG_OVERRIDE(0x6088,0x411A),
// Tx: Configure PA ramping setting
HW_REG_OVERRIDE(0x608C,0x8213),
// override_phy_lrm_rom_dsss8.xml
// PHY: Configure DSSS=8
HW_REG_OVERRIDE(0x505C,0x073C),
// override_phy_rx_rssi_offset_5db.xml
// Rx: Set RSSI offset to adjust reported RSSI by +5 dB (default: 0), trimmed for external bias and differential configuration
(uint32_t)0x00FB88A3,
// TX power override
// Tx: Set PA trim to max (in ADI0, set PACTL0=0xF8)
ADI_REG_OVERRIDE(0,12,0xF8),
(uint32_t)0xFFFFFFFF
};
// CMD_PROP_RADIO_DIV_SETUP
// Proprietary Mode Radio Setup Command for All Frequency Bands
rfc_CMD_PROP_RADIO_DIV_SETUP_t RF_cmdPropRadioDivSetup =
{
.commandNo = 0x3807,
.status = 0x0000,
.pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx
.startTime = 0x00000000,
.startTrigger.triggerType = 0x0,
.startTrigger.bEnaCmd = 0x0,
.startTrigger.triggerNo = 0x0,
.startTrigger.pastTrig = 0x0,
.condition.rule = 0x1,
.condition.nSkip = 0x0,
.modulation.modType = 0x0,
.modulation.deviation = 0x14,
.symbolRate.preScale = 0xF,
.symbolRate.rateWord = 0x199A,
.symbolRate.decimMode = 0x0,
.rxBw = 0x20,
.preamConf.nPreamBytes = 0x5,
.preamConf.preamMode = 0x0,
.formatConf.nSwBits = 0x20,
.formatConf.bBitReversal = 0x0,
.formatConf.bMsbFirst = 0x0,
.formatConf.fecMode = 0x8,
.formatConf.whitenMode = 0x0,
.config.frontEndMode = 0x0,
.config.biasMode = 0x1,
.config.analogCfgMode = 0x0,
.config.bNoFsPowerUp = 0x0,
.txPower = 0xA73F,
.pRegOverride = pOverrides,
.centerFreq = 0x0365,
.intFreq = 0x8000,
.loDivider = 0x05
};
// CMD_FS
// Frequency Synthesizer Programming Command
rfc_CMD_FS_t RF_cmdFs =
{
.commandNo = 0x0803,
.status = 0x0000,
.pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx
.startTime = 0x00000000,
.startTrigger.triggerType = 0x0,
.startTrigger.bEnaCmd = 0x0,
.startTrigger.triggerNo = 0x0,
.startTrigger.pastTrig = 0x0,
.condition.rule = 0x1,
.condition.nSkip = 0x0,
.frequency = 0x0365,
.fractFreq = 0x8667,
.synthConf.bTxMode = 0x0,
.synthConf.refFreq = 0x0,
.__dummy0 = 0x00,
.__dummy1 = 0x00,
.__dummy2 = 0x00,
.__dummy3 = 0x0000
};
// CMD_PROP_TX
// Proprietary Mode Transmit Command
rfc_CMD_PROP_TX_t RF_cmdPropTx =
{
.commandNo = 0x3801,
.status = 0x0000,
.pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx
.startTime = 0x00000000,
.startTrigger.triggerType = 0x0,
.startTrigger.bEnaCmd = 0x0,
.startTrigger.triggerNo = 0x0,
.startTrigger.pastTrig = 0x0,
.condition.rule = 0x1,
.condition.nSkip = 0x0,
.pktConf.bFsOff = 0x0,
.pktConf.bUseCrc = 0x1,
.pktConf.bVarLen = 0x1,
.pktLen = 0x14,
.syncWord = 0x00000000,
.pPkt = 0 // INSERT APPLICABLE POINTER: (uint8_t*)&xxx
};
// CMD_PROP_RX
// Proprietary Mode Receive Command
rfc_CMD_PROP_RX_t RF_cmdPropRx =
{
.commandNo = 0x3802,
.status = 0x0000,
.pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx
.startTime = 0x00000000,
.startTrigger.triggerType = 0x0,
.startTrigger.bEnaCmd = 0x0,
.startTrigger.triggerNo = 0x0,
.startTrigger.pastTrig = 0x0,
.condition.rule = 0x1,
.condition.nSkip = 0x0,
.pktConf.bFsOff = 0x0,
.pktConf.bRepeatOk = 0x0,
.pktConf.bRepeatNok = 0x0,
.pktConf.bUseCrc = 0x1,
.pktConf.bVarLen = 0x1,
.pktConf.bChkAddress = 0x0,
.pktConf.endType = 0x0,
.pktConf.filterOp = 0x0,
.rxConf.bAutoFlushIgnored = 0x0,
.rxConf.bAutoFlushCrcErr = 0x0,
.rxConf.bIncludeHdr = 0x1,
.rxConf.bIncludeCrc = 0x0,
.rxConf.bAppendRssi = 0x0,
.rxConf.bAppendTimestamp = 0x0,
.rxConf.bAppendStatus = 0x1,
.syncWord = 0x00000000,
.maxPktLen = 0xFF,
.address0 = 0xAA,
.address1 = 0xBB,
.endTrigger.triggerType = 0x1,
.endTrigger.bEnaCmd = 0x0,
.endTrigger.triggerNo = 0x0,
.endTrigger.pastTrig = 0x0,
.endTime = 0x00000000,
.pQueue = 0, // INSERT APPLICABLE POINTER: (dataQueue_t*)&xxx
.pOutput = 0 // INSERT APPLICABLE POINTER: (uint8_t*)&xxx
};
sprinkler_hub.py
Pythonimport requests
import json
import datetime
import time
import subprocess
try:
from urllib.request import urlopen
except ImportError:
from urllib2 import urlopen
import re
from bs4 import BeautifulSoup
# API Information
zipCode = "75035"
apiKey = "7bb3876c673b5e44af8873139d59fadd"
openWeatherCurrent = "http://192.241.245.161/data/2.5/weather?zip=" + zipCode + ",us&APPID=" + apiKey
openWeather5Day = "http://192.241.245.161/data/2.5/forecast?zip=" + zipCode + ",us&APPID=" + apiKey
# Site parser resources
url = 'http://23.22.156.78/tables/'
solenoids = None
sprinklerStatus = [None] * 4
# Parameters
# Desired Moisture Levels
dm1 = 50
dm2 = 50
dm3 = 50
dm4 = 50
## Day dictionary
dayDict = {
"Sunday": 0,
"Monday": 1,
"Tuesday": 2,
"Wednesday": 3,
"Thursday": 4,
"Friday": 5,
"Saturday": 6
}
## Schedule Class
class Schedule:
def __init__(self, day, start, end):
self.day = day
self.start = start
self.end = end
## Schedule Class
class Solenoids:
def __init__(self, s1, s2, s3, s4):
self.s1 = s1
self.s2 = s2
self.s3 = s3
self.s4 = s4
########### INITIAL PARAMETERS ############
timeMatched = False
moistureLevel = False
isNotRaining = True
isNotForecastedToRain = True
sprinklerOn = False
runProgram = True
lev1 = 0
lev2 = 0
lev3 = 0
lev4 = 0
s1 = False
s2 = False
s3 = False
s4 = False
webRefereshInterval = 5
weatherRefereshInterval = 3600
## Main Program
while runProgram:
# Gather moisture levels from Text file
moistureLevels = open("sensor.txt", "r")
mLevels = moistureLevels.readlines()
lev1 = int(mLevels[0],10)
lev2 = int(mLevels[1],10) - 100
lev3 = int(mLevels[2],10) - 200
lev4 = int(mLevels[3],10) - 300
# Check if ready to referesh weather
if weatherRefereshInterval == 3600:
curTime = datetime.datetime.now().strftime('%H:%M')
curDay = datetime.datetime.now().strftime("%A")
# Get JSON for current weather and 5 day weather
try:
currentInfo = requests.get(url = openWeatherCurrent)
# Load JSON
currentWeather = json.loads(currentInfo.text)
# Parse JSON
currentConditions = currentWeather["weather"][0]["main"]
except requests.exceptions.ConnectionError:
print("Current Weather connection refused. ");
webRefereshInterval = 3570
try:
fiveDayInfo = requests.get(url = openWeather5Day)
fiveDay = json.loads(fiveDayInfo.text)
# Store each day for reference
day1 = fiveDay["list"][0]["weather"][0]["main"]
day2 = fiveDay["list"][1]["weather"][0]["main"]
day3 = fiveDay["list"][2]["weather"][0]["main"]
day4 = fiveDay["list"][3]["weather"][0]["main"]
day5 = fiveDay["list"][4]["weather"][0]["main"]
except requests.exceptions.ConnectionError:
print("5-Day Weather connection refused. ")
webRefereshInterval = 3570
# Reset Weather interval
weatherRefereshInterval = 0
# Check if ready to refresh from website
try:
if webRefereshInterval == 5:
soup = BeautifulSoup(urlopen(url), "html.parser")
data = []
schedules = [None] * 7
# Parse scheduling table and add to class
for rows in soup.find_all('tr'):
for td in rows.find_all('td'):
data.append(td.text)
# Add schedule values
schedules[0] = Schedule(data[1], data[2], data[3])
schedules[1] = Schedule(data[5], data[6], data[7])
schedules[2] = Schedule(data[9], data[10], data[11])
schedules[3] = Schedule(data[13], data[14], data[15])
schedules[4] = Schedule(data[17], data[18], data[19])
schedules[5] = Schedule(data[21], data[22], data[23])
schedules[6] = Schedule(data[25], data[26], data[27])
# Add manual override values
solenoids = Solenoids(data[29], data[30], data[31], data[32])
# Reset Web Refresh Interval
webRefereshInterval = 0
except Exception:
print("Unable to retrieve schedule. ")
## Flag current conditions ##
if currentConditions == "Rain" or currentConditions == "Shower Rain" or currentConditions == "Thunderstorm" or currentConditions == "Snow" or currentConditions == "Drizzle":
isNotRaining = False
else:
isNotRaining = True
## Flag forecast ##
if day1 == "Rain" or day1 == "Shower Rain" or day1 == "Thunderstorm" or day1 == "Snow" or day1 == "Drizzle":
isNotForecastedToRain = False
else:
if day2 == "Rain" or day2 == "Shower Rain" or day2 == "Thunderstorm" or day2 == "Snow" or day2 == "Drizzle":
isNotForecastedToRain = False
else:
if day3 == "Rain" or day3 == "Shower Rain" or day3 == "Thunderstorm" or day3 == "Snow" or day3 == "Drizzle":
isNotForecastedToRain = False
else:
isNotForecastedToRain = True
## Get time ##
curTime = datetime.datetime.now().strftime('%H:%M')
curDay = datetime.datetime.now().strftime("%A")
dayNum = dayDict[curDay]
## Get Schedule ##
todayStart = schedules[dayNum].start
todayStart = todayStart[:-3]
todayEnd = schedules[dayNum].end
todayEnd = todayEnd[:-3]
## Check schedule ##
if curTime == todayStart or (curTime > todayStart and curTime < todayEnd):
timeMatched = True
if timeMatched == True and curTime == todayEnd:
timeMatched = False
statusFile = open("status.txt", "w+")
# Flag solenoids
s1 = (lev1 < dm1) or (int(solenoids.s1) == 1)
s2 = (lev2 < dm2) or (int(solenoids.s2) == 1)
s3 = (lev3 < dm3) or (int(solenoids.s3) == 1)
s4 = (lev4 < dm4) or (int(solenoids.s4) == 1)
## Decision Tree ##
# If these conditions are met, write the elgibility of each solenoid (based on moisture) to the file
if timeMatched and isNotRaining and isNotForecastedToRain:
print("Scheduled Mode")
print("S1: " + str(s1==True) + " S2: " + str(s2==True) + " S3: " + str(s3==True) + " S4: " + str(s4==True))
statusString = str(int(s1 == True)) + str(int(s2 == True)) + str(int(s3 == True)) + str(int(s4 == True))
statusFile.write(statusString)
# If the conditions are not met, just worry about manual override
else:
print("Manual Override Mode")
print(str(solenoids.s1) + str(solenoids.s2) + str(solenoids.s3) + str(solenoids.s4))
statusString = str(solenoids.s1) + str(solenoids.s2) + str(solenoids.s3) + str(solenoids.s4)
statusFile.write(statusString)
print("Calling irrigation script");
subprocess.call(["python3", "irrigation2.py"])
# Update timers
weatherRefereshInterval = weatherRefereshInterval + 5
webRefereshInterval = webRefereshInterval + 5
# Sleep for 5 seconds
time.sleep(5)
#ifndef _SMARTRF_SETTINGS_H_
#define _SMARTRF_SETTINGS_H_
//*********************************************************************************
// Generated by SmartRF Studio version 2.12.1 (build#160)
// The applied template is compatible with CC13x0 SDK 2.30.xx.xx
// Device: CC1310 Rev. B (2.1)
//
//*********************************************************************************
#include <ti/devices/DeviceFamily.h>
#include DeviceFamily_constructPath(driverlib/rf_mailbox.h)
#include DeviceFamily_constructPath(driverlib/rf_common_cmd.h)
#include DeviceFamily_constructPath(driverlib/rf_prop_cmd.h)
#include <ti/drivers/rf/RF.h>
// TI-RTOS RF Mode Object
extern RF_Mode RF_prop;
// RF Core API commands
extern rfc_CMD_PROP_RADIO_DIV_SETUP_t RF_cmdPropRadioDivSetup;
extern rfc_CMD_FS_t RF_cmdFs;
extern rfc_CMD_PROP_TX_t RF_cmdPropTx;
extern rfc_CMD_PROP_RX_t RF_cmdPropRx;
// RF Core API Overrides
extern uint32_t pOverrides[];
#endif // _SMARTRF_SETTINGS_H_
// Enable the ADC
adcEnableSync(ADC_REF_FIXED, ADC_SAMPLE_TIME_2P7_US, ADC_TRIGGER_MANUAL);
// Set DIO3 High
gpioSetOutput(AUXIO_O_DIGITAL03);
output.a = 0;
while(output.a < 65535){
output.a = output.a + 1;
}
// Sample the analog sensor
adcGenManualTrigger();
adcReadFifo(output.adcValue);
// Disable the ADC
adcDisable();
// Set DIO3 Low
gpioClearOutput(AUXIO_O_DIGITAL03);
// Schedule the next execution
fwScheduleTask(1);
import time
import datetime
import serial
import os
from os import path
import sys
# public variables
sensors = [] # list of sensor readings
wait = True
sensor_count = 10 # the zero based count of sensors
def pwr_solenoid(solenoid0=0, solenoid1=0, solenoid2=0, solenoid3=0):
# Defaults are for low signal values
# compile output
output = '9{solenoid0}{solenoid1}{solenoid2}{solenoid3}' \
.format(solenoid0=solenoid0, solenoid1=solenoid1, solenoid2=solenoid2, solenoid3=solenoid3).encode()
with serial.Serial('/dev/ttyACM0', baudrate=9600) as ser:
print("created connection to '/dev/ttyACM0'\n")
print("going to send:\t'{}'".format(output))
ser.write(output)
ser.reset_output_buffer()
# for testing to console
print("Value sent to the uC:\t'{}'".format(output.decode()))
# ***** READ from UART *****
#ser.in_waiting >= 12:
val = str(ser.read(16).decode()) # (3).decode()[2:])
#exit()
raw_sensors = val.split('z')
if raw_sensors[0] == '':
raw_sensors = raw_sensors[1:]
# raw_sensors = str(val).split('z')
# update the sensor values
sensor_count = len(raw_sensors)
data = ''
for i in range(0, sensor_count):
data += "{}\n".format(raw_sensors[i])
# data = "{sensor}{humidity}\n".format(sensor=raw_sensors[i][1:1], humidity=raw_sensors[i][2:1])
# output to file
filename = "sensor.txt"
with open(filename, "w") as file:
file.write("{input}\n".format(input=data))
print("data received:\n{data}".format(data=data))
ser.close()
class SensorReading(object):
# _sensorID = 'X'
# _humidity = 0
def __init__(self, sensorID, humidity):
self._sensorID = sensorID
self._humidity = humidity
def __str__(self):
return "Sensor ID:\t{sensorID}\tHumidity:\t{humidity}%\tDTS:\t{dts}" \
.format(sensorID=self._sensorID, humidity=self._humidity,
dts=datetime.datetime.utcnow().strftime("%a%d%m%Y"))
@property
def humidity(self):
return self._humidity
@humidity.setter
def humidity(self, value):
self._humidity = value
@property
def sensorID(self):
return self._sensorID
@sensorID.setter
def sensorID(self, value):
self._sensorID = value
def main():
os.system("clear")
# check arguments for path to the file
if len(sys.argv) > 1:
filepath = sys.argv[1]
else:
filepath = "{basepath}/home/debian/status.txt".format(basepath=os.path.dirname(sys.argv[0]))
print("path used: '{path}'".format(path=filepath))
# create the list of sensors (hardwired to 10 at the moment)
# make sure that the list of sensor values is clear
sensors.clear()
for i in range(0, sensor_count):
sensors.append(SensorReading(sensorID=i, humidity=0))
'''
print("Enter solenoid values in form of 1/0 (1 == HIGH, 0 == LOW) or 'exit' to exit\n")
print('\n')
print("default value will remain '0000' until altered...\n")
print("Note, after altered values are input, it will wait for input from UART||USB\n")
print('\n')
'''
solenoid = '0000'
# while wait:
# solenoid = input("please input solenoid signal values.\n")
with open(filepath, "r") as file:
solenoid = file.readline()
solenoid = solenoid.rstrip()
print (solenoid)
if solenoid.lower() == "exit":
#break
exit(1)
elif solenoid.lower() == "ports":
import serial.tools.list_ports
ports = [tuple(p) for p in list(serial.tools.list_ports.comports())]
print(ports)
elif solenoid.isdigit():
# checks to make sure all characters are digits (does not check for correctness of
# individual characters
print("isdigit()")
pwr_solenoid(solenoid0=solenoid[0:1], solenoid1=solenoid[1:2], solenoid2=solenoid[2:3], solenoid3=solenoid[3:4])
# time.sleep(3)
# read_from_uart()
# else:
# continue
if __name__ == "__main__":
main()
Comments