![]() |
Forum Index : Microcontroller and PC projects : Wireless (RF) to micromite
Author | Message | ||||
dmasz Newbie ![]() Joined: 12/09/2013 Location: PolandPosts: 21 |
Hello, During beta phase of micromite Geoff mention about possible firmware feature of wireless communication based on NRF24L01+ Is it still on, new feature, to do list? br Dan |
||||
halldave![]() Senior Member ![]() Joined: 04/05/2014 Location: AustraliaPosts: 121 |
If this was implemented I would love the NRF24L01+ to support a star configuration with 1 master and up to 6 nodes. This would allow remote uMites to communicate with the Master uMite, maybe even solar powered, waking up when the sun shines enough to generate 3.3volts.... a bit the way those cheap solar lights work, yet they charge and store, and turn on at night . Standard message The key part would be if it was to be implemented as a standard in future MMBasic would be to define a standard message format early and adopt that for 1-1 and 1 to many communications, for NRF24L01, Bluetooth, and 315Mhz. Example star code form the evilduino world /* Copyright (C) 2011 J. Coliz <maniacbug@ymail.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. */ #include <SPI.h> #include <EEPROM.h> #include "nRF24L01.h" #include "RF24.h" #include "printf.h" // // Hardware configuration // // Set up nRF24L01 radio on SPI bus plus pins 9 & 10 RF24 radio(9,10); // sets the role of this unit in hardware. Connect to GND to be the 'pong' receiver // Leave open to be the 'pong' receiver. const int role_pin = 7; // // Topology // // Radio pipe addresses for the nodes to communicate. Only ping nodes need // dedicated pipes in this topology. Each ping node has a talking pipe // that it will ping into, and a listening pipe that it will listen for // the pong. The pong node listens on all the ping node talking pipes // and sends the pong back on the sending node's specific listening pipe. const uint64_t talking_pipes[5] = { 0xF0F0F0F0D2LL, 0xF0F0F0F0C3LL, 0xF0F0F0F0B4LL, 0xF0F0F0F0A5LL, 0xF0F0F0F096LL }; const uint64_t listening_pipes[5] = { 0x3A3A3A3AD2LL, 0x3A3A3A3AC3LL, 0x3A3A3A3AB4LL, 0x3A3A3A3AA5LL, 0x3A3A3A3A96LL }; // // Role management // // Set up role. This sketch uses the same software for all the nodes // in this system. Doing so greatly simplifies testing. The hardware itself specifies // which node it is. // // This is done through the role_pin // // The various roles supported by this sketch typedef enum { role_invalid = 0, role_ping_out, role_pong_back } role_e; // The debug-friendly names of those roles const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"}; // The role of the current running sketch role_e role; // // Address management // // Where in EEPROM is the address stored? const uint8_t address_at_eeprom_location = 0; // What is our address (SRAM cache of the address from EEPROM) // Note that zero is an INVALID address. The pong back unit takes address // 1, and the rest are 2-6 uint8_t node_address; void setup(void) { // // Role // // set up the role pin pinMode(role_pin, INPUT); digitalWrite(role_pin,HIGH); delay(20); // Just to get a solid reading on the role pin // read the address pin, establish our role if ( digitalRead(role_pin) ) role = role_ping_out; else role = role_pong_back; // // Address // if ( role == role_pong_back ) node_address = 1; else { // Read the address from EEPROM uint8_t reading = EEPROM.read(address_at_eeprom_location); // If it is in a valid range for node addresses, it is our // address. if ( reading >= 2 && reading <= 6 ) node_address = reading; // Otherwise, it is invalid, so set our address AND ROLE to 'invalid' else { node_address = 0; role = role_invalid; } } // // Print preamble // Serial.begin(57600); printf_begin(); printf("\n\rRF24/examples/starping/\n\r"); printf("ROLE: %s\n\r",role_friendly_name[role]); printf("ADDRESS: %i\n\r",node_address); // // Setup and configure rf radio // radio.begin(); // // Open pipes to other nodes for communication // // The pong node listens on all the ping node talking pipes // and sends the pong back on the sending node's specific listening pipe. if ( role == role_pong_back ) { radio.openReadingPipe(1,talking_pipes[0]); radio.openReadingPipe(2,talking_pipes[1]); radio.openReadingPipe(3,talking_pipes[2]); radio.openReadingPipe(4,talking_pipes[3]); radio.openReadingPipe(5,talking_pipes[4]); } // Each ping node has a talking pipe that it will ping into, and a listening // pipe that it will listen for the pong. if ( role == role_ping_out ) { // Write on our talking pipe radio.openWritingPipe(talking_pipes[node_address-2]); // Listen on our listening pipe radio.openReadingPipe(1,listening_pipes[node_address-2]); } // // Start listening // radio.startListening(); // // Dump the configuration of the rf unit for debugging // radio.printDetails(); // // Prompt the user to assign a node address if we don't have one // if ( role == role_invalid ) { printf("\n\r*** NO NODE ADDRESS ASSIGNED *** Send 1 through 6 to assign an address\n\r"); } } void loop(void) { // // Ping out role. Repeatedly send the current time // if (role == role_ping_out) { // First, stop listening so we can talk. radio.stopListening(); // Take the time, and send it. This will block until complete unsigned long time = millis(); printf("Now sending %lu...",time); radio.write( &time, sizeof(unsigned long) ); // Now, continue listening radio.startListening(); // Wait here until we get a response, or timeout (250ms) unsigned long started_waiting_at = millis(); bool timeout = false; while ( ! radio.available() && ! timeout ) if (millis() - started_waiting_at > 250 ) timeout = true; // Describe the results if ( timeout ) { printf("Failed, response timed out.\n\r"); } else { // Grab the response, compare, and send to debugging spew unsigned long got_time; radio.read( &got_time, sizeof(unsigned long) ); // Spew it printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time); } // Try again 1s later delay(1000); } // // Pong back role. Receive each packet, dump it out, and send it back // if ( role == role_pong_back ) { // if there is data ready uint8_t pipe_num; if ( radio.available(&pipe_num) ) { // Dump the payloads until we've gotten everything unsigned long got_time; bool done = false; while (!done) { // Fetch the payload, and see if this was the last one. done = radio.read( &got_time, sizeof(unsigned long) ); // Spew it printf("Got payload %lu from node %i...",got_time,pipe_num+1); } // First, stop listening so we can talk radio.stopListening(); // Open the correct pipe for writing radio.openWritingPipe(listening_pipes[pipe_num-1]); // Retain the low 2 bytes to identify the pipe for the spew uint16_t pipe_id = listening_pipes[pipe_num-1] & 0xffff; // Send the final one back. radio.write( &got_time, sizeof(unsigned long) ); printf("Sent response to %04x.\n\r",pipe_id); // Now, resume listening so we catch the next packets. radio.startListening(); } } // // Listen for serial input, which is how we set the address // if (Serial.available()) { // If the character on serial input is in a valid range... char c = Serial.read(); if ( c >= '1' && c <= '6' ) { // It is our address EEPROM.write(address_at_eeprom_location,c-'0'); // And we are done right now (no easy way to soft reset) printf("\n\rManually reset address to: %c\n\rPress RESET to continue!",c); while(1) ; } } } // vim:ai:ci sts=2 sw=2 ft=cpp |
||||
Geoffg![]() Guru ![]() Joined: 06/06/2011 Location: AustraliaPosts: 3269 |
Yes Dan, it is still on the todo list for the next major version - which is at least a couple of months away. That looks like quite a sophisticated setup Dave. I will keep it in mind. Geoff Geoff Graham - http://geoffg.net |
||||
halldave![]() Senior Member ![]() Joined: 04/05/2014 Location: AustraliaPosts: 121 |
Geoff, I'll try to document it generic and in MMBasic to simplify it, Do you have a current list of proposed enhancements for the next major release? regards David |
||||
MicroBlocks![]() Guru ![]() Joined: 12/05/2012 Location: ThailandPosts: 2209 |
Personally i like what this guy has made: Maniacbug It uses a tree topology that can address more then 3000 units. The hard work has been done. As you can see in the examples the usage is very simple. Just send a message to a node. The examples uses polling to detect arrival of a message, and constantly 'pumps' the network. Remember this is on a Arduino and in C so the usage is different then what is suitable for basic. In a MMBasic environment it would be great if the firmware does the 'pump' in the background and fires an interrupt on arrival of a new message. Using it from basic can then be very simple. You would need to assign the pins used and setup a function that handles the interrupt. [code] 'RF24 EN_PIN, CSEPIN, NodeNumber, InterruptRoutine RF24 23, 24, 0, DataArrived SUB DataArrived() 'This will have FromNode and Data received available 'Process the Data END SUB [/code] Using this would also allow interoperability with the arduino world, mixing arduinos and uMites would be very easy. As an example i used in my MakerSpace i made a little remote controlled 'robot' using this.(It could rotate his head and wave a hand and was made of cardboard. It no longer exists!). The code was pretty easy to make. Node 0, with two servos (This also contains code for receiving command through the console and send it to the addressed node): I made the relevant lines RF24 network code lines bold (It is unfortunately hard to see). There are only a few of those lines. [code] /* This code runs on a Arduino UNO */ #include <Servo.h> #include <SPI.h> /* Connect the RF24L01 module as follows: 1 - GND !!!!!!!!!!!!!!!! 2 - VCC 3.3V !!! NOT 5V 5v will blow up the RF24L01!!! If you do not have 3.3v you can use 2-3 diodes in series Measure it with a volt meter !!!!!!!!!!!!!! 2.8 - 3.3 v is ok !!!!!!!!!!!!!!!! 3 - CE to Arduino pin 9 4 - CSN to Arduino pin 10 5 - SCK to Arduino pin 13 6 - MOSI to Arduino pin 11 7 - MISO to Arduino pin 12 8 - UNUSED - V1.00 21 december 2013 */ /* libraries */ #include <RF24.h> #include <RF24Network.h> #include <MicroBlocks.h> /* Define the pins used for controlling the RF24L01 module */ #define CE_PIN 9 #define CSN_PIN 10 /* Other pins are standard SPI pins. */ /* Define pins for the two servos */ int ServoPinX = 7; int ServoPinY = 6; Servo Servo_X; Servo Servo_Y; /* Setup the RF24 network */ RF24 radio(CE_PIN, CSN_PIN); RF24Network network(radio); /* Global variables */ MicroBlocks_analog_joystick analog; MicroBlocks_digital_joystick digital; MicroBlocks_byte_array byte_array; const uint16_t this_node = 0; //Base node = 0 void setup(void) { Serial.begin(9600); Serial.setTimeout(10); //Very short timeout reading from the serial port. Servo_X.attach(ServoPinX); Servo_Y.attach(ServoPinY); delay(1000); // Wait until everything stabalizes and is connected Serial.println("RF24Network Base station."); SPI.begin(); radio.begin(); network.begin(90, this_node); } void loop(void) { // Pump the network regularly // DON'T USE DELAY() anywhere, it will cause the network to fail. network.update(); // Is there anything ready for us? while ( network.available() ) { // If so, grab it and print it out RF24NetworkHeader header; network.peek(header); // Dispatch the message to the correct handler. switch (header.type) { case MICROBLOCKS_ANALOG_JOYSTICK: network.read(header, &analog,sizeof(analog)); Serial.print("Node = " ); Serial.print(header.from_node); Serial.print(" SequenceId = " ); Serial.print(header.id); Serial.print(" type = ANALOG JOYSTICK"); Serial.print(" x = "); Serial.print(analog.x); Serial.print(" y = "); Serial.print(analog.y); Serial.print(" joyswitch = ");Serial.println(analog.data.joyswitch); Servo_X.write(analog.x/6); Servo_Y.write(analog.y/6); break; case MICROBLOCKS_DIGITAL_JOYSTICK: network.read(header, &digital,sizeof(digital)); Serial.print("Node = " ); Serial.print(header.from_node); Serial.print(" SequenceId = " ); Serial.print(header.id); Serial.print(" type = DIGITAL JOYSTICK"); Serial.print(" up = "); Serial.print(digital.data.up); Serial.print(" down = "); Serial.print(digital.data.down); Serial.print(" left = "); Serial.print(digital.data.left); Serial.print(" right = "); Serial.print(digital.data.right); Serial.print(" joyswitch = ");Serial.println(digital.data.joyswitch); Servo_X.write(digital.data.up?180:digital.data.down?0:90); Servo_Y.write(digital.data.right?180:digital.data.left?0:90) ; break; default: network.read(header, &byte_array,sizeof(byte_array)); Serial.print("Unknown message type "); Serial.println(header.type); } } // end while while (Serial.available() > 0 && Serial.read() == '!') { // A '!' has been found. This is the start of a serial message. // You can send them through the serial monitor, set it at 9600 baud. // The message format is a '!' as a start, followed by the node_id a comma and the command type, // then depending on the kind of message you can add 0 to 4 values, the last character is a '#' // to signal the end of the message. // The command types can be found in the Microbocks.h file // Example messages are: // Setting interval to 1 second: "!1,9,1000#" // Stop sending messages: "1,32#" // Start sending messages: "1,33#" Serial.print("Serial available "); //Serial.println(Serial.available()); MicroBlocks_command command; // look for the next valid integer in the incoming serial stream: uint16_t node_id = Serial.parseInt(); command.type = Serial.parseInt(); int index = 0; int values[4]; while(Serial.peek() != '#' && index < 4) { values[index++] = Serial.parseInt(); } //Serial.print("number of values is "); Serial.println(index); //Look at the struct definition in the MicroBlocks.h file. It uses a union to be able to use the //same struct for carrying different kinds of values. switch(index) { case 0: command.data.longValue = 0; break; case 1: command.data.longValue = values[0]; break; case 2: // If module expects byte values it can retrieve it with data.byteValues[0] and data.byteValues[2] command.data.intValues[0] = values[0]; command.data.intValues[1] = values[1]; break; case 3: command.data.byteValues[0] = values[0] & 255; command.data.byteValues[1] = values[1] & 255; command.data.byteValues[2] = values[2] & 255; command.data.byteValues[3] = 0; break; case 4: command.data.byteValues[0] = values[0] & 255; command.data.byteValues[1] = values[1] & 255; command.data.byteValues[2] = values[2] & 255; command.data.byteValues[3] = values[3] & 255; break; } // look for the '#'. That's the end the command if (Serial.read() == '#') { // print the three numbers in one string as hexadecimal: Serial.print("Node_id = "); Serial.print(node_id, HEX); Serial.print(" Command_type = "); Serial.print(command.type, HEX); Serial.print(" Value = "); Serial.println(command.data.longValue, HEX); //Create a header of type MICROBLOCKS_COMMAND RF24NetworkHeader header(node_id, MICROBLOCKS_COMMAND); boolean ok = network.write(header, &command, sizeof(command)); Serial.println(ok?"Command send successfully!":"Command send failed!"); } else { Serial.println("Command syntax error"); } } } [/code] And this runs as a Joystick and sends its data to node 0 [code] /* This runs on a PRO MICRO 5V module */ /* Connect the RF24L01 module as follows: 1 - GND !!!!!!!!!!!!!!!! 2 - VCC 3.3V !!! NOT 5V 5v will blow up the RF24L01!!! If you do not have 3.3v you can use 2-3 diodes in series Measure it with a volt meter !!!!!!!!!!!!!! 2.8 - 3.3 v is ok !!!!!!!!!!!!!!!! 3 - CE to Arduino pin 9 4 - CSN to Arduino pin 10 5 - SCK to Arduino pin 13 6 - MOSI to Arduino pin 11 7 - MISO to Arduino pin 12 8 - UNUSED - Analog Joystick or two 10K potentiometers: GND to Arduino GND VCC to Arduino +5V X Pot to Arduino A0 Y Pot to Arduino A1 Switch to Arduino D4 Mode to Arduino D5 - V1.00 21 december 2013 */ /* Libraries */ #include <SPI.h> #include <RF24.h> #include <RF24Network.h> #include <MicroBlocks.h> #define MODE_PIN 6 // Pin to switch between analog and digital mode // High = analog (default), low = digital #define START_SWITCH 5 /* Define the pins used for the Analog Joystick (PS2 type with switch) */ #define JOYSTICK_X A0 #define JOYSTICK_Y A1 #define JOYSTICK_SWITCH 4 /* Define the pins that are used for controlling the RF24L01 module */ #define CE_PIN 9 #define CSN_PIN 10 RF24 radio(CE_PIN, CSN_PIN); RF24Network network(radio); /* Global variables */ MicroBlocks_analog_joystick analog; MicroBlocks_digital_joystick digital; MicroBlocks_digital_joystick previous_digital; uint16_t this_node = 1; uint16_t other_node = 0; unsigned long interval = 100; //ms unsigned long last_sent; boolean silent = false; boolean changes_only = false; void setup(void) { /* Setup the pins */ pinMode(JOYSTICK_SWITCH, INPUT); // Set the switch pin as input digitalWrite(JOYSTICK_SWITCH, HIGH); // Use internal pullup resistor pinMode(MODE_PIN, INPUT); // Set the mode switch pin as input digitalWrite(MODE_PIN, HIGH); // Use internal pullup resistor pinMode(START_SWITCH, INPUT); digitalWrite(START_SWITCH, HIGH); Serial.begin(9600); delay(1000); Serial.println("RF24Network transmitter."); SPI.begin(); radio.begin(); network.begin(90, this_node); } void loop(void) { network.update(); // Make sure messages for other nodes are forwarded if(!digitalRead(START_SWITCH)) silent = false; // Pressing the start button will start sending messages again. unsigned long now = millis(); if (network.available() ) { Serial.println("Network data available!"); RF24NetworkHeader receive_header; MicroBlocks_command command; network.peek(receive_header); switch(receive_header.type) { case MICROBLOCKS_COMMAND: network.read(receive_header, &command, sizeof(command)); switch(command.type) { case MICROBLOCKS_COMMAND_SILENT: Serial.println("Silent command received."); silent = true; break; case MICROBLOCKS_COMMAND_TALK: Serial.println("Talk command received."); silent = false; break; case MICROBLOCKS_COMMAND_SET_INTERVAL: Serial.println("Set interval command received."); interval = command.data.uIntValues[0]; changes_only = false; break; case MICROBLOCKS_COMMAND_CHANGES_ONLY: Serial.println("Set interval command received."); changes_only = true; digital.data.switches = 0; break; default: Serial.print("Unknown command type = 0x "); Serial.print(command.type,HEX); Serial.print(" value = 0x"); Serial.println(command.data.longValue,HEX); break; } break; default: Serial.print("Unknown message type "); Serial.println(receive_header.type); network.read(receive_header, &command, sizeof(command)); break; } } else if (!silent) { if ( changes_only || now - last_sent > interval ) { last_sent = now; /* Analog values are 10 bits (0 - 1023). Store it into a unsigned int */ analog.x = analogRead(JOYSTICK_X); analog.y = analogRead(JOYSTICK_Y); analog.data.joyswitch = !digitalRead(JOYSTICK_SWITCH); // pin is HIGH when button is not pressed. boolean isAnalogMode = digitalRead(MODE_PIN); boolean ok = false; if(isAnalogMode && !changes_only) { // when changes only is true then only DIGITAL mode is active; RF24NetworkHeader header(other_node, MICROBLOCKS_ANALOG_JOYSTICK); TXLED1; Serial.print("Sending MICROBLOCKS_ANALOG_JOYSTICK ... "); ok = network.write(header, &analog, sizeof(analog)); Serial.println(ok ? "OK" : "Failed!"); } else { /* Change analog values in digital button on/off values */ digital.data.left = (analog.x < 100); digital.data.right = (analog.x > 800); digital.data.up = (analog.y < 100); digital.data.down = (analog.y > 800); digital.data.joyswitch = analog.data.joyswitch; if (!changes_only || digital.data.switches != previous_digital.data.switches) { RF24NetworkHeader header(other_node, MICROBLOCKS_DIGITAL_JOYSTICK); TXLED1; Serial.print("Sending MICROBLOCKS_DIGITAL_JOYSTICK ... "); ok = network.write(header, &digital, sizeof(digital)); if(ok) previous_digital.data.switches = digital.data.switches; Serial.println(ok ? "OK" : "Failed!"); } TXLED0; } } } } [/code] Microblocks. Build with logic. |
||||
dmasz Newbie ![]() Joined: 12/09/2013 Location: PolandPosts: 21 |
Cool :), I will keep my fingers crossed for RF support in xMITE. br Daniel |
||||
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |