├── Examples ├── LED_Control_Gateway │ └── LED_Control_Gateway.ino ├── LED_Control_Node │ └── LED_Control_Node.ino ├── RFM12B_Struct_gateway │ └── RFM12B_Struct_gateway.ino ├── RFM12B_Struct_node │ └── RFM12B_Struct_node.ino ├── Receive │ └── Receive.ino ├── Send │ └── Send.ino ├── Test_2way_gateway │ └── Test_2way_gateway.ino ├── Test_2way_node │ └── Test_2way_node.ino ├── Test_2way_node_withFlash │ └── Test_2way_node_withFlash.ino ├── WirelessProgramming_gateway │ └── WirelessProgramming_gateway.ino └── WirelessProgramming_node │ └── WirelessProgramming_node.ino ├── README.md ├── RFM12B.cpp ├── RFM12B.h ├── RFM12B_AIR_KBPS_Calculator.xlsx ├── keywords.txt └── library.json /Examples/LED_Control_Gateway/LED_Control_Gateway.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 by Felix Rusu 3 | * 4 | * This file is free software; you can redistribute it and/or modify 5 | * it under the terms of either the GNU General Public License version 2 6 | * or the GNU Lesser General Public License version 2.1, both as 7 | * published by the Free Software Foundation. 8 | */ 9 | 10 | // This is a GATEWAY sketch (LEDControl_Gateway) for a Moteino that can act as 11 | // a coordinator to control the LEDs on other Moteinos loaded 12 | // with the corresponding NODE sketches (LEDControl_Node) 13 | // Designed by Felix Rusu (felix@lowpowerlab.com), www.LowPowerLab.com 14 | 15 | // The LEDs on the endpoint Moteinos can be controlled as follows. In the GATEWAY's serial terminal: 16 | // Type a node ID in the rage [2-9] to set the target node 17 | // Type a LED mode [b,p,o] - blink, pulse, off 18 | // Type a speed for the LED action [s,m,f] - slow, medium, fast 19 | // Hit [ENTER] to send request to target 20 | // The target Moteino receives the message and applies the requested LED mode and speed 21 | 22 | #include 23 | 24 | #define NETWORKID 100 25 | #define NODEID 1 // network ID used for this unit 26 | #define KEY "ABCDABCDABCDABCD" //(16 bytes of your choice - keep the same on all encrypted nodes) 27 | #define REQUESTACK true // whether to request ACKs for sent messages 28 | #define ACK_TIME 50 // # of ms to wait for an ack 29 | #define SERIAL_BAUD 115200 30 | #define LED 9 31 | 32 | uint8_t input; 33 | RFM12B radio; 34 | char mode = 'b'; //mode : 'b' = blinking, 'p' = pulsing, 'o' = 'off' 35 | char theSpeed = 'm'; //speed : 's' = slow, 'm' = medium, 'f' = 'fast' 36 | byte nodeId = 2; //node to send message to 37 | char * sendBuf="x:y"; //format is MODE:SPEED 38 | 39 | void displayMenu(boolean includeHeader=true) 40 | { 41 | if (includeHeader) 42 | { 43 | Serial.println("******************************************************************"); 44 | Serial.println("* Node blinking/pulsing program *"); 45 | } 46 | Serial.println("******************************************************************"); 47 | Serial.println("* [2 (default) to 9] - change target node ID *"); 48 | Serial.println("* [b=BLINK (default), p=PULSE, o=OFF] - change target LED mode *"); 49 | Serial.println("* [s=SLOW, m=MEDIUM (default), f=FAST] - change target LED speed *"); 50 | Serial.println("* [ENTER] - send request to target *"); 51 | Serial.println("******************************************************************\n"); 52 | } 53 | 54 | void setup() 55 | { 56 | pinMode(LED, OUTPUT); 57 | radio.Initialize(NODEID, RF12_915MHZ, NETWORKID); 58 | radio.Encrypt((uint8_t*)KEY); 59 | Serial.begin(SERIAL_BAUD); 60 | displayMenu(); 61 | } 62 | 63 | void loop() 64 | { 65 | //check for serial input (from 2-9, to correspond to node IDs [2,9]) 66 | if (Serial.available() > 0) { 67 | input = Serial.read(); 68 | 69 | if (input == 'p' || input == 'b' || input == 'o') 70 | { 71 | Serial.print("Switched mode to [" ); 72 | Serial.print(input == 'p' ? "PULSE" : input == 'b' ? "BLINK" : "OFF"); 73 | Serial.println("]..."); 74 | mode = input; 75 | } 76 | else if (input == 's' || input == 'm' || input == 'f') 77 | { 78 | Serial.print("Switched speed to [" ); 79 | Serial.print(input == 's' ? "SLOW" : input == 'm' ? "MEDIUM" : "FAST"); 80 | Serial.println("]..."); 81 | theSpeed = input; 82 | } 83 | else if (input >= '2' && input <= '9') //[2 to 9] 84 | { 85 | nodeId = input-48; 86 | Serial.print("Switched to node [" ); 87 | Serial.print(nodeId); 88 | Serial.println("]...\n"); 89 | } 90 | else if (input == 13) //ENTER key pressed, send message with existing data (mode, nodeid) 91 | sendMessage(); 92 | else 93 | { 94 | Serial.println("Invalid input, try again:\n"); 95 | displayMenu(false); 96 | } 97 | } 98 | 99 | //pass any received RF message to serial 100 | if (radio.ReceiveComplete()) 101 | { 102 | if (radio.CRCPass()) 103 | { 104 | digitalWrite(LED,1); 105 | Serial.print('[');Serial.print(radio.GetSender(), DEC);Serial.print("] "); 106 | for (byte i = 0; i < *radio.DataLen; i++) 107 | Serial.print((char)radio.Data[i]); 108 | 109 | if (radio.ACKRequested()) 110 | { 111 | radio.SendACK(); 112 | Serial.print(" - ACK sent"); 113 | } 114 | delay(5); 115 | digitalWrite(LED,0); 116 | } 117 | else Serial.print("BAD-CRC"); 118 | 119 | Serial.println(); 120 | } 121 | } 122 | 123 | void sendMessage() 124 | { 125 | sprintf(sendBuf, "%c:%c", mode, theSpeed); 126 | radio.Send(nodeId, sendBuf, 3, REQUESTACK); 127 | Serial.print("Request sent "); 128 | Serial.print(nodeId); 129 | Serial.print(":"); 130 | Serial.print(mode=='b'?"BLINK":mode=='p'?"PULSE":"OFF"); 131 | if (mode!='o') 132 | { 133 | Serial.print(":"); 134 | Serial.print(theSpeed=='s'?"SLOW":theSpeed=='m'?"MEDIUM":"FAST"); 135 | } 136 | Serial.println(waitForAck() ? " (ACK OK)" : "(no ACK reply)"); 137 | Blink(LED, 5); 138 | } 139 | 140 | // wait up to ACK_TIME for proper ACK, return true if received 141 | static bool waitForAck() { 142 | long now = millis(); 143 | while (millis() - now <= ACK_TIME) 144 | if (radio.ACKReceived(nodeId)) 145 | return true; 146 | return false; 147 | } 148 | 149 | void Blink(byte PIN, byte DELAY_MS) 150 | { 151 | pinMode(PIN, OUTPUT); 152 | digitalWrite(PIN,HIGH); 153 | delay(DELAY_MS); 154 | digitalWrite(PIN,LOW); 155 | } -------------------------------------------------------------------------------- /Examples/LED_Control_Node/LED_Control_Node.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 by Felix Rusu 3 | * 4 | * This file is free software; you can redistribute it and/or modify 5 | * it under the terms of either the GNU General Public License version 2 6 | * or the GNU Lesser General Public License version 2.1, both as 7 | * published by the Free Software Foundation. 8 | */ 9 | 10 | // This is a NODE sketch (LEDControl_Node) for a Moteino that can act as 11 | // a endpoint which blinks/pulses/stops the onboard LEDs per requests from Moteino(s) 12 | // loaded with GATEWAY sketches (LEDControl_Gateway) 13 | // Designed by Felix Rusu (felix@lowpowerlab.com), www.LowPowerLab.com 14 | 15 | // The LEDs on the endpoint Moteinos can be controlled as follows. In the GATEWAY's serial terminal: 16 | // Type a node ID in the rage [2-9] to set the target node 17 | // Type a LED mode [b,p,o] - blink, pulse, off 18 | // Type a speed for the LED action [s,m,f] - slow, medium, fast 19 | // Hit [ENTER] to send request to target 20 | // The target Moteino receives the message and applies the requested LED mode and speed 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define NETWORKID 100 //what network this node is on 28 | #define NODEID 2 //this node's ID, should be unique among nodes on this NETWORKID 29 | #define GATEWAYID 1 //central node to report data to 30 | #define KEY "ABCDABCDABCDABCD" //(16 bytes of your choice - keep the same on all encrypted nodes) 31 | #define LED 9 //pin connected to onboard LED (digital 9, pulse width modulation capable for pulsing) 32 | #define SERIAL_BAUD 115200 33 | 34 | RFM12B radio; 35 | char mode = 'b'; // mode : 'b' = blinking, 'p' = pulsing, 'o' = 'off' 36 | char theSpeed = 'm'; // speed : 's' = slow, 'm' = medium, 'f' = 'fast' 37 | boolean LEDState; // current LED state when blinking 38 | long now=0, LED_lastStateChange=0; 39 | float in, out; // used for fading the LED 40 | 41 | void setup(void) 42 | { 43 | radio.Initialize(NODEID, RF12_915MHZ, NETWORKID); 44 | radio.Encrypt((uint8_t*)KEY); 45 | Serial.begin(SERIAL_BAUD); 46 | pinMode(LED, OUTPUT); 47 | Serial.println("Listening for LED blink/pulse/stop requests...\n"); 48 | handleRequest(mode, theSpeed); 49 | Serial.println(); 50 | } 51 | 52 | void loop() 53 | { 54 | now = millis(); 55 | 56 | //handle blinking/fading/stopping 57 | if (mode=='p') 58 | { 59 | //do some fading for 50ms & continue next loop where fading left off (to give radio a chance to listen) 60 | while(millis()-now < 50) 61 | { 62 | if (in > 6.283) in = 0; 63 | in += .00628; 64 | out = sin(in) * 127.5 + 127.5; 65 | analogWrite(LED, out); 66 | delayMicroseconds(theSpeed=='s'?1500:theSpeed=='m'?1000:500); 67 | } 68 | } 69 | else if (mode=='b' && millis()-LED_lastStateChange >= (theSpeed=='s'?1000:theSpeed=='m'?500:100)) 70 | { 71 | LEDState = !LEDState; 72 | digitalWrite(LED, LEDState); 73 | LED_lastStateChange = now; 74 | } 75 | else if (mode == 'o')//not 'b'linking or 'p'ulsing so turn LED off 76 | digitalWrite(LED, 0); 77 | 78 | //pass any received RF message to serial 79 | if (radio.ReceiveComplete()) 80 | { 81 | if (radio.CRCPass()) 82 | { 83 | Serial.print('[');Serial.print(radio.GetSender(), DEC);Serial.print("] "); 84 | for (byte i = 0; i < *radio.DataLen; i++) 85 | Serial.print((char)radio.Data[i]); 86 | 87 | Serial.print(" - "); 88 | //check for a LED blink/pulse/stop request 89 | if (*radio.DataLen == 3 90 | && (radio.Data[0]=='b' || radio.Data[0]=='p' || radio.Data[0]=='o') 91 | && radio.Data[1] == ':' 92 | && (radio.Data[2]=='s' || radio.Data[2]=='m' || radio.Data[2]=='f')) 93 | handleRequest(radio.Data[0], radio.Data[2]); 94 | 95 | if (radio.ACKRequested()) 96 | { 97 | radio.SendACK(); 98 | Serial.print(" (ACK sent)"); 99 | } 100 | } 101 | else Serial.print("BAD-CRC"); 102 | 103 | Serial.println(); 104 | } 105 | } 106 | 107 | void handleRequest(char requestMode, char requestSpeed) 108 | { 109 | Serial.print(requestMode=='b'?"BLINK ":requestMode=='p'?"PULSE ":"OFF"); 110 | if (requestMode != 'o') 111 | Serial.print(requestSpeed=='s'?"SLOW":requestSpeed=='m'?"MEDIUM":"FAST"); 112 | mode = requestMode; 113 | theSpeed = requestSpeed; 114 | } -------------------------------------------------------------------------------- /Examples/RFM12B_Struct_gateway/RFM12B_Struct_gateway.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define NODEID 1 6 | #define NETWORKID 100 7 | #define FREQUENCY RF12_915MHZ //Match this with the version of your Moteino! (others: RF69_433MHZ, RF69_868MHZ) 8 | #define KEY "thisIsEncryptKey" //has to be same 16 characters/bytes on all nodes, not more not less! 9 | #define LED 9 10 | #define SERIAL_BAUD 115200 11 | #define ACK_TIME 30 // # of ms to wait for an ack 12 | 13 | RFM12B radio; 14 | SPIFlash flash(8, 0xEF30); //EF40 for 16mbit windbond chip 15 | bool promiscuousMode = false; //set to 'true' to sniff all packets on the same network 16 | 17 | typedef struct { 18 | int nodeId; //store this nodeId 19 | unsigned long uptime; //uptime in ms 20 | float temp; //temperature maybe? 21 | } Payload; 22 | Payload theData; 23 | 24 | void setup() { 25 | Serial.begin(SERIAL_BAUD); 26 | delay(10); 27 | radio.Initialize(NODEID, FREQUENCY, NETWORKID); 28 | radio.Encrypt((byte*)KEY); 29 | char buff[50]; 30 | sprintf(buff, "\nListening at %d Mhz...", FREQUENCY==RF12_433MHZ ? 433 : FREQUENCY==RF12_868MHZ ? 868 : 915); 31 | Serial.println(buff); 32 | if (flash.initialize()) 33 | Serial.println("SPI Flash Init OK!"); 34 | else 35 | Serial.println("SPI Flash Init FAIL! (is chip present?)"); 36 | } 37 | 38 | byte ackCount=0; 39 | void loop() { 40 | //process any serial input 41 | if (Serial.available() > 0) 42 | { 43 | char input = Serial.read(); 44 | 45 | if (input == 'd') //d=dump flash area 46 | { 47 | Serial.println("Flash content:"); 48 | int counter = 0; 49 | 50 | while(counter<=256){ 51 | Serial.print(flash.readByte(counter++), HEX); 52 | Serial.print('.'); 53 | } 54 | while(flash.busy()); 55 | Serial.println(); 56 | } 57 | if (input == 'e') 58 | { 59 | Serial.print("Erasing Flash chip ... "); 60 | flash.chipErase(); 61 | while(flash.busy()); 62 | Serial.println("DONE"); 63 | } 64 | if (input == 'i') 65 | { 66 | Serial.print("DeviceID: "); 67 | word jedecid = flash.readDeviceId(); 68 | Serial.println(jedecid, HEX); 69 | } 70 | } 71 | 72 | if (radio.ReceiveComplete()) 73 | { 74 | if (radio.CRCPass()) 75 | { 76 | Serial.print('[');Serial.print(radio.GetSender(), DEC);Serial.print("] "); 77 | 78 | if (*radio.DataLen != sizeof(Payload)) 79 | Serial.print("Invalid payload received, not matching Payload struct!"); 80 | else 81 | { 82 | theData = *(Payload*)radio.Data; //assume radio.DATA actually contains our struct and not something else 83 | Serial.print(" nodeId="); 84 | Serial.print(theData.nodeId); 85 | Serial.print(" uptime="); 86 | Serial.print(theData.uptime); 87 | Serial.print(" temp="); 88 | Serial.print(theData.temp); 89 | } 90 | 91 | if (radio.ACKRequested()) 92 | { 93 | byte theNodeID = radio.GetSender(); 94 | radio.SendACK(); 95 | //when a node requests an ACK, respond to the ACK and also send a packet requesting an ACK 96 | //This way both TX/RX NODE functions are tested on 1 end at the GATEWAY 97 | Serial.print(" - ACK sent. Sending packet to node "); 98 | Serial.print(theNodeID); 99 | delay(10); 100 | radio.Send(theNodeID, "ACK TEST", 8, true); 101 | Serial.print(" - waiting for ACK..."); 102 | if (waitForAck(theNodeID)) Serial.print("ok!"); 103 | else Serial.print("nothing..."); 104 | } 105 | Serial.println(); 106 | Blink(LED,3); 107 | } 108 | } 109 | } 110 | 111 | void Blink(byte PIN, int DELAY_MS) 112 | { 113 | pinMode(PIN, OUTPUT); 114 | digitalWrite(PIN,HIGH); 115 | delay(DELAY_MS); 116 | digitalWrite(PIN,LOW); 117 | } 118 | 119 | // wait a few milliseconds for proper ACK to me, return true if indeed received 120 | static bool waitForAck(byte theNodeID) { 121 | long now = millis(); 122 | while (millis() - now <= ACK_TIME) { 123 | if (radio.ACKReceived(theNodeID)) 124 | return true; 125 | } 126 | return false; 127 | } 128 | -------------------------------------------------------------------------------- /Examples/RFM12B_Struct_node/RFM12B_Struct_node.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define NODEID 99 6 | #define NETWORKID 100 7 | #define GATEWAYID 1 8 | #define FREQUENCY RF12_915MHZ //Match this with the version of your Moteino! (others: RF69_433MHZ, RF69_868MHZ) 9 | #define KEY "thisIsEncryptKey" //has to be same 16 characters/bytes on all nodes, not more not less! 10 | #define LED 9 11 | #define SERIAL_BAUD 115200 12 | #define ACK_TIME 30 // # of ms to wait for an ack 13 | 14 | int TRANSMITPERIOD = 600; //transmit a packet to gateway so often (in ms) 15 | byte sendSize=0; 16 | boolean requestACK = false; 17 | SPIFlash flash(8, 0xEF30); //EF40 for 16mbit windbond chip 18 | RFM12B radio; 19 | 20 | typedef struct { 21 | int nodeId; //store this nodeId 22 | unsigned long uptime; //uptime in ms 23 | float temp; //temperature maybe? 24 | } Payload; 25 | Payload theData; 26 | 27 | void setup() { 28 | Serial.begin(SERIAL_BAUD); 29 | radio.Initialize(NODEID, FREQUENCY, NETWORKID, 0); 30 | radio.Encrypt((byte*)KEY); 31 | 32 | char buff[50]; 33 | sprintf(buff, "\nTransmitting at %d Mhz...", FREQUENCY==RF12_433MHZ ? 433 : FREQUENCY==RF12_868MHZ ? 868 : 915); 34 | Serial.println(buff); 35 | 36 | if (flash.initialize()) 37 | Serial.println("SPI Flash Init OK!"); 38 | else 39 | Serial.println("SPI Flash Init FAIL! (is chip present?)"); 40 | } 41 | 42 | long lastPeriod = -1; 43 | void loop() { 44 | //process any serial input 45 | if (Serial.available() > 0) 46 | { 47 | char input = Serial.read(); 48 | if (input >= 48 && input <= 57) //[0,9] 49 | { 50 | TRANSMITPERIOD = 100 * (input-48); 51 | if (TRANSMITPERIOD == 0) TRANSMITPERIOD = 1000; 52 | Serial.print("\nChanging delay to "); 53 | Serial.print(TRANSMITPERIOD); 54 | Serial.println("ms\n"); 55 | } 56 | 57 | if (input == 'd') //d=dump flash area 58 | { 59 | Serial.println("Flash content:"); 60 | int counter = 0; 61 | 62 | while(counter<=256){ 63 | Serial.print(flash.readByte(counter++), HEX); 64 | Serial.print('.'); 65 | } 66 | while(flash.busy()); 67 | Serial.println(); 68 | } 69 | if (input == 'e') 70 | { 71 | Serial.print("Erasing Flash chip ... "); 72 | flash.chipErase(); 73 | while(flash.busy()); 74 | Serial.println("DONE"); 75 | } 76 | if (input == 'i') 77 | { 78 | Serial.print("DeviceID: "); 79 | word jedecid = flash.readDeviceId(); 80 | Serial.println(jedecid, HEX); 81 | } 82 | } 83 | 84 | //check for any received packets 85 | if (radio.ReceiveComplete()) 86 | { 87 | if (radio.CRCPass()) 88 | { 89 | Serial.print('[');Serial.print(radio.GetSender(), DEC);Serial.print("] "); 90 | for (byte i = 0; i < *radio.DataLen; i++) 91 | Serial.print((char)radio.Data[i]); 92 | 93 | if (radio.ACKRequested()) 94 | { 95 | radio.SendACK(); 96 | Serial.println(" - ACK sent."); 97 | } 98 | Blink(LED,5); 99 | } 100 | } 101 | 102 | int currPeriod = millis()/TRANSMITPERIOD; 103 | if (currPeriod != lastPeriod) 104 | { 105 | //fill in the struct with new values 106 | theData.nodeId = NODEID; 107 | theData.uptime = millis(); 108 | theData.temp = 91.23; //it's hot! 109 | 110 | Serial.print("Sending struct ("); 111 | Serial.print(sizeof(theData)); 112 | Serial.print(" bytes) ... "); 113 | 114 | requestACK = !requestACK; //request every other time 115 | 116 | radio.Send(GATEWAYID, (const void*)(&theData), sizeof(theData), requestACK); 117 | if (requestACK) 118 | { 119 | Serial.print(" - waiting for ACK..."); 120 | if (waitForAck(GATEWAYID)) Serial.print("ok!"); 121 | else Serial.print("nothing..."); 122 | } 123 | 124 | Serial.println(); 125 | Blink(LED,3); 126 | lastPeriod=currPeriod; 127 | } 128 | } 129 | 130 | void Blink(byte PIN, int DELAY_MS) 131 | { 132 | pinMode(PIN, OUTPUT); 133 | digitalWrite(PIN,HIGH); 134 | delay(DELAY_MS); 135 | digitalWrite(PIN,LOW); 136 | } 137 | 138 | // wait a few milliseconds for proper ACK to me, return true if indeed received 139 | static bool waitForAck(byte theNodeID) { 140 | long now = millis(); 141 | while (millis() - now <= ACK_TIME) { 142 | if (radio.ACKReceived(theNodeID)) 143 | return true; 144 | } 145 | return false; 146 | } 147 | -------------------------------------------------------------------------------- /Examples/Receive/Receive.ino: -------------------------------------------------------------------------------- 1 | // Simple serial pass through program 2 | // It initializes the RFM12B radio with optional encryption and passes through any valid messages to the serial port 3 | // felix@lowpowerlab.com 4 | 5 | #include 6 | 7 | // You will need to initialize the radio by telling it what ID it has and what network it's on 8 | // The NodeID takes values from 1-127, 0 is reserved for sending broadcast messages (send to all nodes) 9 | // The Network ID takes values from 0-255 10 | // By default the SPI-SS line used is D10 on Atmega328. You can change it by calling .SetCS(pin) where pin can be {8,9,10} 11 | #define NODEID 1 //network ID used for this unit 12 | #define NETWORKID 99 //the network ID we are on 13 | #define SERIAL_BAUD 115200 14 | 15 | //encryption is OPTIONAL 16 | //to enable encryption you will need to: 17 | // - provide a 16-byte encryption KEY (same on all nodes that talk encrypted) 18 | // - to call .Encrypt(KEY) to start encrypting 19 | // - to stop encrypting call .Encrypt(NULL) 20 | uint8_t KEY[] = "ABCDABCDABCDABCD"; 21 | 22 | // Need an instance of the Radio Module 23 | RFM12B radio; 24 | void setup() 25 | { 26 | radio.Initialize(NODEID, RF12_433MHZ, NETWORKID); 27 | radio.Encrypt(KEY); //comment this out to disable encryption 28 | Serial.begin(SERIAL_BAUD); 29 | Serial.println("Listening..."); 30 | } 31 | 32 | void loop() 33 | { 34 | if (radio.ReceiveComplete()) 35 | { 36 | if (radio.CRCPass()) 37 | { 38 | Serial.print('[');Serial.print(radio.GetSender());Serial.print("] "); 39 | for (byte i = 0; i < *radio.DataLen; i++) //can also use radio.GetDataLen() if you don't like pointers 40 | Serial.print((char)radio.Data[i]); 41 | 42 | if (radio.ACKRequested()) 43 | { 44 | radio.SendACK(); 45 | Serial.print(" - ACK sent"); 46 | } 47 | } 48 | else 49 | Serial.print("BAD-CRC"); 50 | 51 | Serial.println(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Examples/Send/Send.ino: -------------------------------------------------------------------------------- 1 | // Simple RFM12B sender program, with ACK and optional encryption 2 | // It initializes the RFM12B radio with optional encryption and passes through any valid messages to the serial port 3 | // felix@lowpowerlab.com 4 | 5 | #include 6 | #include 7 | 8 | // You will need to initialize the radio by telling it what ID it has and what network it's on 9 | // The NodeID takes values from 1-127, 0 is reserved for sending broadcast messages (send to all nodes) 10 | // The Network ID takes values from 0-255 11 | // By default the SPI-SS line used is D10 on Atmega328. You can change it by calling .SetCS(pin) where pin can be {8,9,10} 12 | #define NODEID 2 //network ID used for this unit 13 | #define NETWORKID 99 //the network ID we are on 14 | #define GATEWAYID 1 //the node ID we're sending to 15 | #define ACK_TIME 50 // # of ms to wait for an ack 16 | #define SERIAL_BAUD 115200 17 | 18 | //encryption is OPTIONAL 19 | //to enable encryption you will need to: 20 | // - provide a 16-byte encryption KEY (same on all nodes that talk encrypted) 21 | // - to call .Encrypt(KEY) to start encrypting 22 | // - to stop encrypting call .Encrypt(NULL) 23 | uint8_t KEY[] = "ABCDABCDABCDABCD"; 24 | 25 | int interPacketDelay = 1000; //wait this many ms between sending packets 26 | char input = 0; 27 | 28 | // Need an instance of the Radio Module 29 | RFM12B radio; 30 | byte sendSize=0; 31 | char payload[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~!@#$%^&*(){}[]`|<>?+=:;,."; 32 | bool requestACK=false; 33 | 34 | void setup() 35 | { 36 | Serial.begin(SERIAL_BAUD); 37 | radio.Initialize(NODEID, RF12_433MHZ, NETWORKID); 38 | radio.Encrypt(KEY); 39 | radio.Sleep(); //sleep right away to save power 40 | Serial.println("Transmitting...\n\n"); 41 | } 42 | 43 | void loop() 44 | { 45 | //serial input of [0-9] will change the transmit delay between 100-1000ms 46 | if (Serial.available() > 0) { 47 | input = Serial.read(); 48 | if (input >= 48 && input <= 57) //[1..9] = {100..900}ms; [0]=1000ms 49 | { 50 | interPacketDelay = 100 * (input-48); 51 | if (interPacketDelay == 0) interPacketDelay = 1000; 52 | Serial.print("\nChanging delay to "); 53 | Serial.print(interPacketDelay); 54 | Serial.println("ms\n"); 55 | } 56 | } 57 | 58 | Serial.print("Sending["); 59 | Serial.print(sendSize+1); 60 | Serial.print("]:"); 61 | for(byte i = 0; i < sendSize+1; i++) 62 | Serial.print((char)payload[i]); 63 | 64 | requestACK = !(sendSize % 3); //request ACK every 3rd xmission 65 | 66 | radio.Wakeup(); 67 | radio.Send(GATEWAYID, payload, sendSize+1, requestACK); 68 | if (requestACK) 69 | { 70 | Serial.print(" - waiting for ACK..."); 71 | if (waitForAck()) Serial.print("ok!"); 72 | else Serial.print("nothing..."); 73 | } 74 | radio.Sleep(); 75 | 76 | sendSize = (sendSize + 1) % 88; 77 | Serial.println(); 78 | delay(interPacketDelay); 79 | } 80 | 81 | // wait a few milliseconds for proper ACK, return true if received 82 | static bool waitForAck() { 83 | long now = millis(); 84 | while (millis() - now <= ACK_TIME) 85 | if (radio.ACKReceived(GATEWAYID)) 86 | return true; 87 | return false; 88 | } 89 | -------------------------------------------------------------------------------- /Examples/Test_2way_gateway/Test_2way_gateway.ino: -------------------------------------------------------------------------------- 1 | // Test sketch that is loaded on master Moteinos 2 | // It will listen for any packets received from slave Moteinos 3 | // If an ACK request message is received, it sends and ACK and also 4 | // sends an ACKed message to the slave, ensuring that 2-way 5 | // communication is functional on the slave Moteino 6 | 7 | #include 8 | 9 | #define SERIAL_BAUD 115200 10 | #define NODEID 1 // network ID used for this unit 11 | #define NETWORKID 100 12 | #define FREQUENCY RF12_433MHZ //Match this with the version of your Moteino! (others: RF12_433MHZ, RF12_915MHZ) 13 | #define KEY "ABCDABCDABCDABCD" //encryption key 14 | #define ACK_TIME 50 // # of ms to wait for an ack 15 | 16 | 17 | uint8_t input[RF12_MAXDATA]; 18 | RFM12B radio; 19 | 20 | void setup() 21 | { 22 | pinMode(9, OUTPUT); 23 | radio.Initialize(NODEID, FREQUENCY, NETWORKID); 24 | radio.Encrypt((byte*)KEY); 25 | Serial.begin(SERIAL_BAUD); 26 | char buff[50]; 27 | sprintf(buff, "Listening at %d Mhz...", FREQUENCY == RF12_433MHZ ? 433 : FREQUENCY== RF12_868MHZ ? 868 : 915); 28 | Serial.println(buff); 29 | } 30 | 31 | byte recvCount = 0; 32 | 33 | void loop() 34 | { 35 | if (radio.ReceiveComplete()) 36 | { 37 | if (radio.CRCPass()) 38 | { 39 | digitalWrite(9,1); 40 | Serial.print('[');Serial.print(radio.GetSender(), DEC);Serial.print("] "); 41 | for (byte i = 0; i < *radio.DataLen; i++) 42 | Serial.print((char)radio.Data[i]); 43 | 44 | if (radio.ACKRequested()) 45 | { 46 | byte theNodeID = radio.GetSender(); 47 | radio.SendACK(); 48 | //when a node requests an ACK, respond to the ACK and also send a packet requesting an ACK 49 | //This way both TX/RX NODE functions are tested on 1 end at the GATEWAY 50 | Serial.print(" - ACK sent. Sending packet to node "); 51 | Serial.print(theNodeID); 52 | delay(10); 53 | radio.Send(theNodeID, "ACK TEST", 8, true); 54 | Serial.print(" - waiting for ACK..."); 55 | if (waitForAck(theNodeID)) Serial.print("ok!"); 56 | else Serial.print("nothing..."); 57 | } 58 | delay(5); 59 | digitalWrite(9,0); 60 | } 61 | else Serial.print("BAD-CRC"); 62 | 63 | Serial.println(); 64 | } 65 | } 66 | 67 | // wait a few milliseconds for proper ACK to me, return true if indeed received 68 | static bool waitForAck(byte theNodeID) { 69 | long now = millis(); 70 | while (millis() - now <= ACK_TIME) { 71 | if (radio.ACKReceived(theNodeID)) 72 | return true; 73 | } 74 | return false; 75 | } 76 | -------------------------------------------------------------------------------- /Examples/Test_2way_node/Test_2way_node.ino: -------------------------------------------------------------------------------- 1 | // Test sketch that is loaded on slave Moteinos 2 | // It will send an encrypted message to the master/gateway every TRANSMITPERIOD 3 | // It will respond to any ACKed messages from the master 4 | // Every 3rd message will also be ACKed (request ACK from master). 5 | #include 6 | #include 7 | 8 | #define MYID 111 // node ID used for this unit 9 | #define NETWORKID 100 10 | #define GATEWAYID 1 11 | #define FREQUENCY RF12_433MHZ //Match this with the version of your Moteino! (others: RF12_433MHZ, RF12_915MHZ) 12 | #define KEY "ABCDABCDABCDABCD" 13 | #define TRANSMITPERIOD 600 //transmit a packet to gateway so often (in ms) 14 | 15 | #define SERIAL_BAUD 115200 16 | #define ACK_TIME 50 // # of ms to wait for an ack 17 | 18 | int interPacketDelay = 1000; //wait this many ms between sending packets 19 | char input = 0; 20 | RFM12B radio; 21 | 22 | boolean requestACK = false; 23 | byte sendSize=0; 24 | char payload[] = "123 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 25 | 26 | void setup() 27 | { 28 | Serial.begin(SERIAL_BAUD); 29 | radio.Initialize(MYID, FREQUENCY, NETWORKID, 0); 30 | radio.Encrypt((byte*)KEY); 31 | char buff[50]; 32 | sprintf(buff, "Transmitting at %d Mhz...", FREQUENCY == RF12_433MHZ ? 433 : FREQUENCY== RF12_868MHZ ? 868 : 915); 33 | Serial.println(buff); 34 | } 35 | 36 | long lastPeriod = -1; 37 | void loop() 38 | { 39 | //process any serial input 40 | if (Serial.available() > 0) { 41 | input = Serial.read(); 42 | if (input >= 48 && input <= 57) //[0,9] 43 | { 44 | interPacketDelay = 100 * (input-48); 45 | if (interPacketDelay == 0) interPacketDelay = 1000; 46 | Serial.print("\nChanging delay to "); 47 | Serial.print(interPacketDelay); 48 | Serial.println("ms\n"); 49 | } 50 | } 51 | 52 | //check for any received packets 53 | if (radio.ReceiveComplete()) 54 | { 55 | if (radio.CRCPass()) 56 | { 57 | Serial.print('[');Serial.print(radio.GetSender(), DEC);Serial.print("] "); 58 | for (byte i = 0; i < *radio.DataLen; i++) 59 | Serial.print((char)radio.Data[i]); 60 | 61 | if (radio.ACKRequested()) 62 | { 63 | radio.SendACK(); 64 | Serial.print(" - ACK sent."); 65 | } 66 | Blink(9,5); 67 | } 68 | } 69 | 70 | if ((int)(millis()/TRANSMITPERIOD) > lastPeriod) 71 | { 72 | lastPeriod++; 73 | //Send data periodically to GATEWAY 74 | Serial.print("Sending["); 75 | Serial.print(sendSize); 76 | Serial.print("]: "); 77 | for(byte i = 0; i < sendSize; i++) 78 | Serial.print((char)payload[i]); 79 | 80 | requestACK = ((sendSize % 3) == 0); //request ACK every 3rd xmission 81 | radio.Send(GATEWAYID, payload, sendSize, requestACK); 82 | if (requestACK) 83 | { 84 | Serial.print(" - waiting for ACK..."); 85 | if (waitForAck(GATEWAYID)) Serial.print("ok!"); 86 | else Serial.print("nothing..."); 87 | } 88 | 89 | sendSize = (sendSize + 1) % 31; 90 | Serial.println(); 91 | Blink(9,5); 92 | } 93 | } 94 | 95 | // wait a few milliseconds for proper ACK to me, return true if indeed received 96 | static bool waitForAck(byte theNodeID) { 97 | long now = millis(); 98 | while (millis() - now <= ACK_TIME) { 99 | if (radio.ACKReceived(theNodeID)) 100 | return true; 101 | } 102 | return false; 103 | } 104 | 105 | void Blink(byte PIN, int DELAY_MS) 106 | { 107 | pinMode(PIN, OUTPUT); 108 | digitalWrite(PIN,HIGH); 109 | delay(DELAY_MS); 110 | digitalWrite(PIN,LOW); 111 | } 112 | -------------------------------------------------------------------------------- /Examples/Test_2way_node_withFlash/Test_2way_node_withFlash.ino: -------------------------------------------------------------------------------- 1 | // Test sketch that is loaded on slave Moteinos 2 | // It will send an encrypted message to the master/gateway every TRANSMITPERIOD 3 | // It will respond to any ACKed messages from the master 4 | // Every 3rd message will also be ACKed (request ACK from master). 5 | #include 6 | #include 7 | #include b 8 | #include 9 | 10 | #define MYID 123 // node ID used for this unit 11 | #define NETWORKID 100 12 | #define GATEWAYID 1 13 | #define FREQUENCY RF12_433MHZ //Match this with the version of your Moteino! (others: RF12_433MHZ, RF12_915MHZ) 14 | #define KEY "ABCDABCDABCDABCD" 15 | int TRANSMITPERIOD = 500; //transmit a packet to gateway so often (in ms) 16 | 17 | #define SERIAL_BAUD 115200 18 | #define ACK_TIME 50 // # of ms to wait for an ack 19 | 20 | char input = 0; 21 | RFM12B radio; 22 | SPIFlash flash(8, 0xEF30); //EF30 for Windbond 4mbit flash 23 | boolean requestACK = false; 24 | byte sendSize=0; 25 | char payload[] = "123 ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 26 | 27 | void setup() 28 | { 29 | Serial.begin(SERIAL_BAUD); 30 | radio.Initialize(MYID, FREQUENCY, NETWORKID, 0); 31 | radio.Encrypt((byte*)KEY); 32 | char buff[50]; 33 | sprintf(buff, "Transmitting at %d Mhz...", FREQUENCY == RF12_433MHZ ? 433 : FREQUENCY== RF12_868MHZ ? 868 : 915); 34 | Serial.println(buff); 35 | if (flash.initialize()) 36 | Serial.println("SPI Flash Init OK!"); 37 | else 38 | Serial.println("SPI Flash Init FAIL! (is chip present?)"); 39 | } 40 | 41 | long lastPeriod = -1; 42 | void loop() 43 | { 44 | //process any serial input 45 | if (Serial.available() > 0) { 46 | input = Serial.read(); 47 | if (input >= 48 && input <= 57) //[0,9] 48 | { 49 | TRANSMITPERIOD = 100 * (input-48); 50 | if (TRANSMITPERIOD == 0) TRANSMITPERIOD = 1000; 51 | Serial.print("\nChanging delay to "); 52 | Serial.print(TRANSMITPERIOD); 53 | Serial.println("ms\n"); 54 | } 55 | else if (input == 'd') //d=dump flash area 56 | { 57 | Serial.println("Flash content:"); 58 | int counter = 0; 59 | 60 | while(counter<=256){ 61 | Serial.print(flash.readByte(counter++), HEX); 62 | Serial.print('.'); 63 | } 64 | while(flash.busy()); 65 | Serial.println(); 66 | } 67 | else if (input == 'e') 68 | { 69 | Serial.print("Erasing Flash chip ... "); 70 | flash.chipErase(); 71 | while(flash.busy()); 72 | Serial.println("DONE"); 73 | } 74 | else if (input == 'i') 75 | { 76 | Serial.print("DeviceID: "); 77 | Serial.println(flash.readDeviceId(), HEX); 78 | } 79 | } 80 | 81 | //check for any received packets 82 | if (radio.ReceiveComplete()) 83 | { 84 | if (radio.CRCPass()) 85 | { 86 | Serial.print('[');Serial.print(radio.GetSender(), DEC);Serial.print("] "); 87 | for (byte i = 0; i < *radio.DataLen; i++) 88 | Serial.print((char)radio.Data[i]); 89 | 90 | if (radio.ACKRequested()) 91 | { 92 | radio.SendACK(); 93 | Serial.print(" - ACK sent."); 94 | } 95 | Blink(9,5); 96 | } 97 | } 98 | 99 | int currPeriod = millis()/TRANSMITPERIOD; 100 | if (currPeriod != lastPeriod) 101 | { 102 | lastPeriod=currPeriod; 103 | //Send data periodically to GATEWAY 104 | Serial.print("Sending["); 105 | Serial.print(sendSize); 106 | Serial.print("]: "); 107 | for(byte i = 0; i < sendSize; i++) 108 | Serial.print((char)payload[i]); 109 | 110 | requestACK = ((sendSize % 3) == 0); //request ACK every 3rd xmission 111 | radio.Send(GATEWAYID, payload, sendSize, requestACK); 112 | if (requestACK) 113 | { 114 | Serial.print(" - waiting for ACK..."); 115 | if (waitForAck(GATEWAYID)) Serial.print("ok!"); 116 | else Serial.print("nothing..."); 117 | } 118 | 119 | sendSize = (sendSize + 1) % 31; 120 | Serial.println(); 121 | Blink(9,5); 122 | } 123 | //else { Serial.print("currPeriod = "); Serial.print(currPeriod); Serial.print(" lastPeriod = "); Serial.println(lastPeriod); } 124 | } 125 | 126 | // wait a few milliseconds for proper ACK to me, return true if indeed received 127 | static bool waitForAck(byte theNodeID) { 128 | long now = millis(); 129 | while (millis() - now <= ACK_TIME) { 130 | if (radio.ACKReceived(theNodeID)) 131 | return true; 132 | } 133 | return false; 134 | } 135 | 136 | void Blink(byte PIN, int DELAY_MS) 137 | { 138 | pinMode(PIN, OUTPUT); 139 | digitalWrite(PIN,HIGH); 140 | delay(DELAY_MS); 141 | digitalWrite(PIN,LOW); 142 | } 143 | -------------------------------------------------------------------------------- /Examples/WirelessProgramming_gateway/WirelessProgramming_gateway.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 by Felix Rusu 3 | * 4 | * This file is free software; you can redistribute it and/or modify 5 | * it under the terms of either the GNU General Public License version 2 6 | * or the GNU Lesser General Public License version 2.1, both as 7 | * published by the Free Software Foundation. 8 | */ 9 | 10 | // This sketch is an example of how wireless programming can be achieved with a Moteino 11 | // that was loaded with a custom 1k Optiboot that is capable of loading a new sketch from 12 | // an external SPI flash chip 13 | // This is the GATEWAY node, it does not need a custom Optiboot nor any external FLASH memory chip 14 | // (ONLY the target node will need those) 15 | // The sketch includes logic to receive the new sketch from the serial port (from a host computer) and 16 | // transmit it wirelessly to the target node 17 | // The handshake protocol that receives the sketch from the serial port 18 | // is handled by the SPIFLash/WirelessHEX library, which also relies on the RFM12B library 19 | // These libraries and custom 1k Optiboot bootloader for the target node are at: http://github.com/lowpowerlab 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #define NETWORKID 100 //what network this node is on 26 | #define NODEID 5 //this node's ID, should be unique among nodes on this NETWORKID 27 | //Match frequency to the hardware version of the radio on your Moteino (uncomment one): 28 | //#define FREQUENCY RF12_433MHZ 29 | //#define FREQUENCY RF12_868MHZ 30 | #define FREQUENCY RF12_915MHZ 31 | 32 | #define SERIAL_BAUD 115200 33 | #define ACK_TIME 20 // # of ms to wait for an ack 34 | #define TIMEOUT 3000 35 | #define LED 9 36 | 37 | RFM12B radio; 38 | char c = 0; 39 | char FLASH[97] = ":1234567890ABCDEFGHIJKLM:ABCDEFGHIJKLM1234567890:1234567890AAAAAABBBBBBB:CCCCCCDDDDDDD1234567890"; 40 | char input[64]; //serial input buffer 41 | byte targetID=0; 42 | 43 | void setup(){ 44 | Serial.begin(SERIAL_BAUD); 45 | radio.Initialize(NODEID, FREQUENCY, NETWORKID); 46 | Serial.print("Start..."); 47 | } 48 | 49 | void loop(){ 50 | byte inputLen = readSerialLine(input, 10, 64, 100); //readSerialLine(char* input, char endOfLineChar=10, byte maxLength=64, uint16_t timeout=1000); 51 | 52 | if (inputLen == 4 && input[0]=='F' && input[1]=='L' && input[2]=='X' && input[3]=='?') { 53 | if (targetID==0) 54 | Serial.println("TO?"); 55 | else 56 | CheckForSerialHEX((byte*)input, inputLen, radio, targetID, TIMEOUT, ACK_TIME, false); 57 | } 58 | else if (inputLen>3 && inputLen<=6 && input[0]=='T' && input[1]=='O' && input[2]==':') 59 | { 60 | byte newTarget=0; 61 | for (byte i = 3; i=48 && input[i]<=57) 63 | newTarget = newTarget*10+input[i]-48; 64 | else 65 | { 66 | newTarget=0; 67 | break; 68 | } 69 | if (newTarget>0) 70 | { 71 | targetID = newTarget; 72 | Serial.print("TO:"); 73 | Serial.print(newTarget); 74 | Serial.println(":OK"); 75 | } 76 | else 77 | { 78 | Serial.print(input); 79 | Serial.print(":INV"); 80 | } 81 | } 82 | else if (inputLen>0) { //just echo back 83 | Serial.print("SERIAL IN > ");Serial.println(input); 84 | } 85 | 86 | if (radio.ReceiveComplete()) 87 | { 88 | if (radio.CRCPass()) 89 | { 90 | for (byte i = 0; i < *radio.DataLen; i++) 91 | Serial.print((char)radio.Data[i]); 92 | 93 | if (radio.ACKRequested()) 94 | { 95 | radio.SendACK(); 96 | Serial.print(" - ACK sent"); 97 | } 98 | } 99 | else Serial.print("BAD-CRC"); 100 | 101 | Serial.println(); 102 | } 103 | Blink(LED,5); //heartbeat 104 | } 105 | 106 | void Blink(byte PIN, int DELAY_MS) 107 | { 108 | pinMode(PIN, OUTPUT); 109 | digitalWrite(PIN,HIGH); 110 | delay(DELAY_MS); 111 | digitalWrite(PIN,LOW); 112 | } -------------------------------------------------------------------------------- /Examples/WirelessProgramming_node/WirelessProgramming_node.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 by Felix Rusu 3 | * 4 | * This file is free software; you can redistribute it and/or modify 5 | * it under the terms of either the GNU General Public License version 2 6 | * or the GNU Lesser General Public License version 2.1, both as 7 | * published by the Free Software Foundation. 8 | */ 9 | 10 | // This sketch is an example of how wireless programming can be achieved with a Moteino 11 | // that was loaded with a custom 1k Optiboot that is capable of loading a new sketch from 12 | // an external SPI flash chip 13 | // The sketch includes logic to receive the new sketch 'over-the-air' and store it in 14 | // the FLASH chip, then restart the Moteino so the bootloader can continue the job of 15 | // actually reflashing the internal flash memory from the external FLASH memory chip flash image 16 | // The handshake protocol that receives the sketch wirelessly by means of the RFM12B radio 17 | // is handled by the SPIFLash/WirelessHEX library, which also relies on the RFM12B library 18 | // These libraries and custom 1k Optiboot bootloader are at: http://github.com/lowpowerlab 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #define MYID 55 // node ID used for this unit 27 | #define GROUPID 100 28 | #define GATEWAYID 1 29 | //Match frequency to the hardware version of the radio on your Moteino (uncomment one): 30 | //#define FREQUENCY RF12_433MHZ 31 | //#define FREQUENCY RF12_868MHZ 32 | #define FREQUENCY RF12_915MHZ 33 | #define SERIAL_BAUD 115200 34 | #define ACK_TIME 50 // # of ms to wait for an ack 35 | #define LED 9 36 | #define BLINKPERIOD 800 37 | 38 | RFM12B radio; 39 | char input = 0; 40 | long lastPeriod = -1; 41 | 42 | ////////////////////////////////////////////////////////////////////////////// 43 | // flash(SPI_CS, MANUFACTURER_ID) 44 | // SPI_CS - CS pin attached to SPI flash chip (8 in case of Moteino) 45 | // MANUFACTURER_ID - OPTIONAL, 0x1F44 for adesto(ex atmel) 4mbit flash 46 | // 0xEF30 for windbond 4mbit flash 47 | // 0xEF40 for windbond 16/64mbit flash 48 | ////////////////////////////////////////////////////////////////////////////// 49 | SPIFlash flash(8, 0xEF30); //0xEF30 windbond 4mbit 50 | 51 | void setup(){ 52 | pinMode(LED, OUTPUT); 53 | Serial.begin(SERIAL_BAUD); 54 | radio.Initialize(MYID, FREQUENCY, GROUPID); 55 | 56 | Serial.print("Start..."); 57 | 58 | if (flash.initialize()) 59 | Serial.println("SPI Flash Init OK!"); 60 | else 61 | Serial.println("SPI Flash Init FAIL!"); 62 | } 63 | 64 | void loop(){ 65 | /* 66 | // This part is optional. 67 | // Handle serial input (to allow basic DEBUGGING of FLASH chip) 68 | // ie: display first 256 bytes in FLASH, erase chip, write bytes at first 10 positions, etc 69 | if (Serial.available() > 0) { 70 | input = Serial.read(); 71 | if (input == 'd') //d=dump flash area 72 | { 73 | Serial.println("Flash content:"); 74 | int counter = 0; 75 | 76 | while(counter<=256){ 77 | Serial.print(flash.readByte(counter++), HEX); 78 | Serial.print('.'); 79 | } 80 | 81 | Serial.println(); 82 | } 83 | else if (input == 'e') 84 | { 85 | Serial.print("Erasing Flash chip ... "); 86 | flash.chipErase(); 87 | while(flash.busy()); 88 | Serial.println("DONE"); 89 | } 90 | else if (input == 'i') 91 | { 92 | Serial.print("DeviceID: "); 93 | Serial.println(flash.readDeviceId(), HEX); 94 | } 95 | else if (input == 'r') 96 | { 97 | Serial.print("Rebooting"); 98 | resetUsingWatchdog(true); 99 | } 100 | else if (input >= 48 && input <= 57) //0-9 101 | { 102 | Serial.print("\nWriteByte("); Serial.print(input); Serial.print(")"); 103 | flash.writeByte(input-48, millis()%2 ? 0xaa : 0xbb); 104 | } 105 | } 106 | */ 107 | 108 | // Check for existing RF data, potentially for a new sketch wireless upload 109 | // For this to work this check has to be done often enough to be 110 | // picked up when a GATEWAY is trying hard to reach this node for a new sketch wireless upload 111 | if (radio.ReceiveComplete()) 112 | { 113 | if (radio.CRCPass()) 114 | { 115 | Serial.print("Got ["); 116 | Serial.print(*radio.DataLen); 117 | Serial.print("] > "); 118 | for (byte i = 0; i < *radio.DataLen; i++) 119 | Serial.print((char)radio.Data[i], HEX); 120 | Serial.println(); 121 | 122 | CheckForWirelessHEX(radio, flash, true); 123 | } 124 | else Serial.print("BAD-CRC"); 125 | 126 | Serial.println(); 127 | } 128 | 129 | //////////////////////////////////////////////////////////////////////////////////////////// 130 | // Real sketch code here, let's blink the onboard LED 131 | if ((int)(millis()/BLINKPERIOD) > lastPeriod) 132 | { 133 | lastPeriod++; 134 | digitalWrite(LED, lastPeriod%2); 135 | } 136 | //////////////////////////////////////////////////////////////////////////////////////////// 137 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NOTE: not actively maintained 2 | ---------------- 3 | #### This library is not actively maintained anymore but is kept for reference and can still be used with RFM12b transceivers. 4 | 5 | RFM12B Library 6 | ---------------- 7 | By Felix Rusu (felix@lowpowerlab.com) 8 |
9 | Based on the RFM12 driver from jeelabs.com (2009-02-09 ) 10 |
11 | http://opensource.org/licenses/mit-license.php 12 | 13 | ### Features: 14 | - easy API with a few simple functions for basic usage 15 | - 127 possible nodes on 256 possible networks 16 | - 128 bytes max message length 17 | - customizable transmit power (8 levels) for low-power transmission control 18 | - customizable air-Kbps rate allows fine tuning the transmission reliability vs speed (transmitting slower is more reliable but takes more time which implies more power usage) 19 | - Sleep/Wakeup functionality for power saving 20 | - Low battery detector with customizable low voltage threshold 21 | - Interrupt driven 22 | - Support for targeted ACK instead of broadcasted ACK (possible because of the new source byte in the header) 23 | encryption with XXTEA algorithm by David Wheeler, adapted from http://en.wikipedia.org/wiki/XXTEA 24 | Support for these chips: ATMega8 family (ATmega168, ATMega328) ATMega2560, ATMega1280, ATMega644P, ATTiny84, ATTiny44, ATMega32u4. So far only tested on ATMega 328/P 25 | - wireless programming (for more info click [here](http://lowpowerlab.com/blog/2013/04/18/moteino-wireless-programming-source-code/), [here](http://lowpowerlab.com/?p=643) and [here](http://lowpowerlab.com/?p=669)) 26 | - the library needed for wireless programming is now [here](https://github.com/LowPowerLab/WirelessProgramming), install it in your Arduino/libraries folder (WirelessHEX for RFM12B) 27 | 28 | ### Installation 29 | Copy the content of this library in the "Arduino/libraries/RFM12B" folder. 30 |
31 | To find your Arduino folder go to File>Preferences in the Arduino IDE. 32 |
33 | See [this tutorial](http://learn.adafruit.com/arduino-tips-tricks-and-techniques/arduino-libraries) on Arduino libraries. 34 | 35 | ### Saple usage 36 | - [Sender](https://github.com/LowPowerLab/RFM12B/blob/master/Examples/Send/Send.ino) 37 | - [Receiver](https://github.com/LowPowerLab/RFM12B/blob/master/Examples/Receive/Receive.ino) 38 | - More examples in the [Examples folder](https://github.com/LowPowerLab/RFM12B/tree/master/Examples) 39 | 40 | 41 | ### TODOs (in order of priority): 42 | - Support automatic ACK handling 43 | - Refactor changing the SPI CS signal 44 | - Add support for hosting multiple radios on 1 MCU 45 | -------------------------------------------------------------------------------- /RFM12B.cpp: -------------------------------------------------------------------------------- 1 | // RFM12B driver definitions 2 | // http://opensource.org/licenses/mit-license.php 3 | // 2012-12-12 (C) felix@lowpowerlab.com 4 | // Based on the RFM12 driver from jeelabs.com (2009-02-09 ) 5 | 6 | #include "RFM12B.h" 7 | 8 | uint8_t RFM12B::cs_pin; // CS pin for SPI 9 | uint8_t RFM12B::nodeID; // address of this node 10 | uint8_t RFM12B::networkID; // network group ID 11 | long RFM12B::rf12_seq; 12 | uint32_t RFM12B::seqNum; 13 | uint32_t RFM12B::cryptKey[4]; 14 | volatile uint8_t RFM12B::rxfill; // number of data bytes in rf12_buf 15 | volatile int8_t RFM12B::rxstate; // current transceiver state 16 | volatile uint16_t RFM12B::rf12_crc; // running crc value 17 | volatile uint8_t rf12_buf[RF_MAX]; // recv/xmit buf, including hdr & crc bytes 18 | 19 | // function to set chip select 20 | void RFM12B::SetCS(uint8_t arduinoPin) 21 | { 22 | if (arduinoPin==10) cs_pin = 2; 23 | if (arduinoPin== 9) cs_pin = 1; 24 | if (arduinoPin== 8) cs_pin = 0; 25 | } 26 | 27 | void RFM12B::SPIInit() { 28 | bitSet(SS_PORT, cs_pin); 29 | bitSet(SS_DDR, cs_pin); 30 | digitalWrite(SPI_SS, 1); 31 | pinMode(SPI_SS, OUTPUT); 32 | pinMode(SPI_MOSI, OUTPUT); 33 | pinMode(SPI_MISO, INPUT); 34 | pinMode(SPI_SCK, OUTPUT); 35 | #ifdef SPCR 36 | SPCR = _BV(SPE) | _BV(MSTR); 37 | #if F_CPU > 10000000 38 | // use clk/2 (2x 1/4th) for sending (and clk/8 for recv, see XFERSlow) 39 | SPSR |= _BV(SPI2X); 40 | #endif 41 | #else 42 | // ATtiny 43 | USICR = bit(USIWM0); 44 | #endif 45 | pinMode(RFM_IRQ, INPUT); 46 | digitalWrite(RFM_IRQ, 1); // pull-up 47 | } 48 | 49 | uint8_t RFM12B::Byte(uint8_t out) { 50 | #ifdef SPDR 51 | SPDR = out; 52 | // this loop spins 4 usec with a 2 MHz SPI clock 53 | while (!(SPSR & _BV(SPIF))); 54 | return SPDR; 55 | #else 56 | // ATtiny 57 | USIDR = out; 58 | byte v1 = bit(USIWM0) | bit(USITC); 59 | byte v2 = bit(USIWM0) | bit(USITC) | bit(USICLK); 60 | #if F_CPU <= 5000000 61 | // only unroll if resulting clock stays under 2.5 MHz 62 | USICR = v1; USICR = v2; 63 | USICR = v1; USICR = v2; 64 | USICR = v1; USICR = v2; 65 | USICR = v1; USICR = v2; 66 | USICR = v1; USICR = v2; 67 | USICR = v1; USICR = v2; 68 | USICR = v1; USICR = v2; 69 | USICR = v1; USICR = v2; 70 | #else 71 | for (uint8_t i = 0; i < 8; ++i) { 72 | USICR = v1; 73 | USICR = v2; 74 | } 75 | #endif 76 | return USIDR; 77 | #endif 78 | } 79 | 80 | uint16_t RFM12B::XFERSlow(uint16_t cmd) { 81 | // slow down to under 2.5 MHz 82 | #if F_CPU > 10000000 83 | bitSet(SPCR, SPR0); 84 | #endif 85 | bitClear(SS_PORT, cs_pin); 86 | uint16_t reply = Byte(cmd >> 8) << 8; 87 | reply |= Byte(cmd); 88 | bitSet(SS_PORT, cs_pin); 89 | #if F_CPU > 10000000 90 | bitClear(SPCR, SPR0); 91 | #endif 92 | return reply; 93 | } 94 | 95 | void RFM12B::XFER(uint16_t cmd) { 96 | #if OPTIMIZE_SPI 97 | // writing can take place at full speed, even 8 MHz works 98 | bitClear(SS_PORT, cs_pin); 99 | Byte(cmd >> 8) << 8; 100 | Byte(cmd); 101 | bitSet(SS_PORT, cs_pin); 102 | #else 103 | XFERSlow(cmd); 104 | #endif 105 | } 106 | 107 | // Call this once with params: 108 | // - node ID (0-31) 109 | // - frequency band (RF12_433MHZ, RF12_868MHZ, RF12_915MHZ) 110 | // - networkid [optional - default = 170] (0-255 for RF12B, only 212 allowed for RF12) 111 | // - txPower [optional - default = 0 (max)] (7 is min value) 112 | // - AirKbps [optional - default = 38.31Kbps] 113 | // - lowVoltageThreshold [optional - default = RF12_2v75] 114 | void RFM12B::Initialize(uint8_t ID, uint8_t freqBand, uint8_t networkid, uint8_t txPower, uint8_t airKbps, uint8_t lowVoltageThreshold) 115 | { 116 | //while(millis()<60); 117 | cs_pin = SS_BIT; 118 | nodeID = ID; 119 | networkID = networkid; 120 | SPIInit(); 121 | XFER(0x0000); // intitial SPI transfer added to avoid power-up problem 122 | XFER(RF_SLEEP_MODE); // DC (disable clk pin), enable lbd 123 | 124 | // wait until RFM12B is out of power-up reset, this takes several *seconds* 125 | XFER(RF_TXREG_WRITE); // in case we're still in OOK mode 126 | while (digitalRead(RFM_IRQ) == 0) 127 | XFER(0x0000); 128 | 129 | XFER(0x80C7 | (freqBand << 4)); // EL (ena TX), EF (ena RX FIFO), 12.0pF 130 | XFER(0xA640); // Frequency is exactly 434/868/915MHz (whatever freqBand is) 131 | XFER(0xC600 + airKbps); //Air transmission baud rate: 0x08= ~38.31Kbps 132 | XFER(0x94A2); // VDI,FAST,134kHz,0dBm,-91dBm 133 | XFER(0xC2AC); // AL,!ml,DIG,DQD4 134 | if (networkID != 0) { 135 | XFER(0xCA83); // FIFO8,2-SYNC,!ff,DR 136 | XFER(0xCE00 | networkID); // SYNC=2DXX; 137 | } else { 138 | XFER(0xCA8B); // FIFO8,1-SYNC,!ff,DR 139 | XFER(0xCE2D); // SYNC=2D; 140 | } 141 | XFER(0xC483); // @PWR,NO RSTRIC,!st,!fi,OE,EN 142 | XFER(0x9850 | (txPower > 7 ? 7 : txPower)); // !mp,90kHz,MAX OUT //last byte=power level: 0=highest, 7=lowest 143 | XFER(0xCC77); // OB1,OB0, LPX,!ddy,DDIT,BW0 144 | XFER(0xE000); // NOT USE 145 | XFER(0xC800); // NOT USE 146 | XFER(0xC043); // Clock output (1.66MHz), Low Voltage threshold (2.55V) 147 | 148 | rxstate = TXIDLE; 149 | #if PINCHG_IRQ 150 | #if RFM_IRQ < 8 151 | if (nodeID != 0) { 152 | bitClear(DDRD, RFM_IRQ); // input 153 | bitSet(PORTD, RFM_IRQ); // pull-up 154 | bitSet(PCMSK2, RFM_IRQ); // pin-change 155 | bitSet(PCICR, PCIE2); // enable 156 | } else 157 | bitClear(PCMSK2, RFM_IRQ); 158 | #elif RFM_IRQ < 14 159 | if (nodeID != 0) { 160 | bitClear(DDRB, RFM_IRQ - 8); // input 161 | bitSet(PORTB, RFM_IRQ - 8); // pull-up 162 | bitSet(PCMSK0, RFM_IRQ - 8); // pin-change 163 | bitSet(PCICR, PCIE0); // enable 164 | } else 165 | bitClear(PCMSK0, RFM_IRQ - 8); 166 | #else 167 | if (nodeID != 0) { 168 | bitClear(DDRC, RFM_IRQ - 14); // input 169 | bitSet(PORTC, RFM_IRQ - 14); // pull-up 170 | bitSet(PCMSK1, RFM_IRQ - 14); // pin-change 171 | bitSet(PCICR, PCIE1); // enable 172 | } else 173 | bitClear(PCMSK1, RFM_IRQ - 14); 174 | #endif 175 | #else 176 | if (nodeID != 0) 177 | attachInterrupt(0, RFM12B::InterruptHandler, LOW); 178 | else 179 | detachInterrupt(0); 180 | #endif 181 | } 182 | 183 | // access to the RFM12B internal registers with interrupts disabled 184 | uint16_t RFM12B::Control(uint16_t cmd) { 185 | #ifdef EIMSK 186 | bitClear(EIMSK, INT0); 187 | uint16_t r = XFERSlow(cmd); 188 | bitSet(EIMSK, INT0); 189 | #else 190 | // ATtiny 191 | bitClear(GIMSK, INT0); 192 | uint16_t r = XFERSlow(cmd); 193 | bitSet(GIMSK, INT0); 194 | #endif 195 | return r; 196 | } 197 | 198 | void RFM12B::InterruptHandler() { 199 | // a transfer of 2x 16 bits @ 2 MHz over SPI takes 2x 8 us inside this ISR 200 | // correction: now takes 2 + 8 µs, since sending can be done at 8 MHz 201 | XFER(0x0000); 202 | 203 | if (rxstate == TXRECV) { 204 | uint8_t in = XFERSlow(RF_RX_FIFO_READ); 205 | 206 | if (rxfill == 0 && networkID != 0) 207 | rf12_buf[rxfill++] = networkID; 208 | 209 | //Serial.print(out, HEX); Serial.print(' '); 210 | rf12_buf[rxfill++] = in; 211 | rf12_crc = _crc16_update(rf12_crc, in); 212 | 213 | if (rxfill >= rf12_len + 6 || rxfill >= RF_MAX) 214 | XFER(RF_IDLE_MODE); 215 | } else { 216 | uint8_t out; 217 | 218 | if (rxstate < 0) { 219 | uint8_t pos = 4 + rf12_len + rxstate++; 220 | out = rf12_buf[pos]; 221 | rf12_crc = _crc16_update(rf12_crc, out); 222 | } else 223 | switch (rxstate++) { 224 | case TXSYN1: out = 0x2D; break; 225 | case TXSYN2: out = networkID; rxstate = -(3 + rf12_len); break; 226 | case TXCRC1: out = rf12_crc; break; 227 | case TXCRC2: out = rf12_crc >> 8; break; 228 | case TXDONE: XFER(RF_IDLE_MODE); // fall through 229 | default: out = 0xAA; 230 | } 231 | 232 | //Serial.print(out, HEX); Serial.print(' '); 233 | XFER(RF_TXREG_WRITE + out); 234 | } 235 | } 236 | 237 | 238 | #if PINCHG_IRQ 239 | #if RFM_IRQ < 8 240 | ISR(PCINT2_vect) { 241 | while (!bitRead(PIND, RFM_IRQ)) 242 | RFM12B::InterruptHandler(); 243 | } 244 | #elif RFM_IRQ < 14 245 | ISR(PCINT0_vect) { 246 | while (!bitRead(PINB, RFM_IRQ - 8)) 247 | RFM12B::InterruptHandler(); 248 | } 249 | #else 250 | ISR(PCINT1_vect) { 251 | while (!bitRead(PINC, RFM_IRQ - 14)) 252 | RFM12B::InterruptHandler(); 253 | } 254 | #endif 255 | #endif 256 | 257 | void RFM12B::ReceiveStart() { 258 | rxfill = rf12_len = 0; 259 | rf12_crc = ~0; 260 | if (networkID != 0) 261 | rf12_crc = _crc16_update(~0, networkID); 262 | rxstate = TXRECV; 263 | XFER(RF_RECEIVER_ON); 264 | } 265 | 266 | bool RFM12B::ReceiveComplete() { 267 | if (rxstate == TXRECV && (rxfill >= rf12_len + 6 || rxfill >= RF_MAX)) { 268 | rxstate = TXIDLE; 269 | if (rf12_len > RF12_MAXDATA) 270 | rf12_crc = 1; // force bad crc if packet length is invalid 271 | if (RF12_DESTID == 0 || RF12_DESTID == nodeID) { //if (!(rf12_hdr & RF12_HDR_DST) || (nodeID & NODE_ID) == 31 || (rf12_hdr & RF12_HDR_MASK) == (nodeID & NODE_ID)) { 272 | if (rf12_crc == 0 && crypter != 0) 273 | crypter(false); 274 | else 275 | rf12_seq = -1; 276 | return true; // it's a broadcast packet or it's addressed to this node 277 | } 278 | } 279 | if (rxstate == TXIDLE) 280 | ReceiveStart(); 281 | return false; 282 | } 283 | 284 | bool RFM12B::CanSend() { 285 | // no need to test with interrupts disabled: state TXRECV is only reached 286 | // outside of ISR and we don't care if rxfill jumps from 0 to 1 here 287 | if (rxstate == TXRECV && rxfill == 0 && (Byte(0x00) & (RF_RSSI_BIT >> 8)) == 0) { 288 | XFER(RF_IDLE_MODE); // stop receiver 289 | //XXX just in case, don't know whether these RF12 reads are needed! 290 | // rf12_XFER(0x0000); // status register 291 | // rf12_XFER(RF_RX_FIFO_READ); // fifo read 292 | rxstate = TXIDLE; 293 | return true; 294 | } 295 | return false; 296 | } 297 | 298 | void RFM12B::SendStart(uint8_t toNodeID, bool requestACK, bool sendACK) { 299 | rf12_hdr1 = toNodeID | (sendACK ? RF12_HDR_ACKCTLMASK : 0); 300 | rf12_hdr2 = nodeID | (requestACK ? RF12_HDR_ACKCTLMASK : 0); 301 | if (crypter != 0) crypter(true); 302 | rf12_crc = ~0; 303 | rf12_crc = _crc16_update(rf12_crc, networkID); 304 | rxstate = TXPRE1; 305 | XFER(RF_XMITTER_ON); // bytes will be fed via interrupts 306 | } 307 | 308 | void RFM12B::SendStart(uint8_t toNodeID, const void* sendBuf, uint8_t sendLen, bool requestACK, bool sendACK, uint8_t waitMode) { 309 | rf12_len = sendLen; 310 | memcpy((void*) rf12_data, sendBuf, sendLen); 311 | SendStart(toNodeID, requestACK, sendACK); 312 | SendWait(waitMode); 313 | } 314 | 315 | /// Should be called immediately after reception in case sender wants ACK 316 | void RFM12B::SendACK(const void* sendBuf, uint8_t sendLen, uint8_t waitMode) { 317 | while (!CanSend()) ReceiveComplete(); 318 | SendStart(RF12_SOURCEID, sendBuf, sendLen, false, true, waitMode); 319 | } 320 | 321 | void RFM12B::Send(uint8_t toNodeID, const void* sendBuf, uint8_t sendLen, bool requestACK, uint8_t waitMode) 322 | { 323 | while (!CanSend()) ReceiveComplete(); 324 | SendStart(toNodeID, sendBuf, sendLen, requestACK, false, waitMode); 325 | } 326 | 327 | void RFM12B::SendWait(uint8_t waitMode) { 328 | // wait for packet to actually finish sending 329 | // go into low power mode, as interrupts are going to come in very soon 330 | while (rxstate != TXIDLE) 331 | if (waitMode) { 332 | // power down mode is only possible if the fuses are set to start 333 | // up in 258 clock cycles, i.e. approx 4 us - else must use standby! 334 | // modes 2 and higher may lose a few clock timer ticks 335 | set_sleep_mode(waitMode == 3 ? SLEEP_MODE_PWR_DOWN : 336 | #ifdef SLEEP_MODE_STANDBY 337 | waitMode == 2 ? SLEEP_MODE_STANDBY : 338 | #endif 339 | SLEEP_MODE_IDLE); 340 | sleep_mode(); 341 | } 342 | } 343 | 344 | void RFM12B::OnOff(uint8_t value) { 345 | XFER(value ? RF_XMITTER_ON : RF_IDLE_MODE); 346 | } 347 | 348 | void RFM12B::Sleep(char n) { 349 | if (n < 0) 350 | Control(RF_IDLE_MODE); 351 | else { 352 | Control(RF_WAKEUP_TIMER | 0x0500 | n); 353 | Control(RF_SLEEP_MODE); 354 | if (n > 0) 355 | Control(RF_WAKEUP_MODE); 356 | } 357 | rxstate = TXIDLE; 358 | } 359 | void RFM12B::Sleep() { Sleep(0); } 360 | void RFM12B::Wakeup() { Sleep(-1); } 361 | 362 | bool RFM12B::LowBattery() { 363 | return (Control(0x0000) & RF_LBD_BIT) != 0; 364 | } 365 | 366 | uint8_t RFM12B::GetSender(){ 367 | return RF12_SOURCEID; 368 | } 369 | 370 | volatile uint8_t * RFM12B::GetData() { return rf12_data; } 371 | uint8_t RFM12B::GetDataLen() { return *DataLen; } 372 | bool RFM12B::ACKRequested() { return RF12_WANTS_ACK; } 373 | 374 | /// Should be polled immediately after sending a packet with ACK request 375 | bool RFM12B::ACKReceived(uint8_t fromNodeID) { 376 | if (ReceiveComplete()) 377 | return CRCPass() && 378 | RF12_DESTID == nodeID && 379 | (RF12_SOURCEID == fromNodeID || fromNodeID == 0) && 380 | (rf12_hdr1 & RF12_HDR_ACKCTLMASK) && 381 | !(rf12_hdr2 & RF12_HDR_ACKCTLMASK); 382 | return false; 383 | } 384 | 385 | 386 | // XXTEA by David Wheeler, adapted from http://en.wikipedia.org/wiki/XXTEA 387 | #define DELTA 0x9E3779B9 388 | #define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (cryptKey[(uint8_t)((p&3)^e)] ^ z))) 389 | void RFM12B::CryptFunction(bool sending) { 390 | uint32_t y, z, sum, *v = (uint32_t*) rf12_data; 391 | uint8_t p, e, rounds = 6; 392 | 393 | if (sending) { 394 | // pad with 1..4-byte sequence number 395 | *(uint32_t*)(rf12_data + rf12_len) = ++seqNum; 396 | uint8_t pad = 3 - (rf12_len & 3); 397 | rf12_len += pad; 398 | rf12_data[rf12_len] &= 0x3F; 399 | rf12_data[rf12_len] |= pad << 6; 400 | ++rf12_len; 401 | // actual encoding 402 | char n = rf12_len / 4; 403 | if (n > 1) { 404 | sum = 0; 405 | z = v[n-1]; 406 | do { 407 | sum += DELTA; 408 | e = (sum >> 2) & 3; 409 | for (p=0; p 1) { 419 | sum = rounds*DELTA; 420 | y = v[0]; 421 | do { 422 | e = (sum >> 2) & 3; 423 | for (p=n-1; p>0; p--) 424 | z = v[p-1], y = v[p] -= MX; 425 | z = v[n-1]; 426 | y = v[0] -= MX; 427 | } while ((sum -= DELTA) != 0); 428 | } 429 | // strip sequence number from the end again 430 | if (n > 0) { 431 | uint8_t pad = rf12_data[--rf12_len] >> 6; 432 | rf12_seq = rf12_data[rf12_len] & 0x3F; 433 | while (pad-- > 0) 434 | rf12_seq = (rf12_seq << 8) | rf12_data[--rf12_len]; 435 | } 436 | } 437 | } 438 | 439 | void RFM12B::Encrypt(const uint8_t* key, uint8_t keyLen) { 440 | // by using a pointer to CryptFunction, we only link it in when actually used 441 | if (key != 0) { 442 | for (uint8_t i = 0; i < keyLen; ++i) 443 | ((uint8_t*) cryptKey)[i] = key[i]; 444 | crypter = CryptFunction; 445 | } else crypter = 0; 446 | } 447 | -------------------------------------------------------------------------------- /RFM12B.h: -------------------------------------------------------------------------------- 1 | // RFM12B driver definitions 2 | // http://opensource.org/licenses/mit-license.php 3 | // 2012-12-12 (C) felix@lowpowerlab.com 4 | // Based on the RFM12 driver from jeelabs.com (2009-02-09 ) 5 | 6 | #ifndef RFM12B_h 7 | #define RFM12B_h 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #if ARDUINO >= 100 15 | #include // Arduino 1.0 16 | #else 17 | #include // Arduino 0022 18 | #endif 19 | 20 | 21 | ///RF12 Driver version 22 | #define OPTIMIZE_SPI 1 // uncomment this to write to the RFM12B @ 8 Mhz 23 | 24 | /// RF12 CTL bit mask. 25 | //#define RF12_HDR_CTL 0x80 26 | /// RF12 DST bit mask. 27 | //#define RF12_HDR_DST 0x40 28 | /// RF12 ACK bit mask. 29 | //#define RF12_HDR_ACK 0x20 30 | /// RF12 HDR bit mask. 31 | //#define RF12_HDR_MASK 0x1F 32 | /// RF12 SENDER extracted from last packet 33 | //#define RF12_SOURCEID rf12_hdr & RF12_HDR_MASK 34 | 35 | /// RF12 Maximum message size in bytes. 36 | #define RF12_MAXDATA 128 37 | /// Max transmit/receive buffer: 4 header + data + 2 crc bytes 38 | #define RF_MAX (RF12_MAXDATA + 6) 39 | 40 | //frequency bands 41 | #define RF12_315MHZ 0 42 | #define RF12_433MHZ 1 43 | #define RF12_868MHZ 2 44 | #define RF12_915MHZ 3 45 | 46 | //Low batteery threshold (eg 2v25 = 2.25V) 47 | #define RF12_2v25 0 48 | #define RF12_2v55 3 49 | #define RF12_2v65 4 50 | #define RF12_2v75 5 51 | #define RF12_3v05 8 52 | #define RF12_3v15 9 53 | #define RF12_3v25 10 54 | 55 | #define RF12_HDR_IDMASK 0x7F 56 | #define RF12_HDR_ACKCTLMASK 0x80 57 | #define RF12_DESTID (rf12_hdr1 & RF12_HDR_IDMASK) 58 | #define RF12_SOURCEID (rf12_hdr2 & RF12_HDR_IDMASK) 59 | 60 | // shorthands to simplify sending out the proper ACK when requested 61 | #define RF12_WANTS_ACK ((rf12_hdr2 & RF12_HDR_ACKCTLMASK) && !(rf12_hdr1 & RF12_HDR_ACKCTLMASK)) 62 | 63 | // options for RF12_sleep() 64 | #define RF12_SLEEP 0 65 | #define RF12_WAKEUP -1 66 | 67 | /// Shorthand for RF12 group byte in rf12_buf. 68 | #define rf12_grp rf12_buf[0] 69 | /// pointer to 1st header byte in rf12_buf (CTL + DESTINATIONID) 70 | #define rf12_hdr1 rf12_buf[1] 71 | /// pointer to 2nd header byte in rf12_buf (ACK + SOURCEID) 72 | #define rf12_hdr2 rf12_buf[2] 73 | 74 | /// Shorthand for RF12 length byte in rf12_buf. 75 | #define rf12_len rf12_buf[3] 76 | /// Shorthand for first RF12 data byte in rf12_buf. 77 | #define rf12_data (rf12_buf + 4) 78 | 79 | 80 | // pin change interrupts are currently only supported on ATmega328's 81 | // #define PINCHG_IRQ 1 // uncomment this to use pin-change interrupts 82 | 83 | // pins used for the RFM12B interface - yes, there *is* logic in this madness: 84 | // - leave RFM_IRQ set to the pin which corresponds with INT0, because the 85 | // current driver code will use attachInterrupt() to hook into that 86 | // - (new) you can now change RFM_IRQ, if you also enable PINCHG_IRQ - this 87 | // will switch to pin change interrupts instead of attach/detachInterrupt() 88 | // - use SS_DDR, SS_PORT, and SS_BIT to define the pin you will be using as 89 | // select pin for the RFM12B (you're free to set them to anything you like) 90 | // - please leave SPI_SS, SPI_MOSI, SPI_MISO, and SPI_SCK as is, i.e. pointing 91 | // to the hardware-supported SPI pins on the ATmega, *including* SPI_SS ! 92 | #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__) 93 | #define RFM_IRQ 2 94 | #define SS_DDR DDRB 95 | #define SS_PORT PORTB 96 | #define SS_BIT 0 97 | #define SPI_SS 53 // PB0, pin 19 98 | #define SPI_MOSI 51 // PB2, pin 21 99 | #define SPI_MISO 50 // PB3, pin 22 100 | #define SPI_SCK 52 // PB1, pin 20 101 | #elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) 102 | #define RFM_IRQ 10 103 | #define SS_DDR DDRB 104 | #define SS_PORT PORTB 105 | #define SS_BIT 4 106 | #define SPI_SS 4 107 | #define SPI_MOSI 5 108 | #define SPI_MISO 6 109 | #define SPI_SCK 7 110 | #elif defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny44__) 111 | #define RFM_IRQ 2 112 | #define SS_DDR DDRB 113 | #define SS_PORT PORTB 114 | #define SS_BIT 1 115 | #define SPI_SS 1 // PB1, pin 3 116 | #define SPI_MISO 4 // PA6, pin 7 117 | #define SPI_MOSI 5 // PA5, pin 8 118 | #define SPI_SCK 6 // PA4, pin 9 119 | #elif defined(__AVR_ATmega32U4__) //Arduino Leonardo, MoteinoLeo 120 | #define RFM_IRQ 0 // PD0, INT0, Digital3 121 | #define SS_DDR DDRB 122 | #define SS_PORT PORTB 123 | //OLD from Jeelib: #define SS_BIT 6 // Dig10, PB6 124 | #define SS_BIT 0 // Dig17, PB0 125 | #define SPI_SS 17 // PB0, pin 8, Digital17 126 | #define SPI_MISO 14 // PB3, pin 11, Digital14 127 | #define SPI_MOSI 16 // PB2, pin 10, Digital16 128 | #define SPI_SCK 15 // PB1, pin 9, Digital15 129 | #else 130 | // ATmega168, ATmega328, etc. 131 | #define RFM_IRQ 2 132 | #define SS_DDR DDRB 133 | #define SS_PORT PORTB 134 | #define SS_BIT 2 // for PORTB: 2 = d.10, 1 = d.9, 0 = d.8 135 | #define SPI_SS 10 // PB2, pin 16 136 | #define SPI_MOSI 11 // PB3, pin 17 137 | #define SPI_MISO 12 // PB4, pin 18 138 | #define SPI_SCK 13 // PB5, pin 19 139 | #endif 140 | 141 | // RF12 command codes 142 | #define RF_RECEIVER_ON 0x82DD 143 | #define RF_XMITTER_ON 0x823D 144 | #define RF_IDLE_MODE 0x820D 145 | #define RF_SLEEP_MODE 0x8205 146 | #define RF_WAKEUP_MODE 0x8207 147 | #define RF_TXREG_WRITE 0xB800 148 | #define RF_RX_FIFO_READ 0xB000 149 | #define RF_WAKEUP_TIMER 0xE000 150 | 151 | //RF12 status bits 152 | #define RF_LBD_BIT 0x0400 153 | #define RF_RSSI_BIT 0x0100 154 | 155 | // transceiver states, these determine what to do with each interrupt 156 | enum { 157 | TXCRC1, TXCRC2, TXTAIL, TXDONE, TXIDLE, 158 | TXRECV, 159 | TXPRE1, TXPRE2, TXPRE3, TXSYN1, TXSYN2, 160 | }; 161 | 162 | extern volatile uint8_t rf12_buf[RF_MAX]; // recv/xmit buf, including hdr & crc bytes 163 | class RFM12B 164 | { 165 | static volatile uint8_t rxfill; // number of data bytes in rf12_buf 166 | static volatile int8_t rxstate; // current transceiver state 167 | static volatile uint16_t rf12_crc; // running crc value 168 | static uint32_t seqNum; // encrypted send sequence number 169 | static uint32_t cryptKey[4]; // encryption key to use 170 | static long rf12_seq; // seq number of encrypted packet (or -1) 171 | static uint8_t cs_pin; // chip select pin 172 | void (*crypter)(bool); // does en-/decryption (null if disabled) 173 | static uint8_t Byte(uint8_t out); 174 | static uint16_t XFERSlow(uint16_t cmd); 175 | static void XFER(uint16_t cmd); 176 | 177 | void SPIInit(); 178 | 179 | public: 180 | //constructor 181 | RFM12B():Data(rf12_data),DataLen(&rf12_buf[3]){} 182 | 183 | static uint8_t networkID; // network group 184 | static uint8_t nodeID; // address of this node 185 | static const byte DATAMAXLEN; 186 | volatile uint8_t* Data; 187 | volatile uint8_t* DataLen; 188 | 189 | static void InterruptHandler(); 190 | 191 | //Defaults: Group: 0xAA=170, transmit power: 0(max), KBPS: 38.3Kbps (air transmission baud - has to be same on all radios in same group) 192 | void Initialize(uint8_t nodeid, uint8_t freqBand, uint8_t groupid=0xAA, uint8_t txPower=0, uint8_t airKbps=0x08, uint8_t lowVoltageThreshold=RF12_2v75); 193 | void SetCS(uint8_t pin); 194 | void ReceiveStart(); 195 | bool ReceiveComplete(); 196 | bool CanSend(); 197 | uint16_t Control(uint16_t cmd); 198 | 199 | void SendStart(uint8_t toNodeId, bool requestACK=false, bool sendACK=false); 200 | void SendStart(uint8_t toNodeId, const void* sendBuf, uint8_t sendLen, bool requestACK=false, bool sendACK=false, uint8_t waitMode=SLEEP_MODE_STANDBY); 201 | void SendACK(const void* sendBuf = "", uint8_t sendLen=0, uint8_t waitMode=SLEEP_MODE_IDLE); 202 | void Send(uint8_t toNodeId, const void* sendBuf, uint8_t sendLen, bool requestACK = false, uint8_t waitMode=SLEEP_MODE_STANDBY); 203 | void SendWait(uint8_t waitMode=0); 204 | 205 | void OnOff(uint8_t value); 206 | void Sleep(char n); 207 | void Sleep(); 208 | void Wakeup(); 209 | 210 | volatile uint8_t * GetData(); 211 | uint8_t GetDataLen(); //how many bytes were received 212 | uint8_t GetSender(); 213 | bool LowBattery(); 214 | bool ACKRequested(); 215 | bool ACKReceived(uint8_t fromNodeID=0); 216 | static void CryptFunction(bool sending); 217 | void Encrypt(const uint8_t* key, uint8_t keyLen = 16); 218 | bool CRCPass() { return rf12_crc == 0; } 219 | }; 220 | 221 | #endif 222 | -------------------------------------------------------------------------------- /RFM12B_AIR_KBPS_Calculator.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LowPowerLab/RFM12B/b1ff98905c1a142702c9b1f4665f719392274dba/RFM12B_AIR_KBPS_Calculator.xlsx -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map for RFM12B 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | ####################################### 10 | # Methods and Functions (KEYWORD2) 11 | ####################################### 12 | Initialize KEYWORD2 13 | SetCS KEYWORD2 14 | Control KEYWORD2 15 | ReceiveStart KEYWORD2 16 | ReceiveComplete KEYWORD2 17 | CanSend KEYWORD2 18 | SendStart KEYWORD2 19 | SendACK KEYWORD2 20 | SendWait KEYWORD2 21 | Send KEYWORD2 22 | OnOff KEYWORD2 23 | Sleep KEYWORD2 24 | Wakeup KEYWORD2 25 | LowBattery KEYWORD2 26 | CryptFunction KEYWORD2 27 | Encrypt KEYWORD2 28 | CRCPass KEYWORD2 29 | 30 | ####################################### 31 | # Instances (KEYWORD2) 32 | ####################################### 33 | RFM12B KEYWORD2 34 | 35 | ####################################### 36 | # Constants (LITERAL1) 37 | ####################################### 38 | RF12_VERSION LITERAL1 39 | RF12_HDR_CTL LITERAL1 40 | RF12_HDR_DST LITERAL1 41 | RF12_HDR_ACK LITERAL1 42 | RF12_HDR_MASK LITERAL1 43 | RF12_MAXDATA LITERAL1 44 | RF12_433MHZ LITERAL1 45 | RF12_868MHZ LITERAL1 46 | RF12_915MHZ LITERAL1 47 | RF12_EEPROM_ADDR LITERAL1 48 | RF12_EEPROM_SIZE LITERAL1 49 | RF12_EEPROM_EKEY LITERAL1 50 | RF12_EEPROM_ELEN LITERAL1 51 | RF12_SLEEP LITERAL1 52 | RF12_WAKEUP LITERAL1 53 | RF12_DATA_RATE_9 LITERAL1 54 | RF12_DATA_RATE_8 LITERAL1 55 | RF12_DATA_RATE_7 LITERAL1 56 | RF12_DATA_RATE_6 LITERAL1 57 | RF12_DATA_RATE_5 LITERAL1 58 | RF12_DATA_RATE_4 LITERAL1 59 | RF12_DATA_RATE_3 LITERAL1 60 | RF12_DATA_RATE_2 LITERAL1 61 | RF12_DATA_RATE_1 LITERAL1 62 | RF12_2v25 LITERAL1 63 | RF12_2v55 LITERAL1 64 | RF12_2v65 LITERAL1 65 | RF12_2v75 LITERAL1 66 | RF12_3v05 LITERAL1 67 | RF12_3v15 LITERAL1 68 | RF12_3v25 LITERAL1 69 | ####################################### 70 | # Macros (LITERAL2) 71 | ####################################### 72 | RF12_WANTS_ACK LITERAL1 73 | RF12_ACK_REPLY LITERAL1 74 | 75 | ####################################### 76 | # Variables (LITERAL2) 77 | ####################################### 78 | rf12_buf LITERAL2 79 | rf12_grp LITERAL2 80 | rf12_hdr LITERAL2 81 | rf12_len LITERAL2 82 | rf12_data LITERAL2 83 | rf12_crc LITERAL2 84 | rf12_seq LITERAL2 85 | 86 | groupID LITERAL2 87 | nodeID LITERAL2 88 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RFM12B", 3 | "keywords": "rf, radio, wireless, spi", 4 | "description": "RFM12B Universal ISM Band FSK Transceiver (433, 868 and 915 MHz bands)", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/LowPowerLab/RFM12B.git" 9 | }, 10 | "frameworks": "arduino", 11 | "platforms": "atmelavr" 12 | } 13 | --------------------------------------------------------------------------------