#include <Wire.h>
#include "PCA9685.h"
#include "Adafruit_MCP23017.h"
#include "printf.h"
#include <RF24.h>
#include <RF24Network.h>
#include <rf24network_payloads.h>
#define SERIAL_0_BAUD 115200
#define I2C_CLOCK_SPEED 1000000
#define PCA9685_PWM_FREQ 1000
const uint8_t mcu_bus_power_enable_pin = 2;
const uint8_t rx_status_led_pin = 3;
uint16_t bus_voltage_input = 0;
/*RF24 radio setup*/
RF24 radio(40, 9); // nRF24L01 teensy 4.1 pins CE 40 CSN 9
RF24Network network(radio); // use radio object on this network
const uint16_t this_node = 00; // Address of our node in Octal format ( 04,031, etc)
const uint16_t transmit_node_one = 01; // Address of the other node in Octal format
const uint16_t transmit_node_two = 02; // Address of the other node in Octal format
const uint16_t transmit_node_three = 03;
//number of nodes in network, including this node.
const uint16_t NUM_NODES = 4;
env_sensor_payload rx1_payload, rx2_payload; //rf24network_payloads.h
outlet_switch_payload rx3_payload; //rf24network_payloads.h
//rf24 buffer
uint8_t dataBuffer[MAX_PAYLOAD_SIZE]; //MAX_PAYLOAD_SIZE is defined in RF24Network_config.h
//rf24 status led
unsigned long led_toggle_timer = 0;
unsigned long led_toggle_on_time_msec = 15UL;
PCA9685 pwmController; // 1khz pwm source
Adafruit_MCP23017 mcp; // i2c GPIO expander
void pin_setup() {
pinMode(mcu_bus_power_enable_pin, OUTPUT);
digitalWrite(mcu_bus_power_enable_pin, HIGH);
pinMode(rx_status_led_pin, OUTPUT);
digitalWrite(rx_status_led_pin, HIGH);
pinMode(A0, INPUT);
}
void init_devices() {
//USB serial
Serial.begin(SERIAL_0_BAUD);
//set i2c clock speed
Wire.setClock(I2C_CLOCK_SPEED);
//start mcp object at address 0
mcp.begin(&Wire); // use default address 0
//init mcp pins
for (int i = 0; i < 16; i++) {
mcp.pinMode(i, OUTPUT);
mcp.digitalWrite(i, LOW);
}
//pca9685 setup
pwmController.resetDevices(); // Resets all PCA9685 devices on i2c line
pwmController.init(); // Initializes module using default totem-pole driver mode, and default disabled phase balancer
pwmController.setPWMFrequency(PCA9685_PWM_FREQ); // Set PWM freq (default is 200Hz, supports 24Hz to 1526Hz)
if (!radio.begin()) {
Serial.println(F("RF24 radio hardware not responding, check connections!"));
//implement sensorless routine here
while (1) {
// hold in infinite loop
}
}
network.begin(/*channel*/ 90, /*node address*/ this_node);
printf_begin(); // Used to enable printf on AVR devices
radio.printDetails(); // requires printf support
Serial.print(F("this_node=")); Serial.println(this_node);
}
bool payload_switch(uint16_t node, RF24NetworkHeader header, uint16_t payloadSize) {
switch (node) {
case 1: {
if (payloadSize == sizeof(rx1_payload)) {
network.read(header, &rx1_payload, sizeof(rx1_payload)); // Get the data
return true;
} else {
//garbage can
network.read(header, &dataBuffer, payloadSize); // Get the data
return false;
}
break;
}
case 2: {
if (payloadSize == sizeof(rx2_payload)) {
network.read(header, &rx2_payload, sizeof(rx2_payload)); // Get the data
return true;
} else {
//garbage can
network.read(header, &dataBuffer, payloadSize); // Get the data
return false;
}
break;
}
case 3: {
if (payloadSize == sizeof(rx3_payload)) {
network.read(header, &rx3_payload, sizeof(rx3_payload)); // Get the data
return true;
} else {
//garbage can
network.read(header, &dataBuffer, payloadSize); // Get the data
return false;
}
break;
}
default: {
Serial.print(F("Unknown node: \"")); Serial.print(node); Serial.println(F("\""));
return false;
break;
}
}
return false;
}
uint32_t try_commit_payload_to_memory(uint16_t node, RF24NetworkHeader header, uint16_t payloadSize, uint32_t timeBetweenPackets) {
digitalWrite(rx_status_led_pin, HIGH);
led_toggle_timer = millis();
static uint32_t time_since_print[NUM_NODES + 1] = {0};
uint32_t elapsed_millis = millis() - timeBetweenPackets;
uint16_t t_node = 0;
//unknown node print
if (node > NUM_NODES-1) {
t_node = NUM_NODES;
}
else {
t_node = node;
}
uint32_t elapsed_millis_since_print = millis() - time_since_print[t_node];
if (elapsed_millis > 300UL || elapsed_millis_since_print >= 10000UL) {
Serial.print(F("Node ")); Serial.print(node); Serial.print(F(" rx packet, size ")); Serial.print(payloadSize);
Serial.print(F(" (")); Serial.print(elapsed_millis); Serial.print(F("ms since last rx, ")); Serial.print(elapsed_millis_since_print);
Serial.println(F("ms since last print this node's status)"));
time_since_print[t_node] = millis();
}
timeBetweenPackets = millis();
//this is where what is received gets copied into memory
bool ok = payload_switch(node, header, payloadSize);
if (ok == false) {
Serial.print(F("Packet length mismatch Node ")); Serial.println(node);
}
return timeBetweenPackets;
}
void rx_handler(RF24NetworkHeader header, uint16_t payloadSize) {
uint16_t node = header.from_node;
static uint32_t timeBetweenPackets[NUM_NODES + 1] = {0};
if (node <= (NUM_NODES - 1)) {
timeBetweenPackets[node] = try_commit_payload_to_memory(node, header, payloadSize, timeBetweenPackets[node]);
}
else {
//unknown node packet timer
timeBetweenPackets[NUM_NODES] = try_commit_payload_to_memory(node, header, payloadSize, timeBetweenPackets[node]);
}
}
void network_handler() {
//how long since we received a packet from N node timer
static uint32_t keep_alive[NUM_NODES + 1] = {0};
//timer for printing status
static uint32_t keep_alive_print_timer[NUM_NODES + 1] = {0};
//turn off status led if it's time
if ((millis() - led_toggle_timer) >= led_toggle_on_time_msec) {
digitalWrite(rx_status_led_pin, LOW);
}
network.update(); // Check the network regularly
while (network.available()) { // Is there anything ready for us?
RF24NetworkHeader header; // If so, grab it and print it out
uint16_t payloadSize = network.peek(header); // Use peek() to get the size of the payload
//sort the mail
rx_handler(header, payloadSize);
//if the transmitting node is unknown (higher address) keep track of how often we get packets (same timer for all unknown nodes)
if (header.from_node >= NUM_NODES) {
keep_alive[NUM_NODES] = millis(); //the last element in keep_alive is the unknown node timer
}
else {
keep_alive[header.from_node] = millis(); //we know about all these nodes
}
}
//this "see if we have anything to print" doesn't need to happen every call, probably 50-500ms would be more than enough
for (int i = 1; i < NUM_NODES; i++) {
uint32_t elapsed_time = millis() - keep_alive[i];
if (elapsed_time >= 1000UL) {
if (millis() - keep_alive_print_timer[i] >= 1000UL) {
uint32_t temp = elapsed_time / 1000;
Serial.print(F("Haven't heard from node ")); Serial.print(i); Serial.print(F(" in ")); Serial.print(temp); Serial.println(F(" seconds"));
keep_alive_print_timer[i] = millis();
}
}
}
}
//pwm demo
void do_pwm_things() {
//using some leds for testing
static int pwm_val = 0;
static bool fade_up = true;
pwmController.setChannelPWM(0, pwm_val); //pwm_val = 0-4095
pwmController.setChannelPWM(1, pwm_val); //pwm_val = 0-4095
pwmController.setChannelPWM(2, pwm_val); //pwm_val = 0-4095
pwmController.setChannelPWM(3, pwm_val); //pwm_val = 0-4095
if (fade_up == true && pwm_val <= 4095) {
pwm_val++;
if (pwm_val > 4095) {
pwm_val = 4096; //will immediately be caught by next if statement and -- to 4095
fade_up = false;
}
}
if (fade_up == false && pwm_val >= 1) {
pwm_val--;
if (pwm_val < 1) {
pwm_val = 0; //gets set next run through loop()
fade_up = true;
}
}
}
//mcp demo
void mcp_demo() {
static unsigned long timer = 0;
static bool toggle = false;
if ((millis() - timer) >= 20UL) {
for (int i = 0; i < 4; i++) {
if (toggle == false) {
mcp.digitalWrite(i, HIGH);
} else {
mcp.digitalWrite(i, LOW);
}
}
if (toggle == false) {
toggle = true;
} else {
toggle = false;
}
timer = millis();
}
}
void setup(void) {
pin_setup();
init_devices();
}
void loop(void) {
network_handler(); //semi-production network handler
do_pwm_things(); //pca9685 1khz pwm demo
mcp_demo(); //mcp i2c GPIO expander demo (blink port A 1-4)
//A0 is connected to 3.3v bus power through a voltage divider, the idea here is to disconnect I2C and SPI if we lose bus power
//should be disconnected using analog switches CD4066? I think 400ma is more than enough current handling capability for my i2c and SPI
static uint32_t bus_power_sense_timer = 0;
if (millis() - bus_power_sense_timer >= 10UL) {
bus_voltage_input = analogRead(A0);
}
}