Ligne 2 : |
Ligne 2 : |
| | | |
| == Introduction == | | == Introduction == |
− | {{ambox|text=Before starting this point, we recommand to follow all the sensors testing steps (BMP280 sensor, TMP36 Sensor, RFM69HCW radio and RFM69HCW Testing). | + | {{ambox|text=Before starting this point, we recommand to follow all the sensors testing steps (BMP280 sensor, TMP36 Sensor, RFM69HCW radio, RFM69HCW Testing and onboard NeoPixel). |
| | | |
| It contains all the details about the wiring, install needed libraries and conduct basic testing.}} | | It contains all the details about the wiring, install needed libraries and conduct basic testing.}} |
Ligne 9 : |
Ligne 9 : |
| * Air temperature | | * Air temperature |
| * Air pressure | | * Air pressure |
− | and transmissing the information via the RFM69HCW radio module. | + | and transmitting the information via the RFM69HCW radio module. |
| | | |
| == Wiring == | | == Wiring == |
Ligne 60 : |
Ligne 60 : |
| |} | | |} |
| | | |
− | == The code == | + | == Download the code == |
| The code is available for download on the [https://github.com/mchobby/cansat-belgium GitHub associated to this wiki]. | | The code is available for download on the [https://github.com/mchobby/cansat-belgium GitHub associated to this wiki]. |
| | | |
| {{download-box|Téléchargez mission1-serial-radio-capture.ino|https://raw.githubusercontent.com/mchobby/cansat-belgium/master/mission1-serial-radio-capture/mission1-serial-radio-capture.ino}} | | {{download-box|Téléchargez mission1-serial-radio-capture.ino|https://raw.githubusercontent.com/mchobby/cansat-belgium/master/mission1-serial-radio-capture/mission1-serial-radio-capture.ino}} |
| | | |
− | == Testing == | + | == About testing == |
| Now, we will move forward in several steps. | | Now, we will move forward in several steps. |
| # Getting data from sensors + send them it over the serial connexion (to confirm good working) + transmit over radio | | # Getting data from sensors + send them it over the serial connexion (to confirm good working) + transmit over radio |
Ligne 71 : |
Ligne 71 : |
| # Going autonomous (removing Serial Connexion waiting) + add the Lipo | | # Going autonomous (removing Serial Connexion waiting) + add the Lipo |
| | | |
− | The code proposed here under has been tested up to 22620128 (22.6 millions) iterations without issue, time where we decided to end the test :-) . | + | The code proposed here under has been tested up to 23197 iterations without issue, time when we decided to ends the test :-) . |
| | | |
− | === LEDs and errors === | + | Once uploaded to your Feather, open the Serial Monitor and set it to 9600 bauds. '''The sketch would wait until you open the Serial Monitor to start transmitting the data'''. |
− | Being able to understand rapidly what's happening inside your object is essential to fix the issue. | + | |
| + | You should see the following messages appears on the Serial Monitor. |
| + | |
| + | [[Fichier:ENG-CANSAT-MISSION1-CAPTURE-20.png]] |
| + | |
| + | Where we could see the transmitted messages with the packetnum packet index, timing and data. |
| + | |
| + | The screen also displays the '''ACK''' acknowledgement send back by the receiver. |
| + | |
| + | == Structuring the data == |
| + | The radio module only sends buffer of binary data to the receiver. This is a bit rough but efficient. |
| + | |
| + | So to transport the data to the receiver, we need to transform the values (float, integer) into their string representation. |
| + | |
| + | When having multiple data in their string representation is not enough, they must also been organized. |
| + | |
| + | '''The final format must be easy to parse and very compact''' (smaller is the radio message and higher is the chance for him to get to the ground without error). |
| + | |
| + | We propose the following format: |
| + | <nowiki>:data1|data2|data3|data4;/r/n</nowiki> |
| + | |
| + | where: |
| + | * ''':''' is the begin of data stream |
| + | * ''';''' is the end of data stream |
| + | * '''/r/n''' are optional carriage return + line feed characters.<br />This will would make the messages user friendly when the the messages are viewed in a console or terminal. |
| + | * '''|''' is the separator between data items. |
| + | * '''datax''' are the string representation of the various data. The characters ;:| are forbidden in this area. |
| + | |
| + | we would also recommend to use: |
| + | * '''packetnum''' as data1. packetnum is a simple variable increment of one unit after each transmission. This would allow the receiver to detect lost message (since it would exist holes in the numbering of received messages). |
| + | * '''timing_info''' as data2. This would help to create timing chart or time base data analysis. We suggest to use the Arduino's {{fname|millis()}} function which count the number of milliseconds since the last microcontroler reset. |
| + | |
| + | As explained later in the code the {{fname|packet_str}} variable contains the message to be transmitted to the ground. The Arduino's {{fname|String}} class would ease the transformation of data to their string representation. |
| + | <syntaxhighlight lang="c"> |
| + | String packet_str = String( ":"+String(packetnum,DEC)+"|" ); |
| + | packet_str.concat( String( ms,DEC)+"|" ); |
| + | packet_str.concat( String( temperature, 2 )+"|" ); |
| + | packet_str.concat( String( bme_hpa, 2 )+"|" ); |
| + | packet_str.concat( String( bme_temp, 2 )+";\r\n" ); |
| + | </syntaxhighlight> |
| + | |
| + | == LEDs and Error management == |
| + | Being able to understand rapidly what's happening inside your object is essential to rapidly fix the issue. |
| | | |
| The best is to figure out what's happening is to use LED, blink status, heartbeat. | | The best is to figure out what's happening is to use LED, blink status, heartbeat. |
Ligne 115 : |
Ligne 157 : |
| |} | | |} |
| | | |
− | === The code explained ===
| + | == The code explained == |
− | Here some explanation about the | + | Here some explanation about the {{fname|mission1-serial-radio-capture.ino}} sketch used in the CanSat. |
| | | |
| This Arduino sketch would: | | This Arduino sketch would: |
Ligne 123 : |
Ligne 165 : |
| # Send it to serial connexion | | # Send it to serial connexion |
| # Send it over the radio connexion | | # Send it over the radio connexion |
− |
| |
| | | |
| <div style="margin: 15px 0; background: rgba(255,204,102,.3); display: block; padding: 15px 15px 15px 15px; -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; border: 1px solid #ff9900;" >Don't forget to update the radio frequency {{fname|RF69_FREQ}} and the encryption key {{fname|key[]}} </div> | | <div style="margin: 15px 0; background: rgba(255,204,102,.3); display: block; padding: 15px 15px 15px 15px; -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; border: 1px solid #ff9900;" >Don't forget to update the radio frequency {{fname|RF69_FREQ}} and the encryption key {{fname|key[]}} </div> |
Ligne 198 : |
Ligne 239 : |
| </syntaxhighlight> | | </syntaxhighlight> |
| | | |
| + | Now, we can focus on the main loop. |
| | | |
| + | The first step is to send the column header (so we know what are the data) if not done yet. |
| + | |
| + | Then we reads the sensors (as we have tested them, this should not be a surprise). We also capture the time with the function {{fname|millis()}}, so the {{fname|ms}} variable contains the number of milliseconds since the last reset. |
| + | |
| + | Finally, we do increment the {{fname|packetnum}} variable. This would allows to track lost packets on the receiver side. |
| + | |
| + | {{fname|packet_str}} is the message to send via radio. It is composed with {{fname|String}} objects and concatenation operations. {{fname|String}} are welcome to transform '''float''' into string representation since most common float to string C standard functions would fail to work properly onto Arduino alike plateforms. |
| + | |
| + | The key function to transform the '''String object''' into a '''C buffer'' is {{fname|packet_str.c_str()}} which offer an access to the underlying array of bytes (exactly what the radio module library would need). |
| + | |
| + | The remaining of the radio transmission code is almost the same as the RFM69HCW module testing code (except that error messages are remplaced by Blinking LED). |
| + | |
| <syntaxhighlight lang="c"> | | <syntaxhighlight lang="c"> |
− | bool header_send = false; // if header has not been send yet... | + | bool header_send = false; |
− | int16_t packetnum = 0; // packet number increment at each data emission
| + | // packet number increment at each data transmission |
| + | int16_t packetnum = 0; |
| void loop() { | | void loop() { |
− | // send columns header | + | // --- SEND COLUMNS HEADER ------------------- |
| if( !(header_send) ){ | | if( !(header_send) ){ |
| send_header(); | | send_header(); |
Ligne 209 : |
Ligne 264 : |
| } | | } |
| | | |
− | // read the voltage of TMP36 | + | // --- READ SENSORS --------------------------- |
| float voltage = getVoltage(temperaturePin); | | float voltage = getVoltage(temperaturePin); |
− | // convert voltage to temperature
| |
− | // Degrees = (voltage - 500mV) multiplied by 100
| |
| float temperature = (voltage - .5) *100; | | float temperature = (voltage - .5) *100; |
| | | |
Ligne 221 : |
Ligne 274 : |
| packetnum += 1; // increment | | packetnum += 1; // increment |
| | | |
− | // char radiopacket[40] = "Hello World #"; | + | // --- Compose the Message to send ------------ |
| String packet_str = String( ":"+String(packetnum,DEC)+"|" ); | | String packet_str = String( ":"+String(packetnum,DEC)+"|" ); |
| packet_str.concat( String(ms,DEC)+"|" ); | | packet_str.concat( String(ms,DEC)+"|" ); |
Ligne 255 : |
Ligne 308 : |
| // Going to next round | | // Going to next round |
| } | | } |
| + | </syntaxhighlight> |
| + | |
| + | The {{fname|init_radio_module()}} function is called from the {{fname|setup()}}. |
| + | |
| + | This function does all the stuff to initialize the RFM69HCW modules. Set the transmission power, the frequency and the '''encryption key'''. |
| | | |
| + | <syntaxhighlight lang="c"> |
| void init_radio_module() { | | void init_radio_module() { |
| pinMode(RADIO_LED, OUTPUT); | | pinMode(RADIO_LED, OUTPUT); |
Ligne 296 : |
Ligne 355 : |
| Serial.println(" MHz"); | | Serial.println(" MHz"); |
| } | | } |
| + | </syntaxhighlight> |
| | | |
| + | This function send the header information to the Serial monitor. |
| + | |
| + | Ideally, this function should also send it via the radio. |
| + | |
| + | <syntaxhighlight lang="c"> |
| void send_header() { | | void send_header() { |
− | // Send header about the data Serial.println(F("***START***"));
| |
| String s1 = String( F("***HEADER***\r\n") ); | | String s1 = String( F("***HEADER***\r\n") ); |
| Serial.print( s1 ); | | Serial.print( s1 ); |
− | // use : as begin of data and ; as end of data
| |
| String s2 = String( F(":counter|time_ms|temperature|pressure_hpa|temp2;\r\n") ); | | String s2 = String( F(":counter|time_ms|temperature|pressure_hpa|temp2;\r\n") ); |
| Serial.print(s2); | | Serial.print(s2); |
Ligne 308 : |
Ligne 371 : |
| | | |
| } | | } |
| + | </syntaxhighlight> |
| | | |
| + | Helper function used to blink a LED. Note that a pause of 3 time the blinking time. This will ease the identification of blink code into other blinking patterns. |
| + | |
| + | <syntaxhighlight lang="c"> |
| void Blink(byte PIN, byte DELAY_MS, byte loops) { | | void Blink(byte PIN, byte DELAY_MS, byte loops) { |
| for (byte i=0; i<loops; i++) { | | for (byte i=0; i<loops; i++) { |
Ligne 319 : |
Ligne 386 : |
| delay( 3* DELAY_MS ); | | delay( 3* DELAY_MS ); |
| } | | } |
| + | </syntaxhighlight> |
| | | |
− | /*
| + | This function returns the voltage for the analog Pin. |
− | * getVoltage() - return the voltage of an analog pin
| + | |
− | */
| + | It converts a digital value between 0 & 1024 (from ADC) to voltage between 0 & 3.3 volts. |
| + | |
| + | <syntaxhighlight lang="c"> |
| float getVoltage(int pin){ | | float getVoltage(int pin){ |
− | // AS the sketch does not call the analogReadResolution() | + | // each unit equal 3.3 / 1024 = 3.2 millivolts |
− | // function to change the analog reading resolution
| |
− | // THEN Arduino use the defaut 12 bits resolution!
| |
− | // Under 12 bits resolution, the analogRead() returns
| |
− | // a value between 0 & 1024.
| |
− | //
| |
− | // Convert digital value between 0 & 1024 to
| |
− | // voltage between 0 & 3.3 volts.
| |
− | // (each unit equal 3.3 / 1024 = 3.2 millivolts)
| |
| return (analogRead(pin) * .0032); | | return (analogRead(pin) * .0032); |
| } | | } |
| </syntaxhighlight> | | </syntaxhighlight> |
| + | |
| + | == Fault tolerant design == |
| + | The goal is to transmit the data to the ground station.<br />The code of the Emitter (this section) and Receiver (next section) are doing the job. |
| + | |
| + | However, what would happens to your data if the antenna did break? All the data are lots! |
| + | |
| + | This is where the "Extra Flash" would be a great help! |
| + | |
| + | As showed earlier, it is also possible to store/save the data into the Flash. |
| + | |
| + | A good approach would be: |
| + | # to save the data in the Flash |
| + | # then send it over Radio. |
| + | |
| + | In this way, the data stays available inside the CanSat and could be extracted as suited. |
| | | |
| {{ENG-CANSAT-TRAILER}} | | {{ENG-CANSAT-TRAILER}} |