Compare commits

..

198 Commits

Author SHA1 Message Date
Ben Lye
d5885d96b9 Bump action versions 2023-06-14 12:46:28 +01:00
Ben Lye
f1ede37f6d Used a fixed version of Arduino CLI 2023-06-14 12:46:10 +01:00
Ben Lye
c814cc1bd4 Switch back to non-devel boards for CI/CD (#863) 2023-06-14 13:29:34 +02:00
pascallanger
4e7c1502ff Update Protocols_Details.md 2023-06-04 13:48:04 +02:00
pascallanger
8ddee12de5 Update _Config.h 2023-06-04 12:50:39 +02:00
pascallanger
692bcc1fbb Update Scorpio_cyrf6936.ino 2023-06-04 12:38:09 +02:00
pascallanger
0bb345e3fc J6Pro save some flash space 2023-06-04 12:20:10 +02:00
pascallanger
c193d0b9dd Update Multiprotocol.ino 2023-06-04 12:12:22 +02:00
pascallanger
410ce5cc4c Fixed SX1276 protocol 2023-06-04 12:07:27 +02:00
pascallanger
cefe69a692 Fix SPORT when using FRSKYX (V1) protocol set to EU LBT 2023-06-04 12:03:43 +02:00
pascallanger
daa4ded390 New protocol Scorpio
Model Falco 300
2023-06-04 12:02:37 +02:00
pascallanger
5949fca990 Update Protocols_Details.md (#856) 2023-05-15 08:26:13 +02:00
pascallanger
ed2e2e4f32 Update Protocols_Details.md 2023-05-15 08:17:19 +02:00
Frankie Arzu
b2f8f482bb V0.54 Enhacements (#846)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

* #766  make editable Gain Values

Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers.

* #766 More enhacements

Added AR630
Make numbers right justified
Cleanup some log messages and line types.
Updated DSM FWD prog documentation

* #751 more cosmetic things

1. Added AR10360T,
2. Simplify way to configured the hack for more receivers.
3. Change some texts on menus to march spektrum
4. Background color in Spektrum theme to match

* #751 A few final changes

1. Update channel names to include channel number. i.e: Ch5 (Gear)
2,  Fix flight mode display for Heli Receiver
3. i think the unknown lines are to request info about the TX settings

* #751

1. Added Warning Screen
2. Correct handling of Unknown lines in Gyro Settings->Initial Setup

* #751

New v0.51 version.
- Added new menus to configure Model/Wing type.  Without it, the initial setup will not work properly.

* #751 More fixes on mixers and servo reverse

-- Fix problem reversing servos when using vtail/delta mix
-- Properly detect ch order of multimodule

* #751 Updated channel naming and docs

Updated readme documentation
Consistent naming of Ch across the code.

* #751 Fix message displaying data path

* #751  More improvements

1. Much easier to select channels > Ch6 for FMode, Gain and Panic channels
2. B&W version for smaller screens (128x64).. Memory footprint still a problem.
3. Fix a lot typos/misspell/grammar in the documentation

* Create DSM_AR636_TextGen.lua

Script to show Telemetry TextGen screens
for AR636 Receiver.
Really useful for BLADE helis using the AR636

Could replace dsmPID.lua

Still needs to be ported to smaller screens.

* #751 Enhancements for Lua Script tools

Enhancements

* Delete DSM_AR636_TextGen.lua

The TextGen functionality is included in DSM_AR636_Tel.lua. No longer needed

* Version 0.54

1. Fix problem with "Attitude Trim" Menu
2. New "MINimalistic" version for radios with very low memory
3. Externalized menu messages shared by all versions. the idea is to allow to translate it into other languages.
4. Correction of TextGen tools to work on black&white radios (some Lua functional differences). TextGen will be working on EdgeTx 2,8.3

---------

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2023-04-16 21:42:31 +02:00
Frankie Arzu
89023d4b55 Remove DSM_AR636_TextGen.lua (#837)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

* #766  make editable Gain Values

Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers.

* #766 More enhacements

Added AR630
Make numbers right justified
Cleanup some log messages and line types.
Updated DSM FWD prog documentation

* #751 more cosmetic things

1. Added AR10360T,
2. Simplify way to configured the hack for more receivers.
3. Change some texts on menus to march spektrum
4. Background color in Spektrum theme to match

* #751 A few final changes

1. Update channel names to include channel number. i.e: Ch5 (Gear)
2,  Fix flight mode display for Heli Receiver
3. i think the unknown lines are to request info about the TX settings

* #751

1. Added Warning Screen
2. Correct handling of Unknown lines in Gyro Settings->Initial Setup

* #751

New v0.51 version.
- Added new menus to configure Model/Wing type.  Without it, the initial setup will not work properly.

* #751 More fixes on mixers and servo reverse

-- Fix problem reversing servos when using vtail/delta mix
-- Properly detect ch order of multimodule

* #751 Updated channel naming and docs

Updated readme documentation
Consistent naming of Ch across the code.

* #751 Fix message displaying data path

* #751  More improvements

1. Much easier to select channels > Ch6 for FMode, Gain and Panic channels
2. B&W version for smaller screens (128x64).. Memory footprint still a problem.
3. Fix a lot typos/misspell/grammar in the documentation

* Create DSM_AR636_TextGen.lua

Script to show Telemetry TextGen screens
for AR636 Receiver.
Really useful for BLADE helis using the AR636

Could replace dsmPID.lua

Still needs to be ported to smaller screens.

* #751 Enhancements for Lua Script tools

Enhancements

* Delete DSM_AR636_TextGen.lua

The TextGen functionality is included in DSM_AR636_Tel.lua. No longer needed

---------

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2023-03-19 06:19:35 +01:00
Frankie Arzu
66ac007d4c Frankie dsm telemetry lua scripts enhancements (#835)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

* #766  make editable Gain Values

Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers.

* #766 More enhacements

Added AR630
Make numbers right justified
Cleanup some log messages and line types.
Updated DSM FWD prog documentation

* #751 more cosmetic things

1. Added AR10360T,
2. Simplify way to configured the hack for more receivers.
3. Change some texts on menus to march spektrum
4. Background color in Spektrum theme to match

* #751 A few final changes

1. Update channel names to include channel number. i.e: Ch5 (Gear)
2,  Fix flight mode display for Heli Receiver
3. i think the unknown lines are to request info about the TX settings

* #751

1. Added Warning Screen
2. Correct handling of Unknown lines in Gyro Settings->Initial Setup

* #751

New v0.51 version.
- Added new menus to configure Model/Wing type.  Without it, the initial setup will not work properly.

* #751 More fixes on mixers and servo reverse

-- Fix problem reversing servos when using vtail/delta mix
-- Properly detect ch order of multimodule

* #751 Updated channel naming and docs

Updated readme documentation
Consistent naming of Ch across the code.

* #751 Fix message displaying data path

* #751  More improvements

1. Much easier to select channels > Ch6 for FMode, Gain and Panic channels
2. B&W version for smaller screens (128x64).. Memory footprint still a problem.
3. Fix a lot typos/misspell/grammar in the documentation

* Create DSM_AR636_TextGen.lua

Script to show Telemetry TextGen screens
for AR636 Receiver.
Really useful for BLADE helis using the AR636

Could replace dsmPID.lua

Still needs to be ported to smaller screens.

* #751 Enhancements for Lua Script tools

Enhancements

---------

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2023-03-18 19:31:48 +01:00
Frankie Arzu
139cd4c583 Telemetry TextGen/Screens for BLADE Helis with AR636 (#813)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

* #766  make editable Gain Values

Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers.

* #766 More enhacements

Added AR630
Make numbers right justified
Cleanup some log messages and line types.
Updated DSM FWD prog documentation

* #751 more cosmetic things

1. Added AR10360T,
2. Simplify way to configured the hack for more receivers.
3. Change some texts on menus to march spektrum
4. Background color in Spektrum theme to match

* #751 A few final changes

1. Update channel names to include channel number. i.e: Ch5 (Gear)
2,  Fix flight mode display for Heli Receiver
3. i think the unknown lines are to request info about the TX settings

* #751

1. Added Warning Screen
2. Correct handling of Unknown lines in Gyro Settings->Initial Setup

* #751

New v0.51 version.
- Added new menus to configure Model/Wing type.  Without it, the initial setup will not work properly.

* #751 More fixes on mixers and servo reverse

-- Fix problem reversing servos when using vtail/delta mix
-- Properly detect ch order of multimodule

* #751 Updated channel naming and docs

Updated readme documentation
Consistent naming of Ch across the code.

* #751 Fix message displaying data path

* #751  More improvements

1. Much easier to select channels > Ch6 for FMode, Gain and Panic channels
2. B&W version for smaller screens (128x64).. Memory footprint still a problem.
3. Fix a lot typos/misspell/grammar in the documentation

* Create DSM_AR636_TextGen.lua

Script to show Telemetry TextGen screens
for AR636 Receiver.
Really useful for BLADE helis using the AR636

Could replace dsmPID.lua

Still needs to be ported to smaller screens.

---------

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2023-02-07 10:48:46 +01:00
richardclli
0fed2486f5 FX9630 protocol support, resolves #801 (#805)
New protocol FX9630
2023-01-16 20:06:27 +01:00
Filip Kotoucek
11c01004bf KF606: subprotocol ZC-Z50v2 Cessna (#797)
Maybe newer iteration of Z50. My plane does not have front propeller.
But if there is one, its just for design. This model does not have front motor.

Thanks @pascallanger for support and reviews.
2023-01-15 21:26:35 +01:00
Frankie Arzu
f49f03d7da Frankie dsm fwrd prg enhancements (#806)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

* #766  make editable Gain Values

Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers.

* #766 More enhacements

Added AR630
Make numbers right justified
Cleanup some log messages and line types.
Updated DSM FWD prog documentation

* #751 more cosmetic things

1. Added AR10360T,
2. Simplify way to configured the hack for more receivers.
3. Change some texts on menus to march spektrum
4. Background color in Spektrum theme to match

* #751 A few final changes

1. Update channel names to include channel number. i.e: Ch5 (Gear)
2,  Fix flight mode display for Heli Receiver
3. i think the unknown lines are to request info about the TX settings

* #751

1. Added Warning Screen
2. Correct handling of Unknown lines in Gyro Settings->Initial Setup

* #751

New v0.51 version.
- Added new menus to configure Model/Wing type.  Without it, the initial setup will not work properly.

* #751 More fixes on mixers and servo reverse

-- Fix problem reversing servos when using vtail/delta mix
-- Properly detect ch order of multimodule

* #751 Updated channel naming and docs

Updated readme documentation
Consistent naming of Ch across the code.

* #751 Fix message displaying data path

* #751  More improvements

1. Much easier to select channels > Ch6 for FMode, Gain and Panic channels
2. B&W version for smaller screens (128x64).. Memory footprint still a problem.
3. Fix a lot typos/misspell/grammar in the documentation

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2023-01-15 11:27:34 +01:00
Louis Botha
90bb5f8871 Update Protocols_Details.md (#796)
Fixed spelling mistake
2023-01-12 10:59:20 +01:00
Frankie Arzu
401cc76f20 Fix message displaying the DataPath after saving (#787)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

* #766  make editable Gain Values

Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers.

* #766 More enhacements

Added AR630
Make numbers right justified
Cleanup some log messages and line types.
Updated DSM FWD prog documentation

* #751 more cosmetic things

1. Added AR10360T,
2. Simplify way to configured the hack for more receivers.
3. Change some texts on menus to march spektrum
4. Background color in Spektrum theme to match

* #751 A few final changes

1. Update channel names to include channel number. i.e: Ch5 (Gear)
2,  Fix flight mode display for Heli Receiver
3. i think the unknown lines are to request info about the TX settings

* #751

1. Added Warning Screen
2. Correct handling of Unknown lines in Gyro Settings->Initial Setup

* #751

New v0.51 version.
- Added new menus to configure Model/Wing type.  Without it, the initial setup will not work properly.

* #751 More fixes on mixers and servo reverse

-- Fix problem reversing servos when using vtail/delta mix
-- Properly detect ch order of multimodule

* #751 Updated channel naming and docs

Updated readme documentation
Consistent naming of Ch across the code.

* #751 Fix message displaying data path

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-12-20 23:48:23 +01:00
Frankie Arzu
6297810edc Updated Ch naming across all display, and updated Docs (#786)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

* #766  make editable Gain Values

Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers.

* #766 More enhacements

Added AR630
Make numbers right justified
Cleanup some log messages and line types.
Updated DSM FWD prog documentation

* #751 more cosmetic things

1. Added AR10360T,
2. Simplify way to configured the hack for more receivers.
3. Change some texts on menus to march spektrum
4. Background color in Spektrum theme to match

* #751 A few final changes

1. Update channel names to include channel number. i.e: Ch5 (Gear)
2,  Fix flight mode display for Heli Receiver
3. i think the unknown lines are to request info about the TX settings

* #751

1. Added Warning Screen
2. Correct handling of Unknown lines in Gyro Settings->Initial Setup

* #751

New v0.51 version.
- Added new menus to configure Model/Wing type.  Without it, the initial setup will not work properly.

* #751 More fixes on mixers and servo reverse

-- Fix problem reversing servos when using vtail/delta mix
-- Properly detect ch order of multimodule

* #751 Updated channel naming and docs

Updated readme documentation
Consistent naming of Ch across the code.

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-12-20 22:58:38 +01:00
Frankie Arzu
01aef0a822 Frankie dsm fwrd prg enhancements (#785)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

* #766  make editable Gain Values

Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers.

* #766 More enhacements

Added AR630
Make numbers right justified
Cleanup some log messages and line types.
Updated DSM FWD prog documentation

* #751 more cosmetic things

1. Added AR10360T,
2. Simplify way to configured the hack for more receivers.
3. Change some texts on menus to march spektrum
4. Background color in Spektrum theme to match

* #751 A few final changes

1. Update channel names to include channel number. i.e: Ch5 (Gear)
2,  Fix flight mode display for Heli Receiver
3. i think the unknown lines are to request info about the TX settings

* #751

1. Added Warning Screen
2. Correct handling of Unknown lines in Gyro Settings->Initial Setup

* #751

New v0.51 version.
- Added new menus to configure Model/Wing type.  Without it, the initial setup will not work properly.

* #751 More fixes on mixers and servo reverse

-- Fix problem reversing servos when using vtail/delta mix
-- Properly detect ch order of multimodule

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-12-20 10:12:34 +01:00
Frankie Arzu
186730231e Fwrd Programming New Model/Wingtype Setup Menus (#784)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

* #766  make editable Gain Values

Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers.

* #766 More enhacements

Added AR630
Make numbers right justified
Cleanup some log messages and line types.
Updated DSM FWD prog documentation

* #751 more cosmetic things

1. Added AR10360T,
2. Simplify way to configured the hack for more receivers.
3. Change some texts on menus to march spektrum
4. Background color in Spektrum theme to match

* #751 A few final changes

1. Update channel names to include channel number. i.e: Ch5 (Gear)
2,  Fix flight mode display for Heli Receiver
3. i think the unknown lines are to request info about the TX settings

* #751

1. Added Warning Screen
2. Correct handling of Unknown lines in Gyro Settings->Initial Setup

* #751

New v0.51 version.
- Added new menus to configure Model/Wing type.  Without it, the initial setup will not work properly.

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-12-18 19:45:50 +01:00
pascallanger
770a12ffdf EU 2022-12-08 08:28:45 +01:00
pascallanger
40fa242012 Update Protocols_Details.md 2022-12-06 15:53:42 +01:00
Frankie Arzu
c4e70ab4fd Frankie dsm fwrd prg enhancements (#776)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

* #766  make editable Gain Values

Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers.

* #766 More enhacements

Added AR630
Make numbers right justified
Cleanup some log messages and line types.
Updated DSM FWD prog documentation

* #751 more cosmetic things

1. Added AR10360T,
2. Simplify way to configured the hack for more receivers.
3. Change some texts on menus to march spektrum
4. Background color in Spektrum theme to match

* #751 A few final changes

1. Update channel names to include channel number. i.e: Ch5 (Gear)
2,  Fix flight mode display for Heli Receiver
3. i think the unknown lines are to request info about the TX settings

* #751

1. Added Warning Screen
2. Correct handling of Unknown lines in Gyro Settings->Initial Setup

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-12-02 08:53:13 +01:00
Frankie Arzu
2da8b3c42a Frankie dsm fwrd prg enhancements (#773)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

* #766  make editable Gain Values

Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers.

* #766 More enhacements

Added AR630
Make numbers right justified
Cleanup some log messages and line types.
Updated DSM FWD prog documentation

* #751 more cosmetic things

1. Added AR10360T,
2. Simplify way to configured the hack for more receivers.
3. Change some texts on menus to march spektrum
4. Background color in Spektrum theme to match

* #751 A few final changes

1. Update channel names to include channel number. i.e: Ch5 (Gear)
2,  Fix flight mode display for Heli Receiver
3. i think the unknown lines are to request info about the TX settings

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-12-01 08:57:33 +01:00
Frankie Arzu
38a79c816a Frankie dsm fwrd prg enhancements (#772)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

* #766  make editable Gain Values

Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers.

* #766 More enhacements

Added AR630
Make numbers right justified
Cleanup some log messages and line types.
Updated DSM FWD prog documentation

* #751 more cosmetic things

1. Added AR10360T,
2. Simplify way to configured the hack for more receivers.
3. Change some texts on menus to march spektrum
4. Background color in Spektrum theme to match

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-11-29 23:23:10 +01:00
Frankie Arzu
1d8f1a0857 Frankie dsm fwrd prg enhancements (#771)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

* #766  make editable Gain Values

Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers.

* #766 More enhacements

Added AR630
Make numbers right justified
Cleanup some log messages and line types.
Updated DSM FWD prog documentation

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-11-29 07:46:40 +01:00
Frankie Arzu
5901bac374 #766 Fix for editable Gains + Number formatting (#770)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

* #766  make editable Gain Values

Gains and other settings should be editable even when they are VALUE_NOCHANGING. Flight Mode is an exception that is handled properly. Right align numbers.

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-11-28 20:00:16 +01:00
Frankie Arzu
2e7b1dc904 #766 change validation of required files (#768)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

* #766

Change the way to detect that the files exist. now works on both ETX and OTX

* #766 Strange Flickering in OTX

Strange Flickering happening on OTX. Refreshing the screen on every cycle fixed the problem

* #766 Change way of dectecting EdgeTX

Change way of detecting OTX in multiple versions: OTX 2.3.14 and 2.3.15

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-11-27 15:10:02 +01:00
Frankie Arzu
7052751261 Frankie dsm fwrd prg enhancements (#763)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

* #751

Write documentation about the protocol so that we don't forget later what we know, and enable others to understand the logs and maybe help solve problems.

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-11-21 09:52:15 +01:00
Frankie Arzu
60047e2c73 Frankie dsm fwrd prg enhancements (#762)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

* #751 add check for required libraries

Add check that the required files in DSMLIB exist

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-11-20 18:24:47 +01:00
Frankie Arzu
40b393ac4a Frankie DSM fwrd Prog (more improvements) (#760)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

* #751 Cosmetic and Show Orientation Images

#751
1. Fix problems when text contradictions between Menu/Line Headers and List Values
2. Show Images of RX orientations
3. Able to Hack getting into Initial Setup and other menus who was failing before
4. Custumize the way Flight Mode reports the Value on Screen

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-11-20 18:01:16 +01:00
Frankie Arzu
1c6dc01959 Frankie dsm fwrd prg enhancements (#757)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

* #751 Fix problems With Reset RX

1. Fix problem when trying to Factory Reset. Enter Bind Mode. Save backup, Restore Backup

2. Found a way to advance on the Gyro initial Setup menus.. a bit of a hack, but works.

3. Handle RX resets properly. It needed after initial setup

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-11-18 10:41:59 +01:00
pascallanger
5148449066 FS2A: Turning on/off the LNA during bind. 2022-11-17 20:08:56 +01:00
Frankie Arzu
fcc4d19430 Frankie dsm fwrd prg enhancements (#755)
* #751 DSM Enhancements

 #751 DSM Forward Programming Enhancements (New GUI, etc)

* Make both work on EdgeTx and OpenTX

* #751 Turn OFF simulation by default

Distribution code with RX simulation OFF
Simulation should be only for Development

* #751 Update Readme Documentation

Updated the Readme.txt documentation
and removed compiled luac file that was check in by mistake

Co-authored-by: pascallanger <pascal_langer@yahoo.fr>
2022-11-17 17:50:36 +01:00
richardclli
c80f705fa2 Added MPM for Flysky PL18 PCB and casing design. (#754) 2022-11-17 11:37:11 +01:00
pascallanger
c95db35b41 Update Protocols_Details.md 2022-11-17 10:58:38 +01:00
pascallanger
35f3548992 Compilation option 2022-11-17 10:29:53 +01:00
pascallanger
58f6716035 Update README.md 2022-11-17 09:49:30 +01:00
pascallanger
2b15de0f90 Update README.md 2022-11-17 09:47:22 +01:00
pascallanger
be91409df9 Delete DSM FwdPrg.lua 2022-11-17 09:44:18 +01:00
Frankie Arzu
4d8e440965 Frankie dsm fwrd prg enhancements (#753)
DSM Forward Programming Enhancements (New GUI for Color+Touch and BW, etc). Work on EdgeTx and OpenTX.
2022-11-17 09:42:04 +01:00
pascallanger
2d469d074e Merge branch 'master' of https://github.com/pascallanger/DIY-Multiprotocol-TX-Module 2022-10-30 11:40:11 +01:00
pascallanger
96405d27b5 Extend Kyosho KT-17 bind time 2022-10-30 11:40:07 +01:00
yakulis
75c9fb40a7 Update DSM FwdPrg.lua (#735)
Panic Text Updates - The following strings were updated:
Text[0x00D2]="Panic Channel"
Text[0x008E]="Panic Delay"
Text[0x01FC]="Panic Flight Mode"
2022-10-12 11:35:09 +02:00
Pieter du Preez
cd1c15a45a Added documentation for using dfu-util for flashing STM32 targets. (#727)
This patch adds documentation, explaining how to flash firmware to DFU
capable multimodules.

A dfu-util command was taken and adapted from:
https://github.com/benlye/flash-multi/blob/master/doc/Troubleshooting.md

Using dfu-util is straight forward, easy and very safe.
2022-10-12 11:34:32 +02:00
pascallanger
2bd50f4c8c V761/TOPRC: new sub protocol
Top RC Hobby mini planes
2022-09-26 12:02:38 +02:00
yakulis
4c2ddcbe48 Update DSM FwdPrg.lua (#733)
Reversing 'Off' and 'On' text values in lines #555 and #556.  They are used in the SAFE and AS3X menu items in the DSM Forward Programming section, and they are functioning backwards. This will correct the issue. This is the fix for Issue #728
2022-09-11 06:01:02 -04:00
Ben Lye
d7f9ef6967 Use devel boards in CI workflow (#709) 2022-07-21 10:20:16 +02:00
pascallanger
bfc8c2f9fd Kyosho2/KT-17: only 1 ID 2022-07-20 17:20:27 +02:00
pascallanger
ce6243d6e3 FX/620: IDs 2022-07-17 09:07:28 +02:00
pascallanger
80f9ff4163 Update FX_nrf24l01.ino 2022-07-15 15:44:10 +02:00
pascallanger
d9f8a3989a FX/FX620: only 1 ID 2022-07-15 15:43:04 +02:00
pascallanger
416f7d5c19 FX/FX620 another try 2022-07-12 08:05:41 +02:00
pascallanger
eb24dd5549 FX/FX620
I messed up the hopping, I must have looked at the wrong file.
2022-07-11 11:12:20 +02:00
pascallanger
ba19592973 FX/FX620 debug off 2022-07-11 09:11:11 +02:00
pascallanger
98d8d7fb5f FX/FX620 new protocol 2022-07-11 09:03:31 +02:00
pascallanger
ad0947b0b7 Updated MT99xx/PA18: need someone to test... 2022-07-09 00:43:28 +02:00
pascallanger
c093f21b31 Update README.md 2022-07-08 10:38:13 +02:00
pascallanger
c5a3e305f9 Update Protocols_Details.md 2022-07-08 09:46:58 +02:00
pascallanger
e085602a6c FX816 is not limited to the P38 2022-07-08 09:46:28 +02:00
pascallanger
dee25215cf Update Protocols_Details.md 2022-07-08 09:41:16 +02:00
Michael
ef1bb1ead7 adjusted comment according to change in commit da33df43 (#696) 2022-07-07 11:44:46 +02:00
pascallanger
90170493c0 Update doc 2022-06-30 17:40:55 +02:00
pascallanger
00a9057186 Update Protocols_Details.md 2022-06-28 14:24:19 +02:00
pascallanger
05c300e979 Update Protocols_Details.md 2022-06-28 10:23:20 +02:00
pascallanger
6730ed6246 Update Protocols_Details.md 2022-06-27 17:20:55 +02:00
pascallanger
0a0463652b E129/C186 protocol
C186/E120 models
2022-06-27 16:59:48 +02:00
pascallanger
4f580a4286 Update Hardware.md 2022-06-16 10:36:34 +02:00
Michael
0616cab386 Fixes a problem where setting failsafe values was only possible once after bind and only set values for the first 12 channels. This fix allows setting failsafe values without having to rebind and sets failsafe values correctly for all possible 16 channels. (#695)
Notes for OpenTX/EdgeTX:
This fix doesn't exhibit servo jitter if failsafe mode was left on Custom, however it is still recommended to set failsafe mode back to Receiver after setting failsafe values.
It is still necessary to wait at least 8 seconds switching back from failsafe mode Custom to failsafe mode Receiver as OpenTX/EdgeTX will trigger the data transfer in Custom mode only every 7 seconds.

Bench tested with HoTT receivers GR-16, GR-24pro, GR-32
Flight tested with HoTT receiver GR-24
2022-06-13 14:28:21 +02:00
Michael
175cfa5e93 This is the Graupner HoTT adapted version of the Model Locator script. It uses the Graupner HoTT telemetry sensor "Rssi" instead of the OpenTX sensor "RSSI". (#693)
Reasoning: The OpenTX sensor "RSSI" is populated by the individual OpenTX telemetry protocol implementations and returns a value from 0..100 (percent) originating from the early FrSky implementation. It turns out that FrSky did not really provide a genuine signal strength indicator in units of dbm but a link quality indicator in 0..100%. With Graupner HoTT the link quality indicator is not a good basis for the model locator as it is very non-linear and doesn't change much with distance. Using the Graupner HoTT telemetry sensor "Rssi" which is a true signal strength indicator serves the purpose of locating a model much better as it varies much more with distance.
2022-06-07 10:51:26 +02:00
Pascal Langer
dbfccad568 Update DSM FwdPrg.lua 2022-04-25 10:33:16 +02:00
Pascal Langer
3fe6dc64fa KF606/MIG320: Added LED control 2022-04-15 17:29:25 +02:00
Peter Feerick
49068af59f fix: MultiConfig lua set and reset subProtocol (#672) 2022-03-25 09:12:14 +01:00
pascallanger
fbd81c6e26 Update Protocols_Details.md 2022-02-07 18:49:38 +01:00
Ryan5732
9cac96d6a7 Small Readme Typos (#661) 2022-02-03 14:11:26 +01:00
Pascal Langer
5987aa40a6 Another try on PA18 2022-01-25 15:21:22 +01:00
Pascal Langer
30cb812549 Try PA18 2022-01-24 08:36:00 +01:00
Pascal Langer
0fd2e36046 test PA18 2022-01-23 19:08:46 +01:00
Pascal Langer
8b05e55ebd MT99xx2/PA18: new protocol 2022-01-23 17:56:00 +01:00
Pascal Langer
af71a208fd Ready for release 2022-01-16 11:00:50 +01:00
mheimlich
7e5cb8e884 mention XK A800 in V911S/E119 details (#654) 2022-01-16 10:54:04 +01:00
MattCarothers
8c669ea015 Updated several text fields (#651) 2022-01-09 17:25:11 +01:00
Anders Höglund
acd8379077 A few more Spektrum Fwd Programming text updates (#632)
* DSM FP, Added AR8360T

* DSM FP, Added a few unkown empty lines
2021-11-19 08:37:28 +01:00
Pascal Langer
97c6088715 fix compilation? 2021-11-13 19:08:38 +01:00
Pascal Langer
db017c73b6 HS6200 emulation using CYRF for E01X protocol 2021-11-13 19:03:01 +01:00
Anders Höglund
24aaa0dd0c Text updates in Spektrum Fwd Prog LUA script (#630)
* DSM FP, Added AR636B

* DSM FP, Added AR637TA

* DSM FP, Added On, conflict with AR636B

* DSM FP, Rx name conflict resolved.
2021-11-12 18:37:52 +01:00
Michael
da33df4346 added packet_in[14] to transfer HoTT device warnings to OpenTx (#627) 2021-11-04 14:57:25 +01:00
Pascal Langer
22590ed4f7 Update DSM FwdPrg.lua 2021-10-26 16:34:38 +02:00
Pascal Langer
c0bc64c2aa V911S: increased bind time 2021-10-24 12:03:09 +02:00
Pascal Langer
694d968e8c Update Propel_nrf24l01.ino 2021-10-24 11:04:57 +02:00
Pascal Langer
fff3b8830e MT99xx/A180: F949S flags for RATE, RXLED and 3D6G 2021-10-24 11:04:44 +02:00
Pascal Langer
b740f4cd24 V911S/E119: add 6G_3D channel for P40 plane 2021-10-24 09:57:24 +02:00
Pascal Langer
e247b8b9c1 DSM Fwd Pgm updated 2021-10-20 15:13:56 +02:00
Pascal Langer
4f7af553d1 Update doc 2021-09-17 17:07:15 +02:00
Pascal Langer
570e6f8c64 Update Protocols_Details.md 2021-09-17 09:48:35 +02:00
Pascal Langer
b6e665b567 Devo: fix typo 2021-09-16 16:13:39 +02:00
Pascal Langer
48e82304a0 Update Devo_cyrf6936.ino 2021-09-16 00:30:46 +02:00
Pascal Langer
7b58b9aca0 Devo: support for 2 GPS formats
Use the Fixed ID field, 0/1 first GPS format, +2 for the other format -> 2/3
2021-09-16 00:11:25 +02:00
Pascal Langer
dfcf3f533c Update NRF24l01_SPI.ino 2021-09-16 00:08:47 +02:00
Pascal Langer
4c14925b4f Bump version for release 2021-09-12 19:35:11 +02:00
Pascal Langer
4c74bc869d Update _Config.h 2021-09-11 00:08:09 +02:00
Pascal Langer
45edfa1ec0 AFHDS2A doc update 2021-09-10 23:58:12 +02:00
Pascal Langer
88343b7424 Proto list: fix flags 2021-09-09 08:40:09 +02:00
Pascal Langer
13197840c2 V761: code cleanup 2021-09-09 08:39:37 +02:00
Pascal Langer
ffe4ac68fd Proto list: add number of protocols in the list 2021-09-08 14:30:01 +02:00
Pascal Langer
5bab5f03db Update Multi_Protos.ino 2021-09-08 13:38:25 +02:00
Pascal Langer
ae748e25f8 Update XN297Dump_nrf24l01.ino 2021-09-07 23:17:21 +02:00
Pascal Langer
e9c7959ecb Update V761_nrf24l01.ino 2021-09-07 23:17:11 +02:00
Pascal Langer
bf09855014 Another attempt 2021-09-07 16:59:06 +02:00
Pascal Langer
32bd39f209 Save space in the 5in1 modules 2021-09-07 15:07:37 +02:00
Pascal Langer
940f241b9d Fix compilation issues 2021-09-07 14:29:35 +02:00
Pascal Langer
5c00ce6b88 New 0 protocol: enable to list all protocols and sub protocols 2021-09-07 14:08:24 +02:00
Pascal Langer
016b282246 Multi Status: send frame as soon as a new protocol is selected 2021-09-05 22:15:56 +02:00
Pascal Langer
d6ecac1302 Xerall: few improvements 2021-09-04 11:48:41 +02:00
Ben Lye
901f8ca6b0 Disable some protocols for the DIY 5-in-1 (#618) 2021-09-01 15:20:10 +02:00
Pascal Langer
9c3b6ba9f6 Update Validate.h 2021-09-01 09:45:38 +02:00
Pascal Langer
53ec484028 Update Validate.h 2021-09-01 01:39:26 +02:00
Pascal Langer
246e808cb4 Xerall protocol 2021-09-01 01:21:38 +02:00
Pascal Langer
a7b68dc2aa F949G: will it work? 2021-08-27 10:22:47 +02:00
Pascal Langer
08404f9223 F949G: another try... 2021-08-26 23:43:42 +02:00
Pascal Langer
149554e61c Update MT99xx_ccnrf.ino 2021-08-26 23:23:38 +02:00
Pascal Langer
7b71425db2 F949G test with original radio ID 2021-08-26 23:19:59 +02:00
Pascal Langer
f9cfb7f20f Update Protocols_Details.md 2021-08-26 22:51:57 +02:00
Pascal Langer
506ee43d41 Update _Config.h 2021-08-26 22:35:42 +02:00
Pascal Langer
ebde0915cd MT99xx/F949G: new sub protocol 2021-08-26 22:33:38 +02:00
Pascal Langer
2501656bf4 Update XN297Dump_nrf24l01.ino 2021-08-26 11:52:36 +02:00
Pascal Langer
9356e7654e XN297EMU: allows to send and receive 0 payload length in enhanced mode 2021-08-25 19:07:36 +02:00
Pascal Langer
4f1e5d2452 BUGSMINI: fix
No one has reported an issue but from the look of the code it was broken since some time...
2021-08-25 19:05:24 +02:00
Ben Lye
64c75414d8 Add release build for 64KB CC2500-only module (#615) 2021-08-24 20:54:46 +02:00
Pascal Langer
5147833487 DSM RX: use directly the TX type and channels 2021-08-21 10:14:05 +02:00
Ben Lye
324419241f Prefix release binaries with mm instead of multi (#596)
Better for radios with 128x64 LCDs.
2021-08-16 17:59:07 +02:00
Pascal Langer
1a921b1cdb Disable multi config if telemetry is disabled 2021-07-05 17:08:57 +02:00
Pascal Langer
25c052c0a3 KF606: new sub protocol MIG320 2021-07-04 14:05:42 +02:00
Pascal Langer
a02586cca9 RadioLink: RSSI correction 2021-07-03 14:09:28 +02:00
Pascal Langer
226e082c5a E129 and E010R5 not supported on atmega328p 2021-06-30 09:45:25 +02:00
Pascal Langer
5afdff8477 V911S: Rate channel on CH6 (default is high) 2021-06-28 18:10:44 +02:00
Pascal Langer
c52ac2cefc Fix XN297Dump enhanced payload detection 2021-06-28 18:04:17 +02:00
pascallanger
09f39ea60f Update README.md 2021-06-27 12:38:53 +02:00
Harry Phillips
41e31eae6d Set reserved field in CABELL packet to current channel (#593) 2021-06-24 11:49:47 +02:00
Pascal Langer
dcf20951ba Update Protocols_Details.md 2021-06-20 18:51:23 +02:00
Pascal Langer
6e9ebfd695 Mould King: control up to 4 bricks at the same time 2021-06-20 18:39:27 +02:00
Pascal Langer
c36b112437 Update Multi_Config.ino 2021-06-17 15:42:09 +02:00
Pascal Langer
24fbd44f5b Multi Config: eeprom format for atmega 2021-06-17 15:40:28 +02:00
Pascal Langer
d0db7829e5 Multi config: enabled by default, LUA script update 2021-06-17 15:30:46 +02:00
Pascal Langer
58ed8ca60f Mould King: Analog and digital sub protocols 2021-05-27 19:01:08 +02:00
Pascal Langer
4d2c8ef04e Fix RadioLink: request telemetry every time to time like original instead of all the time 2021-05-25 14:54:17 +02:00
Pascal Langer
438e85c551 MouldKing: match original 2021-05-09 03:36:42 +02:00
Pascal Langer
06b336ee59 Mould King: fix frequencies 2021-05-04 14:58:40 +02:00
pascallanger
a93adeb75e Update Protocols_Details.md 2021-05-04 11:29:44 +02:00
Pascal Langer
802e69563e ... 2021-05-04 10:53:38 +02:00
Pascal Langer
a8897df3f2 New protocol: Mould King 2021-05-04 10:39:13 +02:00
Pascal Langer
7157d3d906 Losi: optimization 2021-05-03 16:57:08 +02:00
Pascal Langer
96b8a500b4 New protocol: Losi 2021-05-03 15:29:24 +02:00
Pascal Langer
51a3ef8ee9 Update XN297Dump_nrf24l01.ino 2021-05-01 18:45:20 +02:00
Pascal Langer
652d6604e7 Update Multiprotocol.h 2021-05-01 18:35:47 +02:00
Pascal Langer
b9028ab4b4 XN297Dump: CC2500 dump @250K 2021-05-01 18:15:34 +02:00
Konstantin Tretyakov
62c509b03c Add the IKEA Ansluta remote-controllable lighting protocol (#566)
* Add the IKEA Ansluta remote-controllable lighting protocol

* Fixes to IKEA Ansluta implementation
2021-04-24 21:46:29 +02:00
Pascal Langer
1648a0780a XK: fix twitch on other channels than rudder 2021-04-24 12:54:17 +02:00
pascallanger
3b1af68ed6 Update FUNDING.yml 2021-04-21 13:17:46 +02:00
pascallanger
aeed7bac57 Create FUNDING.yml 2021-04-20 23:17:50 +02:00
Pascal Langer
2bb76a40f3 Pelikan/SCX24: add second hopping table 2021-04-20 22:24:01 +02:00
Pascal Langer
0c129a45aa SCX24 IDs 2021-04-18 16:41:49 +02:00
Pascal Langer
2a383c7285 Pelikan/SCX24: fix? 2021-04-17 22:55:43 +02:00
Pascal Langer
34f16b0b66 MT99XX/Dragon: batt telemetry correction & low batt flag 2021-04-17 18:31:41 +02:00
Pascal Langer
a9d9c4cdfa Update A7105_SPI.ino 2021-04-17 11:57:00 +02:00
Pascal Langer
4a1c986dd0 Kyosho/FHSS fix? 2021-04-17 11:52:21 +02:00
Pascal Langer
756e9b3213 Update Protocols_Details.md 2021-04-16 15:00:33 +02:00
Pascal Langer
61a284863e Update Protocols_Details.md 2021-04-16 14:59:10 +02:00
Pascal Langer
39410c290b Pelikan/SCX24: new sub protocol 2021-04-16 14:50:53 +02:00
Pascal Langer
9e0684b6cb CABELL: code optimization 2021-04-07 09:02:50 +02:00
Pascal Langer
13add5b9f2 Devo: fix GPS year 2021-04-06 18:15:34 +02:00
Pascal Langer
475cd1af98 Atmega CC2500 build: remove FRSKYL to fit 2021-04-06 10:36:15 +02:00
Pascal Langer
04d08c6d67 FrSkyX2: changed bind bytes 2021-04-06 10:18:48 +02:00
Pascal Langer
6295bb1bbc Packed protocols definition to save space 2021-04-05 17:43:42 +02:00
Pascal Langer
931ba2bd68 Hitec: fix freq tune 2021-04-02 17:42:44 +02:00
Pascal Langer
769fb4ff94 Update Protocols_Details.md 2021-03-30 16:41:29 +02:00
Pascal Langer
aa7c18a2bd FrSkyX: fix the need to rebind RXs 2021-03-30 15:13:00 +02:00
Pascal Langer
f95cfa1261 Update Protocols_Details.md 2021-03-30 14:13:18 +02:00
Pascal Langer
8c12aaf2c1 DSMR: new surface protocol 2021-03-30 12:10:29 +02:00
Pascal Langer
94f73bd54f MT99xx: Added voltage telemetry for the Dragon sub protocol 2021-03-27 11:51:53 +01:00
Pascal Langer
9b3549d4a9 FrSkyX: code review 2021-03-25 10:10:53 +01:00
Pascal Langer
7bc05cb63c Update Multiprotocol.h 2021-03-19 17:11:06 +01:00
Pascal Langer
84780a9d90 Multi Config 2021-03-19 17:06:58 +01:00
Pascal Langer
37e029c612 Bug fix in XN297 emu layer when address is 3 bytes and CC2500 in use 2021-03-17 19:24:42 +01:00
Pascal Langer
c47ed8aca8 E016H has its own protocol now 2021-03-17 18:46:48 +01:00
Pascal Langer
5bb0752d5e Update include to xn297... 2021-03-17 17:19:41 +01:00
Pascal Langer
4a626eaf14 Change XN297 emulation layer
Loads of protocols have been touched by this change. Some testing has been done but please test on all your models.
The XN297 emulation selects in this order:
 - the CC2500 if it is available and bitrate=250K. Configure the option field automatically for RF tune.
 - the NRF for all bitrates if it is available
 - if NRF is not available and bitrate=1M then an invalid protocol is sent automatically to the radio.
CC2500 @250K can now receive normal and enhanced payloads.
OMP protocol supports telemetry on CC2500 and is also for NRF only modules including telemetry.
Separation of E016H (new protocol) from E01X due to different structure.
MJXQ, MT99XX, Q303 and XK: some sub protocols available on CC2500 only.
2021-03-17 17:05:42 +01:00
pascallanger
47de19c8a5 DSM RX output ranges 2021-03-15 08:45:27 +01:00
Pascal Langer
3d4cfa30ac Remove spaces 2021-03-15 08:43:25 +01:00
pascallanger
d658bee05a Add files via upload 2021-03-15 08:39:06 +01:00
pascallanger
820c181394 Update CPPM_HW_Mod.md 2021-03-15 08:07:42 +01:00
pascallanger
c9774f557e Add files via upload 2021-03-15 08:06:24 +01:00
Pascal Langer
b17618a5a8 Update Protocols_Details.md 2021-03-14 19:22:49 +01:00
160 changed files with 116875 additions and 3416 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: [pascallanger]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=VF2K9T23DRY56&lc=US&item_name=DIY%20Multiprotocol&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted

View File

@@ -55,10 +55,12 @@ jobs:
BOARD: ${{ matrix.board }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install Arduino CLI
uses: arduino/setup-arduino-cli@v1.1.1
uses: arduino/setup-arduino-cli@v1.1.2
with:
version: "0.32.2"
- name: Prepare build environment
run: |
@@ -67,15 +69,22 @@ jobs:
echo "Event action: ${{ github.event.action }}"
echo "Tag name: ${{ github.event.release.tag_name }}"
arduino-cli config init --additional-urls https://raw.githubusercontent.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/master/package_multi_4in1_board_index.json
arduino-cli config init --additional-urls https://raw.githubusercontent.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/master/package_multi_4in1_board_index.json,https://raw.githubusercontent.com/pascallanger/DIY-Multiprotocol-TX-Module-Boards/devel/source/package_multi_4in1_board_devel_index.json
arduino-cli core update-index
if [[ "$BOARD" =~ "multi4in1:avr:" ]]; then
if [[ "$BOARD" =~ ":avr:" ]]; then
arduino-cli core install arduino:avr;
fi
if [[ "$BOARD" =~ "multi4in1-devel:avr" ]]; then
arduino-cli core install multi4in1-devel:avr
elif [[ "$BOARD" =~ "multi4in1:avr" ]]; then
arduino-cli core install multi4in1:avr
fi
if [[ "$BOARD" =~ "multi4in1:STM32F1:" ]]; then
if [[ "$BOARD" =~ "multi4in1-devel:STM32F1:" ]]; then
arduino-cli core install multi4in1-devel:STM32F1
elif [[ "$BOARD" =~ "multi4in1:STM32F1:" ]]; then
arduino-cli core install multi4in1:STM32F1
fi
@@ -109,18 +118,18 @@ jobs:
echo "ALL_RFMODULES=$(echo $ALL_RFMODULES)" >> $GITHUB_ENV
# Disable CHECK_FOR_BOOTLOADER when not needed
if [[ "$BOARD" == "multi4in1:avr:multiatmega328p:bootloader=none" ]]; then
if [[ "$BOARD" =~ ":avr:multiatmega328p:bootloader=none" ]]; then
opt_disable CHECK_FOR_BOOTLOADER;
fi
# Trim the build down for the Atmega328p board
if [[ "$BOARD" =~ "multi4in1:avr:multiatmega328p:" ]]; then
if [[ "$BOARD" =~ ":avr:multiatmega328p:" ]]; then
opt_disable $ALL_PROTOCOLS
opt_enable FRSKYX_CC2500_INO AFHDS2A_A7105_INO MJXQ_NRF24L01_INO DSM_CYRF6936_INO;
fi
# Trim the enabled protocols down for the STM32F103CB board with debugging or the STM32F103C8 board in general
if [[ "$BOARD" == "multi4in1:STM32F1:multistm32f103cb:debug_option=ftdi" ]] || [[ "$BOARD" == "multi4in1:STM32F1:multistm32f103cb:debug_option=native" ]] || [[ "$BOARD" =~ "multi4in1:STM32F1:multistm32f103c8" ]]; then
if [[ "$BOARD" =~ ":STM32F1:multistm32f103cb:debug_option=ftdi" ]] || [[ "$BOARD" =~ ":STM32F1:multistm32f103cb:debug_option=native" ]] || [[ "$BOARD" =~ ":STM32F1:multistm32f103c8" ]]; then
opt_disable $ALL_PROTOCOLS;
opt_enable FRSKYX_CC2500_INO AFHDS2A_A7105_INO MJXQ_NRF24L01_INO DSM_CYRF6936_INO;
fi
@@ -133,7 +142,7 @@ jobs:
- name: Build default configuration
run: |
# Skip the default build for boards where it's too large now
if [[ "$BOARD" == "multi4in1:STM32F1:multistm32f103cb:debug_option=none" ]] || [[ "$BOARD" == "multi4in1:STM32F1:multi5in1t18int" ]]; then
if [[ "$BOARD" =~ ":STM32F1:multistm32f103cb:debug_option=none" ]] || [[ "$BOARD" =~ ":STM32F1:multi5in1t18int" ]]; then
printf "Not testing default build for $BOARD.";
else
source ./buildroot/bin/buildFunctions;
@@ -189,7 +198,7 @@ jobs:
- name: 'Upload Artifacts'
if: env.HAVE_FILES == 'true'
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: multi-test-build
path: ./binaries/

View File

@@ -1,601 +0,0 @@
local toolName = "TNS|DSM Forward Programming|TNE"
---- #########################################################################
---- # #
---- # Copyright (C) OpenTX #
-----# #
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
---- # #
---- # 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. #
---- # #
---- # This program is distributed in the hope that it will be useful #
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
---- # GNU General Public License for more details. #
---- # #
---- #########################################################################
--###############################################################################
-- Multi buffer for DSM description
-- Multi_Buffer[0..2]=="DSM" -> Lua script is running
-- Multi_Buffer[3]==0x70+len -> TX to RX data ready to be sent
-- Multi_Buffer[4..9]=6 bytes of TX to RX data
-- Multi_Buffer[10..25]=16 bytes of RX to TX data
--
-- To start operation:
-- Write 0x00 at address 3
-- Write 0x00 at address 10
-- Write "DSM" at address 0..2
--###############################################################################
local RX_VERSION, WAIT_CMD, MENU_TITLE, MENU_LINES, MENU_VALUES, VALUE_CHANGING, VALUE_CHANGING_WAIT, VALUE_CHANGED = 0, 1, 2, 3, 4, 5, 6, 7
local MENU, LIST_MENU_NOCHANGING, LIST_MENU2, PERCENTAGE_VALUE = 0x1C, 0x6C, 0x4C, 0xC0
local Phase = RX_VERSION
local Waiting_RX = 0
local Text = {}
local Retry=100
local Blink = 0
local Value_Changed=0
local Menu = { Cur=nil, Id=nil, Title="", Prev=nil, PrevId=nil, Next=nil, NextId=nil, Back=nil, BackId=nil, CurLine=nil, SelLine=nil, EditLine=nil }
local Line = {}
local RX = { Name="", Version="" }
local function conv_int16(number)
if number >= 0x8000 then
return number - 0x10000
end
return number
end
local function Get_Text(index)
out = Text[index]
if out == nil then -- unknown...
out = "Unknown_"..string.format("%X",index)
end
return out
end
local function DSM_Release()
multiBuffer( 0, 0 )
end
local function DSM_Send(...)
local arg = {...}
for i = 1 , #arg do
multiBuffer( 3+i, arg[i])
end
multiBuffer( 3, 0x70+#arg)
end
local function Value_Add(dir)
local line=Line[Menu.SelLine]
Speed = getRotEncSpeed()
if Speed == ROTENC_MIDSPEED then
line.Val = line.Val + (5 * dir)
elseif Speed == ROTENC_HIGHSPEED then
line.Val = line.Val + (15 * dir)
else
line.Val = line.Val + dir
end
if line.Val > line.Max then
line.Val = line.Max
elseif line.Val < line.Min then
line.Val = line.Min
end
if Line[Menu.SelLine].Type ~= LIST_MENU_NOCHANGING then
Phase = VALUE_CHANGING
Waiting_RX = 0
end
end
local function DSM_Menu(event)
local Speed = 0
if event == EVT_VIRTUAL_NEXT then
if Menu.EditLine == nil then
-- not changing a value
if Menu.SelLine ~= nil then
if Menu.SelLine < 7 then
local num = Menu.SelLine
for i = Menu.SelLine + 1, 6, 1 do
if Line[i].Type ~= nil and Line[i].Next ~= nil then
Menu.SelLine=i
break
end
end
if num == Menu.SelLine then
if Menu.Next ~= 0 then -- Next
Menu.SelLine = 7
elseif Menu.Prev ~= 0 then -- Prev
Menu.SelLine = 8
end
end
elseif Menu.Prev ~= 0 then -- Prev
Menu.SelLine = 8
end
end
else -- need to inc the value
Value_Add(1)
end
elseif event == EVT_VIRTUAL_PREV then
if Menu.EditLine == nil then
if Menu.SelLine ~= nil then
if Menu.SelLine == 8 and Menu.Next ~= 0 then
Menu.SelLine = 7
elseif Menu.SelLine > 0 then
if Menu.SelLine > 6 then
Menu.SelLine = 7
end
local num = Menu.SelLine
for i = Menu.SelLine-1, 0, -1 do
if Line[i].Type ~= nil and Line[i].Next ~= nil then
Menu.SelLine=i
break
end
end
if num == Menu.SelLine then -- Back
Menu.SelLine = -1
end
else
Menu.SelLine = -1 -- Back
end
end
else -- need to dec the value
Value_Add(-1)
end
elseif event == EVT_VIRTUAL_ENTER then
if Menu.SelLine == -1 then -- Back
Menu.Cur = Menu.Back
Menu.Id = Menu.BackId
Menu.SelLine = 0
Phase = MENU_TITLE
Waiting_RX = 0
elseif Menu.SelLine == 7 then -- Next
Menu.Cur = Menu.Next
Menu.Id = Menu.NextId
Menu.SelLine = 0
Phase = MENU_TITLE
Waiting_RX = 0
elseif Menu.SelLine == 8 then -- Prev
Menu.Cur = Menu.Prev
Menu.Id = Menu.PrevId
Menu.SelLine = 0
Phase = MENU_TITLE
Waiting_RX = 0
elseif Menu.SelLine ~= nil and Line[Menu.SelLine].Next ~= nil then
if Line[Menu.SelLine].Type == MENU then -- Next menu exist
Menu.Cur = Line[Menu.SelLine].Next
Menu.Id = Line[Menu.SelLine].NextId
Phase = MENU_TITLE
Waiting_RX = 0
else
-- value entry
if Menu.EditLine == Menu.SelLine then
Menu.EditLine = nil
Value_Changed = 0
Phase = VALUE_CHANGED
Waiting_RX = 0
else
Menu.EditLine = Menu.SelLine
end
end
end
end
end
local function DSM_Send_Receive()
if Waiting_RX == 0 then
Waiting_RX = 1
-- Need to send a request
if Phase == RX_VERSION then -- request RX version
DSM_Send(0x11,0x06,0x00,0x14,0x00,0x00)
elseif Phase == WAIT_CMD then -- keep connection open
DSM_Send(0x00,0x04,0x00,0x00)
elseif Phase == MENU_TITLE then -- request menu title
if Menu.Cur == nil then
DSM_Send(0x12,0x06,0x00,0x14,0x00,0x00) -- first menu only
Menu.Cur = 0
else
DSM_Send(0x16,0x06,Menu.Id,Menu.Cur,0x00,Menu.SelLine)
end
elseif Phase == MENU_LINES then -- request menu lines
if Menu.CurLine == nil then
DSM_Send(0x13,0x04,Menu.Id,Menu.Cur) -- line 0
elseif Menu.CurLine >= 0x80 then
local last_byte={0x40,0x01,0x02,0x04,0x00,0x00} -- unknown...
DSM_Send(0x20,0x06,Menu.CurLine-0x80,Menu.CurLine-0x80,0x00,last_byte[Menu.CurLine-0x80+1]) -- line X
else
DSM_Send(0x14,0x06,Menu.Id,Menu.Cur,0x00,Menu.CurLine) -- line X
end
elseif Phase == MENU_VALUES then -- request menu values
DSM_Send(0x15,0x06,Menu.Id,Menu.Cur,Line[Menu.CurLine].ValId,Menu.CurLine) -- line X
elseif Phase == VALUE_CHANGING then -- send value
local value=Line[Menu.SelLine].Val
if value < 0 then
value = 0x10000 + value
end
DSM_Send(0x18,0x06,Line[Menu.SelLine].ValId,Menu.SelLine,bit32.rshift(value,8),bit32.band(value,0xFF)) -- send current value
Phase = VALUE_CHANGING_WAIT
elseif Phase == VALUE_CHANGED then -- send value
if Value_Changed == 0 then
local value=Line[Menu.SelLine].Val
if value < 0 then
value = 0x10000 + value
end
DSM_Send(0x18,0x06,Line[Menu.SelLine].ValId,Menu.SelLine,bit32.rshift(value,8),bit32.band(value,0xFF)) -- send current value
Value_Changed = Value_Changed + 1
Waiting_RX = 0
elseif Value_Changed == 1 then
DSM_Send(0x19,0x06,Line[Menu.SelLine].ValId,Menu.SelLine) -- validate
-- Value_Changed = Value_Changed + 1
-- Waiting_RX = 0
--elseif Value_Changed == 2 then
-- DSM_Send(0x1B,0x06,0x10,Menu.SelLine) -- validate again?
-- Value_Changed = Value_Changed + 1
end
elseif Phase == VALUE_CHANGING_WAIT then
DSM_Send(0x1A,0x06,Line[Menu.SelLine].ValId,Menu.SelLine)
end
multiBuffer(10,0x00);
Retry = 50
elseif multiBuffer(10) == 0x09 then
-- Answer received
--if multiBuffer(11) == 0x00 then -- waiting for commands?
if multiBuffer(11) == 0x01 then -- read version
--ex: 0x09 0x01 0x00 0x15 0x02 0x22 0x01 0x00 0x14 0x00 0x00 0x00 0x00 0x00 0x00 0x00
RX.Name = Get_Text(multiBuffer(13))
RX.Version = multiBuffer(14).."."..multiBuffer(15).."."..multiBuffer(16)
Phase = MENU_TITLE
elseif multiBuffer(11) == 0x02 then -- read menu title
--ex: 0x09 0x02 0x4F 0x10 0xA5 0x00 0x00 0x00 0x50 0x10 0x10 0x10 0x00 0x00 0x00 0x00
Menu.Cur = multiBuffer(12)
Menu.Id = multiBuffer(13)
Menu.Title = Get_Text(multiBuffer(14)+multiBuffer(15)*256)
Menu.Prev = multiBuffer(16)
Menu.PrevId = multiBuffer(17)
Menu.Next = multiBuffer(18)
Menu.NextId = multiBuffer(19)
Menu.Back = multiBuffer(20)
Menu.BackId = multiBuffer(21)
for i = 0, 6 do -- clear menu
Line[i] = { Menu = nil, Id = nil, Type = nil, Text="", Next = nil, NextId = nil, ValLine = nil, ValId = nil, Min, Max, Def, Val, Unit, Step }
end
Menu.CurLine = nil
if Menu.Next ~= 0 then
Menu.SelLine = 7 -- highlight Next
else
Menu.SelLine = -1 -- highlight Back
end
Blink = 0
Phase = MENU_LINES
elseif multiBuffer(11) == 0x03 then -- read menu lines
--ex: 0x09 0x03 0x00 0x10 0x00 0x1C 0xF9 0x00 0x10 0x10 0x00 0x00 0x00 0x00 0x03 0x00
-- Menu Id line Type Text_idx Next V_Id Val_Min Val_Max Val_Def
--ex: 0x09 0x03 0x61 0x10 0x00 0x6C 0x50 0x00 0x00 0x10 0x36 0x00 0x49 0x00 0x36 0x00
Menu.CurLine = multiBuffer(14)
local line = Line[Menu.CurLine]
line.Menu = multiBuffer(12)
line.Id = multiBuffer(13) -- not quite sure yet
line.Type = multiBuffer(15) -- not quite sure yet: 1C is text menu only, 4C/6C is text followed by text list, C0 is text followed by percentage value
line.Text = Get_Text(multiBuffer(16)+multiBuffer(17)*256)
if multiBuffer(18) == Menu.Cur then
line.Next = nil
else
line.Next = multiBuffer(18) -- not quite sure yet: 1C=text menu=>next menu, others=>line number of the value
end
if Menu.SelLine == -1 and line.Next ~= nil then -- Auto select first line of the menu
Menu.SelLine = Menu.CurLine
end
line.NextId = multiBuffer(19) -- not quite sure yet
line.ValLine = multiBuffer(18) -- not quite sure yet
line.ValId = multiBuffer(19) -- not quite sure yet
line.Min = conv_int16(multiBuffer(20)+multiBuffer(21)*256)
line.Max = conv_int16(multiBuffer(22)+multiBuffer(23)*256)
line.Def = conv_int16(multiBuffer(24)+multiBuffer(25)*256)
if line.Type == MENU then
-- nothing to do on menu entries
elseif line.Type == LIST_MENU_NOCHANGING or line.Type == LIST_MENU2 then
line.Val = nil --line.Def - line.Min -- use default value not sure if needed
line.Def = line.Min -- pointer to the start of the list in Text
line.Max = line.Max - line.Min -- max index
line.Min = 0 -- min index
else -- default to numerical value
line.Val = nil --line.Def -- use default value not sure if needed
end
if line.Type ~= 0x1C then -- value to follow
line.Text = line.Text..":"
end
Phase = MENU_LINES
elseif multiBuffer(11) == 0x04 then -- read menu values
--ex: 0x09 0x04 0x53 0x10 0x00 0x10 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
-- Menu MeId line VaId Value
--ex: 0x09 0x04 0x61 0x10 0x02 0x10 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
Menu.CurLine = multiBuffer(14)
Line[Menu.CurLine].Val = conv_int16(multiBuffer(16)+multiBuffer(17)*256)
Phase = MENU_VALUES
elseif multiBuffer(11) == 0x05 then -- unknown... need to get through the lines...
Menu.CurLine = 0x80 + multiBuffer(12)
Phase = MENU_LINES
elseif multiBuffer(11) == 0x00 and Phase == VALUE_CHANGING then
Phase = VALUE_CHANGING_WAIT
end
-- Data processed
Waiting_RX = 0
multiBuffer(10,0x00)
Retry = 50
else
Retry = Retry - 1
if Retry <= 0 then
-- Retry the RX request
Retry = 50
Waiting_RX = 0
if Phase ~= RX_VERSION and Phase ~= VALUE_CHANGING_WAIT then
Phase = WAIT_CMD
end
end
end
end
local function DSM_Display()
lcd.clear()
if LCD_W == 480 then
--Draw title
lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR)
lcd.drawText(1, 5, "DSM Forward Programming", MENU_TITLE_COLOR)
--Draw RX Menu
if Phase == RX_VERSION then
lcd.drawText(10,50,"No compatible DSM RX...", BLINK)
else
if Menu.Title ~= nil then
local attrib=0;
lcd.drawText(80,32,Menu.Title,MIDSIZE)
for i = 0, 6 do
if i == Menu.SelLine then
attrib = INVERS
else
attrib = 0
end
if Line[i] ~= nil and Line[i].Type ~= nil then
if Line[i].Type ~= MENU then -- list/value
if Line[i].Val ~= nil then
local text=""
if Line[i].Type == LIST_MENU_NOCHANGING or Line[i].Type == LIST_MENU2 then
text = Get_Text(Line[i].Val+Line[i].Def)
elseif Line[i].Type == PERCENTAGE_VALUE then
text = Line[i].Val.." %"
else
text = Line[i].Val
end
if Menu.EditLine == Menu.SelLine then -- blink edited entry
Blink = Blink + 1
if Blink > 25 then
attrib = 0
if Blink > 50 then
Blink = 0
end
end
end
lcd.drawText(240,32+20*(i+2), text, attrib) -- display value
end
attrib = 0
end
lcd.drawText(10,32+20*(i+2), Line[i].Text, attrib) -- display text
end
end
if Menu.SelLine == -1 then
lcd.drawText(437,32, "Back", INVERS)
else
lcd.drawText(437,32, "Back", 0)
end
lcd.drawRectangle(437-5, 32-2, 47, 25)
if Menu.Next ~= 0 then
if Menu.SelLine == 7 then
lcd.drawText(437,220, "Next",INVERS)
else
lcd.drawText(437,220, "Next")
end
lcd.drawRectangle(437-5, 220-2, 47, 25)
end
if Menu.Prev ~= 0 then
if Menu.SelLine == 8 then
lcd.drawText(5,220, "Prev",INVERS)
else
lcd.drawText(5,220, "Prev")
end
lcd.drawRectangle(5-5, 220-2, 47, 25)
end
end
lcd.drawText(170,252, "RX "..RX.Name.." v"..RX.Version) -- display RX info
end
else
-- --Draw RX Menu on LCD_W=128
-- if multiBuffer( 4 ) == 0xFF then
-- lcd.drawText(2,17,"No compatible DSM RX...",SMLSIZE)
-- else
-- if Retry_128 ~= 0 then
-- --Intro page
-- Retry_128 = Retry_128 - 1
-- lcd.drawScreenTitle("DSM Forward Programming",0,0)
-- lcd.drawText(2,17,"Press Prev Page for previous Menu" ,SMLSIZE)
-- else
-- --Menu page
-- for line = 0, 7, 1 do
-- for i = 0, 21-1, 1 do
-- value=multiBuffer( line*21+6+i )
-- if value > 0x80 then
-- value = value - 0x80
-- lcd.drawText(2+i*6,1+8*line,string.char(value).." ",SMLSIZE+INVERS)
-- else
-- lcd.drawText(2+i*6,1+8*line,string.char(value),SMLSIZE)
-- end
-- end
-- end
-- end
-- end
end
end
-- Init
local function DSM_Init()
--Set protocol to talk to
multiBuffer( 0, string.byte('D') )
--test if value has been written
if multiBuffer( 0 ) ~= string.byte('D') then
error("Not enough memory!")
return 2
end
--Init TX buffer
multiBuffer( 3, 0x00 )
--Init RX buffer
multiBuffer( 10, 0x00 )
--Init telemetry
multiBuffer( 0, string.byte('D') )
multiBuffer( 1, string.byte('S') )
multiBuffer( 2, string.byte('M') )
--Text to be displayed -> need to use a file instead?
--RX names--
Text[0x0014]="SPM4651T"
Text[0x0015]="AR637T"
--Lists--
Text[0x0036]="Throttle"
Text[0x0037]="Aileron"
Text[0x0038]="Elevator"
Text[0x0039]="Rudder"
Text[0x003A]="Gear"
--******
--This part is strange since the AR637T needs
for i=1,7 do -- 3B..41
Text[0x003A+i]="Aux"..i
end
for i=1,8 do -- 41..49
Text[0x0041+i]="XPlus-"..i
end
--But FOTO-PETE reports that it should be:
Text[0x0040]="Roll"
Text[0x0041]="Pitch"
Text[0x0042]="Yaw"
Text[0x0046]="Priority"
--******
Text[0x004A]="Failsafe"
Text[0x004B]="Main Menu"
Text[0x004E]="Position"
Text[0x0050]="Outputs"
Text[0x0051]="Output Channel 1"
Text[0x0052]="Output Channel 2"
Text[0x0053]="Output Channel 3"
Text[0x0054]="Output Channel 4"
Text[0x0055]="Output Channel 5"
Text[0x0056]="Output Channel 6"
--Text[0x005E]="Inhibit"
Text[0x005F]="Hold Last"
Text[0x0060]="Preset"
--Text[0x0061]="Custom"
--Messages--
Text[0x0078]="FM Channel"
Text[0x0080]="Orientation"
Text[0x0085]="Frame Rate"
Text[0x0086]="System Setup"
Text[0x0087]="F-Mode Setup"
Text[0x0088]="Enabled F-Modes"
Text[0x008A]="Gain Sensitivity"
Text[0x0090]="Apply"
Text[0x0093]="Complete"
Text[0x0094]="Done"
Text[0x0097]="Factory Reset"
Text[0x009A]="Capture Failsafe Positions"
Text[0x009C]="Custom Failsafe"
Text[0x00A5]="First Time Setup"
Text[0x00AD]="Gain Channel Select"
Text[0x00B6]="FM1"
Text[0x00B7]="FM2"
Text[0x00B8]="FM3"
Text[0x00B9]="FM4"
Text[0x00BA]="FM5"
Text[0x00BB]="FM6"
Text[0x00BC]="FM7"
Text[0x00BD]="FM8"
Text[0x00BE]="FM9"
Text[0x00BF]="FM10"
Text[0x00F9]="Gyro settings"
Text[0x00FE]="Stick Priority"
Text[0x0100]="Make sure the model has been"
Text[0x0101]="configured, including wing type,"
Text[0x0102]="reversing, travel, trimmed, etc."
Text[0x0103]="before continuing setup."
Text[0x0104]="0104"
Text[0x0105]="0105"
Text[0x0106]="Any wing type, channel assignment,"
Text[0x0107]="subtrim, or servo reversing changes"
Text[0x0108]="require running through initial"
Text[0x0109]="setup again."
Text[0x010A]="010A"
Text[0x010B]="010B"
Text[0x0190]="Relearn Servo Settings"
Text[0x019C]="Enter Receiver Bind Mode"
Text[0x01DC]="AS3X"
Text[0x01DD]="AS3X Settings"
Text[0x01DE]="AS3X Gains"
Text[0x01E0]="Rate Gains"
Text[0x020A]="Restore from Backup"
Text[0x0209]="Save to Backup"
Text[0x020D]="First Time SAFE Setup"
Text[0x021A]="Set the model level,"
Text[0x021B]="and press Continue."
Text[0x021C]="021C"
Text[0x021D]="021D"
Text[0x021F]="Set the model on its nose,"
Text[0x0220]="and press Continue. If the"
Text[0x0221]="orientation on the next"
Text[0x0222]="screen is wrong go back"
Text[0x0223]="and try again."
Text[0x0224]="Continue"
Text[0x0229]="Set Orientation Manually"
Text[0x0227]="Other settings"
Text[0x022B]="WARNING!"
Text[0x022C]="This will reset the"
Text[0x022D]="configuration to factory"
Text[0x022E]="defaults. This does not"
Text[0x022F]="affect the backup config."
Text[0x0230]="0230"
Text[0x0231]="This will overwrite the"
Text[0x0232]="backup memory with your"
Text[0x0233]="current configuartion."
Text[0x0234]="0234"
Text[0x0235]="0235"
Text[0x0236]="This will overwrite the"
Text[0x0237]="current config with"
Text[0x0238]="that which is in"
Text[0x0239]="the backup memory."
Text[0x023A]="023A"
Text[0x023D]="Copy Flight Mode Settings"
Text[0x0240]="Utilities"
Text[0x8001]="Flight Mode 1"
Text[0x8002]="Flight Mode 2"
Text[0x8003]="Flight Mode 3"
end
-- Main
local function DSM_Run(event)
if event == nil then
error("Cannot be run as a model script!")
return 2
elseif event == EVT_VIRTUAL_EXIT then
DSM_Release()
return 2
else
DSM_Menu(event)
DSM_Send_Receive()
DSM_Display()
return 0
end
end
return { init=DSM_Init, run=DSM_Run }

View File

@@ -0,0 +1,549 @@
local toolName = "TNS|DSM Forward Prog v0.54 (Text B&W) |TNE"
local VERSION = "v0.54"
---- #########################################################################
---- # #
---- # Copyright (C) OpenTX #
-----# #
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
---- # #
---- # 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. #
---- # #
---- # This program is distributed in the hope that it will be useful #
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
---- # GNU General Public License for more details. #
---- # #
---- #########################################################################
------------------------------------------------------------------------------
-- This script library is a rewrite of the original DSM forward programming Lua
-- Script. The goal is to make it easier to understand, mantain, and to
-- separate the GUI from the DSM Forward programming engine/logic
-- in this way, GUIs can evolve independent. OpenTX Gui, EdgeTx GUI, Small Radios, etc.
-- Code is based on the code/work by: Pascal Langer (Author of the Multi-Module)
-- Rewrite/Enhancements By: Francisco Arzu
------------------------------------------------------------------------------
local SIMULATION_ON = false -- FALSE: don't show simulation menu (DEFAULT), TRUE: show simulation menu
local DEBUG_ON = 1 -- 0=NO DEBUG, 1=HIGH LEVEL 2=LOW LEVEL (Debug logged into the /LOGS/dsm.log)
local DEBUG_ON_LCD = false -- Interactive Information on LCD of Menu data from RX
local DSMLIB_PATH = "/SCRIPTS/TOOLS/DSMLIB/"
local dsmLib = assert(loadScript(DSMLIB_PATH.."DsmSetupLib.lua"), "Not-Found: DSMLIB/DsmSetupLib.lua")(DEBUG_ON,SIMULATION_ON)
local PHASE = dsmLib.PHASE
local LINE_TYPE = dsmLib.LINE_TYPE
local DISP_ATTR = dsmLib.DISP_ATTR
local DSM_Context = dsmLib.DSM_Context
local IS_EDGETX = false -- DEFAULT until Init changed it
local LCD_W_USABLE = LCD_W-10
-- X for Menu Lines
local LCD_X_LINE_MENU = 10
-- X offsets for (Title: [Value] debugInfo) lines
local LCD_X_LINE_TITLE = 10
local LCD_X_LINE_VALUE = 230
local LCD_X_LINE_DEBUG = 390
-- Line Height: make it smaller debugging info tp LCD (some space buttom)
local LCD_Y_LINE_HEIGHT = (DEBUG_ON_LCD and 23) or 27 -- if DEBUG 23 else 27
-- Y offsets
local LCD_Y_MENU_TITLE = 20
-- Y offet
local LCD_Y_LINE_FIRST = LCD_Y_MENU_TITLE + 30
local LCD_Y_LOWER_BUTTONS = LCD_Y_LINE_FIRST + 7 * LCD_Y_LINE_HEIGHT
local LCD_W_BUTTONS = 47
local LCD_H_BUTTONS = 25
local LCD_X_RIGHT_BUTTONS = LCD_W - LCD_W_BUTTONS - 5
local TEXT_SIZE = 0 -- NORMAL
local lastRefresh=0 -- Last time the screen was refreshed
local REFRESH_GUI_MS = 500/10 -- 500ms.. Screen Refresh Rate.. to not use unneded CPU time (in 10ms units to be compatible with getTime())
local originalValue = nil
local warningScreenON = true
------------------------------------------------------------------------------------------------------------
local function GUI_SwitchToRX()
-- Force to refresh DSM Info in MODEL (dsmLib pointing to the setup Script)
local dsmChannelInfo, description = dsmLib.CreateDSMPortChannelInfo()
dsmLib.ReleaseConnection()
dsmLib.LOG_close()
SIMULATION_ON = false
dsmLib = assert(loadScript(DSMLIB_PATH.."DsmFwPrgLib.lua"),"Not-Found: DSMLIB/DsmFwPrgLib.lua")(DEBUG_ON)
DSM_Context = dsmLib.DSM_Context
dsmLib.Init(toolName) -- Initialize Library
dsmLib.SetDSMChannelInfo(dsmChannelInfo, description) -- send the dsmChannelInfo to new instance library
dsmLib.StartConnection()
DSM_Context.Refresh_Display = true
end
local function GUI_SwitchToSIM()
dsmLib.ReleaseConnection()
dsmLib.LOG_close()
SIMULATION_ON = true
dsmLib = assert(loadScript(DSMLIB_PATH.."DsmFwPrgSIMLib.lua"), "Not-Found: DSMLIB/DsmFwPrgSIMLib.lua")(DEBUG_ON)
DSM_Context = dsmLib.DSM_Context
dsmLib.Init(toolName) -- Initialize Library
dsmLib.StartConnection()
DSM_Context.Refresh_Display = true
end
local function openTx_lcd_sizeText(s)
return string.len(s)*5
end
local function GUI_Diplay_Button(x,y,w,h,text,selected)
local attr = (selected) and INVERS or 0 -- INVERS if line Selected
if (TEXT_SIZE~=SMLSIZE) then
lcd.drawText(x+5,y+2, text, attr + TEXT_SIZE)
lcd.drawRectangle(x, y, w, h, LINE_COLOR)
else -- SMALL Screen
lcd.drawText(x,y, text, attr + TEXT_SIZE)
end
end
local function GUI_Display_Menu(menu)
local ctx = DSM_Context
local w= LCD_W_USABLE - LCD_W_BUTTONS - 10 -- usable Width for the Menu/Lines
-- Center Header
local tw = openTx_lcd_sizeText(menu.Text)
local x = w/2 - tw/2 -- Center of Screen - Center of Text
if (x < 0) then x=0 end -- in case text is too wide
local bold = BOLD
lcd.drawText(x,LCD_Y_MENU_TITLE,menu.Text,bold + TEXT_SIZE)
-- Back
if menu.BackId ~= 0 then
GUI_Diplay_Button(LCD_X_RIGHT_BUTTONS,LCD_Y_MENU_TITLE,LCD_W_BUTTONS,LCD_H_BUTTONS,"Back",ctx.SelLine == dsmLib.BACK_BUTTON)
end
-- Next ?
if menu.NextId ~= 0 then
GUI_Diplay_Button(LCD_X_RIGHT_BUTTONS,LCD_Y_LOWER_BUTTONS,LCD_W_BUTTONS,LCD_H_BUTTONS,"Next",ctx.SelLine == dsmLib.NEXT_BUTTON)
end
-- Prev?
if menu.PrevId ~= 0 then
GUI_Diplay_Button(0,LCD_Y_LOWER_BUTTONS,LCD_W_BUTTONS,LCD_H_BUTTONS,"Prev",ctx.SelLine == dsmLib.PREV_BUTTON)
end
-- Debug into LCD
if (DEBUG_ON_LCD) then lcd.drawText(0,LCD_Y_MENU_TITLE,dsmLib.phase2String(ctx.Phase),TEXT_SIZE + WARNING_COLOR) end -- Phase we are in
if (DEBUG_ON_LCD) then lcd.drawText(LCD_X_LINE_MENU,240,dsmLib.menu2String(menu),TEXT_SIZE + WARNING_COLOR) end -- Menu Info
end
local function GUI_Display_Line_Menu(x,y,w,h,line,selected)
local attr = (selected and INVERS) or 0 -- INVERS if line Selected
local bold = 0
local text = line.Text
if dsmLib.isSelectableLine(line) then
-- Menu Line
text = text .. " >"
else -- SubHeaders and plain text lines
bold = (dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR._BOLD) and BOLD) or 0
if dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR._RIGHT) then -- Right Align???
local tw = openTx_lcd_sizeText(line.Text)+4
x = LCD_X_LINE_VALUE - tw -- Right
elseif dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR._CENTER) then -- Center??
local tw = openTx_lcd_sizeText(line.Text)
x = x + (LCD_X_LINE_VALUE - LCD_X_LINE_MENU)/2 - tw/2 -- Center - 1/2 Text
end
if (x < 0) then x=0 end -- in case text is too wide
end
lcd.drawText(x,y, text, attr + bold + TEXT_SIZE)
end
------------------------------------------------------------------------------------------------------------
local function GUI_Display_Line_Value(lineNum, line, value, selected, editing)
local bold = 0
local y = LCD_Y_LINE_FIRST+(LCD_Y_LINE_HEIGHT*lineNum)
local x = LCD_X_LINE_TITLE
---------- NAME Part
local header = line.Text
-- ONLY do this for Flight Mode (Right Align or Centered)
if (dsmLib.isFlightModeLine(line)) then
-- Display Header + Value together
header = dsmLib.GetFlightModeValue(line)
-- Flight mode display attributes
bold = (dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR._BOLD) and BOLD) or 0
if dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR._RIGHT) then -- Right Align
local tw = openTx_lcd_sizeText(header)+4
x = LCD_X_LINE_VALUE - tw -- Right
elseif dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR._CENTER) then -- Centered
local tw = openTx_lcd_sizeText(header)
x = x + (LCD_X_LINE_VALUE - LCD_X_LINE_TITLE)/2 - tw/2 -- Center - 1/2 Text
end
if (x < 0) then x=0 end -- in case text is too wide
else
-- No Flight Mode, no effects here
header = header .. ":"
end
lcd.drawText(x, y, header, bold + TEXT_SIZE) -- display Line Header
--------- VALUE PART, Skip for Flight Mode since already show the value
if not dsmLib.isFlightModeLine(line) then
local attrib = 0
if selected then
attrib = INVERS
if editing then -- blink editing entry
attrib = attrib + BLINK
value = "[" .. value .. "]"
end
end
value = value .. " " .. (line.Format or "") -- Append % if needed
lcd.drawText(LCD_X_LINE_VALUE,y, value, attrib + TEXT_SIZE) -- display value
end
if (DEBUG_ON_LCD) then lcd.drawText(LCD_X_LINE_DEBUG,y, line.MinMaxDebug or "", TEXT_SIZE + WARNING_COLOR) end -- display debug
end
------------------------------------------------------------------------------------------------------------
local function GUI_ShowBitmap(x,y,imgData)
-- imgData format "bitmap.png|alt message"
local f = string.gmatch(imgData, '([^%|]+)') -- Iterator over values split by '|'
local imgName, imgMsg = f(), f()
if (LCD_W > 128) then
lcd.drawText(x, y, imgMsg or "", TEXT_SIZE) -- Alternate Image MSG
else
local f = string.gmatch(imgMsg, '([^%:]+)') -- Iterator over values split by ':'
local msg1,msg2 = f(), f()
lcd.drawText(x, y, (msg1 or "")..":", TEXT_SIZE) -- Alternate Image MSG
lcd.drawText(x, y+10, msg2 or "", TEXT_SIZE) -- Alternate Image MSG
end
-- NO IMAGES in Text B&W
--local imgPath = IMAGE_PATH .. (imgName or "")
--local bitmap = Bitmap.open(imgPath)
--if (bitmap~=nil) then
-- lcd.drawBitmap(bitmap, x,y+20)
--end
end
------------------------------------------------------------------------------------------------------------
local function GUI_Display()
local ctx = DSM_Context
lcd.clear()
local header = "DSM Fwrd Programming "
if (TEXT_SIZE==SMLSIZE) then -- Small Screen no title
header = ""
end
if ctx.Phase ~= PHASE.RX_VERSION then
header = header .. ctx.RX.Name.." v"..ctx.RX.Version
end
--Draw title
if (TEXT_SIZE~=SMLSIZE) then -- ignore tool title small size screens
lcd.drawFilledRectangle(0, 0, LCD_W, 20, TITLE_BGCOLOR)
lcd.drawText(5, 0, header, MENU_TITLE_COLOR + TEXT_SIZE)
else -- Small Screen
lcd.drawText(20, LCD_Y_LOWER_BUTTONS+1, header, TEXT_SIZE)
end
--Draw RX Menu
if ctx.Phase == PHASE.RX_VERSION then
if (ctx.isReset) then
lcd.drawText(LCD_X_LINE_TITLE,50,dsmLib.Get_Text(0x301), BLINK + TEXT_SIZE) -- Resetting
else
lcd.drawText(LCD_X_LINE_TITLE,50,dsmLib.Get_Text(0x300), BLINK + TEXT_SIZE) -- Waiting for RX Version
end
else
local menu = ctx.Menu
if menu.Text ~= nil then
GUI_Display_Menu(menu)
for i = 0, dsmLib.MAX_MENU_LINES do
local line = ctx.MenuLines[i]
if i == ctx.SelLine then
-- DEBUG: Display Selected Line info for ON SCREEN Debugging
if (DEBUG_ON_LCD) then lcd.drawText(LCD_X_LINE_TITLE,255,dsmLib.menuLine2String(line),TEXT_SIZE + WARNING_COLOR) end
end
if line ~= nil and line.Type ~= 0 then
if line.Type == LINE_TYPE.MENU then
-- Menu Line
GUI_Display_Line_Menu(LCD_X_LINE_MENU,LCD_Y_LINE_FIRST+(LCD_Y_LINE_HEIGHT*i), 350, LCD_Y_LINE_HEIGHT, line, i == ctx.SelLine)
else
-- list/value line
local value = line.Val
if line.Val ~= nil then
if dsmLib.isListLine(line) then -- for Lists of Strings, get the text
value = dsmLib.Get_List_Text(line.Val + line.TextStart) -- TextStart is the initial offset for text
local imgData = dsmLib.Get_List_Text_Img(line.Val + line.TextStart) -- Complentary IMAGE for this value to Display??
if (imgData and i == ctx.SelLine) then -- Optional Image and Msg for selected value
GUI_ShowBitmap(LCD_X_LINE_TITLE,LCD_Y_LINE_FIRST+LCD_Y_LINE_HEIGHT, imgData)
end
end
GUI_Display_Line_Value(i, line, value, i == ctx.SelLine, i == ctx.EditLine)
end
end -- if ~MENU
end -- if Line[i]~=nil
end -- for
end
end
end
-------------------------------------------------------------------------------------------------------------
local function GUI_RotEncVal(dir) -- return encoder speed to inc or dec values
local inc = 0
local Speed = getRotEncSpeed()
if Speed == ROTENC_MIDSPEED then
inc = (5 * dir)
elseif Speed == ROTENC_HIGHSPEED then
inc = (15 * dir)
else
inc = dir
end
return inc
end
------------------------------------------------------------------------------------------------------------
local function GUI_HandleEvent(event, touchState)
local ctx = DSM_Context
local menu = ctx.Menu
local menuLines = ctx.MenuLines
if event == EVT_VIRTUAL_EXIT then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_EXIT\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.Phase == PHASE.RX_VERSION then
dsmLib.ReleaseConnection()
else
if ctx.isEditing() then -- Editing a Line, need to restore original value
ctx.MenuLines[ctx.EditLine].Val = originalValue
dsmLib.Value_Write_Validate(menuLines[ctx.EditLine])
else
dsmLib.ChangePhase(PHASE.EXIT) -- Exit
end
end
return
end
if event == EVT_VIRTUAL_NEXT then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_NEXT\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.isEditing() then -- Editing a Line, need to inc the value
local line=ctx.MenuLines[ctx.EditLine]
dsmLib.Value_Add(line, GUI_RotEncVal(1))
else -- not editing, move selected line to NEXT
dsmLib.MoveSelectionLine(1)
end
return
end
if event == EVT_VIRTUAL_PREV then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_PREV\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.isEditing() then -- Editiing a line, need to dec the value
local line=ctx.MenuLines[ctx.EditLine]
dsmLib.Value_Add(line, GUI_RotEncVal(-1))
else -- not editing, move selected line to PREV
dsmLib.MoveSelectionLine(-1)
end
return
end
if event == EVT_VIRTUAL_ENTER_LONG then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_ENTER_LONG\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.isEditing() then
-- reset the value to default
dsmLib.Value_Default( menuLines[ctx.EditLine]) -- Update RX value as needed
end
return
end
if event == EVT_VIRTUAL_ENTER then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_ENTER\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.SelLine == dsmLib.BACK_BUTTON then -- Back
dsmLib.GotoMenu(menu.BackId,0x80)
elseif ctx.SelLine == dsmLib.NEXT_BUTTON then -- Next
dsmLib.GotoMenu(menu.NextId,0x82)
elseif ctx.SelLine == dsmLib.PREV_BUTTON then -- Prev
dsmLib.GotoMenu(menu.PrevId,0x81)
elseif menuLines[ctx.SelLine].ValId ~= 0 then
if menuLines[ctx.SelLine].Type == LINE_TYPE.MENU then -- Next menu exist
if (menuLines[ctx.SelLine].ValId==0xFFF1) then
-- SPECIAL Simulation menu to Simulator
GUI_SwitchToSIM()
elseif (menuLines[ctx.SelLine].ValId==0xFFF2) then
-- SPECIAL Simulation menu to go to RX
GUI_SwitchToRX()
else
dsmLib.GotoMenu(menuLines[ctx.SelLine].ValId, ctx.SelLine) -- ValId is the MenuId to navigate to
end
else
-- Editing a Line????
if ctx.isEditing() then
-- Change the Value and exit edit
dsmLib.Value_Write_Validate(menuLines[ctx.SelLine])
else
-- enter Edit the current line
ctx.EditLine = ctx.SelLine
originalValue = menuLines[ctx.SelLine].Val
dsmLib.ChangePhase(PHASE.VALUE_CHANGING_WAIT)
end
end
end
end
end
local function init_screen_pos()
-- osName in OpenTX is nil, otherwise is EDGETX
local ver, radio, maj, minor, rev, osname = getVersion()
if (osname==nil) then osname = "OpenTX" end -- OTX 2.3.14 and below returns nil
IS_EDGETX = string.sub(osname,1,1) =='E'
if LCD_W == 480 then -- TX16
-- use defaults in the script header
elseif LCD_W == 128 then --TX12 (128x64) -- Still needs some work on the vertical
DEBUG_ON_LCD = false -- no space for this
TEXT_SIZE = SMLSIZE
LCD_W_USABLE = 128
LCD_W_BUTTONS = 16
LCD_H_BUTTONS = 10
LCD_X_RIGHT_BUTTONS = 128 - LCD_W_BUTTONS - 3
LCD_X_LINE_MENU = 0
-- X offsets for (Title: [Value] debugInfo) lines
LCD_X_LINE_TITLE = 0
LCD_X_LINE_VALUE = 75
LCD_X_LINE_DEBUG = 110
LCD_Y_LINE_HEIGHT = 7
LCD_Y_MENU_TITLE = 0
LCD_Y_LINE_FIRST = LCD_Y_MENU_TITLE + 8
LCD_Y_LOWER_BUTTONS = LCD_Y_LINE_FIRST + (7 * LCD_Y_LINE_HEIGHT)
end
end
local function GUI_Warning(event)
lcd.clear()
local header = "DSM Forward Programming "..VERSION.." "
--Draw title
if (LCD_W > 128) then
lcd.drawFilledRectangle(0, 0, LCD_W, 17, TITLE_BGCOLOR)
lcd.drawText(5, 0, header, MENU_TITLE_COLOR + TEXT_SIZE)
lcd.drawText(100,20,"INFO", BOLD)
lcd.drawText(5,40,"DSM Forward programing shares TX Servo/Output settings", TEXT_SIZE)
lcd.drawText(5,60,"with the RX. Make sure you setup your plane first in ", TEXT_SIZE)
lcd.drawText(5,80,"the TX before your start programming your RX.", TEXT_SIZE)
lcd.drawText(5,100,"Wing & Tail type can be configured using this tool.", TEXT_SIZE)
lcd.drawText(5,150,"TX Servo settings are sent to the RX during 'Initial Setup'", TEXT_SIZE)
lcd.drawText(5,170,"as well as when using RX menu 'Relearn Servo Settings'", TEXT_SIZE)
lcd.drawText(5,200,"ALWAYS TEST Gyro reactions after this conditions before flying.", BOLD+TEXT_SIZE)
lcd.drawText(100,250," OK ", INVERS + BOLD + TEXT_SIZE)
else
lcd.drawText(0,15,"Make sure you setup your plane", TEXT_SIZE)
lcd.drawText(0,22,"first. Wing and Tail type.", TEXT_SIZE)
lcd.drawText(0,30,"TX Servo settings are sent to ", TEXT_SIZE)
lcd.drawText(0,37,"the RX during 'Initial Setup' and ", TEXT_SIZE)
lcd.drawText(0,45,"ALWAYS TEST Gyro reactions", TEXT_SIZE)
lcd.drawText(0,52,"before flying!!!", TEXT_SIZE)
lcd.drawText(10,0," OK ", INVERS + BOLD + TEXT_SIZE)
end
if event == EVT_VIRTUAL_EXIT or event == EVT_VIRTUAL_ENTER then
warningScreenON = false
end
return 0
end
------------------------------------------------------------------------------------------------------------
-- Init
local function DSM_Init()
init_screen_pos()
dsmLib.Init(toolName) -- Initialize Library
return dsmLib.StartConnection()
end
------------------------------------------------------------------------------------------------------------
-- Main
local function DSM_Run(event)
local ctx = DSM_Context
if event == nil then
error("Cannot be run as a model script!")
dsmLib.LOG_close()
return 2
end
if (warningScreenON) then
return GUI_Warning(event)
end
GUI_HandleEvent(event)
dsmLib.Send_Receive() -- Handle Send and Receive DSM Forward Programming Messages
local refreshInterval = REFRESH_GUI_MS
-- When using LCD BLINK attribute, we need faster refresh for BLINK to SHOW on LCD
if (ctx.EditLine or (ctx.Phase == PHASE.RX_VERSION)) then -- Editing or Requesting RX Version?
ctx.Refresh_Display=true
refreshInterval = 20 -- 200ms
end
if (not IS_EDGETX) then -- OPENTX NEEDS REFRESH ON EVERY CYCLE
GUI_Display()
-- Refresh display only if needed and no faster than 500ms, utilize more CPU to speedup DSM communications
elseif (ctx.Refresh_Display and (getTime()-lastRefresh) > refreshInterval) then --300ms from last refresh
GUI_Display()
ctx.Refresh_Display=false
lastRefresh=getTime()
end
if ctx.Phase == PHASE.EXIT_DONE then
dsmLib.LOG_close()
return 2
else
return 0
end
end
return { init=DSM_Init, run=DSM_Run }

View File

@@ -0,0 +1,708 @@
local toolName = "TNS|DSM Forward Prog v0.54 (Color+Touch) |TNE"
local VERSION = "v0.54"
---- #########################################################################
---- # #
---- # Copyright (C) OpenTX #
-----# #
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
---- # #
---- # 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. #
---- # #
---- # This program is distributed in the hope that it will be useful #
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
---- # GNU General Public License for more details. #
---- # #
---- #########################################################################
------------------------------------------------------------------------------
-- This script library is a rewrite of the original DSM forward programming Lua
-- Script. The goal is to make it easier to understand, mantain, and to
-- separate the GUI from the DSM Forward programming engine/logic
-- in this way, GUIs can evolve independent. OpenTX Gui, EdgeTx GUI, Small Radios, etc.
-- Code is based on the code/work by: Pascal Langer (Author of the Multi-Module)
-- Rewrite/Enhancements By: Francisco Arzu
------------------------------------------------------------------------------
local SIMULATION_ON = false -- FALSE:don't show simulation memu (DEFAULT), TRUE: show simulation menu
local DEBUG_ON = 1 -- 0=NO DEBUG, 1=HIGH LEVEL 2=LOW LEVEL (Debug logged into the /LOGS/dsm.log)
local DEBUG_ON_LCD = false -- Interactive Information on LCD of Menu data from RX
local USE_SPECKTRUM_COLORS = true -- true: Use spectrum colors, false: use theme colors (default on OpenTX)
local DSMLIB_PATH = "/SCRIPTS/TOOLS/DSMLIB/"
local IMAGE_PATH = DSMLIB_PATH .. "img/"
local dsmLib = assert(loadScript(DSMLIB_PATH.."DsmSetupLib.lua"), "Not-Found: DSMLIB/DsmSetupLib.lua")(DEBUG_ON,SIMULATION_ON)
local PHASE = dsmLib.PHASE
local LINE_TYPE = dsmLib.LINE_TYPE
local DISP_ATTR = dsmLib.DISP_ATTR
local DSM_Context = dsmLib.DSM_Context
local lastRefresh=0 -- Last time the screen was refreshed
local REFRESH_GUI_MS = 300/10 -- 300ms.. Screen Refresh Rate.. to not waste CPU time (in 10ms units to be compatible with getTime())
local originalValue = nil
local touchButtonArea = {}
local EDIT_BUTTON = { DEFAULT=1001, DEC_10=1002, DEC_1=1003, INC_1=1004, INC_10=5, OK=1006, ESC=1007 }
local IS_EDGETX = false -- DEFAULT until Init changed it
local LCD_Y_MENU_TITLE = 20
local LCD_W_MENU_TITLE = LCD_W-100
local LCD_X_LINE_MENU = 30
local LCD_W_LINE_MENU = 350
local LCD_X_LINE_TITLE = 30
local LCD_X_LINE_VALUE = 230
local LCD_X_LINE_DEBUG = 390
local LCD_Y_LINE_START = LCD_Y_MENU_TITLE + 30
local LCD_Y_LINE_HEIGHT = (DEBUG_ON_LCD and 23) or 27 -- if DEBUG 23 else 27
local LCD_Y_LOWER_BUTTONS = LCD_Y_LINE_START + 3 + (7 * LCD_Y_LINE_HEIGHT)
-- TOOL BG COLOR
local LCD_TOOL_BGCOLOR = TEXT_BGCOLOR
-- TOOL HEADER
local LCD_TOOL_HDR_COLOR = MENU_TITLE_COLOR
local LCD_TOOL_HDR_BGCOLOR = TITLE_BGCOLOR
-- MENU HEADER
local LCD_MENU_COLOR = MENU_TITLE_COLOR
local LCD_MENU_BGCOLOR = MENU_TITLE_BGCOLOR
-- LINE SELECTED
local LCD_SELECTED_COLOR = TEXT_INVERTED_COLOR
local LCD_SELECTED_BGCOLOR = TEXT_INVERTED_BGCOLOR
local LCD_EDIT_BGCOLOR = MENU_TITLE_BGCOLOR -- WARNING_COLOR
-- NORMAL TEXT
local LCD_NORMAL_COLOR = TEXT_COLOR
local LCD_DISABLE_COLOR = TEXT_DISABLE_COLOR
local LCD_DEBUG_COLOR = LINE_COLOR
-- NORMAL BOX FRAME COLOR
local LCD_BOX_COLOR = TEXT_DISABLE_COLOR
local warningScreenON = true
--------------------- lcd.sizeText replacement -------------------------------------------------
-- EdgeTx dont have lcd.sizeText, so we do an equivalent one using the string length and 5px per character
local function my_lcd_sizeText(s)
-- return: If IS_EDGETX then lcd.sizeText() else string.len()
return (IS_EDGETX and lcd.sizeText(s)) or (string.len(s)*10)
end
local function GUI_SwitchToRX()
-- Force to refresh DSM Info in MODEL (dsmLib pointing to the setup Script)
local dsmChannelInfo, description = dsmLib.CreateDSMPortChannelInfo()
dsmLib.ReleaseConnection()
dsmLib.LOG_close()
SIMULATION_ON = false
dsmLib = assert(loadScript(DSMLIB_PATH.."DsmFwPrgLib.lua"),"Not-Found: DSMLIB/DsmFwPrgLib.lua")(DEBUG_ON)
DSM_Context = dsmLib.DSM_Context
dsmLib.Init(toolName) -- Initialize Library
dsmLib.SetDSMChannelInfo(dsmChannelInfo, description) -- send the dsmChannelInfo to new instance library
dsmLib.StartConnection()
DSM_Context.Refresh_Display = true
end
local function GUI_SwitchToSIM()
dsmLib.ReleaseConnection()
dsmLib.LOG_close()
SIMULATION_ON = true
dsmLib = assert(loadScript(DSMLIB_PATH.."DsmFwPrgSIMLib.lua"), "Not-Found: DSMLIB/DsmFwPrgSIMLib.lua")(DEBUG_ON)
DSM_Context = dsmLib.DSM_Context
dsmLib.Init(toolName) -- Initialize Library
dsmLib.StartConnection()
DSM_Context.Refresh_Display = true
end
--------------------- Toucch Button Helpers ------------------------------------------------------------
local function GUI_addTouchButton(x,y,w,h,line)
-- Add new button info to end of the array
touchButtonArea[#touchButtonArea+1] = {x=x, y=y, w=w, h=h, line=line}
end
local function GUI_getTouchButton(x,y)
for i = 1, #touchButtonArea do
local button = touchButtonArea[i]
-- is the coordinate inside the button area??
if (x >= button.x and x <= (button.x+button.w) and y >= button.y and (y <= button.y+button.h)) then
return button.line
end
end
return nil
end
local function GUI_clearTouchButtons()
touchButtonArea = {}
end
---------- Return Color to display Menu Lines ----------------------------------------------------------------
local function GUI_GetTextColor(lineNum)
local ctx = DSM_Context
local txtColor = LCD_NORMAL_COLOR
-- Gray Out any other line except the one been edited
if (ctx.isEditing() and ctx.EditLine~=lineNum) then txtColor=LCD_DISABLE_COLOR end
return txtColor
end
local function GUI_GetFrameColor(lineNum) -- Frame Color for Value/Menu Boxes
local ctx = DSM_Context
local txtColor = LCD_BOX_COLOR
-- Gray Out any other line except the one been edited
if (ctx.EditLine~=lineNum) then txtColor=LCD_DISABLE_COLOR end
return txtColor
end
--------------------------------------------------------------------------------------------------------
-- Display Text inside a Rectangle. Inv: true means solid rectangle, false=only perimeter
local function GUI_Display_Boxed_Text(lineNum,x,y,w,h,text,inv, isNumber)
local ctx = DSM_Context
local txtColor = GUI_GetTextColor(lineNum)
local frameColor = GUI_GetFrameColor(lineNum)
-- If editing this lineNum, chose EDIT Color, else SELECTED Color
local selectedBGColor = (ctx.EditLine==lineNum and LCD_EDIT_BGCOLOR) or LCD_SELECTED_BGCOLOR
if (inv) then
txtColor = LCD_SELECTED_COLOR
lcd.drawFilledRectangle(x-5, y-2, w, h, selectedBGColor)
else
lcd.drawRectangle(x-5, y-2, w, h, frameColor)
end
if (isNumber) then
lcd.drawNumber(x+w-10 , y, text, txtColor + RIGHT)
else
lcd.drawText(x , y, text, txtColor)
end
end
------ Display Pre/Next/Back buttons
local function GUI_Diplay_Button(x,y,w,h,text,selected)
GUI_Display_Boxed_Text(-1,x,y,w,h,text,selected, false)
end
------ Display MENU type of lines (Navigation, SubHeaders, and plain text comments)
local function GUI_Display_Line_Menu(lineNum,line,selected)
-- Menu Lines can be navidation to other Menus (if Selectable)
-- Or SubHeaders or Messages
local txtColor = GUI_GetTextColor(lineNum)
local y = LCD_Y_LINE_START+(LCD_Y_LINE_HEIGHT*lineNum)
local x = LCD_X_LINE_MENU
if dsmLib.isSelectableLine(line) then -- Draw Selectable Menus in Boxes
GUI_Display_Boxed_Text(lineNum,x, y, LCD_W_LINE_MENU, LCD_Y_LINE_HEIGHT, line.Text,selected, false)
GUI_addTouchButton(x, y, LCD_W_LINE_MENU, LCD_Y_LINE_HEIGHT,lineNum)
else
-- Non Selectable Menu Lines, plain text
-- Can be use for sub headers or just regular text lines (like warnings)
local bold = (dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR._BOLD) and BOLD) or 0
if dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR._RIGHT) then -- Right Align???
local tw = my_lcd_sizeText(line.Text)+4
x = LCD_X_LINE_VALUE - tw -- Right
elseif dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR._CENTER) then -- Center??
local tw = my_lcd_sizeText(line.Text)
x = x + (LCD_X_LINE_VALUE - LCD_X_LINE_MENU)/2 - tw/2 -- Center - 1/2 Text
end
lcd.drawText(x, y, line.Text, txtColor + bold)
end
end
------ Display NAME : VALUES type of lines
local function GUI_Display_Line_Value(lineNum, line, value, selected, editing)
-- This Displays Name and Value Pairs
local txtColor = GUI_GetTextColor(lineNum)
local bold = 0
local y = LCD_Y_LINE_START+(LCD_Y_LINE_HEIGHT*lineNum)
local x = LCD_X_LINE_TITLE
---------- NAME Part
local header = line.Text
-- ONLY do this for Flight Mode (Right Align or Centered)
if (dsmLib.isFlightModeLine(line)) then
-- Display Header + Value together
header = dsmLib.GetFlightModeValue(line)
-- Bold Text???
bold = (dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR._BOLD) and BOLD) or 0
if dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR._RIGHT) then -- Right Align
local tw = my_lcd_sizeText(header)+4
x = LCD_X_LINE_VALUE - tw -- Right
elseif dsmLib.isDisplayAttr(line.TextAttr,DISP_ATTR._CENTER) then -- Centered
local tw = my_lcd_sizeText(header)
x = x + (LCD_X_LINE_VALUE - LCD_X_LINE_TITLE)/2 - tw/2 -- Center - 1/2 Text
end
else
-- No Flight Mode, no effects here
header = header .. ":"
end
lcd.drawText(x, y, header, txtColor + bold) -- display Line Header
--------- VALUE PART, Skip for Flight Mode since already show the value
if not dsmLib.isFlightModeLine(line) then
if dsmLib.isSelectableLine(line) then
--if (editing) then -- Any Special color/effect when editing??
-- value = "["..value .. "]"
--end
-- Can select/edit value, Box it
local tw = math.max(my_lcd_sizeText(value)+10,45) -- Width of the Text in the lcd
GUI_Display_Boxed_Text(lineNum,LCD_X_LINE_VALUE,y,tw,LCD_Y_LINE_HEIGHT,value,selected, not dsmLib.isListLine(line))
GUI_addTouchButton(LCD_X_LINE_VALUE,y,tw,LCD_Y_LINE_HEIGHT,lineNum)
lcd.drawText(LCD_X_LINE_VALUE+tw+5, y, (line.Format or ""), txtColor + bold)
else -- Not Editable, Plain Text
lcd.drawText(LCD_X_LINE_VALUE, y, value, txtColor)
end
end
-- Debug info for line Value RANGE when Debug on LCD
if (DEBUG_ON_LCD) then lcd.drawText(LCD_X_LINE_DEBUG, y, line.MinMaxDebug or "", SMLSIZE + LCD_DEBUG_COLOR) end -- display debug Min/Max
end
local function GUI_Display_Menu(menu)
local ctx = DSM_Context
local w= LCD_W_MENU_TITLE
-- Center Header
local tw = my_lcd_sizeText(menu.Text)
local x = w/2 - tw/2 -- Center of Screen - Center of Text
lcd.drawFilledRectangle(0, LCD_Y_MENU_TITLE-2, w, LCD_Y_LINE_HEIGHT-2, LCD_MENU_BGCOLOR)
lcd.drawText(x,LCD_Y_MENU_TITLE,menu.Text, LCD_MENU_COLOR + BOLD)
-- Back Button
if menu.BackId ~= 0 then
GUI_Diplay_Button(437-5,LCD_Y_MENU_TITLE+3,47,LCD_Y_LINE_HEIGHT,"Back",ctx.SelLine == dsmLib.BACK_BUTTON)
GUI_addTouchButton(437-5,LCD_Y_MENU_TITLE+3,47,LCD_Y_LINE_HEIGHT,dsmLib.BACK_BUTTON)
end
-- Next Button
if menu.NextId ~= 0 then
GUI_Diplay_Button(437-5,LCD_Y_LOWER_BUTTONS,47,LCD_Y_LINE_HEIGHT,"Next",ctx.SelLine == dsmLib.NEXT_BUTTON)
GUI_addTouchButton(437-5,LCD_Y_LOWER_BUTTONS,47,LCD_Y_LINE_HEIGHT,dsmLib.NEXT_BUTTON)
end
-- Prev Button
if menu.PrevId ~= 0 then
GUI_Diplay_Button(10,LCD_Y_LOWER_BUTTONS,47,LCD_Y_LINE_HEIGHT,"Prev",ctx.SelLine == dsmLib.PREV_BUTTON)
GUI_addTouchButton(10,LCD_Y_LOWER_BUTTONS,47,LCD_Y_LINE_HEIGHT,dsmLib.PREV_BUTTON)
end
-- Debug on LCD, Show the menu Indo and Phase we are on
if (DEBUG_ON_LCD) then lcd.drawText(0,LCD_Y_MENU_TITLE,dsmLib.phase2String(ctx.Phase),SMLSIZE+LCD_DEBUG_COLOR) end -- Phase we are in
if (DEBUG_ON_LCD) then lcd.drawText(0,240,dsmLib.menu2String(menu),SMLSIZE+LCD_DEBUG_COLOR) end -- Menu Info
end
------------------------------------------------------------------------------------------------------------
-- Display the EDIT mode buttons when editing a value
local function GUI_Display_Edit_Buttons(line)
GUI_clearTouchButtons() -- Only this buttons can be touched
local x = 15 -- Inittial X position
local w = 55 -- Width of the buttons
local showPrev = line.Val > line.Min
local showNext = line.Val < line.Max
GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,"ESC",true)
GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.ESC)
x=x+w+10
GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," Def",true)
GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.DEFAULT)
x=x+w+10
if (not dsmLib.isListLine(line)) then
GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," << ",showPrev)
GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.DEC_10)
end
x=x+w+10
GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," <",showPrev)
GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.DEC_1)
x=x+w+10
GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," >",showNext)
GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.INC_1)
x=x+w+10
if (not dsmLib.isListLine(line)) then
GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," >>",showNext)
GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.INC_10)
end
x=x+w+10
GUI_Diplay_Button(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT," OK",true)
GUI_addTouchButton(x,LCD_Y_LOWER_BUTTONS,w,LCD_Y_LINE_HEIGHT,EDIT_BUTTON.OK)
end
local function GUI_ShowBitmap(x,y,imgData)
-- imgData format "bitmap.png|alt message"
local f = string.gmatch(imgData, '([^%|]+)') -- Iterator over values split by '|'
local imgName, imgMsg = f(), f()
lcd.drawText(x, y, imgMsg or "") -- Alternate Image MSG
local imgPath = IMAGE_PATH .. (imgName or "")
local bitmap = Bitmap.open(imgPath)
if (bitmap~=nil) then
lcd.drawBitmap(bitmap, x,y+20)
end
end
------------------------------------------------------------------------------------------------------------
local function GUI_Display()
local ctx = DSM_Context
lcd.clear(LCD_TOOL_BGCOLOR)
GUI_clearTouchButtons()
if LCD_W == 480 then
local header = "DSM Forward Programming "..VERSION.." "
if ctx.Phase ~= PHASE.RX_VERSION then
header = header .. "RX "..ctx.RX.Name.." v"..ctx.RX.Version
end
--Draw title
lcd.drawFilledRectangle(0, 0, LCD_W, 17, LCD_TOOL_HDR_BGCOLOR)
lcd.drawText(5, 0, header, LCD_TOOL_HDR_COLOR + SMLSIZE)
--Draw RX Menu
if ctx.Phase == PHASE.RX_VERSION then
if (ctx.isReset) then
lcd.drawText(LCD_X_LINE_TITLE,100,dsmLib.Get_Text(0x301), BLINK) -- Waiting for Restart
else
lcd.drawText(LCD_X_LINE_TITLE,100,dsmLib.Get_Text(0x300), BLINK) -- Waiting for RX
end
else
local menu = ctx.Menu
if menu.Text ~= nil then
GUI_Display_Menu(menu)
for i = 0, dsmLib.MAX_MENU_LINES do
local line = ctx.MenuLines[i]
if i == ctx.SelLine then
-- DEBUG: Display Selected Line info for ON SCREEN Debugging
if (DEBUG_ON_LCD) then lcd.drawText(0,255,dsmLib.menuLine2String(line),SMLSIZE + LCD_DEBUG_COLOR) end
end
if line ~= nil and line.Type ~= 0 then
if line.Type == LINE_TYPE.MENU then
GUI_Display_Line_Menu(i, line, i == ctx.SelLine)
else
if line.Val ~= nil then
local value = line.Val
if dsmLib.isListLine(line) then -- for Lists of Strings, get the text
value = dsmLib.Get_List_Text(line.Val + line.TextStart) -- TextStart is the initial offset for text
local imgData = dsmLib.Get_List_Text_Img(line.Val + line.TextStart) -- Complentary IMAGE for this value to Display??
if (imgData and i == ctx.SelLine) then -- Optional Image and Msg for selected value
GUI_ShowBitmap(LCD_X_LINE_TITLE,LCD_Y_LINE_START, imgData)
end
end
GUI_Display_Line_Value(i, line, value, i == ctx.SelLine, i == ctx.EditLine)
end
end -- if ~MENU
end -- if Line[i]~=nil
end -- for
if IS_EDGETX and ctx.isEditing() then
-- Display Touch button for Editing values
GUI_Display_Edit_Buttons(ctx.MenuLines[ctx.EditLine])
end
end
end
else
-- Different Resolution.. Maybe just adjusting some of the constants will work, adjust it in DSM_Init??
-- LCD_X_LINE_TITLE, LCD_Y_LINE_START, etc
lcd.drawText(LCD_X_LINE_TITLE,100,"Only supported in Color Radios of 480 resolution", BLINK)
end
end
-------------------------------------------------------------------------------------------------------------
local function GUI_RotEncVal(dir) -- return encoder speed to inc or dec values
local inc = 0
local Speed = getRotEncSpeed()
if Speed == ROTENC_MIDSPEED then inc = (5 * dir)
elseif Speed == ROTENC_HIGHSPEED then inc = (15 * dir)
else inc = dir end
return inc
end
------------------------------------------------------------------------------------
-- Translate Tap/Touch of EDIT buttons to equivalent Key events
local function GUI_Translate_Edit_Buttons(button)
local event = EVT_TOUCH_TAP
local editInc = nil
if (button==EDIT_BUTTON.ESC) then -- ESC
event = EVT_VIRTUAL_EXIT
elseif (button==EDIT_BUTTON.DEFAULT) then -- Default
event = EVT_VIRTUAL_ENTER_LONG
elseif (button==EDIT_BUTTON.DEC_10) then -- -10
event = EVT_VIRTUAL_PREV
editInc = -10
elseif (button==EDIT_BUTTON.DEC_1) then -- -1
event = EVT_VIRTUAL_PREV
editInc = -1
elseif (button==EDIT_BUTTON.INC_1) then -- +1
event = EVT_VIRTUAL_NEXT
editInc = 1
elseif (button==EDIT_BUTTON.INC_10) then -- + 10
event = EVT_VIRTUAL_NEXT
editInc = 10
elseif (button==EDIT_BUTTON.OK) then -- OK
event = EVT_VIRTUAL_ENTER
else
end
return event, editInc
end
------------------------------------------------------------------------------------------------------------
-- Handle Events comming from the GUI
local function GUI_HandleEvent(event, touchState)
local ctx = DSM_Context
local menu = ctx.Menu
local menuLines = ctx.MenuLines
local editInc = nil
if (IS_EDGETX) then
if (event == EVT_TOUCH_TAP and ctx.isEditing()) then -- Touch and Editing
local button = GUI_getTouchButton(touchState.x, touchState.y)
if (button) then
event, editInc = GUI_Translate_Edit_Buttons(button)
end
end
if (event == EVT_TOUCH_TAP or event == EVT_TOUCH_FIRST) and not ctx.isEditing() then -- Touch and NOT editing
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_TOUCH_TAP %d,%d\n",dsmLib.phase2String(ctx.Phase),touchState.x, touchState.y) end
local button = GUI_getTouchButton(touchState.x, touchState.y)
if button then
-- Found a valid line
ctx.SelLine = button
ctx.Refresh_Display=true
if event == EVT_TOUCH_TAP then -- EVT_TOUCH_FIRST only move focus
event = EVT_VIRTUAL_ENTER
end
end
end
end -- IS_EDGETX
if event == EVT_VIRTUAL_EXIT then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_EXIT\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.Phase == PHASE.RX_VERSION then
dsmLib.ReleaseConnection() -- Just Exit the Script
else
if ctx.isEditing() then -- Editing a Line, need to restore original value
local line = ctx.MenuLines[ctx.EditLine]
line.Val = originalValue
dsmLib.Value_Write_Validate(line)
else
dsmLib.ChangePhase(PHASE.EXIT)
end
end
return
end
if event == EVT_VIRTUAL_NEXT then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_NEXT\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.isEditing() then -- Editing a Line, need to inc the value
local line=ctx.MenuLines[ctx.EditLine]
dsmLib.Value_Add(line, editInc or GUI_RotEncVal(1))
else -- not editing, move selected line to NEXT
dsmLib.MoveSelectionLine(1)
end
return
end
if event == EVT_VIRTUAL_PREV then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_PREV\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.isEditing() then -- Editiing a line, need to dec the value
local line=ctx.MenuLines[ctx.EditLine]
dsmLib.Value_Add(line, editInc or GUI_RotEncVal(-1))
else -- not editing, move selected line to PREV
dsmLib.MoveSelectionLine(-1)
end
return
end
if event == EVT_VIRTUAL_ENTER_LONG then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_ENTER_LONG\n",dsmLib.phase2String(ctx.Phase)) end
if ctx.isEditing() then
-- reset the value to default
dsmLib.Value_Default(menuLines[ctx.EditLine]) -- Update value in RX if needed
end
return
end
if event == EVT_VIRTUAL_ENTER then
ctx.Refresh_Display=true
if (DEBUG_ON) then dsmLib.LOG_write("%s: EVT_VIRTUAL_ENTER, SelLine=%d\n",dsmLib.phase2String(ctx.Phase), ctx.SelLine) end
if ctx.SelLine == dsmLib.BACK_BUTTON then -- Back
dsmLib.GotoMenu(menu.BackId,0x80)
elseif ctx.SelLine == dsmLib.NEXT_BUTTON then -- Next
dsmLib.GotoMenu(menu.NextId,0x82)
elseif ctx.SelLine == dsmLib.PREV_BUTTON then -- Prev
dsmLib.GotoMenu(menu.PrevId,0x81)
elseif menuLines[ctx.SelLine].ValId ~= 0 then -- Menu or Value
if menuLines[ctx.SelLine].Type == LINE_TYPE.MENU then -- Navigate to Menu
if (menuLines[ctx.SelLine].ValId==0xFFF1) then
-- SPECIAL Simulation menu to Simulator
GUI_SwitchToSIM()
elseif (menuLines[ctx.SelLine].ValId==0xFFF2) then
-- SPECIAL Simulation menu to go to RX
GUI_SwitchToRX()
else
dsmLib.GotoMenu(menuLines[ctx.SelLine].ValId, ctx.SelLine) -- ValId is the MenuId to navigate to
end
else -- Enter on a Value
if ctx.isEditing() then -- already editing a Line????
dsmLib.Value_Write_Validate(menuLines[ctx.SelLine])
else -- Edit the current value
ctx.EditLine = ctx.SelLine
originalValue = menuLines[ctx.SelLine].Val
dsmLib.ChangePhase(PHASE.VALUE_CHANGING_WAIT)
end
end
end
end
end
local function init_colors()
-- osName in OpenTX is nil, otherwise is EDGETX
local ver, radio, maj, minor, rev, osname = getVersion()
if (osname==nil) then osname = "OpenTX" end -- OTX 2.3.14 and below returns nil
IS_EDGETX = string.sub(osname,1,1) == 'E'
if (IS_EDGETX and USE_SPECKTRUM_COLORS) then
-- SPECKTRUM COLORS (only works on EDGETX)
LCD_TOOL_BGCOLOR = LIGHTWHITE
-- TOOL HEADER
LCD_TOOL_HDR_COLOR = WHITE
LCD_TOOL_HDR_BGCOLOR = DARKBLUE
-- MENU HEADER
LCD_MENU_COLOR = WHITE
LCD_MENU_BGCOLOR = DARKGREY
-- LINE SELECTED
LCD_SELECTED_COLOR = WHITE
LCD_SELECTED_BGCOLOR = ORANGE
LCD_EDIT_BGCOLOR = RED
-- NORMAL TEXT
LCD_NORMAL_COLOR = BLACK
LCD_DISABLE_COLOR = LIGHTGREY
LCD_DEBUG_COLOR = BLUE
-- NORMAL BOX FRAME COLOR
LCD_BOX_COLOR = LIGHTGREY
end
end
local function GUI_Warning(event,touchState)
lcd.clear(LCD_TOOL_BGCOLOR)
local header = "DSM Forward Programming "..VERSION.." "
--Draw title
lcd.drawFilledRectangle(0, 0, LCD_W, 17, LCD_TOOL_HDR_BGCOLOR)
lcd.drawText(5, 0, header, LCD_TOOL_HDR_COLOR + SMLSIZE)
lcd.drawText(100,20,"INFO", BOLD)
lcd.drawText(5,40,"DSM Forward programing shares TX Servo/Output settings", 0)
lcd.drawText(5,60,"with the RX. Make sure you setup your plane first in ", 0)
lcd.drawText(5,80,"the TX before your start Fwrd programming your RX.", 0)
lcd.drawText(5,100,"Wing & Tail type can be configured using this tool.", 0)
lcd.drawText(5,150,"TX Gyro Servo settings are sent to the RX during 'Initial Setup'", 0)
lcd.drawText(5,170,"as well as when using RX 'Relearn Servo Settings'", 0)
lcd.drawText(5,200,"ALWAYS TEST Gyro reactions after this conditions before flying.", BOLD)
lcd.drawText(100,250," OK ", INVERS + BOLD)
if event == EVT_VIRTUAL_EXIT or event == EVT_VIRTUAL_ENTER or event == EVT_TOUCH_TAP then
warningScreenON = false
end
return 0
end
------------------------------------------------------------------------------------------------------------
-- Init
local function DSM_Init()
init_colors()
dsmLib.Init(toolName) -- Initialize Library
return dsmLib.StartConnection()
end
------------------------------------------------------------------------------------------------------------
-- Main
local function DSM_Run(event,touchState)
local ctx = DSM_Context
if event == nil then
error("Cannot be run as a model script!")
dsmLib.LOG_close()
return 2
end
if (warningScreenON) then
return GUI_Warning(event,touchState)
end
GUI_HandleEvent(event,touchState)
dsmLib.Send_Receive() -- Handle Send and Receive DSM Forward Programming Messages
local refreshInterval = REFRESH_GUI_MS
-- When using LCD BLINK attribute, we need faster refresh for BLINK to SHOW on LCD
if (ctx.Phase == PHASE.RX_VERSION) then -- Requesting RX Message Version usea BLINK?
ctx.Refresh_Display=true
refreshInterval = 20 -- 200ms
end
if (not IS_EDGETX) then -- OPENTX NEEDS REFRESH ON EVERY CYCLE
GUI_Display()
-- Refresh display only if needed and no faster than 300ms, utilize more CPU to speedup DSM communications
elseif (ctx.Refresh_Display and (getTime()-lastRefresh) > refreshInterval) then --300ms from last refresh
GUI_Display()
ctx.Refresh_Display=false
lastRefresh=getTime()
end
if ctx.Phase == PHASE.EXIT_DONE then
dsmLib.LOG_close()
return 2
else
return 0
end
end
return { init=DSM_Init, run=DSM_Run }

View File

@@ -0,0 +1,932 @@
local toolName = "TNS|DSM Frwd Prog v0.54-beta (MIN)|TNE"
--local ModelParam = ...
---- #########################################################################
---- # #
---- # Copyright (C) OpenTX #
-----# #
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
---- # #
---- # 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. #
---- # #
---- # This program is distributed in the hope that it will be useful #
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
---- # GNU General Public License for more details. #
---- # #
---- #########################################################################
--###############################################################################
-- Multi buffer for DSM description
-- Multi_Buffer[0..2]=="DSM" -> Lua script is running
-- Multi_Buffer[3]==0x70+len -> TX to RX data ready to be sent
-- Multi_Buffer[4..9]=6 bytes of TX to RX data
-- Multi_Buffer[10..25]=16 bytes of RX to TX data
--
-- To start operation:
-- Write 0x00 at address 3
-- Write 0x00 at address 10
-- Write "DSM" at address 0..2
--###############################################################################
local VERSION = "v0.54-MIN"
local LANGUAGE = "en"
local DSMLIB_PATH = "/SCRIPTS/TOOLS/DSMLIB/"
local LOG_FILE = "/LOGS/dsm_min_log.txt"
local MSG_FILE = DSMLIB_PATH.."msg_fwdp_" .. LANGUAGE .. ".txt"
local MSG_FILE_MIN = DSMLIB_PATH.."MIN_msg_fwdp_" .. LANGUAGE .. ".txt"
-- Phase
local PH_RX_VER, PH_TITLE, PH_TX_INFO, PH_LINES, PH_VALUES = 1, 2, 3, 4, 5
local PH_VAL_CHANGING, PH_VAL_WAIT, PH_VAL_CHNG_END = 6, 7, 8
local PH_WAIT_CMD, PH_EXIT_REQ, PH_EXIT_DONE = 9, 10, 11
-- Line Types
local LT_MENU = 0x1C
local LT_LIST, LT_LIST_VALIDATE, LT_LIST_TOG = 0x6C, 0x0C, 0x4C
local LT_VALUE_NOCHANGING = 0x60
local LT_VALUE_PERCENT, LT_VALUE_DEGREES = 0xC0, 0xE0
local Phase = PH_RX_VER
local Waiting_RX = 0
local Text = {}
local List_Text = {}
local List_Text_Img = {}
local Flight_Mode = { [0] = "Flight Mode" }
local RxName = {}
local InactivityTime = 0
local Value_Change_Step = 0
local TX_Info_Step = 0
local TX_Info_Type = 0
local originalValue = 0
local ctx = {
SelLine = 0, -- Current Selected Line
EditLine = nil, -- Current Editing Line
CurLine = -1, -- Current Line Requested/Parsed via h message protocol
isReset = false -- false when starting from scracts, true when starting from Reset
}
local MODEL = {
modelName = "", -- The name of the model comming from OTX/ETX
modelOutputChannel = {}, -- Output information from OTX/ETX
AirWingTailDesc = "",
--TX_CH_TEXT = {},
--PORT_TEXT = {},
DSM_ChannelInfo = {} -- Data Created by DSM Configuration Script
}
local Menu = { MenuId = 0, Text = "", TextId = 0, PrevId = 0, NextId = 0, BackId = 0 }
local MenuLines = {}
local RX = { Name = "", Version = "" }
local logFile = nil
local logCount = 0
local LCD_X_LINE_TITLE = 0
local LCD_X_LINE_VALUE = 75
local LCD_W_BUTTONS = 19
local LCD_H_BUTTONS = 10
local LCD_X_MAX = 128
local LCD_X_RIGHT_BUTTONS = LCD_X_MAX - LCD_W_BUTTONS - 1
local LCD_Y_LINE_HEIGHT = 7
local LCD_Y_LOWER_BUTTONS = (8 * LCD_Y_LINE_HEIGHT) + 2
local TEXT_ATTR = SMLSIZE
local function LOG_open()
logFile = io.open(LOG_FILE, "w") -- Truncate Log File
end
local function LOG_write(...)
if (logFile == nil) then LOG_open() end
local str = string.format(...)
io.write(logFile, str)
end
local function LOG_close()
if (logFile ~= nil) then io.close(logFile) end
end
---------------- DSM Values <-> Int16 Manipulation --------------------------------------------------------
local function int16_LSB(number) -- Less Significat byte
local r, x = bit32.band(number, 0xFF)
return r
end
local function int16_MSB(number) -- Most signifcant byte
return bit32.rshift(number, 8)
end
local function Dsm_to_Int16(lsb, msb) -- Componse an Int16 value
return bit32.lshift(msb, 8) + lsb
end
local function Dsm_to_SInt16(lsb, msb) -- Componse a SIGNED Int16 value
local value = bit32.lshift(msb, 8) + lsb
if value >= 0x8000 then -- Negative value??
return value - 0x10000
end
return value
end
local function sInt16ToDsm(value) -- Convent to SIGNED DSM Value
if value < 0 then
value = 0x10000 + value
end
return value
end
------------------------------------------------------------------------------------------------------------
local function Get_Text(index)
local out = Text[index] or string.format("Unknown_%X", index)
if (index >= 0x8000) then
out = Flight_Mode[0]
end
return out
end
local function Get_Text_Value(index)
local out = List_Text[index] or Get_Text(index)
return out
end
------------------------------------------------------------------------------------------------------------
local function Get_RxName(index)
local out = RxName[index] or string.format("Unknown_%X", index)
return out
end
------------------------------------------------------------------------------------------------------------
local function DSM_Release()
multiBuffer(0, 0)
Phase = PH_EXIT_DONE
end
------------------------------------------------------------------------------------------------------------
local function DSM_Send(...)
local arg = { ... }
for i = 1, #arg do
multiBuffer(3 + i, arg[i])
end
multiBuffer(3, 0x70 + #arg)
end
------------------------------------------------------------------------------------------------------------
function ChangePhase(newPhase)
Phase = newPhase
Waiting_RX = 0
end
local function Value_Add(dir)
local line = MenuLines[ctx.SelLine]
Speed = getRotEncSpeed()
if Speed == ROTENC_MIDSPEED then
line.Val = line.Val + (5 * dir)
elseif Speed == ROTENC_HIGHSPEED then
line.Val = line.Val + (15 * dir)
else
line.Val = line.Val + dir
end
if line.Val > line.Max then
line.Val = line.Max
elseif line.Val < line.Min then
line.Val = line.Min
end
ChangePhase(PH_VAL_CHANGING)
Value_Change_Step = 0
end
------------------------------------------------------------------------------------------------------------
local function GotoMenu(menuId, lastSelectedLine)
Menu.MenuId = menuId
ctx.SelLine = lastSelectedLine
-- Request to load the menu Again
ChangePhase(PH_TITLE)
end
local function isSelectable(line)
if (line.TextId == 0x00CD) then return true end -- Exceptiom: Level model and capture attitude
if (line.Type == LT_MENU and line.ValId == line.MenuId) then return false end -- Menu to same page
if (line.Type ~= LT_MENU and line.Max == 0) then return false end -- Read only data line
if (line.Type ~= 0 and line.TextId < 0x8000) then return true end -- Not Flight Mode
return false;
end
local function isListLine(line)
return line.Type==LT_LIST or line.Type == LT_LIST_VALIDATE or line.Type == LT_LIST_TOG
end
local function DSM_Menu(event)
if event == EVT_VIRTUAL_EXIT then
if Phase == PH_RX_VER then
DSM_Release() -- Exit program
else
if ctx.EditLine ~= nil then -- Editing a Line, need to restore original value
MenuLines[ctx.EditLine].Val = originalValue
event = EVT_VIRTUAL_ENTER
else
ChangePhase(PH_EXIT_REQ)
end
end
end
if Phase == PH_RX_VER then return end -- nothing else to do
if event == EVT_VIRTUAL_NEXT then
if ctx.EditLine ~= nil then
Value_Add(1)
else
-- not changing a value
if ctx.SelLine < 7 then -- On a regular line
local num = ctx.SelLine -- Find the prev selectable
for i = ctx.SelLine + 1, 6, 1 do
local line = MenuLines[i]
if isSelectable(line) then
ctx.SelLine = i
break
end
end
if num == ctx.SelLine then -- No Selectable Line
if Menu.NextId ~= 0 then
ctx.SelLine = 7 -- Next
elseif Menu.PrevId ~= 0 then
ctx.SelLine = 8 -- Prev
end
end
elseif Menu.PrevId ~= 0 then
ctx.SelLine = 8 -- Prev
end
end
elseif event == EVT_VIRTUAL_PREV then
if ctx.EditLine ~= nil then -- In Edit Mode
Value_Add(-1)
else
if ctx.SelLine == 8 and Menu.NextId ~= 0 then
ctx.SelLine = 7 -- Next
elseif ctx.SelLine > 0 then
if ctx.SelLine > 6 then
ctx.SelLine = 7 --NEXT
end
local num = ctx.SelLine -- Find Prev Selectable line
for i = ctx.SelLine - 1, 0, -1 do
local line = MenuLines[i]
if isSelectable(line) then
ctx.SelLine = i
break
end
end
if num == ctx.SelLine then -- No Selectable Line
if (Menu.BackId > 0) then
ctx.SelLine = -1 -- Back
end
end
else
ctx.SelLine = -1 -- Back
end
end
elseif event == EVT_VIRTUAL_ENTER_LONG then
if ctx.EditLine ~= nil then
-- reset the value to default
--if MenuLines[ctx.SelLine].Type ~= LIST_MENU_NOCHANGING then
MenuLines[ctx.SelLine].Val = MenuLines[ctx.SelLine].Def
ChangePhase(PH_VAL_CHANGING)
Value_Change_Step = 0
--end
end
elseif event == EVT_VIRTUAL_ENTER then
if ctx.SelLine == -1 then -- Back
GotoMenu(Menu.BackId, 0x80)
elseif ctx.SelLine == 7 then -- Next
GotoMenu(Menu.NextId, 0x82)
elseif ctx.SelLine == 8 then -- Prev
GotoMenu(Menu.PrevId, 0x81)
elseif ctx.SelLine >= 0 and MenuLines[ctx.SelLine].Type == LT_MENU then
GotoMenu(MenuLines[ctx.SelLine].ValId, ctx.SelLine) -- ValId is the next menu
else
-- value entry
if ctx.EditLine ~= nil then
ctx.EditLine = nil -- Done Editting
Value_Change_Step = 0
ChangePhase(PH_VAL_CHNG_END)
else -- Start Editing
ctx.EditLine = ctx.SelLine
originalValue = MenuLines[ctx.SelLine].Val
ChangePhase(PH_VAL_WAIT)
end
end
end
end
------------------------------------------------------------------------------------------------------------
local function SendTxInfo(portNo)
-- TxInfo_Type=0 : AR636 Main Menu (Send port/Channel info + SubTrim + Travel)
-- TxInfo_Type=1 : AR630-637 Famly Main Menu (Only Send Port/Channel usage Msg 0x20)
-- TxInfo_Type=1F : AR630-637 Initial Setup/Relearn Servo Settings (Send port/Channel info + SubTrim + Travel +0x24/Unknown)
if (TX_Info_Step == 0) then
-- AR630 family: Both TxInfo_Type (ManinMenu=0x1, Other First Time Configuration = 0x1F)
local info = MODEL.DSM_ChannelInfo[portNo]
DSM_Send(0x20, 0x06, portNo, portNo, info[0],info[1])
LOG_write("DSM_TxInfo_20(Port=#%d, Port Use)\n", portNo)
if (TX_Info_Type == 0x1F) then
TX_Info_Step = 1
elseif (TX_Info_Type == 0x00) then
TX_Info_Step = 2
end
elseif (TX_Info_Step == 1) then
local info = MODEL.modelOutputChannel[portNo]
local leftTravel = math.abs(math.floor(info.min/10))
local rightTravel = math.abs(math.floor(info.max/10))
DSM_Send(0x23, 0x06, 0x00, leftTravel, 0x00, rightTravel)
LOG_write("DSM_TxInfo_23(Port=#%d,ServoTravel(L=%d - R=%d))\n", portNo,leftTravel,rightTravel)
TX_Info_Step = 2
elseif (TX_Info_Step == 2) then
local data = {[0]= -- Start at 0
{[0]= 0x0, 0x00, 0x07, 0xFF }, -- Ch1 Thr: 0 00 07 FF Subtrim ??
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch2 Ail: 0 8E 07 72 Subtrim 0
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch3 Elev: 0 8E 07 72 Subtrim 0
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch4 Rud: 0 8E 07 72 Subtrim 0
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch5 Gear: 0 8E 07 72 Subtrim 0
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch6 Aux1: 0 8E 07 72 Subtrim 0
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch7 Aux2: 0 8E 07 72 Subtrim 0
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch8 Aux3: 0 8E 07 72 Subtrim 0
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch9 Aux4: 0 8E 07 72 Subtrim 0
{[0]= 0x0, 0x8E, 0x07, 0x72 }, -- Ch10 Aux5: 0 8E 07 72 Subtrim 0
}
local info = data[portNo]
local b1,b2,b3,b4 = info[0], info[1], info[2], info[3]
DSM_Send(0x21, 0x06, b1,b2,b3,b4) -- Port is not send anywhere, since the previous 0x20 type message have it.
LOG_write("DSM_TxInfo_21(Port=#%d, SubTrim)\n", portNo)
if (TX_Info_Type == 0x00) then
TX_Info_Step = 5 -- End Step
else
TX_Info_Step = 3
end
elseif (TX_Info_Step == 3) then
LOG_write("DSM_TxInfo_24?(Port=#%d)\n", portNo)
DSM_Send(0x24, 0x06, 0x00, 0x83, 0x5A, 0xB5) -- Still Uknown
TX_Info_Step = 4
elseif (TX_Info_Step == 4) then
LOG_write("DSM_TxInfo_24?(Port=#%d)\n", portNo)
DSM_Send(0x24, 0x06, 0x06, 0x80, 0x25, 0x4B) -- Still Uknown
TX_Info_Step = 5
elseif (TX_Info_Step == 5) then
LOG_write("DSM_TxInfo_22(Port=#%d, END of Data)\n", portNo)
DSM_Send(0x22, 0x04, 0x00, 0x00)
TX_Info_Step = 0 -- Done!!
end
if (TX_Info_Step > 0) then
Waiting_RX = 0 -- keep Transmitig
end
end
local function DSM_SendRequest()
--LOG_write("DSM_SendRequest Phase=%d\n",Phase)
-- Need to send a request
if Phase == PH_RX_VER then -- request RX version
DSM_Send(0x11, 0x06, 0x00, 0x14, 0x00, 0x00)
LOG_write("GetVersion()\n")
elseif Phase == PH_WAIT_CMD then -- keep connection open
DSM_Send(0x00, 0x04, 0x00, 0x00)
elseif Phase == PH_TITLE then -- request menu title
local menuId = Menu.MenuId
if menuId == 0 then
DSM_Send(0x12, 0x06, 0x00, 0x14, 0x00, 0x00) -- first menu only
else
DSM_Send(0x16, 0x06, int16_MSB(menuId), int16_LSB(menuId), 0x00, ctx.SelLine)
if (menuId == 0x0001) then -- Executed Save&Reset menu
Phase = PH_RX_VER
ctx.isReset = true
end
end
LOG_write("GetMenu(M=0x%04X,L=%d)\n", menuId, ctx.SelLine)
elseif Phase == PH_TX_INFO then -- TX Info
SendTxInfo(ctx.CurLine)
elseif Phase == PH_LINES then -- request menu lines
local menuId = Menu.MenuId
if ctx.CurLine == -1 then
DSM_Send(0x13, 0x04, int16_MSB(menuId), int16_LSB(menuId)) -- GetFirstLine
else
DSM_Send(0x14, 0x06, int16_MSB(menuId), int16_LSB(menuId), 0x00, ctx.CurLine) -- line X
end
LOG_write("GetLines(LastLine=%d)\n", ctx.CurLine)
elseif Phase == PH_VALUES then -- request menu values
local menuId = Menu.MenuId
local valId = MenuLines[ctx.CurLine].ValId
DSM_Send(0x15, 0x06,
int16_MSB(menuId), int16_LSB(menuId),
int16_MSB(valId), int16_LSB(valId))
LOG_write("GetValues(LastVId=0x%04X)\n", valId)
elseif Phase == PH_VAL_CHANGING then -- send new value: Two steps, Update & Validate
local line = MenuLines[ctx.SelLine]
local valId = line.ValId
if Value_Change_Step == 0 then
local value = sInt16ToDsm(line.Val)
DSM_Send(0x18, 0x06,
int16_MSB(valId), int16_LSB(valId),
int16_MSB(value), int16_LSB(value)) -- send current values
LOG_write("ChangeValue(VId=0x%04X,Val=%d)\n", valId, value)
if line.Type == LT_LIST_VALIDATE then -- Incremental Validation??
Value_Change_Step = 1
Waiting_RX = 0 -- Do SEND in the next step
end
else
-- Validate Value
DSM_Send(0x19, 0x04, int16_MSB(valId), int16_LSB(valId))
Value_Change_Step = 0
Phase = PH_VAL_WAIT
LOG_write("ValidateValue(VId=0x%04X)\n", valId)
end
elseif Phase == PH_VAL_CHNG_END then
DSM_Send(0x1B, 0x04, 0x00, int16_LSB(ctx.SelLine))
Value_Change_Step = 0
Phase = PH_WAIT_CMD
LOG_write("ValueChangeEnd(L=%d)\n", ctx.SelLine)
elseif Phase == PH_VAL_WAIT then
-- Value Changing Wait
DSM_Send(0x1A, 0x04, 0x00, int16_LSB(ctx.SelLine))
LOG_write("ValueChangeWait(L=%d)\n", ctx.SelLine)
elseif Phase == PH_EXIT_REQ then -- EXIT Request
DSM_Send(0x1F, 0x02, 0xAA)
end
end
local function DSM_ProcessResponse()
local cmd = multiBuffer(11)
-- LOG_write("DSM_ProcessResponse BEGIN: Cmd=%x\n",cmd)
if cmd == 0x01 then -- read version
RX.Name = Get_RxName(multiBuffer(13))
RX.Version = multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16)
--ctx.isReset = false -- no longer resetting
Menu.MenuId = 0
Phase = PH_TITLE
LOG_write("Version: %s %s\n", RX.Name, RX.Version)
elseif cmd == 0x02 then -- read menu title
local menu = Menu
menu.MenuId = Dsm_to_Int16(multiBuffer(12), multiBuffer(13))
menu.TextId = Dsm_to_Int16(multiBuffer(14), multiBuffer(15))
menu.Text = Get_Text(menu.TextId)
menu.PrevId = Dsm_to_Int16(multiBuffer(16), multiBuffer(17))
menu.NextId = Dsm_to_Int16(multiBuffer(18), multiBuffer(19))
menu.BackId = Dsm_to_Int16(multiBuffer(20), multiBuffer(21))
for i = 0, 6 do -- clear menu
MenuLines[i] = { MenuId = 0, Type = 0, TextId = 0, ValId = 0, Min = 0, Max = 0, Def = 0, Val = nil, Unit, Step }
end
ctx.CurLine = -1
ctx.SelLine = -1 -- highlight Back
LOG_write("Menu: Mid=0x%04X \"%s\"\n", menu.MenuId, menu.Text)
if (menu.MenuId == 0x0001) then -- Still in RESETTING MENU???
--menu.MenuId = 0
Phase = PH_RX_VER
else
Phase = PH_LINES
end
elseif cmd == 0x03 then -- read menu lines
local i = multiBuffer(14)
local type = multiBuffer(15)
local line = MenuLines[i]
ctx.CurLine = i
line.lineNum = i
line.MenuId = Dsm_to_Int16(multiBuffer(12), multiBuffer(13))
line.Type = type
line.TextId = Dsm_to_Int16(multiBuffer(16), multiBuffer(17))
line.Text = Get_Text(line.TextId)
line.ValId = Dsm_to_Int16(multiBuffer(18), multiBuffer(19))
-- Singed int values
line.Min = Dsm_to_SInt16(multiBuffer(20), multiBuffer(21))
line.Max = Dsm_to_SInt16(multiBuffer(22), multiBuffer(23))
line.Def = Dsm_to_SInt16(multiBuffer(24), multiBuffer(25))
if line.Type == LT_MENU then
-- nothing to do on menu entries
elseif isListLine(line) then
line.Val = nil --line.Def - line.Min -- use default value not sure if needed
line.Def = line.Min -- pointer to the start of the list in Text
line.Max = line.Max - line.Min -- max index
line.Min = 0 -- min index
else -- default to numerical value
line.Val = nil --line.Def -- use default value not sure if needed
if (line.Min == 0 and line.Max == 100) or (line.Min == -100 and line.Max == 100) or
(line.Min == 0 and line.Max == 150) or (line.Min == -150 and line.Max == 150) then
line.Type = LT_VALUE_PERCENT -- Override to Value Percent
end
end
if ctx.SelLine == -1 and isSelectable(line) then -- Auto select first selectable line of the menu
ctx.SelLine = ctx.CurLine
end
LOG_write("Line: #%d Vid=0x%04X T=0x%02X \"%s\"\n", i, line.ValId, type, line.Text)
Phase = PH_LINES
elseif cmd == 0x04 then -- read menu values
-- Identify the line and update the value
local valId = Dsm_to_Int16(multiBuffer(14), multiBuffer(15))
local value = Dsm_to_SInt16(multiBuffer(16), multiBuffer(17)) --Signed int
local updatedLine = nil
for i = 0, 6 do -- Find the menu line for this value
local line = MenuLines[i]
if line.Type ~= 0 then
if line.Type ~= LT_MENU and line.ValId == valId then -- identifier of ValueId stored in the line
line.Val = value
ctx.CurLine = i
updatedLine = line
local valueTxt = value
if isListLine(line) then
valueTxt = Get_Text_Value(line.Def + value) .. " [" .. value .. "]"
end
LOG_write("Update Value: #%d VId=0x%04X Value=%s\n", i, valId, valueTxt)
break
end
end
end
if (updatedLine == nil) then
LOG_write("Cannot Find Line for ValueId=%x\n", valId)
end
Phase = PH_VALUES
elseif cmd == 0x05 then -- Request TX info
ctx.CurLine = multiBuffer(12)
TX_Info_Type = multiBuffer(13)
TX_Info_Step = 0
Phase = PH_TX_INFO
LOG_write("TXInfoReq: Port=%d T=0x%02X\n", ctx.CurLine, TX_Info_Type)
elseif cmd == 0xA7 then -- answer to EXIT command
DSM_Release()
elseif cmd == 0x00 and Phase == PH_VAL_CHANGING then
Phase = PH_VAL_WAIT
end
--LOG_write("DSM_ProcessResponse END: Cmd=%x\n",cmd)
return cmd
end
local function DSM_Send_Receive()
if Waiting_RX == 0 then
Waiting_RX = 1
DSM_SendRequest()
multiBuffer(10, 0x00);
InactivityTime = getTime() + 200 -- Reset Inactivity timeout
-- -- -- -- -- -- -- -- -- -- -- -- receive part -- -- -- -- -- -- -- -- -- -- -- -- --
elseif multiBuffer(10) == 0x09 then
local cmd = DSM_ProcessResponse()
-- Data processed
multiBuffer(10, 0x00)
if (cmd > 0x00) then -- Any non NULL response
-- Only change to SEND mode if we received a valid response (Ignore NULL Responses, that are really heartbeat i most cases)
Waiting_RX = 0
InactivityTime = getTime() + 200 -- Reset Inactivity timeout
end
else -- No Send or Receive,
-- Check if enouth time has passed from last transmit/receive activity
if getTime() > InactivityTime then
InactivityTime = getTime() + 200
Waiting_RX = 0 -- Switch to Send mode to send heartbeat
if Phase == PH_EXIT_REQ then
DSM_Release()
end
if Phase ~= PH_RX_VER and Phase ~= PH_VAL_WAIT then
Phase = PH_WAIT_CMD
end
end
end
end
------------------------------------------------------------------------------------------------------------
local function showBitmap(x, y, imgDesc)
local f = string.gmatch(imgDesc, '([^%|]+)') -- Iterator over values split by '|'
local imgName, imgMsg = f(), f()
f = string.gmatch(imgMsg or "", '([^%:]+)') -- Iterator over values split by ':'
local p1, p2 = f(), f()
lcd.drawText(x, y, p1 or "", TEXT_ATTR) -- Alternate Image MSG
lcd.drawText(x, y + LCD_Y_LINE_HEIGHT, p2 or "", TEXT_ATTR) -- Alternate Image MSG
end
local function drawButton(x, y, text, active)
local attr = TEXT_ATTR
if (active) then attr = attr + INVERS end
lcd.drawText(x, y, text, attr)
end
local ver_rx_count = 0
local function DSM_Display()
lcd.clear()
--Draw RX Menu
if Phase == PH_RX_VER then
lcd.drawText(1, 0, "DSM Frwd Prog "..VERSION, INVERS)
local msgId = 0x300 -- Waiting for RX
if (ctx.isReset) then msgId=0x301 end -- Waiting for Reset
lcd.drawText(0, 3 * LCD_Y_LINE_HEIGHT, Get_Text(msgId), BLINK)
return
end
-- display Program version or RX version
local msg = RX.Name .. " v" .. RX.Version
if (ver_rx_count < 100) then
msg = RX.Name .. " v" .. RX.Version
ver_rx_count = ver_rx_count + 1
else
msg = "Frwd Prog "..VERSION
ver_rx_count = ver_rx_count + 1
if (ver_rx_count > 200) then ver_rx_count=0 end
end
lcd.drawText(30, LCD_Y_LOWER_BUTTONS, msg, TEXT_ATTR)
if Menu.MenuId == 0 then return end; -- No Title yet
-- Got a Menu
lcd.drawText(1, 0, Menu.Text, TEXT_ATTR + INVERS)
local y = LCD_Y_LINE_HEIGHT + 2
for i = 0, 6 do
local attrib = TEXT_ATTR
if (i == ctx.SelLine) then attrib = attrib + INVERS end -- Selected Line
local line = MenuLines[i]
if line ~= nil and line.Type ~= 0 then
local heading = Get_Text(line.TextId)
if (line.TextId >= 0x8000) then -- Flight mode
heading = " " .. Flight_Mode[0] .. " "
if (line.Val==nil) then heading = heading .. "--" else heading = heading .. ((line.Val or 0) + 1) end
else
local text = "-"
if line.Type ~= LT_MENU then -- list/value
if line.Val ~= nil then
if isListLine(line) then
local textId = line.Val + line.Def
text = Get_Text_Value(textId)
local imgDesc = List_Text_Img[textId]
if (imgDesc and i == ctx.SelLine) then -- Optional Image and Msg for selected value
showBitmap(0, 20, imgDesc)
end
elseif (line.Type == LT_VALUE_PERCENT) then
text = line.Val .. " %"
elseif (line.Type == LT_VALUE_DEGREES) then
text = line.Val .. " @"
else
text = line.Val
end
end -- if is Value
if (ctx.EditLine == i) then -- Editing a Line
attrib = BLINK + INVERS + TEXT_ATTR
end
lcd.drawText(LCD_X_MAX, y, text, attrib + RIGHT) -- display value
attrib = TEXT_ATTR
end
end -- Flight mode
lcd.drawText(0, y, heading, attrib) -- display text
end
y = y + LCD_Y_LINE_HEIGHT
end -- for
if Menu.BackId ~= 0 then
drawButton(LCD_X_RIGHT_BUTTONS, 0, "Back", ctx.SelLine == -1)
end
if Menu.NextId ~= 0 then
drawButton(LCD_X_RIGHT_BUTTONS, LCD_Y_LOWER_BUTTONS, "Next", ctx.SelLine == 7)
end
if Menu.PrevId ~= 0 then
drawButton(0, LCD_Y_LOWER_BUTTONS, "Prev", ctx.SelLine == 8)
end
end
local function load_msg_from_file(fileName, mem, Text, List_Text, List_Text_Img, RxName, Flight_Mode)
local function rtrim(s)
local n = string.len(s)
while n > 0 and string.find(s, "^%s", n) do n = n - 1 end
return string.sub(s, 1, n)
end
--print(string.format("Loading messages from [%s]",fileName))
local dataFile = io.open(fileName, "r") -- read File
-- cannot read file???
assert(dataFile, "Cannot load Message file:" .. fileName)
local data = io.read(dataFile, mem * 1024) -- read up to 10k characters (newline char also counts!)
io.close(dataFile)
collectgarbage("collect")
local lineNo = 0
for line in string.gmatch(data, "[^\r\n]+") do
lineNo = lineNo + 1
--print(string.format("Line [%d]: %s",lineNo,line))
-- Remove Comments
local s = string.find(line, "--", 1, true)
if (s ~= nil) then
line = string.sub(line, 1, s - 1)
end
line = rtrim(line)
if (string.len(line) > 0) then
local a, b, c = string.match(line, "%s*(%a*)%s*|%s*(%w*)%s*|(.*)%s*")
--print(string.format("[%s] [%s] [%s]",a,b,c))
if (a ~= nil) then
local index = tonumber(b)
if (index == nil) then
assert(false, string.format("%s:%d: Invalid Hex num [%s]", fileName, lineNo, b))
elseif (a == "T") then
Text[index] = c
elseif (a == "LT") then
List_Text[index] = c
elseif (a == "LI") then
List_Text_Img[index] = c
elseif (a == "FM") then
Flight_Mode[0] = c
elseif (a == "RX") then
RxName[index] = c
else
assert(false, string.format("%s:%d: Invalid Line Type [%s]", fileName, lineNo, a))
end
end
end
if (lineNo % 50 == 0) then
collectgarbage("collect")
end
end -- For
--print(string.format("Loaded [%d] messages",lineNo))
data = nil
end
local function clean_msg(Text, Flight_Mode)
local function clean_line(c)
if (c==nil) then return c end
local pos
c, pos = string.gsub(c, "/b$", "")
c, pos = string.gsub(c, "/c$", "")
c, pos = string.gsub(c, "/r$", "")
c, pos = string.gsub(c, "/p$", "")
c, pos = string.gsub(c, "/m$", "")
return c
end
-- Clean the line of special markers that are only used in color vesion
for i = 0, 0x0300 do
Text[i] = clean_line(Text[i])
collectgarbage("collect")
end
for i = 0, #Flight_Mode do
-- Clean the line of special markers that are only used in color vesion
Flight_Mode[i] = clean_line(Flight_Mode[i])
end
end
local function DSM_Init_Model()
MODEL.DSM_ChannelInfo= {[0]= -- Start array at position 0
{[0]= 0x00, 0x40}, -- Ch1 Thr (0x40)
{[0]= 0x00, 0x01}, -- Ch2 Ail (0x01)
{[0]= 0x00, 0x02}, -- Ch2 ElE (0x02)
{[0]= 0x00, 0x04}, -- Ch4 Rud (0x04)
{[0]= 0x00, 0x00}, -- Ch5 Gear (0x00)
{[0]= 0x00, 0x00}, -- Ch6 Aux1 (0x00)
{[0]= 0x00, 0x00}, -- Ch7 Aux2 (0x00)
{[0]= 0x00, 0x00}, -- Ch8 Aux3 (0x00)
{[0]= 0x00, 0x00}, -- Ch9 Aux4 (0x00)
{[0]= 0x00, 0x00} -- Ch10 Aux5 (0x00)
}
MODEL.modelOutputChannel = {[0]=
{min=1000, max=1000}, -- Ch1
{min=1000, max=1000}, -- Ch2
{min=1000, max=1000}, -- Ch3
{min=1000, max=1000}, -- Ch4
{min=1000, max=1000}, -- Ch5
{min=1000, max=1000}, -- Ch6
{min=1000, max=1000}, -- Ch7
{min=1000, max=1000}, -- Ch8
{min=1000, max=1000}, -- Ch9
{min=1000, max=1000} -- Ch10
}
end
------------------------------------------------------------------------------------------------------------
-- Init
local function DSM_Init()
LOG_open()
LOG_write("-------- NEW SESSION --------------------\n")
DSM_Init_Model()
--[[
if (ModelParam~=nil) then
LOG_write("Got MODEL PARAMETER... copying\n")
MODEL.DSM_ChannelInfo = ModelParam.DSM_ChannelInfo
else
LOG_write("NO-PARMETER --- Create DEFAULT")
end
--]]
collectgarbage("collect")
LOG_write("Mem before msg =%d\n",collectgarbage("count"))
load_msg_from_file(MSG_FILE, 10, Text, List_Text, List_Text_Img, RxName, Flight_Mode)
collectgarbage("collect")
LOG_write("Mem after msg =%d\n",collectgarbage("count"))
load_msg_from_file(MSG_FILE_MIN, 4, Text, List_Text, List_Text_Img, RxName, Flight_Mode)
collectgarbage("collect")
LOG_write("Mem after msg2 =%d\n",collectgarbage("count"))
clean_msg(Text,Flight_Mode)
collectgarbage("collect")
--Set protocol to talk to
multiBuffer(0, string.byte('D'))
--test if value has been written
if multiBuffer(0) ~= string.byte('D') then
error("Not enough memory!")
return 2
end
--Init TX buffer
multiBuffer(3, 0x00)
--Init RX buffer
multiBuffer(10, 0x00)
--Init telemetry
multiBuffer(0, string.byte('D'))
multiBuffer(1, string.byte('S'))
multiBuffer(2, string.byte('M'))
if (LCD_W > 128) then
TEXT_ATTR = 0
LCD_Y_LINE_HEIGHT = 25
LCD_X_MAX = 300
LCD_X_RIGHT_BUTTONS = LCD_X_MAX - 30
LCD_Y_LOWER_BUTTONS = (8 * LCD_Y_LINE_HEIGHT) + 2
end
end
------------------------------------------------------------------------------------------------------------
-- Main
local function DSM_Run(event)
if event == nil then
error("Cannot be run as a model script!")
return 2
else
DSM_Display()
DSM_Menu(event)
DSM_Send_Receive()
end
if Phase == PH_EXIT_DONE then
LOG_close()
return 2
else
return 0
end
end
return { init = DSM_Init, run = DSM_Run }

View File

@@ -0,0 +1,467 @@
# Forward Programing Protocol
## Introduction
DSM, DSMX and DSM Forward Programming are propietary protocol from the **Spektrum** radio brand. Since they don't make this information public, we have to reverse engineer it by analyzing the data exchanged between the RX and TX.
This document descrives what we know so far.
Thanks to **Pascal Langer** (Author of the Multi-Module) for the initial reverse engineering of the protocol and first version of the code that has been used for a while (Version 0.2)
Thanks to **Francisco Arzu** for taking the time to continue the work on reverse engineering, documenting and making the code more understandable.
New Capabilities in Version 0.5
- Log files of the conversation between RX/TX
- Improve the GUI (EdgeTX touch screen)
- Reversed engineer other things to make it work completly.
# Menu Title and Lines
The menu to be displayed is stored at the RX, the GUI only renders the menu title and menu lines received. The tipical conversation with the RX will be to ask for a menu (using the menuId number), and then wait for the data to come. The first thing will be the Menu (header) data, later we request the next 6 lines (one at a time), and optionally the values for each line.
A typical exchange will look like this in the log:
SEND DSM_getMenu(MenuId=0x1010 LastSelectedLine=0)
RESPONSE Menu: M[Id=0x1010 P=0x0 N=0x0 B=0x1000 Text="Gyro settings"[0xF9]]
SEND DSM_getFirstMenuLine(MenuId=0x1010)
RESPONSE MenuLine: L[#0 T=M VId=0x1011 Text="AS3X Settings"[0x1DD] MId=0x1010 ]
SEND DSM_getNextLine(MenuId=0x1010,LastLine=0)
RESPONSE MenuLine: L[#1 T=M VId=0x1019 Text="SAFE Settings"[0x1E2] MId=0x1010 ]
SEND DSM_getNextLine(MenuId=0x1010,LastLine=1)
RESPONSE MenuLine: L[#2 T=M VId=0x1021 Text="F-Mode Setup"[0x87] MId=0x1010 ]
SEND DSM_getNextLine(MenuId=0x1010,LastLine=2)
RESPONSE MenuLine: L[#3 T=M VId=0x1022 Text="System Setup"[0x86] MId=0x1010 ]
## Menu
The menu has the following information:
Menu: M[Id=0x1010 P=0x0 N=0x0 B=0x1000 Text="Gyro settings"[0xF9]]
- `MenuId`: The menu ID number of the menu (hex, 16 bit number)
- `PrevId`: The menu ID of the previous menu (for navigation), Log show as `"P="`
- `NextId`: The menu ID of the next menu (for navigation), Log shows as `"N="`
- `BackId`: The menu ID of the back menu (for navigation), Log shows as `"B="`
- `TextId`: The message number to display (16 bits, Hex). Log shows as [`0xXX`] after the message.
- `Text`: Retrived using the `TextId` from the script message `Text` array.
## Menu Lines
The menu lines has the following information:
L[#0 T=V_nc VId=0x1000 Text="Flight Mode"[0x8001] Val=1 [0->10,0] MId=0x1021 ]
L[#1 T=M VId=0x7CA6 Text="FM Channel"[0x78] MId=0x1021 ]
L[#2 T=LM VId=0x1002 Text="AS3X"[0x1DC] Val=1|"Act" NL=(0->1,0,S=3) [3->4,3] MId=0x1021 ]
- `MenuId`: of the menu they beling to. Log show as `"MId="` at the end.
- `LineNum`: Line number (0..5). The line number in the screen. Log show as # in the beginning
- `Type`: Type of Line, Log shows as `"T="` (explanation later)
- `TextId`: The message number to display (16 bits, Hex). Log shows as [`0xXXXX`] after the message.
- `Text`: Retrived using the `TextId` from the script message `Text` array.
- `ValueId`: The value or menu ID of the line. Log shows as `"VId="` (16 bits, Hex).
- `Value Range`: Shows as [`Min`->`Max`, `Default`]. This is the RAW data comming from the RX
- `NL`: Computed Normalized LIST (0 reference) for List Values. Source is the RAW range. For example, for lines of list of values. `[3->4,3]` is tranlated to `NL=(0->1,0,S=3)` since the value is also normalize to 0. `"S="` means the initial entry in the `List_Text` array
- `Val`: Current value for line who hold data. Relative to 0 for List Values. For List Values, the log will show the translation of the value to display text. example: `Val=1|"Act"` that is coming from `List_Value[4]`
## Type of Menu Lines
- `LINE_TYPE.MENU (Log: "T=M")`: This could be regular text or a navigation to another menu. if `ValueId` is the same as the current MenuId (`MId=`), is a plain text line (navigation to itself). If the `ValueId` is not the current menuId, then `ValueId` is the MenuId to navigate to.
We have found only one exception to the plain text rule, a true navigation to itself, in that case, in the text of the menu, you can use the "/M" flag at the end of the text to force it to be a menu button.
Example, FM_Channel is a navigation to menuId=0x7CA6.
L[#1 T=M VId=0x7CA6 Text="FM Channel"[0x78] MId=0x1021 ]
- `LINE_TYPE.LIST_MENU_NC (Log T=LM_nc)`: This is a line that shows as text in the GUI. The numeric value is translated to the proper text. The range is important, since it descrives the range of posible values. No incremental RX changes, only at the end.
Example: List of Values, List_Text[] starts at 53, ends at 85, with a default of 85. When normalized to 0, is a range from 0->32 for the numeric value. The Display value `Aux1` is retrive from `List_Text[6+53]`.
L[#0 T=LM_nc VId=0x1000 Text="FM Channel"[0x78] Val=6|"Aux1" NL=(0->32,0,S=53) [53->85,53] MId=0x7CA6 ]
- `LINE_TYPE.LIST_MENU_TOG (Log T=L_tog)`: Mostly the same as LIST_MENU_NC, but is just 2 values. (ON/OFF, Ihn/Act, etc). Should be a toggle in the GUI.
L[#2 T=LM_tog VId=0x1002 Text="AS3X"[0x1DC] Val=1|"Act" NL=(0->1,0,S=3) [3->4,3] MId=0x1021 ]
- `LINE_TYPE.LIST_MENU (Log T=LM)`: Mostly the same as LIST_MENU_NC, but incremental changes to the RX. Some times, it comes with a strange range `[0->244,Default]`. Usually this means that the values are not contiguos range; usually Ihn + Range. Still haven't found where in the data the correct range comes from.
Example: Valid Values: 3, 176->177 (Inh, Self-Level/Angle Dem, Envelope)
L[#3 T=LM VId=0x1003 Text="Safe Mode"[0x1F8] Val=176|"Self-Level/Angle Dem" NL=(0->244,3,S=0) [0->244,3] MId=0x1021 ]
- `LINE_TYPE.VALUE_NUM_I8_NC (Log: "T=V_nc")`: This line is editable, but is not updated to the RX incrementally, but only at the end. The Flight Mode line is of this type, so we have to check the TextId to differenciate between Flight mode and an Editable Value.
Fligh Mode TextId is between 0x8000 and 0x8003
Example, Flight mode comes from Variable ValId=0x1000, with current value of 1. Range of the Value is 0..10.
L[#0 T=V_nc VId=0x1000 Text="Flight Mode"[0x8001] Val=1 [0->10,0] MId=0x1021 ]
- `LINE_TYPE.VALUE_NUM_I8 (Log T=V_i8)`: 8 bit number (1 byte)
- `LINE_TYPE.VALUE_NUM_I16' (Log T=V_i16)`: 16 Bit number (2 bytes)
- `LINE_TYPE.VALUE_NUM_SI16 (Log T=V_si16)`: Signed 16 bit number (2 bytes)
- `LINE_TYPE.VALUE_PERCENT (Log T=L_%)`: Shows a Percent Value. 1 Byte value.
- `LINE_TYPE.VALUE_DEGREES (Log T=L_de)`: Shows a Degrees VAlue. 1 Byte value.
## LIST_TYPE Bitmap
TYPE|Sum|Hex|7 Signed|6 Valid Min/Max??|5 No-Inc-Changing|4 Menu|3 List-Menu|2 text / number|1|0 - 16 bits
|-|-|-|-|-|-|-|-|-|-|-
|MENU|Text|0x1C|0|0|0|1|1|1|0|0
|LIST_MENU|Text|0x0C|0|0|0|0|1|1|0|0
|LIST_MENU_TOG|Text|0x4C|0|1|0|0|1|1|0|0
|LIST_MENU_NC|Text, NC|0x6C|0|1|1|0|1|1|0|0
|VALUE_NUM_I8_NC|I8, NC|0x60|0|1|1|0|0|0|0|0
|VALUE_PERCENT|S8|0xC0|1|1|0|0|0|0|0|0
|VALUE_DEGREES|S8 NC|0xE0|1|1|1|0|0|0|0|0
|VALUE_NUM_I8|I8|0x40|0|1|0|0|0|0|0|0
|VALUE_NUM_I16|I16|0x41|0|1|0|0|0|0|0|1
|VALUE_NUM_SI16|S16|0xC1|1|1|0|0|0|0|0|1
## Important Behavioral differences when updating values
Values who are editable, are updated to RX as they change. For example, when changing attitude trims, the servo moves as we change the value in real-time.
LIST_MENU_NC, VALUE_NUM_I8_NC don't update the RX as it changes. It changes only in the GUI, and only update the RX at the end when confirmed the value. (NO-INC-CHANGES Bit)
After finishing updating a value, a validation command is sent. RX can reject the current value, and will change it to the nearest valid value.
## Special Menus
Seems like menuId=0x0001 is special. When you navigate to this menu, the RX reboots.
When this happens, we need to start from the beginning as if it was a new connection.
# Send and Receive messages
To comunicate with the Multi-Module, Lua scripts in OpenTx/EdgeTx has access to the `Multi_Buffer`. Writting to it will send data to RX, received data will be read from it.
For our specific case, this is how the Multi_Buffer is used:
|0..2|3|4..9|10..25
|--|--|--|--
|DSM|0x70+len|TX->RX data|RX->TX Data
To write a new DSM Fwd Programing command, write the data to address 4..9, and later set the address 3 with the length.
When receiving data, address 10 will have the message type we are receiving, or 0 if nothing has been received.
## Starting a new DSM Forward programming Connection
- Write 0x00 at address 3
- Write 0x00 at address 10
- Write "DSM" at address 0..2
## Disconnect
- Write 0x00 at address 0
# Request Messages (TX->RX)
## DSM_sendHeartbeat()
keep connection open.. We need to send it every 2-3 seconds, otherwise the RX will force close the connection by sending the TX an Exit_Confirm message.
|4|5|6|7|8|9|10
|--|--|--|--|--|--|--
Msg| Len? | ?? | ??
0x00|0x04|0x00|0x00
SEND DSM_sendHeartbeat()
DSM_SEND: [00 04 00 00 ]
## DSM_getRxVersion()
Request the RX information
|4|5|6|7|8|9|10
|--|--|--|--|--|--|--
Msg| Len? | ?? | ?? |??|??
0x11|0x06|0x00|0x14|0x00|0x00
SEND DSM_getRxVersion()
DSM_SEND: [11 06 00 14 00 00 ]
## DSM_getMainMenu()
Request data for the main menu of the RX
|4|5|6|7|8|9|10
|--|--|--|--|--|--|--
Msg| Len? | ?? | ?? |??|??
0x12|0x06|0x00|0x14|0x00|0x00
SEND DSM_getMainMenu()
DSM_SEND: [12 06 00 14 00 00 ]
## DSM_getMenu(menuId, lastSelLine)
Request data for Menu with ID=`menuId`. lastSelLine is the line that was selected to navigate to that menu. Most menus works with 0, but for some special "Enter Bind Mode", "Factory Reset", "Save to Backup" they will not work if we send 0, has to be the line who was selected in the confirmation menu line "Apply".
|4|5|6|7|8|9|10
|--|--|--|--|--|--|--
Msg|Len? | MSB (menuId) | LSB (MenuId) | MSB (line#)??| LSB (line#)
0x16|0x06|0x10|0x60|0x00|0x01
SEND DSM_getMenu(MenuId=0x1060 LastSelectedLine=1)
DSM_SEND: [16 06 10 60 00 01 ]
## DSM_getFirstMenuLine(menuId)
Request the first line of a menu identified as `menuId`. The response will be the first line of the menu. Some times, it return lines shown as `'MenuUknownLine_0x05'` that we still are trying to understand what they are for.
|4|5|6|7|8|9|10
|--|--|--|--|--|--|--
Msg|Len? | MSB (menuId) | LSB (MenuId)
0x13|0x04|0x10|0x60
SEND DSM_getFirstMenuLine(MenuId=0x1000)
DSM_SEND: [13 04 10 00 ]
## DSM_getNextMenuLine(menuId, curLine)
Request the retrival of the next line following the current line. Response is either the next line, or the next value, or nothing.
|4|5|6|7|8|9|10
|--|--|--|--|--|--|--
Msg|Len? | MSB (menuId) | LSB (MenuId) | MSB (line#)??| LSB (line#)
0x14|0x06|0x10|0x60|0x00|0x01
SEND DSM_getNextLine(MenuId=0x1000,LastLine=1)
DSM_SEND: [14 06 10 00 00 01 ]
## DSM_getNextMenuValue(menuId, valId, text)
Retrive the next value after the last `ValId` of the current `menuId`. text is just for debugging purposes to show the header of the value been retrived.
The Response is a Menu Value or nothing if no more data.
|4|5|6|7|8|9|10
|--|--|--|--|--|--|--
Msg|Len? | MSB (menuId) | LSB (MenuId) | MSB (ValId)| LSB (ValId)
0x15|0x06|0x10|0x61|0x10|0x00
SEND DSM_getNextMenuValue(MenuId=0x1061, LastValueId=0x1000) Extra: Text="Outputs"
DSM_SEND: [15 06 10 61 10 00 ]
## DSM_updateMenuValue(valId, val, text, line)
Updates the value identified as `valId` with the numeric value `val`. `text` and `line` are there to add debugging info. No response is expected.
If the value is negative, it has to be translated to the proper DSM negative representaion.
|4|5|6|7|8|9|10
|--|--|--|--|--|--|--
Msg|Len? | MSB (ValId) | LSB (ValId) | MSB (Value)| LSB (Value)
0x18|0x06|0x??|0x??|0x??|0x??
DSM_updateMenuValue(valId, val, text, line)
-->DSM_send(0x18, 0x06, int16_MSB(valId), int16_LSB(valId), int16_MSB(value), int16_LSB(value))
## DSM_validateMenuValue(valId, text, line)
Validates the value identified as `valId`. `text` and `line` are there to add debugging info. The RX can response an Update value if the value is not valid and needs to be corrected.
|4|5|6|7|8|9|10
|--|--|--|--|--|--|--
Msg|Len? | MSB (ValId) | LSB (ValId)
0x19|0x06|0x??|0x??
DSM_validateMenuValue(valId, text, line)
-> DSM_send(0x19, 0x06, int16_MSB(valId), int16_LSB(valId))
## DSM_menuValueChangingWait(valId, text, line)
Durin editing, this serves as a heartbeat that we are editing the value. The value identified as `valId`. `text` and `line` are there to add debugging info. The RX can response an Update value or a NUL response.
|4|5|6|7|8|9|10
|--|--|--|--|--|--|--
Msg|Len?? | MSB (ValId) | LSB (ValId)
0x1A|0x06|0x??|0x??
DSM_menuValueChangingWait(valId, text, line)
->DSM_send(0x1A, 0x06, int16_MSB(valId), int16_LSB(valId))
## DSM_exitRequest()
Request to end the DSM Frd Prog connection. Will reponse with an exit confirmation.
|4|5|6|7|8|9|10
|--|--|--|--|--|--|--
Msg|Len?? | ??
0x1F|0x02|0xAA
CALL DSM_exitRequest()
DSM_SEND: [1F 02 AA ]
# Response Messages (RX->TX)
All responses will have the a response byte in Multi_Buffer[10]=0x09, and the type of message in Multi_Buffer[11].
## RX Version Response
Returns the information about the current RX.
The Display text of name name of the RX is retrive from the `RX_Name` array.
|10|11|12|13|14|15|16
|--|--|--|--|--|--|--
|Resp|Msg|?? |RxId|Major|Minor|Patch
|0x09|0x01|0x00|0x1E|0x02|0x26|0x05
RESPONSE RX: 09 01 00 1E 02 26 05
RESPONSE Receiver=AR631 Version 2.38.5
## Menu Response
Returns the menu information to display and navigation.
The Display text for the menu is retrive from the `Text` array.
|10|11|12|13|14|15|16|17|18|19|20|21
|--|--|--|--|--|--|--|--|--|--|--|--
|Resp|Msg|LSB (menuId)|MSB (menuId)|LSB (TextId)|MSB (TextId)|LSB (PrevId)|MSB (PrevId)|LSB (NextId)|MSB (NextId)|LSB (BackId)|MSB (BackId)
|0x09|0x02|0x5E|0x10|0x27|0x02|0x00|0x00|0x00|0x00|0x00|0x10
RESPONSE RX: 09 02 5E 10 27 02 00 00 00 00 00 10 00 00 00 00
RESPONSE Menu: M[Id=0x105E P=0x0 N=0x0 B=0x1000 Text="Other settings"[0x227]]
## Menu Line Response
Returns the menu line information.
The Display text for the menu line is retrive from the `Text` array.
`Min`,`Max` and `Default` can be signed numbers.
|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25
|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--
|Resp|Msg|LSB (menuId)|MSB (menuId)|Line#|Line Type|LSB (TextId)|MSB (TextId)|LSB (ValId)|MSB (ValId)|LSB (Min)|MSB (Min)|LSB (Max)|MSB (Max)|LSB (Def)|MSB (Def)
|0x09|0x03|0x61|0x10|0x00|0x6C|0x50|0x00|0x00|0x10|0x36|0x00|0x49|0x00|0x36|0x00
RESPONSE RX: 09 03 61 10 00 6C 50 00 00 10 36 00 49 00 36 00
RESPONSE MenuLine: L[#0 T=LM_nc VId=0x1000 Text="Outputs"[0x50] Val=nil NL=(0->19,0,S=54) [54->73,54] MId=0x1061 ]
## Menu Line Value Response
Returns the Value for a line.
The response updates the Value in the line identified by `ValId`.
The Display text for the Value, when it is a list, is retrive from the `List_Text` array.
|10|11|12|13|14|15|16|17
|--|--|--|--|--|--|--|--
|Resp|Msg|LSB (menuId)|MSB (menuId)|LSB (ValId)|MSB (ValId)|LSB (Value)|MSB (Value)
|0x09|0x04|0x61|0x10|0x00|0x10|0x00|0x00
RESPONSE RX: 09 04 61 10 00 10 00 00
RESPONSE MenuValue: UPDATED: L[#0 T=L_m0 VId=0x1000 Text="Outputs"[0x50] Val=0|"Throttle" NL=(0->19,0,S=54) [54->73,54] MId=0x1061 ]
## Exit Response
Response from a Exit Request.
|10|11
|--|--
|Resp|Msg
|0x09|0x07
RESPONSE RX: 09 A7
RESPONSE Exit Confirm
## NULL Response
Can be use as a response, or heartbeat from the RX to keep the connection open.
|10|11
|--|--
|Resp|Msg
|0x09|0x00
RESPONSE RX: 09 00
RESPONSE NULL
# Unknown Lines
TOTALLY UNKNOWN WHAT THIS ARE FOR.. but only works for the Main Menu..
Other menus they just loop on line=0 forever.
## DSM_getNextUknownLine_0x05(menuId, curLine)
Request the retrival of the next Unknown line following the current line. Response is either the next unknow line, next menu line, or the next value, or nothing.
|4|5|6|7|8|9| Comment
|--|--|--|--|--|--|--
Msg|Len? | Line# | Line# | 0x00 | Formula(line#)??
0x20|0x06|0x00|0x00|0x00|0x40 | LastLineLine=0 retrieval
0x20|0x06|0x01|0x01|0x00|0x01| LastLineLine=1 retrieval
0x20|0x06|0x02|0x02|0x00|0x02| LastLineLine=2 retrieval
0x20|0x06|0x03|0x03|0x00|0x04| LastLineLine=3 retrieval
0x20|0x06|0x04|0x04|0x00|0x00| LastLineLine=4 retrieval
0x20|0x06|0x05|0x05|0x00|0x00| LastLineLine=5 retrieval
## Unknown Line Response
We still don't know what is this for, but we have to retrive them and skip then. Works for main menu, but when it happens in another menus, usually we stay in an infinite loop retrieving line=0
|10|11|12|13|14|15|16|17
|--|--|--|--|--|--|--|--
|Resp|Msg|LSB (line#)
|0x09|0x05|0x00|0x01|0x00|0x00|0x00|0x07
|0x09|0x05|0x01|0x01|0x00|0x00|0x00|0x07
## Interaction on Main Menu
This is the normal interaction for the main menu. As you can see, it iterates on the 6 Unknow lines (0..5), and afterwards, it starts sending normal menu lines.
SEND DSM_getFirstMenuLine(MenuId=0x1000)
RESPONSE MenuUknownLine_0x05: LineNum=0 DATA=RX: 09 05 00 01 00 00 00 07 00 00 00 00 00 00 00 00
CALL DSM_getNextUknownLine_0x05(LastLine=0)
RESPONSE MenuUknownLine_0x05: LineNum=1 DATA=RX: 09 05 01 01 00 00 00 07 00 00 00 00 00 00 00 00
CALL DSM_getNextUknownLine_0x05(LastLine=1)
RESPONSE MenuUknownLine_0x05: LineNum=2 DATA=RX: 09 05 02 01 00 00 00 07 00 00 00 00 00 00 00 00
CALL DSM_getNextUknownLine_0x05(LastLine=2)
RESPONSE MenuUknownLine_0x05: LineNum=3 DATA=RX: 09 05 03 01 00 00 00 07 00 00 00 00 00 00 00 00
CALL DSM_getNextUknownLine_0x05(LastLine=3)
RESPONSE MenuUknownLine_0x05: LineNum=4 DATA=RX: 09 05 04 01 00 00 00 07 00 00 00 00 00 00 00 00
CALL DSM_getNextUknownLine_0x05(LastLine=4)
RESPONSE MenuUknownLine_0x05: LineNum=5 DATA=RX: 09 05 05 01 00 00 00 07 00 00 00 00 00 00 00 00
CALL DSM_getNextUknownLine_0x05(LastLine=5)
RESPONSE MenuLine: L[#0 T=M VId=0x1010 Text="Gyro settings"[0xF9] MId=0x1000 ]
## Other menus
If it hapen on other menus. Usualy stays in an infinite loop until it crash/exits.
The screen will show **"Error: Cannot Load Menu Lines from RX"**
The log will look like:
DSM_getMenu(MenuId=0x104F LastSelectedLine=1)
RESPONSE Menu: M[Id=0x104F P=0x0 N=0x0 B=0x1000 Text="First Time Setup"[0x4A]]
SEND DSM_getFirstMenuLine(MenuId=0x104F)
RESPONSE MenuUknownLine_0x05: LineNum=0 DATA=RX: 09 05 00 01 00 00 00 07 00 00 00 00 00 00 00 00
CALL DSM_getNextUknownLine_0x05(LastLine=0)
RESPONSE MenuUknownLine_0x05: LineNum=0 DATA=RX: 09 05 00 01 00 00 00 07 00 00 00 00 00 00 00 00
ERROR: Received Same menu line
CALL DSM_getNextUknownLine_0x05(LastLine=0)
RESPONSE MenuUknownLine_0x05: LineNum=0 DATA=RX: 09 05 00 01 00 00 00 07 00 00 00 00 00 00 00 00
ERROR: Received Same menu line
We found that sometimes, Overriding LastSelectedLine to 0 solves the problem for some specific menus. Not for all (for other, is the oposite (0->1)). But at least no unknown lines are returned with this hack for AR631/AR637. Maybe others also needed.
**Overriding to Zero is not a good solution for every menu. Some menus needs the LastLine to know the behaviour (for example, Factory Reset the RX, Save Backup, Restore Backup, Enter Bind Mode, Some sensor Calibration). Thats why we cannot do it blindly.**
Here is the current code to fix some of this problems in AR631/AR637.
Function `DSM_SelLine_HACK()`
if (ctx.RX.Id == RX.AR637T or ctx.RX.Id == RX.AR637TA or ctx.RX.Id == RX.AR631) then
-- AR631/AR637 Hack for "First time Setup" or
-- "First Time AS3X Setup", use 0 instead of the ctx.SelLine=5
if (ctx.Menu.MenuId == 0x104F or ctx.Menu.MenuId==0x1055) then
LOG_write("First time Setup Menu HACK: Overrideing LastSelectedLine to ZERO\n")
ctx.SelLine = 0
end
-- DID NOT WORK: AR631/AR637 Hack for "Relearn Servo Settings", use 1 instead
-- of the ctx.SelLine=0
--if (ctx.Menu.MenuId == 0x1023) then
-- LOG_write("Relearn Servo Settings HACK: Overrideing LastSelectedLine to 1\n")
-- ctx.SelLine = 1
--end
Now it retrives properly the menu:
Log shows:
First time Setup Menu HACK: Overrideing LastSelectedLine to ZERO
DSM_getMenu(MenuId=0x104F LastSelectedLine=0)
RESPONSE Menu: M[Id=0x104F P=0x0 N=0x0 B=0x105E Text="First Time Setup"[0x4A]]
SEND DSM_getFirstMenuLine(MenuId=0x104F)
.. Good menu data

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
-- OVERRIDES Messges for MIN 128x64 screns
-- FORMAT <LineType>|<Msg#>|<Text>
-- Line Type: Text for Menus (T), List_Text Options (LT), List_Text_Image (LI), Flight Mode (FM), RX Name (RX)
-- IMPORTANT: NO EMPTY LINES
--
T |0x0097|DONT USE: Factory Reset
T |0x0098|DONT USE: Factory Reset
T |0x00A5|DONT USE: First Time Setup
T |0x00B0|Self-Lev/Ang Dem
T |0x00CD|Level model & capt attitude
T |0x0190|DONT USE: Relearn Servo Settings
T |0x020D|DONT USE: First Time SAFE Setup
T |0x0254|Pos = Up, Neg = Down
T |0x0267|Pos = Nose Up/Roll Right
T |0x0268|Neg = Nose Down/Roll Left

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

View File

@@ -0,0 +1,375 @@
-- FORMAT <LineType>|<Msg#>|<Text>
-- Line Type: Text for Menus (T), List_Text Options (LT), List_Text_Image (LI), Flight Mode (FM), RX Name (RX)
-- NO EMPTY LINES
-- Formmatting at end of line: /c=Center, /r=Right, /b=Bold, /m=menu
LT|0x0001|Off
LT|0x0002|On
-- Ihn/Act List Options
LT|0x0003|Inh
LT|0x0004|Act
--
-- Channel selection for SAFE MODE and GAINS on FC6250HX
LT|0x000C|Inhibit?
LT|0x000D|Ch5
LT|0x000E|Ch6
LT|0x000F|Ch7
LT|0x0010|Ch8
LT|0x0011|Ch9
LT|0x0012|Ch10
LT|0x0013|Ch11
LT|0x0014|Ch12
--
-- Servo Output values
LT|0x002D|5.5ms
LT|0x002E|11ms
LT|0x002F|22ms
--
-- Gain Multiplier Values
LT|0x0032|1 X
LT|0x0033|2 X
LT|0x0034|4 X
--
LT|0x0035|Inh?
LT|0x0036|Thr
LT|0x0037|Ail
LT|0x0038|Ele
LT|0x0039|Rud
LT|0x003A|Ch5
LT|0x003B|Ch6
LT|0x003C|Ch7
LT|0x003D|Ch8
LT|0x003E|Ch9
LT|0x003F|Ch10
LT|0x0040|Ch11
LT|0x0041|Ch12
LT|0x0042|Ch13
LT|0x0043|Ch14
LT|0x0044|Ch15
LT|0x0045|Ch16
LT|0x0046|Ch17
LT|0x0047|Ch18
LT|0x0048|Ch19
LT|0x0049|Ch20
--
T |0x0040|Roll
T |0x0041|Pitch
T |0x0042|Yaw
T |0x0043|Gain/c/b
T |0x0045|Differential
T |0x0046|Priority
T |0x0049|Output Setup
T |0x004A|Failsafe
T |0x004B|Main Menu
T |0x004E|Position
--
T |0x0050|Outputs
T |0x0051|Output Channel 1
T |0x0052|Output Channel 2
T |0x0053|Output Channel 3
T |0x0054|Output Channel 4
T |0x0055|Output Channel 5
T |0x0056|Output Channel 6
--
-- FailSafe Options
--LT|0x005E|Inhibit
LT|0x005F|Hold Last
LT|0x0060|Preset
--LT|0x0061|Custom
--
T |0x0071|Proportional
T |0x0072|Integral
T |0x0073|Derivate
--
T |0x0078|FM Channel
--
T |0x0080|Orientation
T |0x0082|Heading
T |0x0085|Frame Rate
T |0x0086|System Setup
T |0x0087|F-Mode Setup
T |0x0088|Enabled F-Modes
T |0x0089|Gain Channel
T |0x008A|Gain Sensitivity/r -- Right Align
T |0x008B|Panic
T |0x008E|Panic Delay
--
LT|0x008D|560hz
--
-- FC6250HX: Callibration Menu -> Begin..Start, Complete, Done
T |0x0091|Begin
T |0x0090|Apply
T |0x0092|Start
T |0x0093|Complete
T |0x0094|Done
--
T |0x0097|Factory Reset
T |0x0098|Factory Reset
--
T |0x0099|Advanced Setup
T |0x009A|Capture Failsafe Positions
T |0x009C|Custom Failsafe
--
T |0x009F|Save Settings -- Save & Reboot RX
--
T |0x00A5|First Time Setup
T |0x00AA|Capture Gyro Gains
T |0x00AD|Gain Channel Select
T |0x00AF|Dynamic
T |0x00B0|Self-Level/Angle Dem
T |0x00B1|Envelope
--
-- Flight Modes List Options
LT|0x00B5|Inhibit
LT|0x00B6|FM1
LT|0x00B7|FM2
LT|0x00B8|FM3
LT|0x00B9|FM4
LT|0x00BA|FM5
LT|0x00BB|FM6
LT|0x00BC|FM7
LT|0x00BD|FM8
LT|0x00BE|FM9
LT|0x00BF|FM10
--
T |0x00BE|Unknown_BE -- Used in Reset menu (0x0001) while the RX is rebooting
--
T |0x00C7|Calibrate Sensor
T |0x00CA|SAFE/Panic Mode Setup
--
T |0x00CD|Level model and capture attitude/m -- SPECIAL MENU to itself who is not a comment
--
-- RX Orientations for AR631/AR637, Optionally attach an Image + Alt Text to display
LT|0x00CB|Pos 1
LI|0x00CB|rx_pos_1.png|Pilot View: RX Label Up, Pins Back
LT|0x00CC|Pos 2
LI|0x00CC|rx_pos_2.png|Pilot View: RX Label Left, Pins Back
LT|0x00CD|Pos 3
LI|0x00CD|rx_pos_3.png|Pilot View: RX Label Down, Pins Back
LT|0x00CE|Pos 4
LI|0x00CE|rx_pos_4.png|Pilot View: RX Label Right, Pins Back
LT|0x00CF|Pos 5
LI|0x00CF|rx_pos_5.png|Pilot View: RX Label UP, Pins to Front
LT|0x00D0|Pos 6
LI|0x00D0|rx_pos_6.png|Pilot View: RX Label Left, Pins Front
LT|0x00D1|Pos 7
LI|0x00D1|rx_pos_7.png|Pilot View: RX Label Down, Pins Front
LT|0x00D2|Pos 8
LI|0x00D2|rx_pos_8.png|Pilot View: RX Label Right, Pins Front
LT|0x00D3|Pos 9
LI|0x00D3|rx_pos_9.png|Pilot View: RX Label Up, Pins Left
LT|0x00D4|Pos 10
LI|0x00D4|rx_pos_10.png|Pilot View: RX Label Back, Pins Left
LT|0x00D5|Pos 11
LI|0x00D5|rx_pos_11.png|Pilot View: RX Label Down, Pins Left
LT|0x00D6|Pos 12
LI|0x00D6|rx_pos_12.png|Pilot View: RX Label Front, Pins Left
LT|0x00D7|Pos 13
LI|0x00D7|rx_pos_13.png|Pilot View: RX Label Up, Pins Right
LT|0x00D8|Pos 14
LI|0x00D8|rx_pos_14.png|Pilot View: RX Label Back, Pins Right
LT|0x00D9|Pos 15
LI|0x00D9|rx_pos_15.png|Pilot View: RX Label Down, Pins Right
LT|0x00DA|Pos 16
LI|0x00DA|rx_pos_16.png|Pilot View: RX Label Front, Pins Right
LT|0x00DB|Pos 17
LI|0x00DB|rx_pos_17.png|Pilot View: RX Label Back, Pins Down
LT|0x00DC|Pos 18
LI|0x00DC|rx_pos_18.png|Pilot View: RX Label Left, Pins Down
LT|0x00DD|Pos 19
LI|0x00DD|rx_pos_19.png|Pilot View: RX Label Front, Pins Down
LT|0x00DE|Pos 20
LI|0x00DE|rx_pos_20.png|Pilot View: RX Label Right, Pins Down
LT|0x00DF|Pos 21
LI|0x00DF|rx_pos_21.png|Pilot View: RX Label Back, Pins Up
LT|0x00E0|Pos 22
LI|0x00E0|rx_pos_22.png|Pilot View: RX Label Left, Pins Up
LT|0x00E1|Pos 23
LI|0x00E1|rx_pos_23.png|Pilot View: RX Label Front, Pins Up
LT|0x00E2|Pos 24
LI|0x00E2|rx_pos_24.png|Pilot View: RX Label Right, Pins Up
LT|0x00E3|Pos Invalid
LI|0x00E3|rx_pos_25.png|Cannot detect orientation of RX
--
T |0x00D1|Receiver will Reboot/b
T |0x00D2|Panic Channel
T |0x00D3|Swashplate
T |0x00D5|Agility
T |0x00D8|Stop
T |0x00DA|SAFE/c/b -- Center + Bold
T |0x00DB|Stability
T |0x00DC|@ per sec
T |0x00DD|Tail rotor
T |0x00DE|Setup
T |0x00DF|AFR
T |0x00E0|Collective
T |0x00E1|Subtrim
T |0x00E2|Phasing
T |0x00E4|E-Ring
T |0x00E5|Swash Type
T |0x00E6|Travel
T |0x00E7|Left
T |0x00E8|Right
--
LT|0x00F2|Fixed
LT|0x00F3|Adjustable
--
T |0x00F6|Direction
T |0x00F8|Settings -- ?? validate on a Spektrum radio
T |0x00F9|Gyro settings
T |0x00FE|Stick Priority/c/b
--
T |0x0100|Make sure the model has been
T |0x0101|configured, including wing type,
T |0x0102|reversing, travel, trimmed, etc.
T |0x0103|before continuing setup.
T |0x0104| -- Blank
--
T |0x0106|Any wing type, channel assignment,
T |0x0107|subtrim, or servo reversing changes
T |0x0108|require running through initial
T |0x0109|setup again.
T |0x010A| -- Blank
--
T |0x0190|Relearn Servo Settings
T |0x019C|Enter Receiver Bind Mode
T |0x01AA|Offset
T |0x01D7|SAFE Select Channel
T |0x01DC|AS3X/c/b -- Center + Bold
T |0x01DD|AS3X Settings
T |0x01DE|AS3X Gains
T |0x01E0|Rate Gains/c/b
T |0x01E2|SAFE Settings
T |0x01E3|SAFE Gains
T |0x01E6|Attitude Trim/c/b
T |0x01E7|Envelope
T |0x01E9|Roll Right
T |0x01EA|Roll Left
T |0x01EB|Pitch Down
T |0x01EC|Pitch Up
T |0x01EE|Thr to Pitch
T |0x01EF|Low Thr to Pitch/c/b
T |0x01F0|High Thr to Pitch/c/b
T |0x01F3|Threshold
T |0x01F4|Angle
T |0x01F6|Failsafe Angles/c/b
T |0x01F8|Safe Mode
T |0x01F9|SAFE Select
T |0x01FC|Panic F-Mode
T |0x01FD|FailSafe Flight Mode -- Safe Flight Mode
T |0x0201|Throttle
T |0x0204|Hover
T |0x0208|Decay
T |0x0209|Save to Backup
T |0x020A|Restore from Backup
T |0x020D|First Time SAFE Setup
--
-- First time safe setup Page 3 :
T |0x020E|AS3X gains must be tuned
T |0x020F|and active in SAFE Flight Modes
T |0x0210|to help reduce wobble.
T |0x0211| -- Blank
T |0x0212| -- Blank
T |0x0213| -- Blank
--
-- AS3X orientation Setting menu (Level)
T |0x021A|Set the model level,
T |0x021B|and press Continue.
T |0x021C| -- Blank
T |0x021D| -- Blank
--
-- AS3X orientation Setting menu (Nose down)
T |0x021F|Set the model on its nose,
T |0x0220|and press Continue. If the
T |0x0221|orientation on the next
T |0x0222|screen is wrong go back
T |0x0223|and try again.
--
T |0x0224|Continue
T |0x0226|Angle Limits/c/b
T |0x0227|Other settings
T |0x0229|Set Orientation Manually
--
-- Factory Default Warning
T |0x022B|WARNING!
T |0x022C|This will reset the
T |0x022D|configuration to factory
T |0x022E|defaults. This does not
T |0x022F|affect the backup config.
T |0x0230| -- Blank
--
-- Backup Warning
T |0x0231|This will overwrite the
T |0x0232|backup memory with your
T |0x0233|current configuartion.
T |0x0234| -- Blank
T |0x0235| -- Blank
--
-- Restore from Backup Warning
T |0x0236|This will overwrite the
T |0x0237|current config with
T |0x0238|that which is in
T |0x0239|the backup memory.
T |0x023A| -- blank line
--
-- Utilities Copy flight modes
T |0x023D|Copy F-Mode Settings
T |0x023E|Source F-Mode
T |0x023F|Target F-Mode
--
T |0x0240|Utilities
--
-- Gain Capture Page
T |0x024C|Gains will be captured on
T |0x024D|Captured gains will be
T |0x024E|Gains on
T |0x024F|were captured and changed
T |0x0250|from Adjustable to Fixed
--
-- Utilities, Copy flight mode (Copy Confirmation, oveerriding FM)
T |0x0251|Are you sure you want to ovewrite the "Target"
T |0x0252|with the "Source" ?
T |0x0253| -- Blank
--
T |0x0254|Postive = Up, Negative = Down
--
-- First time safe setup Page 1 (maybe ask to select Flight Mode cannel)
T |0x0255|Before setting up SAFE
T |0x0256|a Flight Mode channel
T |0x0257|most be configured.
--
-- First time safe setup Page 2 (something related for flight mode)
T |0x025A|Select the desired flight mode
T |0x025B|switch position to adjust settings
T |0x025C|for each flight mode
T |0x025D| -- Blank
T |0x025E| -- Blank
--
-- Utilities, Copy flight mode (Confirm)
T |0x0259|YES
T |0x0260|WARNING: "Target"
T |0x0261|F-Mode will be overwritten
T |0x0262|by "Source"
--
T |0x0263|Fixed/Adjustable Gains/c/b
T |0x0266|Heading Gain/c/b
T |0x0267|Positive = Nose Up/Roll Right
T |0x0268|Negative = Nose Down/Roll Left
T |0x0269|SAFE - Thr to Pitch
T |0x026A|Use CAUTION for Yaw gain!/b
--
T |0x0300|No compatible DSM RX...
T |0x0301|Waiting for RX to Restart
--
FM|0x8000|Flight Mode/c/b
--
RX|0x0001|AR636
RX|0x0014|SPM4651T
RX|0x0015|AR637T
RX|0x0016|AR637TA
RX|0x0018|FC6250HX
RX|0x0019|AR630
RX|0x001A|AR8360T
RX|0x001B|AR8020T
RX|0x001C|AR10360T
RX|0x001E|AR631

View File

@@ -0,0 +1,227 @@
# Credits
Code is based on the code/work by: Pascal Langer (Author of the Multi-Module)
Rewrite/Enhancements by: Francisco Arzu
Thanks to all the people volunteered to test it.
# Introduction (v0.54)
This script library enhances the original DSM Forward Programming tool. DSM Forward Programming is needed to setup many of the new Spektrum Receivers with Gyro AS3X/SAFE features. For the Gyro (/Safe) to correct the plane in flight, it needs to move the right surfaces therefore the RX needs to know the configuration of the plane (Wing Type, Tail Type, Mixers, Servo Assignments, Servo Reverse). That info tells the RX where the aileron(s) are (one or two), where the elevator(s) are (one or two), V-Tail, Delta Wing, etc.
Since EdgeTx/OpenTx doesnt have an equivalent setup that is stored in the radio, we have to create our own version. This info is stored inside the `/MODELS/DSMDATA` directory/folder (which needs to be created by manually).
During `"Gyro Settings->initial setup"`, the RX asks the TX for model information behind the scenes. After setup, `"Gyro Settings->System Tools-> Relearn Servo Settings"` requests the TX configuration and stores it in the RX.
# Deployment
Make sure to manually create `/MODELS/DSMDATA` . The script will complain at startup if it does not exist. Here the script saves the Spektrun settings for each of your models.
Uncompress the Zip file (ZIP version) into your local computer.
In another window, open your TX SDCard and go to /SCRIPTS/TOOLS.
When upgrading from a previous version of this tool, delete your /SCRIPTS/TOOLS/DSMLIB before copying the new one (if you customized your images, inside "DSMLIB/img" do a backup first)
Copy the entire DSMLIB folder.
Copy the main script you want to use (Color or B&W).
Your TX SDCard should looks like this:
/SCRIPTS/TOOLS
DSM FwdPrg_05_BW.lua -- black/white text only
DSM FwdPrg_05_Color.lua -- Color and touch radios
DSM FwdPrg_05_MIN.lua -- `NEW!` Minimalistic version for radios with LOW memory (cannot setup new planes)
/SCRIPTS/TOOLS/DSMLIB/ -- (ALL CAPITALS) Libraries ane extra files
DsmFwPrgLib.lua -- DSM Protocol Message and Menu engine
DsmFwPrgSIMLib.lua -- Simulation of AR631, FC6250HX (For GUI development)
SetupLib.lua -- Model Setup Screens
msg_fwdp_en.txt -- `NEW!` Messages for forward programing externalized. To support other langs
/SCRIPTS/TOOLS/DSMLIB/img -- Images for RX orientations
Other Directories
/MODELS/DSMDATA --(ALL CAPITALS) Data of model config (Wing Type, Servo Assignments)
/LOGS/dsm_log.txt --Readable log of the last RX/TX session, usefull for debugging problems
When upgrading from a previous version of this tool, delete your /SCRIPTS/TOOLS/DSMLIB before copying the new one (if you customized your images, inside "DSMLIB/img" do a backup first)
# Common Questions
1. `RX not accepting channels higher than Ch6 for Flight-mode o Gains:`
V0.53 and newer: The RX is listening to channel changes for this options. Configure the Switch to the channel, togling once the switch will select the channel on the menu field.
2. `Why Ch1 says Ch1 (TX:Ch3/Thr)?`:
Radios with Multi-Module are usually configured to work the standard AETR convention. Spektrum uses TAER. The multi-module does the conversion when transmitting the signals. So `Spektrum Ch1 (Throttle)` really comes from the `TX Ch3`. We show both information (+name from the TX output). If your multi-module/radio is setup as TAER, the script will not do the re-arrangement.
3. `If i change the model name, the original model settings are lost.` This is correct, the model name is used to generate the file name (inside /MODEL/DSMDATA) who stores the model configuration. Currently EdgeTx and OpenTX has differt features where i could get either the Model Name or the YAML file where the EdgeTX model configuration is stored.. to keep the code compatible, the model name is used.
4. `Reversing a channel in my TX do not reverse the AS3X/SAFE reaction.` Correct, the channel stick direction and the Gyro direction are two separate things.
4.1: First, you have setup your model so that the sticks and switches moves the surfaces in the right direction.
4.2: Go to the script, `Model Setup` and setup your wing type, tail type, and select the channel assigment for each surface. Leave the servo settings the same as the values in the TX to start.
4.3: AR63X family: Go to `Forward programming->Gyro Setting->Initial Setup` (New/factory reset), or `Forward programming->Gyro Setting->System Setup->Relearn Servo Settings` (not new RX). This will load your current Gyro servo settings into the plane's RX.
4.4: Verify that the AS3X and SAFE reacts in the proper direction. You can use the Flight mode confugured as "Safe Mode: Auto-Level" to see if it moves the surfaces in the right direction.
4.5: If a surface don't move in the right direction, go to the `Model Setup->Gyro Channel Reverse` to reverse the Gyro on the channels needed, and do again the `Forward programming->Gyro Setting->System Setup->Relearn Servo Settings` to tranfer the new settings to the RX.
4.6: Specktrum TX always passes the TX servo reverse as the Gyro Reverse, but on many OpenTX/EdgeTX radios, the Rud/Ail are usually reversed by default compared to Specktrum. So far i don't think that i can use this as a rule, that is why the `Gyro Channel Reverse` page exist.
---
---
# Changes and fixes
V0.54:
1. Fix a problem in the Attitude Trim page (`Gyro Settings->System Setup->SAFE/Panic Setup->Attitude Trim`). It was not saving the values after exiting the menu. This is to change what SAFE considers "Level" flying.
2. Wings 2-Ail 2-Flaps had a bug on the 2nd flap.
3. New Minimalistic script (`DsmFwdPrg_05_MIN.lua`): For radios with very low memory (FrSky QX7, RM Zorro, others). It can only change existing settings, but does not have the Plane Setup menus to setup a completly new plane. In some radios, the very first time it runs (compile + run), it might give you a `not enouth memory` error.. try to run it again.
4. External menu message file (DSMLIB/msg_en.txt and msg_MIN_es.txt). Intial work to do localization and different languages.
V0.53:
1. Improved channel selection (Flight mode, Panic Channel, Gains Channel). Now during editing a channel, you can select any channel (>Ch4). Also, of you toggle the switch/channel it will populate the screen.
2. Support for smaller screens (128x64) in B&W. The problem with this older radios is memory. In some, it does not have enouth memory to load the additional DSMLIB libraries.
3. Fix formatting problem with some TX channel names who could affect the screen.. for example, rud channel should show "Ch4/rud", but shows "Ch4ud" because /r is for right justify formatting on messages. Now the formatting is only if it appears at the end of the message.
V0.52:
1. Menus to be able to configure Plane in a similar way as Spektrum Radio (v0.52)
2. Make "Gyro Settings"->"Initial Setup" works (Tested on AR631,AR637xx with PLANE type of aircraft)
3. Properly reset and restart after initial configuration and SAFE changes.
4. Write Log of the conversation between RX/TX. To be used for debugging a problem is reported.
5. Provide a simulation of RX to do GUI development in Companion, and understand patterns of how the data is organized.
# Tested Hardware
- AR631/AR637xx
- FC6250HX (Blade 230S V2 Helicopter)
- AR636 (Blade 230S V1 Heli firmware 4.40)
- Radiomaster TX16S (All versions)
- FrSky QX7, Radimaster Boxter (Minimalistic version)
Please report if you have tested it with other receivers to allow us to update the documentation. Code should work up to 10 channels for the main surfaces (Ail/Ele/etc). All Spektrum RX are internally 20 channels, so you can use Ch7 for Flight Mode even if your RX is only 6 channels (See common Questions)
# Messages Displayed in the GUI
If in a screen you get text that looks like `Unknown_XX` (ex: Unknown_D3), that message has not been setup in the script in english. If you can determine what the proper message is, you can send us a message to be added to the library.
The `XX` represents a Hex Number (0..9,A..F) message ID.
### Version 0.53 and older:
If you want to fix it in your local copy, all messages are towards the end in the file `SCRIPT\TOOS\DSMLIB\DsmFwPrgLib.lua`. Messages for Headers are stored in `Text` and messages for Options are stored in `List_Text`. Lua scripts are text files, and can be edited with Notepad or equivalent.
Portion of DsmFwPrgLib.lua:
Text[0x0097] = "Factory Reset"
Text[0x0098] = "Factory Reset" -- FC6250HX: Title
Text[0x0099] = "Advanced Setup"
Text[0x009A] = "Capture Failsafe Positions"
Text[0x009C] = "Custom Failsafe"
Text[0x009F] = "Save & Reset RX" -- TODO: Find the Proper Spektrum Value ??
Text[0x00A5] = "First Time Setup"
Text[0x00AA] = "Capture Gyro Gains"
Text[0x00AD] = "Gain Channel Select"
-- Safe mode options, Inhibit + the values
local safeModeOptions = {0x0003,0x00B0,0x00B1} -- inh (gap), "Self-Level/Angle Dem, Envelope
List_Text[0x00B0] = "Self-Level/Angle Dem"
List_Text[0x00B1] = "Envelope"
For example, if you get `Unknown_9D` in the GUI and your now that it should say **NEW Text**, you can edit the lua script to look like this:
Text[0x009A] = "Capture Failsafe Positions"
Text[0x009C] = "Custom Failsafe"
Text[0x009D] = "NEW Text" -- NEW Text added for AR98xx
Text[0x009F] = "Save & Reset RX" -- TODO: Find the proper Spektrum text
### Version 0.54 and newer:
The menu messages are stored in DSMLIB/msg_fwdp_en.txt (For english). Just add the message there. MIN_msg_fwdp_en.txt has shorter messages overrides for screens who are smaller (for minimalistic 128x64 version). The reference to the message file is at the file `/DSMLIB/DsmFwPrgLib.lua` if you want to change to use another language.
T |0x0097|Factory Reset
LT|0x00B0|Self-Level/Angle Dem
LT|0x00B1|Envelope
# LOG File
The log file of the last use of the script is located at `/LOGS/dsm_log.txt`. **It is overridden on every start to avoid filling up the SD card**. So if you want to keep it, copy or rename it before starting the script again. (it can be renamed in the TX by browsing the SD card)
The log is human readable. The first number is the number of seconds since the start, and then what is the current state of the Library, and what has been sent and received. The info in the log can be easily used to create a new simulation for that RX in the future.
Example Log:
5.340 WAIT_CMD: DSM_GotoMenu(0x1010,LastSelectedLine=0)
5.350 MENU_TITLE: SEND DSM_getMenu(MenuId=0x1010 LastSelectedLine=0)
5.440 MENU_TITLE: RESPONSE Menu: M[Id=0x1010 P=0x0 N=0x0 B=0x1000 Text="Gyro settings"[0xF9]]
5.490 MENU_LINES: SEND DSM_getFirstMenuLine(MenuId=0x1010)
5.590 MENU_LINES: RESPONSE MenuLine: L[#0 T=M VId=0x1011 Text="AS3X Settings"[0x1DD] MId=0x1010 ]
5.640 MENU_LINES: SEND DSM_getNextLine(MenuId=0x1010,LastLine=0)
5.740 MENU_LINES: RESPONSE MenuLine: L[#1 T=M VId=0x1019 Text="SAFE Settings"[0x1E2] MId=0x1010 ]
5.790 MENU_LINES: SEND DSM_getNextLine(MenuId=0x1010,LastLine=1)
5.850 MENU_LINES: RESPONSE MenuLine: L[#2 T=M VId=0x1021 Text="F-Mode Setup"[0x87] MId=0x1010 ]
5.910 MENU_LINES: SEND DSM_getNextLine(MenuId=0x1010,LastLine=2)
5.970 MENU_LINES: RESPONSE MenuLine: L[#3 T=M VId=0x1022 Text="System Setup"[0x86] MId=0x1010 ]
6.020 MENU_LINES: SEND DSM_getNextLine(MenuId=0x1010,LastLine=3
# Validation of data by the RX
The RX validates the data. if you change to an invalid channel or do a invalid number range, the RX will change it at the end of editing the field.
---
# Version 0.53
- Improve Channel selection in menus
- Support smaller screens 128x64 in the black/white mode.
# Version 0.52
- Fix Reversing of Servos
- Properly detect Multimodule Ch settings AETR
---
# Version 0.51 (volunteer testing version, not for production)
- New Screens to Configure Model (Wing Type/Tail Tail, etc)
- Finally got understanding that the previous unknown 0x05 lines are to send Model/Servo data to RX.
- Fix use of AR636B (Firmware version 4.40.0 for Blade 230 heli, is the only one with Forward Programming)
- Aircraft types: Tested With Plane type only.. Glider and other in progress
### Known Problems:
- 4-Servo Wing type (Dual Ail/Tail) in planes give conflicting servo assignments by defaults.. Solution choose your own Ch.
- Glider, Heli, Drone: Still in development. In glider, only a few wing type works.. needs to restrict menu options for the only valid one.
# Version 0.5
- Make the code more readable and understandable
- Separate the DSM Forwards Programming logic from the GUI
- Log the communication with the RX on a /LOGS/dsm_log.txt to allow to debug it easier
and see the exchange of data between the RX/TX
- Created a black/white Text only version with only Key/Roller Inputs
- Created a nicer GUI for EdgeTX touch screen color Radios
- RX simulation for GUI development: turn on `SIMULATION_ON=true` in the beginning of the lua file
- Test it on AR631, AR637xx, FC6250HX (Helicopter)
### Some settings that can change (top of Lua file):
SIMULATION_ON = false -- FALSE: hide similation menu (DEFAULT), TRUE: show RX simulation menu
DEBUG_ON = 1 -- 0=NO DEBUG, 1=HIGH LEVEL 2=LOW LEVEL (Debug logged into the /LOGS/dsm_log.txt)
USE_SPECKTRUM_COLORS = true -- true: Use spectrum colors, false: use theme colors (default on OpenTX, OpenTX handle colors different)
### Known Problems:
1. **Incorrect List Value Options:** Some Menu List line (`LINE_TYPE.LIST_MENU1` or `L_m1` in logs), the range (min/max) of valid values seems to be incorrect, but the RX corrects the values.
in the MINimalistic version, the RX is doing all the range validation, and will show invalid options temporarilly. In an Spektrum radio, it happens so fast, that you don't notice it, but in LUA scripts who are slower, you can see it in the screen.
In the COLOR version, The code has hardcoded the valid ranges to avoid this problem.
2. Glider/Heli/Drone wing types not ready.
For Helicopter, use airplane normal wing and normal tail
# Version 0.2
Original Version from Pascal Langer

View File

@@ -0,0 +1,683 @@
local toolName = "TNS|DSM AR636 Telemetry|TNE"
---- ######################################################################### #
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
---- # #
---- # 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. #
---- # #
---- # This program is distributed in the hope that it will be useful #
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
---- # GNU General Public License for more details. #
---- # #
---- #########################################################################
------------------------------------------------------------------------------
-- Developer: Francisco Arzu
-- Original idea taken from DsmPID.lua.. don't know who is the author
--
local DEBUG_ON = false
--
local TEXT_SIZE = 0 -- NORMAL
local X_COL1_HEADER = 6
local X_COL1_DATA = 60
local X_COL2_HEADER = 170
local X_COL2_DATA = 220
local Y_LINE_HEIGHT = 20
local Y_HEADER = 0
local Y_DATA = Y_HEADER + Y_LINE_HEIGHT*2
local X_DATA_LEN = 80
local X_DATA_SPACE = 5
local function getPage(iParam)
-- get page from 0-based index
-- {0,1,2,3}: cyclic (1), {4,5,6,7}: tail (2)
local res = (math.floor(iParam/4)==0) and 0 or 1
return res
end
local function round(v)
-- round float
local factor = 100
return math.floor(v * factor + 0.5) / factor
end
local function readValue(sensor)
-- read from sensor, round and return
local v = getValue(sensor)
--v = round(v)
return v
end
local function readValueById(sensor)
local i = getFieldInfo(sensor)
if (i==nil) then return nil end
local v = getValue(i.id)
return v
end
local function readBatValue(sensor)
-- read from sensor, round and return
local v = getValue(sensor)
if (v==nil) then return "--" end
return string.format("%2.2f",v)
end
local function readActiveParamValue(sensor)
-- read and return a validated active parameter value
local v = getValue(sensor)
if (v<1 or v>8) then
return -1
end
return v
end
local function drawPIDScreen()
-- draw labels and params on screen
local pageId = getValue("FLss")
lcd.clear()
-- if active gain does not validate then assume
-- Gain Adjustment Mode is disabled
if not (pageId==4401 or pageId==4402) then
lcd.drawText(0,0,"BLADE Gain Adjustment", TEXT_SIZE +INVERS)
lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*1,"Enter Gain Adjustment Mode",TEXT_SIZE)
lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*2,"Stk: Low/R + Low/R + Panic (3 sec)",TEXT_SIZE)
lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*4,"Op: Right Stk: Up/Down to select, Left/Right change value",TEXT_SIZE)
lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*5,"Panic to exit",TEXT_SIZE)
return
end
local activePage = (pageId % 100)-1 --Last 2 digits, make it zero base
lcd.drawText (X_COL1_HEADER, Y_HEADER, "Cyclic (0-200)", TEXT_SIZE + INVERS)
lcd.drawText (X_COL2_HEADER, Y_HEADER, "Tail (0-200)", TEXT_SIZE + INVERS)
local p = readValue("FdeA")
local i = readValue("FdeB")
local d = readValue("FdeL")
local r = readValue("FdeR")
local titles = {[0]="P:", "I:", "D:", "Resp:", "P:","I:","D:", "Filt:"}
local values = {[0]=p,i,d,r,p,i,d,r}
local activeParam = readActiveParamValue("Hold")-1
for iParam=0,7 do
-- highlight selected parameter
local attr = (activeParam==iParam) and INVERS or 0
-- circular index (per page)
local perPageIndx = (iParam % 4)
-- set y draw coord
local y = (perPageIndx+1)*Y_LINE_HEIGHT+Y_DATA
-- check if displaying cyclic params.
local isCyclicPage = (getPage(iParam)==0)
-- labels
local x = isCyclicPage and X_COL1_HEADER or X_COL2_HEADER
-- labels are P,I,D for both pages except for last param
local val = titles[iParam]
lcd.drawText (x, y, val, TEXT_SIZE)
-- gains
-- set all params for non-active page to '--' rather than 'last value'
val = (getPage(iParam)==activePage) and values[iParam] or '--'
x = isCyclicPage and X_COL1_DATA or X_COL2_DATA
if (val~=16384) then -- Active value
lcd.drawText (x, y, val, attr + TEXT_SIZE)
end
end
end
local function drawFlightLogScreen()
-- draw labels and params on screen
local h = getValue("Hold")
local activeParam = h-1 -- H
lcd.clear()
lcd.drawText (X_COL1_HEADER, Y_HEADER, "Flight Log", TEXT_SIZE + INVERS)
-- read and return parameters
local a = getValue("FdeA")
local b = getValue("FdeB")
local l = getValue("FdeL")
local r = getValue("FdeR")
local f = getValue("FLss")
local titles = {[0]="A:", "B:", "L:", "R:", "F:", "H:"}
local values = {[0]=a,b,l,r,f,h}
local y = Y_LINE_HEIGHT+Y_DATA
for iParam=0,3 do -- A,B,L,R
-- highlight selected parameter (rund)
local attr = ((activeParam%4)==iParam) and INVERS or 0
-- labels
local x = X_COL1_HEADER
local val = titles[iParam]
lcd.drawText (x, y, val, TEXT_SIZE)
-- Values
val = values[iParam]
x = X_COL1_DATA + X_DATA_LEN
if (val~=16384) then -- Active value
lcd.drawText (x, y, val, attr + TEXT_SIZE + RIGHT)
end
y = y + Y_LINE_HEIGHT
end
y = Y_LINE_HEIGHT+Y_DATA
for iParam=4,5 do -- F, H
-- labels
local x = X_COL2_HEADER
local val = titles[iParam]
lcd.drawText (x, y, val, TEXT_SIZE + BOLD)
-- Values
val = values[iParam]
x = X_COL2_DATA + X_DATA_LEN
lcd.drawText (x, y, val, TEXT_SIZE + RIGHT + BOLD)
y = y + Y_LINE_HEIGHT
end
-- Bat
y = y + Y_LINE_HEIGHT
local bat = readBatValue("A2") or "--"
lcd.drawText (X_COL2_HEADER, y, "Bat:", TEXT_SIZE)
lcd.drawText (X_COL2_DATA + X_DATA_LEN, y, bat, TEXT_SIZE + RIGHT)
lcd.drawText (X_COL2_DATA + X_DATA_LEN + X_DATA_SPACE, y, "v", TEXT_SIZE)
end
local function servoAdjustScreen()
-- draw labels and params on screen
local pageId = getValue("FLss") -- FLss
local activeParam = getValue("Hold")-1 -- Hold
lcd.clear()
lcd.drawText (0, Y_HEADER, "BLADE Servo SubTrim", TEXT_SIZE + INVERS)
if pageId~=1234 then
lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*1,"Enter Servo Adjustment Mode",TEXT_SIZE)
lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*2,"Stk: Low/L + Low/R + Panic (3 sec)",TEXT_SIZE)
lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*4,"Op: R Stk: Up/Down to select, Left/Right change value",TEXT_SIZE)
lcd.drawText(X_COL1_HEADER,Y_LINE_HEIGHT*5,"Panic to exit",TEXT_SIZE)
return
end
local a = getValue("FdeA")
local b = getValue("FdeB")
local l = getValue("FdeL")
local titles = {[0]="Servo1:", "Servo2:", "Servo3:"}
local values = {[0]=a,b,l}
for iParam=0,#values do -- S1,S2,S3
-- highlight selected parameter
local attr = (activeParam==iParam) and INVERS or 0
-- set y draw coord
local y = (iParam+1)*Y_LINE_HEIGHT+Y_HEADER
-- labels
local x = X_COL1_HEADER
local val = titles[iParam]
lcd.drawText (x, y, val, TEXT_SIZE)
val = values[iParam]
x = X_COL1_DATA
if (val~=16384) then -- Active value
lcd.drawText (x, y, val, attr + TEXT_SIZE)
end
end
end
local function Unsigned_to_SInt16(value)
if value >= 0x8000 then -- Negative value??
return value - 0x10000
end
return value
end
local function getDegreesValue(sensor)
local i = getFieldInfo(sensor)
if (i==nil) then return "-unk-" end
local v = getValue(i.id)
if v==nil then return "---" end
local vs = Unsigned_to_SInt16(v)
return string.format("%0.1f o",vs/10)
end
local function getDecHexValue(sensor)
local i = getFieldInfo(sensor)
if (i==nil) then return "-unk-" end
local v = getValue(i.id)
if v==nil then return "---" end
local vs = Unsigned_to_SInt16(v)
return string.format("%d (0x%04X)",vs,v)
end
local function drawVersionScreen()
local paramV = getValue("FdeA")
local B = getValue("FdeB")
local rxId = getValue("FdeL")
local firmware = getValue("FLss")
local prodId = getValue("Hold")
local bat = readBatValue("A2")
lcd.clear()
lcd.drawText (0, Y_HEADER, "BLADE Version", TEXT_SIZE + INVERS)
--Product ID
local val = "ID_".. prodId
if (prodId==243) then val = "Blade 230 V1"
elseif (prodId==250) then val = "Blade 230 V2 (not Smart)"
elseif (prodId==149) then val = "Blade 250 CFX"
end
local y = Y_DATA
local x_data1 = X_COL1_DATA+X_DATA_LEN
lcd.drawText (X_COL1_HEADER, y, "Prod:", TEXT_SIZE)
lcd.drawText (x_data1, y, val, TEXT_SIZE)
-- RX
val = "ID_"..rxId
if (rxId==1) then val = "AR636"
end
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER, y, "RX:", TEXT_SIZE)
lcd.drawText (x_data1, y, val, TEXT_SIZE)
-- Firmware
val = string.format("%0.2f",firmware/100)
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER, y, "Firmware:", TEXT_SIZE)
lcd.drawText (x_data1, y, val, TEXT_SIZE)
-- ParamV
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER, y, "Params:", TEXT_SIZE)
lcd.drawText (x_data1, y, paramV, TEXT_SIZE)
-- Bat
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER, y, "Bat:", TEXT_SIZE)
lcd.drawText (x_data1, y, bat, TEXT_SIZE)
y = y + Y_LINE_HEIGHT
lcd.drawText(X_COL1_HEADER,y,"Press Panic for 3s",TEXT_SIZE)
y = y + Y_LINE_HEIGHT
lcd.drawText(X_COL1_HEADER,y,"Usually Panic is Ch7 on a switch and Revesed",TEXT_SIZE)
end
local function parseFlightMode(v)
-- FlightMode (Hex: MMSGG) MM=Flight Mode, S=Status (0= off, 1=init, 2=Hold, 3=Running) GG=???
if v==nil then return "---" end
local fm = bit32.rshift(v, 12)
local status = bit32.band(bit32.rshift(v, 8),0xF)
local res = " "..fm.." "
if (fm==0) then res = res .. " NORMAL"
elseif (fm==1) then res = res .. " INTERMEDIATE"
elseif (fm==2) then res = res .. " ADVANCED"
elseif (fm==5) then res = res .. " PANIC"
end
if (status==2) then res=res .. " HOLD" end
if (DEBUG_ON) then
res = res .. string.format(" (0x%04X)",v)
end
return res
end
local function drawAlpha6Monitor()
lcd.clear()
local RxStatus = readValueById("2402") -- FlightMode (Hex: MMSGG) MM=Flight Mode, S=Status (0=init, 2=Ready, 3=Sensor Fault) GG=???
local ARoll = getDegreesValue("2406") --Att Roll
local APitch = getDegreesValue("2408") --Att Pitch
local AYaw = getDegreesValue("240B") --Att Yaw
lcd.drawText (0,0, "BLADE Alpha6 Monitor", TEXT_SIZE+INVERS)
local y = Y_DATA
local x_data1 = X_COL1_DATA+X_DATA_LEN
local x_data2 = X_COL1_DATA+X_DATA_LEN*2
local x_data3 = X_COL1_DATA+X_DATA_LEN*3
-- Flight Mode
lcd.drawText (0,y, "F-Mode:"..parseFlightMode(RxStatus), TEXT_SIZE)
y = y + Y_LINE_HEIGHT
lcd.drawText (x_data1,y, "Attitude", TEXT_SIZE+BOLD + RIGHT)
lcd.drawText (x_data2,y, "Gyro", TEXT_SIZE+BOLD + RIGHT)
lcd.drawText (x_data3,y, "Gain", TEXT_SIZE+BOLD + RIGHT)
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER,y, "Rol:", TEXT_SIZE)
lcd.drawText (x_data1,y, ARoll, TEXT_SIZE + RIGHT)
lcd.drawText (x_data2,y, "-", TEXT_SIZE + RIGHT)
lcd.drawText (x_data3,y, "-", TEXT_SIZE + RIGHT)
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER,y, "Pitch:", TEXT_SIZE)
lcd.drawText (x_data1,y, APitch, TEXT_SIZE + RIGHT)
lcd.drawText (x_data2,y, "-", TEXT_SIZE + RIGHT)
lcd.drawText (x_data3,y, "-", TEXT_SIZE + RIGHT)
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER,y, "Yaw:", TEXT_SIZE)
lcd.drawText (x_data1,y, AYaw, TEXT_SIZE + RIGHT)
lcd.drawText (x_data2,y, "-", TEXT_SIZE + RIGHT)
lcd.drawText (x_data3,y, "-", TEXT_SIZE + RIGHT)
y = y + Y_LINE_HEIGHT + Y_LINE_HEIGHT
lcd.drawText (0,y, "Bat: "..readBatValue("A2").." v", TEXT_SIZE)
-- Debug Values
if (DEBUG_ON) then
local s2400 = getDecHexValue("2400")
local s2402 = getDecHexValue("2402")
local s2404 = getDecHexValue("2404")
local s240D = getDecHexValue("240D")
local s1G00 = getDecHexValue("1G00")
local s1G02 = getDecHexValue("1G02")
local s1G04 = getDecHexValue("1G04")
local s1G06 = getDecHexValue("1G06")
local s1G08 = getDecHexValue("1G08")
local s1G0B = getDecHexValue("1G0B")
local s1G0D = getDecHexValue("1G0D")
local titles = {[0]=
"2400","2402/FM-S-?",
"2404","240D",
"1G00","1G02","1G04",
"1G06","1G08","1G0B","1G0D"}
local values = {[0]=
s2400,s2402,s2404,s240D,
s1G00,s1G02,s1G04,
s1G06,s1G08,s1G0B,s1G0D}
-- draw labels and params on screen
y = Y_LINE_HEIGHT*2 + Y_HEADER
for iParam=0,#titles do -- ??
-- labels
local x = X_COL1_HEADER+220
local val = titles[iParam]
lcd.drawText (x, y, val, TEXT_SIZE)
val = values[iParam]
x = X_COL1_DATA+250
lcd.drawText (x, y, val, TEXT_SIZE)
y = y + Y_LINE_HEIGHT
end
end
end
local function readAlpha3arameters()
end
local function drawAS3XMonitor()
lcd.clear()
local s1G00 = getDecHexValue("1G00")
local s1G02 = getDecHexValue("1G02")
local s1G04 = getDecHexValue("1G04")
local s1G06 = getDecHexValue("1G06")
local s1G08 = getDecHexValue("1G08")
local s1G0B = getDecHexValue("1G0B")
local s1G0D = getDecHexValue("1G0D")
local s6C00 = getDecHexValue("6C00")
local s6C02 = getDecHexValue("6C02")
local s6C04 = getDecHexValue("6C04")
local RRoll = bit32.rshift(getValue("1G00") or 0,8)
local RPitch = bit32.band(getValue("1G00") or 0,0xFF)
local RYaw = bit32.rshift(getValue("1G02") or 0,8)
local HRoll = bit32.band(getValue("1G02") or 0,0xFF)
local HPitch = bit32.rshift(getValue("1G04") or 0,8)
local HYaw = bit32.band(getValue("1G04") or 0,0xFF)
local ARoll = bit32.rshift(getValue("1G06") or 0,8)
local APitch = bit32.band(getValue("1G06") or 0,0xFF)
local AYaw = bit32.rshift(getValue("1G08") or 0,8)
lcd.drawText (0,0, "Plane AR636 AS3X Gains", TEXT_SIZE+INVERS)
local y = Y_DATA
local x_data1 = X_COL1_DATA+X_DATA_LEN
local x_data2 = X_COL1_DATA+X_DATA_LEN*2
local x_data3 = X_COL1_DATA+X_DATA_LEN*3.1
-- Flight Mode
--lcd.drawText (0,y, "F-Mode: "..(nil or "--"), TEXT_SIZE)
y = y + Y_LINE_HEIGHT
lcd.drawText (x_data1,y, "Rate", TEXT_SIZE+BOLD + RIGHT)
lcd.drawText (x_data2,y, "Head", TEXT_SIZE+BOLD + RIGHT)
lcd.drawText (x_data3+X_DATA_SPACE*3,y, "Actual", TEXT_SIZE+BOLD + RIGHT)
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER,y, "Roll %:", TEXT_SIZE)
lcd.drawText (x_data1,y, RRoll, TEXT_SIZE + RIGHT)
lcd.drawText (x_data2,y, HRoll, TEXT_SIZE + RIGHT)
lcd.drawText (x_data3,y, ARoll, TEXT_SIZE + RIGHT)
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER,y, "Pitch %:", TEXT_SIZE)
lcd.drawText (x_data1,y, RPitch, TEXT_SIZE + RIGHT)
lcd.drawText (x_data2,y, HPitch, TEXT_SIZE + RIGHT)
lcd.drawText (x_data3,y, APitch, TEXT_SIZE + RIGHT)
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER,y, "Yaw %:", TEXT_SIZE)
lcd.drawText (x_data1,y, RYaw, TEXT_SIZE + RIGHT)
lcd.drawText (x_data2,y, HYaw, TEXT_SIZE + RIGHT)
lcd.drawText (x_data3,y, AYaw, TEXT_SIZE + RIGHT)
-- Debug Values
if (DEBUG_ON) then
local Alpha3Tags = {[0]=
"1G00/RA+RE","1G02/RY+HA","1G04R HP+HY","1G06/AR+AP","1G08/AY+?","1G0B","1G0D","6C00","6C02","6C04"}
local params = {[0]=
s1G00,s1G02,s1G04,s1G06,s1G08,s1G0B,s1G0D,s6C00,s6C02,s6C04 }
y = Y_LINE_HEIGHT*2 + Y_HEADER
for iParam=0,#Alpha3Tags do -- ??
-- labels
local x = X_COL1_HEADER+220
local val = Alpha3Tags[iParam]
lcd.drawText (x, y, val, TEXT_SIZE)
val = params[iParam]
x = X_COL1_DATA+250
lcd.drawText (x, y, val, TEXT_SIZE)
y = y + Y_LINE_HEIGHT
end
end
end
local function openTelemetryRaw(i2cId)
--Init telemetry (Spectrun Telemetry Raw STR)
multiBuffer( 0, string.byte('S') )
multiBuffer( 1, string.byte('T') )
multiBuffer( 2, string.byte('R') )
multiBuffer( 3, i2cId ) -- Monitor this teemetry data
multiBuffer( 4, 0 ) -- Allow to get Data
end
local function closeTelemetryRaw()
multiBuffer(0, 0) -- Destroy the STR header
multiBuffer(3, 0) -- Not requesting any Telementry ID
end
local lineText = {nil}
local I2C_TEXT_GEN = 0x0C
local function drawTextGen(event)
if (multiBuffer(0)~=string.byte('S')) then -- First time run???
openTelemetryRaw(I2C_TEXT_GEN) -- I2C_ID for TEXT_GEN
lineText = {nil}
end
-- Proces TEXT GEN Telementry message
if multiBuffer( 4 ) == I2C_TEXT_GEN then -- Specktrum Telemetry ID of data received
local instanceNo = multiBuffer( 5 )
local lineNo = multiBuffer( 6 )
local line = ""
for i=0,13 do
line = line .. string.char(multiBuffer( 7 + i ))
end
multiBuffer( 4, 0 ) -- Clear Semaphore, to notify that we fully process the current message
lineText[lineNo]=line
end
lcd.clear()
-- Header
if (lineText[0]) then
lcd.drawText (X_COL1_HEADER,0, " "..lineText[0].." ", TEXT_SIZE + BOLD + INVERS)
else
lcd.drawText (X_COL1_HEADER,0, "TextGen", TEXT_SIZE+INVERS)
end
-- Menu lines
local y = Y_DATA
for i=1,8 do
if (lineText[i]) then
lcd.drawText (X_COL1_HEADER,y, lineText[i], TEXT_SIZE)
end
y = y + Y_LINE_HEIGHT
end
if event == EVT_VIRTUAL_EXIT then -- Exit?? Clear menu data
closeTelemetryRaw()
end
end
local telPage = 1
local telPageSelected = 0
local pageTitle = {[0]="Main", "Blade Version", "Blade Servo Adjust","Blade Gyro Adjust", "Blade Alpha6 Monitor", "Plane AS3X Monitor", "TextGen", "Flight Log"}
local function drawMainScreen(event)
lcd.clear()
lcd.drawText (X_COL1_HEADER, Y_HEADER, "Main Telemetry (AR636)", TEXT_SIZE + INVERS)
for iParam=1,#pageTitle do
-- highlight selected parameter
local attr = (telPage==iParam) and INVERS or 0
-- set y draw coord
local y = (iParam-1)*Y_LINE_HEIGHT+Y_DATA
-- labels
local x = X_COL1_HEADER
local val = pageTitle[iParam]
lcd.drawText (x, y, val, attr + TEXT_SIZE)
end
if event == EVT_VIRTUAL_PREV then
if (telPage>1) then telPage = telPage - 1 end
elseif event == EVT_VIRTUAL_NEXT then
if (telPage<#pageTitle) then telPage = telPage + 1 end
elseif event == EVT_VIRTUAL_ENTER then
telPageSelected = telPage
end
end
local pageDraw = {[0]=drawMainScreen, drawVersionScreen, servoAdjustScreen,drawPIDScreen, drawAlpha6Monitor, drawAS3XMonitor, drawTextGen, drawFlightLogScreen}
local function run_func(event)
if event == nil then
error("Cannot be run as a model script!")
return 2
end
-- draw specific page
pageDraw[telPageSelected](event)
if event == EVT_VIRTUAL_EXIT then
if (telPageSelected==0) then return 1 end -- on Main?? Exit Script
telPageSelected = 0 -- any page, return to Main
end
return 0
end
local function init_func()
if (LCD_W <= 128 or LCD_H <=64) then -- Smaller Screens
TEXT_SIZE = SMLSIZE
X_COL1_HEADER = 0
X_COL1_DATA = 20
X_COL2_HEADER = 60
X_COL2_DATA = 90
X_DATA_LEN = 28
X_DATA_SPACE = 1
Y_LINE_HEIGHT = 8
Y_DATA = Y_HEADER + Y_LINE_HEIGHT
end
end
return { run=run_func, init=init_func }

View File

@@ -0,0 +1,603 @@
local toolName = "TNS|DSM Smart RX Telemetry|TNE"
---- ######################################################################### #
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
---- # #
---- # 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. #
---- # #
---- # This program is distributed in the hope that it will be useful #
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
---- # GNU General Public License for more details. #
---- # #
---- #########################################################################
------------------------------------------------------------------------------
-- Developer: Francisco Arzu
local DEBUG_ON = false
--
local TEXT_SIZE = 0 -- NORMAL
local X_COL1_HEADER = 6
local X_COL1_DATA = 60
local X_COL2_HEADER = 170
local X_COL2_DATA = 220
local Y_LINE_HEIGHT = 20
local Y_HEADER = 0
local Y_DATA = Y_HEADER + Y_LINE_HEIGHT*2
local X_DATA_LEN = 80
local X_DATA_SPACE = 5
local function getPage(iParam)
-- get page from 0-based index
-- {0,1,2,3}: cyclic (1), {4,5,6,7}: tail (2)
local res = (math.floor(iParam/4)==0) and 0 or 1
return res
end
local function round(v)
-- round float
local factor = 100
return math.floor(v * factor + 0.5) / factor
end
local function readValue(sensor)
-- read from sensor, round and return
local v = getValue(sensor)
--v = round(v)
return v
end
local function readValueById(sensor)
local i = getFieldInfo(sensor)
if (i==nil) then return nil end
local v = getValue(i.id)
return v
end
local function readBatValue(sensor)
-- read from sensor, round and return
local v = getValue(sensor)
if (v==nil) then return v end
return string.format("%2.2f",v)
end
local function readActiveParamValue(sensor)
-- read and return a validated active parameter value
local v = getValue(sensor)
if (v<1 or v>8) then
return -1
end
return v
end
local function drawFlightLogScreen(event)
-- draw labels and params on screen
local h = getValue("Hold")
local activeParam = h-1 -- H
lcd.clear()
lcd.drawText (X_COL1_HEADER, Y_HEADER, "Flight Log", TEXT_SIZE + INVERS)
-- read and return parameters
local a = getValue("FdeA")
local b = getValue("FdeB")
local l = getValue("FdeL")
local r = getValue("FdeR")
local f = getValue("FLss")
local titles = {[0]="A:", "B:", "L:", "R:", "F:", "H:"}
local values = {[0]=a,b,l,r,f,h}
local y = Y_LINE_HEIGHT+Y_DATA
for iParam=0,3 do -- A,B,L,R
-- highlight selected parameter (rund)
local attr = ((activeParam%4)==iParam) and INVERS or 0
-- labels
local x = X_COL1_HEADER
local val = titles[iParam]
lcd.drawText (x, y, val, TEXT_SIZE)
-- Values
val = values[iParam]
x = X_COL1_DATA + X_DATA_LEN
if (val~=16384) then -- Active value
lcd.drawText (x, y, val, attr + TEXT_SIZE + RIGHT)
end
y = y + Y_LINE_HEIGHT
end
y = Y_LINE_HEIGHT+Y_DATA
for iParam=4,5 do -- F, H
-- labels
local x = X_COL2_HEADER
local val = titles[iParam]
lcd.drawText (x, y, val, TEXT_SIZE + BOLD)
-- Values
val = values[iParam]
x = X_COL2_DATA + X_DATA_LEN
lcd.drawText (x, y, val, TEXT_SIZE + RIGHT + BOLD)
y = y + Y_LINE_HEIGHT
end
-- Bat
y = y + Y_LINE_HEIGHT
local bat = readBatValue("A2") or "--"
lcd.drawText (X_COL2_HEADER, y, "Bat:", TEXT_SIZE)
lcd.drawText (X_COL2_DATA + X_DATA_LEN, y, bat, TEXT_SIZE + RIGHT)
lcd.drawText (X_COL2_DATA + X_DATA_LEN + X_DATA_SPACE, y, "v", TEXT_SIZE)
end
local function Unsigned_to_SInt16(value)
if value >= 0x8000 then -- Negative value??
return value - 0x10000
end
return value
end
local function getDegreesValue(sensor)
local i = getFieldInfo(sensor)
if (i==nil) then return "-unk-" end
local v = getValue(i.id)
if v==nil then return "---" end
local vs = Unsigned_to_SInt16(v)
return string.format("%0.1f o",vs/10)
end
local function getDecHexValue(sensor)
local i = getFieldInfo(sensor)
if (i==nil) then return "-unk-" end
local v = getValue(i.id)
if v==nil then return "---" end
local vs = Unsigned_to_SInt16(v)
return string.format("%d (0x%04X)",vs,v)
end
local as3xData = {[0]=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
local function drawAS3XSettings(event, page)
local s0500 = getDecHexValue("0500")
local s0502 = getDecHexValue("0502")
local s0504 = getDecHexValue("0504")
local s0506 = getDecHexValue("0506")
local s0508 = getDecHexValue("0508")
local s050B = getDecHexValue("050B")
local s050D = getDecHexValue("050D")
local d0500 = readValueById("0500") or 0
local flags = bit32.rshift(d0500,8)
local state = bit32.band(d0500,0xFF)
local flagsMsg=""
-- flags bits: Safe Envelop, ?, Angle Demand, Stab
if (bit32.band(flags,0x1)~=0) then flagsMsg=flagsMsg.."AS3X Stab" end
-- This one, only one should show
if (bit32.band(flags,0x2)~=0) then flagsMsg=flagsMsg..", Angle Demand"
elseif (bit32.band(flags,0x8)~=0) then flagsMsg=flagsMsg..", Safe Envelope"
elseif (bit32.band(flags,0x4)~=0) then flagsMsg=flagsMsg..", AS3X Heading" end
local d0502 = readValueById("0502") or 0 -- 0x?F?S
local fm = bit32.band(bit32.rshift(d0502,8),0xF) -- 0,1,2
local axis = bit32.band(d0502,0xF) -- 0=Gains,1=Headings,2=Angle Limits (cointinus iterating to provide all values)
local d0504 = readValueById("0504") or 0
local d0506 = readValueById("0506") or 0
local d0508 = readValueById("0508") or 0
local d0 = bit32.rshift(d0504,8)
local d1 = bit32.band(d0504,0xFF)
local d2 = bit32.rshift(d0506,8)
local d3 = bit32.band(d0506,0xFF)
local d4 = bit32.rshift(d0508,8)
local d5 = bit32.band(d0508,0xFF)
--axis: 0=Gains+Headings (RG,PG,YG,RH,PH,YH), 1=Safe Gains (R,P,Y),2=Angle Limits(L,R,U,D)
--Constantly changing from 0..2 to represent different data, thats why we have to store the values
--in a script/global variable, and not local to the function
local s = axis*6
as3xData[s+0] = d0
as3xData[s+1] = d1
as3xData[s+2] = d2
as3xData[s+3] = d3
as3xData[s+4] = d4
as3xData[s+5] = d5
lcd.clear()
lcd.drawText (0,0, "AS3X/SAFE Settings", TEXT_SIZE + INVERS)
local y = Y_DATA
-- Flight Mode
lcd.drawText (X_COL1_HEADER,y, "FM: "..(fm+1), TEXT_SIZE)
lcd.drawText (X_COL1_DATA+X_DATA_LEN*0.3,y, "Flags: "..flags, TEXT_SIZE)
lcd.drawText (X_COL2_HEADER+X_DATA_LEN*0.3,y, "State: "..state, TEXT_SIZE)
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER,y, flagsMsg, TEXT_SIZE)
y = y + Y_LINE_HEIGHT
if (page==1) then
lcd.drawText (X_COL1_HEADER+X_DATA_LEN*0.3,y, "AS3X Gains", TEXT_SIZE+BOLD)
lcd.drawText (X_COL2_HEADER+X_DATA_LEN*0.3,y, "AS3X Headings", TEXT_SIZE+BOLD)
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER,y, "Roll:", TEXT_SIZE)
lcd.drawText (X_COL1_DATA+X_DATA_LEN,y, as3xData[0], TEXT_SIZE + RIGHT) -- Roll G
lcd.drawText (X_COL2_DATA+X_DATA_LEN,y, as3xData[3], TEXT_SIZE + RIGHT) -- Roll H
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER,y, "Pitch:", TEXT_SIZE)
lcd.drawText (X_COL1_DATA+X_DATA_LEN,y,as3xData[1], TEXT_SIZE + RIGHT) -- Pitch G
lcd.drawText (X_COL2_DATA+X_DATA_LEN,y, as3xData[4], TEXT_SIZE + RIGHT) -- Pitch H
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER,y, "Yaw:", TEXT_SIZE)
lcd.drawText (X_COL1_DATA+X_DATA_LEN,y, as3xData[2], TEXT_SIZE + RIGHT) -- Yaw G
lcd.drawText (X_COL2_DATA+X_DATA_LEN,y, as3xData[5], TEXT_SIZE + RIGHT) -- Yaw H
end
if (page==2) then
local x_data1 = X_COL1_DATA+X_DATA_LEN
local x_data2 = X_COL2_HEADER+X_DATA_LEN*1.6
lcd.drawText (X_COL1_HEADER+X_DATA_LEN*0.3,y, "SAFE Gains", TEXT_SIZE+BOLD)
lcd.drawText (X_COL2_HEADER+X_DATA_LEN*0.1,y, "Angle Limits", TEXT_SIZE+BOLD)
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER,y, "Roll:", TEXT_SIZE)
lcd.drawText (x_data1,y, as3xData[6], TEXT_SIZE + RIGHT)
lcd.drawText (X_COL2_HEADER,y, "Roll R:", TEXT_SIZE)
lcd.drawText (x_data2,y, as3xData[12], TEXT_SIZE + RIGHT)
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER,y, "Pitch:", TEXT_SIZE)
lcd.drawText (x_data1,y,as3xData[7], TEXT_SIZE + RIGHT)
lcd.drawText (X_COL2_HEADER,y, "Roll L:", TEXT_SIZE)
lcd.drawText (x_data2,y,as3xData[13], TEXT_SIZE + RIGHT)
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL1_HEADER,y, "Yaw:", TEXT_SIZE)
lcd.drawText (x_data1,y, as3xData[8], TEXT_SIZE + RIGHT)
lcd.drawText (X_COL2_HEADER,y, "Pitch U:", TEXT_SIZE)
lcd.drawText (x_data2,y, as3xData[14], TEXT_SIZE + RIGHT)
y = y + Y_LINE_HEIGHT
lcd.drawText (X_COL2_HEADER,y, "Pitch D:", TEXT_SIZE)
lcd.drawText (x_data2,y, as3xData[15], TEXT_SIZE + RIGHT)
end
-- Debug Values
if (DEBUG_ON) then
local titles = {[0]=
"0500","0502","0504","0506","0508","050B","050D"}
local values = {[0]=
s0500,s0502,s0504,s0506,s0508,s050B,s050D }
y = Y_LINE_HEIGHT*2 + Y_HEADER
for iParam=0,#titles do -- ??
-- labels
local x = X_COL1_HEADER+250
local val = titles[iParam]
lcd.drawText (x, y, val, TEXT_SIZE)
val = values[iParam] or "--"
x = X_COL1_DATA+250
lcd.drawText (x, y, val, TEXT_SIZE)
y = y + Y_LINE_HEIGHT
end
end
end
local function drawAS3XSettingsP1(event)
drawAS3XSettings(event, 1)
end
local function drawAS3XSettingsP2(event)
drawAS3XSettings(event, 2)
end
local function doFloat(v)
if v==nil then return 0.0 end
local vs = string.format("%1.2f",v)
return vs + 0.0
end
local ESC_Title={[0]="","RPM:","Volts:","Motor:","Mot Out:","Throttle:","FET Temp:", "BEC V:", "BEC T:", "BEC A:"}
local ESC_uom={[0]="","","V","A","%","%","C", "V","C","A"}
local ESC_Status={[0]=0,0,0,0,0,0,0,0,0,0,0}
local ESC_Min={[0]=0,0,0,0,0,0,0,0,0,0,0}
local ESC_Max={[0]=0,0,0,0,0,0,0,0,0,0,0}
local function drawESCStatus(event)
lcd.clear()
ESC_Status[1] = getValue("Erpm") -- RPM
ESC_Status[2] = doFloat(getValue("EVIN")) -- Volts
ESC_Status[3] = doFloat(getValue("ECUR")) -- Current
ESC_Status[4] = doFloat(getValue("EOUT")) -- % Output
ESC_Status[5] = doFloat(getValue("ETHR")) -- Throttle % (EOUT)
ESC_Status[6] = getValue("TFET") -- Temp FET
ESC_Status[7] = doFloat(getValue("VBEC")) -- Volts BEC
ESC_Status[8] = getValue("TBEC") -- Temp BEC
ESC_Status[9] = doFloat(getValue("CBEC")) -- Current BEC
for i=1,9 do
if (ESC_Status~=nil) then
if (ESC_Min[i]==0) then
ESC_Min[i]=ESC_Status[i]
else
ESC_Min[i] = math.min(ESC_Min[i],ESC_Status[i])
end
ESC_Max[i] = math.max(ESC_Max[i],ESC_Status[i])
end
end
lcd.drawText (0,0, "ESC", TEXT_SIZE+INVERS)
local y = 0
local x_data = X_COL1_DATA+X_DATA_LEN*1.5
local x_data2 = X_COL2_DATA+X_DATA_LEN*0.5
local x_data3 = x_data2 + X_DATA_LEN*0.8
lcd.drawText (x_data,y , "Status", TEXT_SIZE+BOLD+RIGHT)
lcd.drawText (x_data2,y, "Min", TEXT_SIZE+BOLD+RIGHT)
lcd.drawText (x_data3,y, "Max", TEXT_SIZE+BOLD+RIGHT)
y = Y_DATA
for i=1,9 do
lcd.drawText (X_COL1_HEADER,y, ESC_Title[i], TEXT_SIZE + BOLD)
lcd.drawText (x_data,y, ESC_Status[i] or "--", TEXT_SIZE + RIGHT)
lcd.drawText (x_data + X_DATA_SPACE,y, ESC_uom[i], TEXT_SIZE)
lcd.drawText (x_data2,y, ESC_Min[i] or "--", TEXT_SIZE + RIGHT)
lcd.drawText (x_data3,y, ESC_Max[i] or "--", TEXT_SIZE + RIGHT)
y = y + Y_LINE_HEIGHT
end
end
local function drawBATStatus(event)
local Title={[0]="","Bat:","Temp:","Rem :","Curr:","Used:","Imbal:","Cycles:", "RX:", "BCpT?:"}
local uom={[0]="","V","C","%","mAh","mAh","mV","", "V",""}
local Values={[0]=0,0,0,0,0,0,0,0,0,0,0}
local CellValues={[0]=0,0,0,0,0,0,0,0,0,0,0}
lcd.clear()
local ESC_Volts = getValue("EVIN") or 0 -- Volts
local ESC_Current = getValue("ECUR") or 0 -- Current
Values[1] = 0 -- compute later
Values[2] = getValue("BTmp") -- Current (C)
Values[3] = nil -- Remaining???
Values[4] = getValue("BCur") -- Current (mAh)
Values[5] = getValue("BUse") -- Current Used (mAh)
Values[6] = getValue("CLMa") -- 0.0 (mV) Imbalance
Values[7] = getValue("Cycl") -- Cycles
Values[8] = readBatValue("A2") -- v
Values[9] = getValue("BCpT") -- Current (mAh) ????
--- Total Voltange Calculation
local VTotal=0
for i=1,10 do
CellValues[i] = getValue("Cel"..i)
VTotal = VTotal + CellValues[i]
end
if (VTotal==0) then -- No Inteligent Battery,use intelligent ESC if any
VTotal = ESC_Volts
Values[4] = string.format("%d",ESC_Current * 1000)
end
Values[1] = string.format("%2.2f",VTotal)
--- SCREEN
lcd.drawText (X_COL1_HEADER,0, "Battery Stats", TEXT_SIZE+INVERS)
local y = Y_DATA
local x_data = X_COL1_DATA+X_DATA_LEN+X_DATA_SPACE*3
for i=2,9 do
lcd.drawText (X_COL1_HEADER, y, Title[i], TEXT_SIZE + BOLD)
lcd.drawText (x_data, y, Values[i] or "--", TEXT_SIZE + RIGHT)
lcd.drawText (x_data+X_DATA_SPACE, y, uom[i], TEXT_SIZE)
y = y + Y_LINE_HEIGHT
end
y = Y_DATA
x_data = X_COL2_DATA+X_DATA_LEN+X_DATA_SPACE*5
for i=1,8 do
if ((CellValues[i] or 0) > 0) then
lcd.drawText (X_COL2_HEADER+X_DATA_LEN/2,y, "Cel "..i..":", TEXT_SIZE + BOLD)
lcd.drawText (x_data,y, string.format("%2.2f",CellValues[i] or 0), TEXT_SIZE + RIGHT)
lcd.drawText (x_data+X_DATA_SPACE,y, "v", TEXT_SIZE)
end
y = y + Y_LINE_HEIGHT
end
lcd.drawText (X_COL2_HEADER+X_DATA_LEN/2,0, Title[1], TEXT_SIZE + INVERS + BOLD)
lcd.drawText (x_data,0, string.format("%2.2f",Values[1] or 0), TEXT_SIZE + INVERS+ RIGHT)
lcd.drawText (x_data+X_DATA_SPACE, 0, uom[1], TEXT_SIZE + INVERS)
end
local function openTelemetryRaw(i2cId)
--Init telemetry (Spectrun Telemetry Raw STR)
multiBuffer( 0, string.byte('S') )
multiBuffer( 1, string.byte('T') )
multiBuffer( 2, string.byte('R') )
multiBuffer( 3, i2cId ) -- Monitor this teemetry data
multiBuffer( 4, 0 ) -- Allow to get Data
end
local function closeTelemetryRaw()
multiBuffer(0, 0) -- Destroy the STR header
multiBuffer(3, 0) -- Not requesting any Telementry ID
end
local lineText = {nil}
local I2C_TEXT_GEN = 0x0C
local function drawTextGen(event)
if (multiBuffer(0)~=string.byte('S')) then -- First time run???
openTelemetryRaw(I2C_TEXT_GEN) -- I2C_ID for TEXT_GEN
lineText = {nil}
end
-- Proces TEXT GEN Telementry message
if multiBuffer( 4 ) == I2C_TEXT_GEN then -- Specktrum Telemetry ID of data received
local instanceNo = multiBuffer( 5 )
local lineNo = multiBuffer( 6 )
local line = ""
for i=0,13 do
line = line .. string.char(multiBuffer( 7 + i ))
end
multiBuffer( 4, 0 ) -- Clear Semaphore, to notify that we fully process the current message
lineText[lineNo]=line
end
lcd.clear()
-- Header
if (lineText[0]) then
lcd.drawText (X_COL1_HEADER,0, " "..lineText[0].." ", TEXT_SIZE + BOLD + INVERS)
else
lcd.drawText (X_COL1_HEADER,0, "TextGen", TEXT_SIZE+INVERS)
end
-- Menu lines
local y = Y_DATA
for i=1,8 do
if (lineText[i]) then
lcd.drawText (X_COL1_HEADER,y, lineText[i], TEXT_SIZE)
end
y = y + Y_LINE_HEIGHT
end
if event == EVT_VIRTUAL_EXIT then -- Exit?? Clear menu data
closeTelemetryRaw()
end
end
local telPage = 1
local telPageSelected = 0
local pageTitle = {[0]="Main", "AS3X Settings", "SAFE Settings", "ESC Status", "Battery Status","TextGen","Flight Log"}
local function drawMainScreen(event)
lcd.clear()
lcd.drawText (X_COL1_HEADER, Y_HEADER, "Main Telemetry (Smart RXs)", TEXT_SIZE + INVERS)
for iParam=1,#pageTitle do
-- highlight selected parameter
local attr = (telPage==iParam) and INVERS or 0
-- set y draw coord
local y = (iParam)*Y_LINE_HEIGHT+Y_DATA
-- labels
local x = X_COL1_HEADER
local val = pageTitle[iParam]
lcd.drawText (x, y, val, attr + TEXT_SIZE)
end
if event == EVT_VIRTUAL_PREV then
if (telPage>1) then telPage = telPage - 1 end
elseif event == EVT_VIRTUAL_NEXT then
if (telPage<#pageTitle) then telPage = telPage + 1 end
elseif event == EVT_VIRTUAL_ENTER then
telPageSelected = telPage
end
end
local pageDraw = {[0]=drawMainScreen, drawAS3XSettingsP1, drawAS3XSettingsP2, drawESCStatus, drawBATStatus, drawTextGen, drawFlightLogScreen}
local function run_func(event)
if event == nil then
error("Cannot be run as a model script!")
return 2
end
-- draw specific page
pageDraw[telPageSelected](event)
if event == EVT_VIRTUAL_EXIT then
if (telPageSelected==0) then return 1 end -- on Main?? Exit Script
telPageSelected = 0 -- any page, return to Main
end
return 0
end
local function init_func()
if (LCD_W <= 128 or LCD_H <=64) then -- Smaller Screens
TEXT_SIZE = SMLSIZE
X_COL1_HEADER = 0
X_COL1_DATA = 20
X_COL2_HEADER = 60
X_COL2_DATA = 90
X_DATA_LEN = 28
X_DATA_SPACE = 1
Y_LINE_HEIGHT = 8
Y_DATA = Y_HEADER + Y_LINE_HEIGHT
end
end
return { run=run_func, init=init_func }

View File

@@ -0,0 +1,167 @@
---- #########################################################################
---- # #
---- # Telemetry Widget script for FrSky Horus/Radio Master TX16s #
---- # Copyright (C) EdgeTX #
-----# #
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
---- # #
---- # 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. #
---- # #
---- # This program is distributed in the hope that it will be useful #
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
---- # GNU General Public License for more details. #
---- # #
---- #########################################################################
-- Model Locator by RSSI
-- Offer Shmuely (based on code from Scott Bauer 6/21/2015)
-- Date: 2021
-- ver: 0.1
-- MHA (based on code from Offer Shmuely)
-- Date: 2022
-- ver: 0.11
-- changes: made version for Graupner HoTT. Uses real Rssi data scaled -15db to -115db to 100..0
-- This widget help to find a lost/crashed model based on the RSSI (if still available)
-- The widget produce audio representation (variometer style) of the RSSI from the lost model
-- The widget also display the RSSI in a visible colorized bar (0-100%)
-- There are two way to use it
-- 1. The simple way:
-- walk toward the quad/plane that crashed,
-- as you get closer to your model the beeps will become more frequent with higher pitch (and a visual bar graph as well)
-- until you get close enough to find it visually
-- 2. the more accurate way:
-- turn the antenna straight away (i.e. to point from you, straight away)
-- try to find the weakest signal! (not the highest), i.e. the lowest RSSI you can find, this is the direction to the model.
-- now walk to the side (not toward the model), find again the weakest signal, this is also the direction to your model
-- triangulate the two lines, and it will be :-)
local delayMillis = 100
local nextPlayTime = getTime()
local img = Bitmap.open("/SCRIPTS/TOOLS/Model Locator (by RSSI).png")
--------------------------------------------------------------
local function log(s)
--return;
print("locator: " .. s)
end
--------------------------------------------------------------
-- init_func is called once when model is loaded
local function init()
return 0
end
-- bg_func is called periodically when screen is not visible
local function bg()
return 0
end
-- This function returns green at gvalue, red at rvalue and graduate in between
local function getRangeColor(value, red_value, green_value)
local range = math.abs(green_value - red_value)
if range == 0 then
return lcd.RGB(0, 0xdf, 0)
end
if value == nil then
return lcd.RGB(0, 0xdf, 0)
end
if green_value > red_value then
if value > green_value then
return lcd.RGB(0, 0xdf, 0)
end
if value < red_value then
return lcd.RGB(0xdf, 0, 0)
end
g = math.floor(0xdf * (value - red_value) / range)
r = 0xdf - g
return lcd.RGB(r, g, 0)
else
if value > green_value then
return lcd.RGB(0, 0xdf, 0)
end
if value < red_value then
return lcd.RGB(0xdf, 0, 0)
end
r = math.floor(0xdf * (value - green_value) / range)
g = 0xdf - r
return lcd.RGB(r, g, 0)
end
end
local function main(event)
lcd.clear()
-- fetch uplink rssi (HoTT sensor Rssi)
local rssi = getValue("Rssi")
-- calculate a percentage for the color bar (range -115db to -15db)
local rssiP = rssi
if(rssi ~= 0) then -- rssi < 0 -> telemtry data received
if(rssi >= -15) then -- -15db to -1db => 100%
rssiP = 100
else
if(rssi >= -115) then -- between -115db and -15db => 0% to 100%
rssiP = 100+rssi+15
else
rssiP = 0 -- less than -115 => 0%
end
end
end
lcd.drawBitmap(img, 250, 50, 40)
-- Title
lcd.drawText(3, 3, "Graupner HoTT Rssi Model Locator", 0)
myColor = getRangeColor(rssi, 0, 100)
lcd.setColor(CUSTOM_COLOR, myColor)
-- draw current value
local dx = 0
if rssi < -99 then
dx = -33
end
if rssi == 0 then
lcd.drawText(115, 73, "no telemetry", DBLSIZE + CUSTOM_COLOR)
else
lcd.drawNumber(180+dx, 30, rssi, XXLSIZE + CUSTOM_COLOR)
lcd.drawText(275, 73, "db", 0 + CUSTOM_COLOR)
end
-- draw main bar
lcd.setColor(CUSTOM_COLOR, YELLOW) -- RED / YELLOW
local xMin = 0
local yMin = 270
local xMax = 480
local yMax = 200
local h = 0
local rssiAsX = (rssiP * xMax) / 100
for xx = xMin, rssiAsX, 20 do
lcd.setColor(CUSTOM_COLOR, getRangeColor(xx, xMin, xMax - 40))
h = h + 10
lcd.drawFilledRectangle(xx, yMin - h, 15, h, CUSTOM_COLOR)
end
-- beep
if getTime() >= nextPlayTime then
playFile("/SCRIPTS/TOOLS/Model Locator (by RSSI).wav")
nextPlayTime = getTime() + delayMillis - rssiP
end
return 0
end
return {init = init,run = main,background = bg}

View File

@@ -29,16 +29,16 @@
7,3,Devo,6CH,0,CH5,CH6
7,4,Devo,7CH,0,CH5,CH6,CH7
33,0,DM022,Std,1,Flip,LED,Cam1,Cam2,HLess,RTH,RLow
6,0,DSM,2_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,1,DSM,2_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,2,DSM,X_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,3,DSM,X_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,0,DSM,2_1F,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,1,DSM,2_2F,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,2,DSM,X_1F,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,3,DSM,X_2F,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,4,DSM,AUTO,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,5,DSM,R_1F,0,AUX3,AUX4,AUX5
70,0,DSM_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
70,1,DSM_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
45,0,E01X,E012,1,n-a,Flip,n-a,HLess,RTH
45,1,E01X,E015,1,Arm,Flip,LED,HLess,RTH
45,2,E01X,E016H,1,Stop,Flip,n-a,HLess,RTH
16,0,ESKY,Std,0,Gyro,Pitch
16,1,ESKY,ET4,0,Gyro,Pitch
35,0,ESKY150,4CH,0
@@ -55,6 +55,8 @@
28,3,Flysky_AFHDS2A,PPM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
28,4,Flysky_AFHDS2A,PWM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
28,5,Flysky_AFHDS2A,PPM_IB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
28,6,Flysky_AFHDS2A,PWM_SB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
28,7,Flysky_AFHDS2A,PPM_SB16,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
56,1,Flysky2A_RX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
53,0,Height,5ch,0,Gear
@@ -84,7 +86,8 @@
55,1,FrSkyRX,CloneTX,0
55,2,FrSkyRX,EraseTX,0
55,3,FrSkyRX,CPPM,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
58,0,FX816,P38,1
58,0,FX,816,1
58,1,FX,620,1
20,0,FY326,FY326,1,Flip,RTH,HLess,Expert,Calib
20,1,FY326,FY319,1,Flip,RTH,HLess,Expert,Calib
23,0,FY326,FY326,1,Flip,RTH,HLess,Expert
@@ -112,7 +115,9 @@
22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
71,0,JJRC345,JJRC345,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3
71,1,JJRC345,SkyTmblr,1,Flip,HLess,RTH,LED,UNK1,UNK2,UNK3
49,0,KF606,Std,1,Trim
49,0,KF606,KF606,1,Trim
49,1,KF606,MIG320,1,Trim,LED
49,2,KF606,ZCZ50,1,Trim,UNK
9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim
9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim
73,0,Kyosho,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
@@ -130,10 +135,12 @@
17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess
17,5,MT99XX,A180,0,3D_6G
17,6,MT99XX,Dragon,0,Mode,RTH
17,7,MT99XX,F949G,0,6G_3D,Light
44,0,NCC1701,Std,1,Warp
77,0,OMP,M2,0,THold,IdleUp,6G_3D
60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8
60,1,Pelikan,LITE_V4,0,CH5,CH6,CH7,CH8
60,2,Pelikan,SCX24,0
51,0,Potensic,A20,1,TakLan,Emerg,Mode,HLess
66,0,Propel,74-Z,1,LEDs,RollCW,RolCCW,Fire,Weapon,Calib,AltHol,TakeOf,Land,Train
29,0,Q2x2,Q222,1,Flip,LED,Mod2,Mod1,HLess,RTH,XCal,YCal
@@ -166,8 +173,9 @@
5,1,V2x2,JXD506,1,Flip,Light,Pict,Video,HLess,StaSto,Emerg,Cam_UD
48,0,V761,3CH,0,Gyro,Calib,Flip,RtnAct,Rtn
48,1,V761,4CH,0,Gyro,Calib,Flip,RtnAct,Rtn
46,0,V911s,V911s,1,Calib
46,1,V911s,E119,1,Calib
48,2,V761,TOPRC,0,Gyro,Calib,Flip,RtnAct,Rtn
46,0,V911s,V911s,1,Calib,Rate
46,1,V911s,E119,1,Calib,Rate,6G_3D
22,0,WFLY,WFR0xS,0,CH5,CH6,CH7,CH8,CH9
30,0,WK2x01,WK2801,0,CH5,CH6,CH7,CH8
30,1,WK2x01,WK2401,0
@@ -189,4 +197,14 @@
81,0,E010r5,E010r5,1,Flip,LED,CALIB,HLess,RTH,GLIDE
82,0,LOLI,Std,0,CH5,CH6,CH7,CH8,1SwSePpPw,2SwSePw,3SwSe,4SwSe,5SwSeSb,6SwSe,7SwSePw,8SwSe
83,0,E129,E129,1,TakLan,EmStop,TrimA,TrimE,TrimR
83,1,E129,C186,1,TakLan,EmStop,TrimA,TrimE,TrimR
84,0,JOYSWAY,Std,0
85,0,E016H,Std,1,Stop,Flip,n-a,HLess,RTH
87,0,IKEA
89,0,LOSI
90,0,MouldKg,Analog,0
90,1,MouldKg,Digit,0
91,0,Xerall,Tank,0,FlTa,TakLan,Rate,HLess,Photo,Video,TrimR,TrimE,TrimA
92,0,MT99xx2,PA18,0,MODE,FLIP,RTH
93,0,Kyosho2,KT-17,0
94,0,Scorpio

View File

@@ -280,11 +280,27 @@ local function Multi_Init()
end
--Exceptions on first 4 channels...
if ( protocol == 73 or (protocol == 74 and sub_protocol == 0) ) then -- Kyosho or RadioLink Surface
if ( protocol == 73 or (protocol == 74 and sub_protocol == 0) or (protocol == 60 and sub_protocol == 2) or protocol == 89) then -- Kyosho or RadioLink Surface or Pelikan/SCX24 or Losi
channel_names[1] = "ST"
channel_names[2] = "THR"
channel_names[3] = "CH3"
channel_names[4] = "CH4"
if(protocol == 60 and sub_protocol == 2) then
channel_names[4] = "n-a"
else
channel_names[4] = "CH4"
end
end
if ( protocol == 6 and sub_protocol == 5 ) then -- DSMR
channel_names[1] = "ST"
channel_names[2] = "THR"
channel_names[3] = "AUX1"
channel_names[4] = "AUX2"
end
if ( protocol == 90 ) then -- Mould King
channel_names[1] = "A"
channel_names[2] = "B"
channel_names[3] = "C"
channel_names[4] = "D"
end
--Check MultiChan.txt

529
Lua_scripts/MultiConfig.lua Normal file
View File

@@ -0,0 +1,529 @@
---- #########################################################################
---- # #
---- # Copyright (C) OpenTX #
-----# #
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html #
---- # #
---- # 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. #
---- # #
---- # This program is distributed in the hope that it will be useful #
---- # but WITHOUT ANY WARRANTY; without even the implied warranty of #
---- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
---- # GNU General Public License for more details. #
---- # #
---- #########################################################################
--###############################################################################
-- Multi buffer for Config description
-- To start operation:
-- Write 0xFF at address 4 will request the buffer to be cleared
-- Write "Conf" at address 0..3
-- Read
-- Read at address 12 gives the current config page
-- Read at address 13..172 gives the current data of the page = 8 lines * 20 caracters
-- Write
-- Write at address 5..11 the command
-- Write 0x01 at address 4 will send the command to the module
-- !! Before exiting the script must write 0 at address 0 for normal operation !!
--###############################################################################
local Version = "v0.2"
local Focus = -1
local Page = 0
local Edit = -1
local Edit_pos = 1
local Menu = { {text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""},
{text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""},
{text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""},
{text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""},
{text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""},
{text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""},
{text = "", field_type = 0, field_len = 0, field_value = {}, field_text = ""} }
local Menu_value = {}
local Blink = 0
local ModuleNumber = 0
local ModuleType = ""
local Module = {}
local InitialProtocol = 0
local InitialSubProtocol = 0
function bitand(a, b)
local result = 0
local bitval = 1
while a > 0 and b > 0 do
if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits
result = result + bitval -- set the current bit
end
bitval = bitval * 2 -- shift left
a = math.floor(a/2) -- shift right
b = math.floor(b/2)
end
return result
end
local function Config_Send(page, line, value)
local i
i = (page*16) + line
multiBuffer( 5, i )
for i = 1 , 6 , 1 do
multiBuffer( 5+i, value[i] )
end
multiBuffer( 4, 1 )
end
local function Config_Release()
--Set the protocol back to what it was
Module.protocol = InitialProtocol
Module.subProtocol = InitialSubProtocol
model.setModule(ModuleNumber, Module)
--Stop requesting updates
local i
for i = 3 , 0 , -1 do
multiBuffer( i, 0 )
end
end
local function Config_Page( )
Config_Send(Page, 0, { 0, 0, 0, 0, 0, 0 })
end
local function Config_Draw_Edit( event )
local i
local text
if Menu[Focus].field_type == 0xD0 then
-- Editable Hex value
if Edit == -1 then
-- Init
Edit = 0
Edit_pos = 1
Blink = 0
for i = 1, Menu[Focus].field_len, 1 do
Menu_value[i] = Menu[Focus].field_value[i]
end
end
if Edit == 0 then
-- Not editing value
if event == EVT_VIRTUAL_ENTER then
if Edit_pos > Menu[Focus].field_len then
-- Save or Cancel
Edit = -1
if Edit_pos == Menu[Focus].field_len + 1 then
-- Save
Config_Send(Page, Focus, Menu_value)
end
return
else
-- Switch to edit mode
Edit = 1
end
elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then
-- Move cursor
Edit_pos = Edit_pos - 1
elseif event == EVT_VIRTUAL_NEXT and Edit_pos < Menu[Focus].field_len + 2 then
-- Move cursor
Edit_pos = Edit_pos + 1
end
else
-- Editing value
if event == EVT_VIRTUAL_ENTER then
-- End edit
Edit = 0
elseif event == EVT_VIRTUAL_PREV then
-- Change value
Menu_value[Edit_pos] = Menu_value[Edit_pos] - 1
elseif event == EVT_VIRTUAL_NEXT then
-- Change value
Menu_value[Edit_pos] = Menu_value[Edit_pos] + 1
end
--Blink
Blink = Blink + 1
if Blink > 30 then
Blink = 0
end
end
--Display
if LCD_W == 480 then
lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR)
lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR)
else
lcd.clear()
end
for i = 1, Menu[Focus].field_len, 1 do
if i==Edit_pos and (Edit ~= 1 or Blink > 15) then
attrib = INVERS
else
attrib = 0
end
if LCD_W == 480 then
lcd.drawText(170+12*2*(i-1), 110, string.format('%02X', Menu_value[i]), attrib)
else
lcd.drawText(17+6*2*(i-1), 10, string.format('%02X', Menu_value[i]), attrib + SMLSIZE)
end
end
if Edit_pos == Menu[Focus].field_len + 1 then
attrib = INVERS
else
attrib = 0
end
if LCD_W == 480 then
lcd.drawText(170, 130, "Save", attrib)
else
lcd.drawText(17, 30, "Save", attrib + SMLSIZE)
end
if Edit_pos == Menu[Focus].field_len + 2 then
attrib = INVERS
else
attrib = 0
end
if LCD_W == 480 then
lcd.drawText(260, 130, "Cancel", attrib)
else
lcd.drawText(77, 30, "Cancel", attrib + SMLSIZE)
end
elseif Menu[Focus].field_type == 0x90 then
-- Action text
if Edit == -1 then
-- Init
Edit = 0
Edit_pos = 2
end
if event == EVT_VIRTUAL_ENTER then
-- Exit
Edit = -1
if Edit_pos == 1 then
-- Yes
Config_Send(Page, Focus, { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA } )
end
return
elseif event == EVT_VIRTUAL_PREV and Edit_pos > 1 then
-- Switch to Yes
Edit_pos = Edit_pos - 1
elseif event == EVT_VIRTUAL_NEXT and Edit_pos < 2 then
-- Switch to No
Edit_pos = Edit_pos + 1
end
-- Display
if LCD_W == 480 then
lcd.drawRectangle(160-1, 100-1, 160+2, 55+2, TEXT_COLOR)
lcd.drawFilledRectangle(160, 100, 160, 55, TEXT_BGCOLOR)
else
lcd.clear()
end
if LCD_W == 480 then
lcd.drawText(170, 110, Menu[Focus].field_text .. "?")
else
lcd.drawText(17, 10, Menu[Focus].field_text .. "?", SMLSIZE)
end
if Edit_pos == 1 then
attrib = INVERS
else
attrib = 0
end
if LCD_W == 480 then
lcd.drawText(170, 130, "Yes", attrib)
else
lcd.drawText(17, 30, "Yes", attrib + SMLSIZE)
end
if Edit_pos == 2 then
attrib = INVERS
else
attrib = 0
end
if LCD_W == 480 then
lcd.drawText(260, 130, "No", attrib)
else
lcd.drawText(77, 30, "No", attrib)
end
end
end
local function Config_Next_Prev( event )
-- Next Prev on main menu
local line
if event == EVT_VIRTUAL_PREV then
for line = Focus - 1, 1, -1 do
if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then
Focus = line
break
end
end
elseif event == EVT_VIRTUAL_NEXT then
for line = Focus + 1, 7, 1 do
if Menu[line].field_type >= 0x80 and Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 then
Focus = line
break
end
end
end
end
local function Config_Draw_Menu()
-- Main menu
local i
local value
local line
local length
local text
lcd.clear()
if LCD_W == 480 then
--Draw title
lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR)
lcd.drawText(1, 5, "Multi Config " .. Version, MENU_TITLE_COLOR)
if multiBuffer(13) == 0x00 then
lcd.drawText(10,50,"No Config telemetry...", BLINK)
end
else
--Draw on LCD_W=128
lcd.drawText(1, 0, "Multi Config " .. Version, SMLSIZE)
if multiBuffer(13) == 0x00 then
lcd.drawText(2,17,"No Config telemetry...",SMLSIZE)
end
end
if multiBuffer(13) ~= 0x00 then
if LCD_W == 480 then
--Draw firmware version and channels order
local ch_order = multiBuffer(17)
local channel_names = {}
channel_names[bitand(ch_order,3)+1] = "A"
ch_order = math.floor(ch_order/4)
channel_names[bitand(ch_order,3)+1] = "E"
ch_order = math.floor(ch_order/4)
channel_names[bitand(ch_order,3)+1] = "T"
ch_order = math.floor(ch_order/4)
channel_names[bitand(ch_order,3)+1] = "R"
lcd.drawText(150, 5, ModuleType.." v" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16) .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4], MENU_TITLE_COLOR)
else
lcd.drawText(76, 0, "/Fw" .. multiBuffer(13) .. "." .. multiBuffer(14) .. "." .. multiBuffer(15) .. "." .. multiBuffer(16),SMLSIZE) -- .. " " .. channel_names[1] .. channel_names[2] .. channel_names[3] .. channel_names[4])
end
--Draw Menu
for line = 1, 7, 1 do
--Clear line info
Menu[line].text = ""
Menu[line].field_type = 0
Menu[line].field_len = 0
for i = 1, 7, 1 do
Menu[line].field_value[i] = 0
end
Menu[line].field_text = ""
length = 0
--Read line from buffer
for i = 0, 20-1, 1 do
value=multiBuffer( line*20+13+i )
if value == 0 then
break -- end of line
end
if value > 0x80 and Menu[line].field_type == 0 then
-- Read field type
Menu[line].field_type = bitand(value, 0xF0)
Menu[line].field_len = bitand(value, 0x0F)
length = Menu[line].field_len
if Menu[line].field_type ~= 0xA0 and Menu[line].field_type ~= 0xC0 and Focus == -1 then
-- First actionnable field if nothing was selected
Focus = line;
end
else
if Menu[line].field_type == 0 then
-- Text
Menu[line].text = Menu[line].text .. string.char(value)
else
-- Menu specific fields
length = length - 1
if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then
Menu[line].field_text = Menu[line].field_text .. string.char(value)
else
Menu[line].field_value[Menu[line].field_len-length] = value
end
if length == 0 then
-- End of fields
break
end
end
end
end
-- Display menu text
if Menu[line].text ~= "" then
if Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 or Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then
Menu[line].text = Menu[line].text .. ":"
end
if LCD_W == 480 then
lcd.drawText(10,32+20*line,Menu[line].text )
else
lcd.drawText(2,1+8*line,Menu[line].text,SMLSIZE)
end
end
-- Display specific fields
if line == Focus then
attrib = INVERS
else
attrib = 0
end
if Menu[line].field_type == 0x80 or Menu[line].field_type == 0x90 then
-- Text
if LCD_W == 480 then
lcd.drawText(10+9*#Menu[line].text, 32+20*line, Menu[line].field_text, attrib)
else
lcd.drawText(2+5*#Menu[line].text, 1+8*line, Menu[line].field_text, SMLSIZE + attrib)
end
elseif Menu[line].field_type == 0xA0 or Menu[line].field_type == 0xB0 then
-- Decimal value
value = 0
for i = 1, Menu[line].field_len, 1 do
value = value*256 + value
end
if LCD_W == 480 then
lcd.drawText(10+9*#Menu[line].text, 32+20*line, value, attrib)
else
lcd.drawText(2+5*#Menu[line].text, 1+8*line, value, SMLSIZE + attrib)
end
elseif Menu[line].field_type == 0xC0 or Menu[line].field_type == 0xD0 then
-- Hex value
text=""
for i = 1, Menu[line].field_len, 1 do
text = text .. string.format('%02X ', Menu[line].field_value[i])
end
if LCD_W == 480 then
lcd.drawText(10+9*#Menu[line].text, 32+20*line, text, attrib)
else
lcd.drawText(2+5*#Menu[line].text, 1+8*line, text, SMLSIZE + attrib)
end
end
end
end
end
-- Init
local function Config_Init()
--Find Multi module
Module_int = model.getModule(0)
Module_ext = model.getModule(1)
if Module_int["Type"] ~= 6 and Module_ext["Type"] ~= 6 then
error("No Multi module detected...")
return 2
end
if Module_int["Type"] == 6 and Module_ext["Type"] == 6 then
error("Two Multi modules detected, turn on only the one to be configured.")
return 2
end
if Module_int["Type"] == 6 then
ModuleNumber = 0
ModuleType = "Internal"
else
ModuleNumber = 1
ModuleType = "External"
end
--Get Module settings and set it to config protocol
Module = model.getModule(ModuleNumber)
InitialProtocol = Module.protocol
InitialSubProtocol = Module.subProtocol
Module.protocol = 86
Module.subProtocol = 0
model.setModule(ModuleNumber, Module)
--pause while waiting for the module to switch to config
for i = 0, 10, 1 do end
--Set protocol to talk to
multiBuffer( 0, string.byte('C') )
--test if value has been written
if multiBuffer( 0 ) ~= string.byte('C') then
error("Not enough memory!")
return 2
end
--Request init of the buffer
multiBuffer( 4, 0xFF )
multiBuffer(13, 0x00 )
--Continue buffer init
multiBuffer( 1, string.byte('o') )
multiBuffer( 2, string.byte('n') )
multiBuffer( 3, string.byte('f') )
-- Test set
-- multiBuffer( 12, 0 )
-- multiBuffer( 13, 1 )
-- multiBuffer( 14, 3 )
-- multiBuffer( 15, 2 )
-- multiBuffer( 16, 62 )
-- multiBuffer( 17, 0 + 1*4 + 2*16 + 3*64)
-- multiBuffer( 33, string.byte('G') )
-- multiBuffer( 34, string.byte('l') )
-- multiBuffer( 35, string.byte('o') )
-- multiBuffer( 36, string.byte('b') )
-- multiBuffer( 37, string.byte('a') )
-- multiBuffer( 38, string.byte('l') )
-- multiBuffer( 39, string.byte(' ') )
-- multiBuffer( 40, string.byte('I') )
-- multiBuffer( 41, string.byte('D') )
-- multiBuffer( 42, 0xD0 + 4 )
-- multiBuffer( 43, 0x12 )
-- multiBuffer( 44, 0x34 )
-- multiBuffer( 45, 0x56 )
-- multiBuffer( 46, 0x78 )
-- multiBuffer( 47, 0x9A )
-- multiBuffer( 48, 0xBC )
-- multiBuffer( 53, 0x90 + 9 )
-- multiBuffer( 54, string.byte('R') )
-- multiBuffer( 55, string.byte('e') )
-- multiBuffer( 56, string.byte('s') )
-- multiBuffer( 57, string.byte('e') )
-- multiBuffer( 58, string.byte('t') )
-- multiBuffer( 59, string.byte(' ') )
-- multiBuffer( 60, string.byte('G') )
-- multiBuffer( 61, string.byte('I') )
-- multiBuffer( 62, string.byte('D') )
-- multiBuffer( 63, 0x00 )
end
-- Main
local function Config_Run(event)
if event == nil then
error("Cannot be run as a model script!")
return 2
elseif event == EVT_VIRTUAL_EXIT then
Config_Release()
return 2
else
Config_Draw_Menu()
if ( event == EVT_VIRTUAL_PREV_PAGE or event == EVT_VIRTUAL_NEXT_PAGE ) and Edit < 1 then
-- Not editing, ok to change page
if event == EVT_VIRTUAL_PREV_PAGE then
killEvents(event)
if Page > 0 then
--Page = Page - 1
--Config_Page()
end
else
--Page = Page + 1
--Config_Page()
end
end
if Focus > 0 then
-- At least one line has an action
if Edit >= 0 then
-- Currently editing
Config_Draw_Edit( event )
elseif event == EVT_VIRTUAL_ENTER then
-- Switch to edit
Config_Draw_Edit( 0 )
elseif event == EVT_VIRTUAL_PREV or event == EVT_VIRTUAL_NEXT then
-- Main menu selection
Config_Next_Prev( event )
end
end
return 0
end
end
return { init=Config_Init, run=Config_Run }

View File

@@ -13,6 +13,19 @@ If you like this project and want to support further development please consider
</tr>
</table>
## MultiConfig
Enables to modify on a Multi module the Global ID, Cyrf ID or format the EEPROM.
Matching the ID of 2 Multi modules enable them to control the same receivers without rebinding. Be carefull the 2 modules should not be used at the same time unless you know what you are doing.
Notes:
- Supported from Multi v1.3.2.85 or above and OpenTX 2.3.12 or above
- The Multi module to be configured must be active, if there is a second Multi module in the radio it must be off
- Located on the radio SD card under \SCRIPTS\TOOLS
[![MultiCconfig](https://img.youtube.com/vi/lGyCV2kpqHU/0.jpg)](https://www.youtube.com/watch?v=lGyCV2kpqHU)
## MultiChannelsUpdater
Automatically name the channels based on the loaded Multi protocol and sub protocol including the module channel order convention.
@@ -39,16 +52,22 @@ Notes:
[![Text mode video](https://img.youtube.com/vi/81wd8NlF3Qw/0.jpg)](https://www.youtube.com/watch?v=81wd8NlF3Qw)
## Graupner HoTT Model Locator
This is the Graupner HoTT adapted version of the Model Locator script using RSSI.
The OpenTX sensor "RSSI" is populated by the individual OpenTX telemetry protocol implementations and returns a value from 0..100 (percent) originating from the early FrSky implementation. It turns out that FrSky did not really provide a genuine signal strength indicator in units of dbm but a link quality indicator in 0..100%. With Graupner HoTT the link quality indicator is not a good basis for the model locator as it is very non-linear and doesn't change much with distance. Using the Graupner HoTT telemetry sensor "Rssi" which is a true signal strength indicator serves the purpose of locating a model much better as it varies much more with distance.
## DSM Forward Programming
This is a work in progress. It's only available for color screens (Horus, TX16S, T16, T18...).
This is a work in progress. It's available for color(+touch) and B&W screens.
If some text appears as Unknown_xxx, please report xxx and what the exact text display should be.
Need OpenTX 2.3.10 nightly or above. Located on the radio SD card under \SCRIPTS\TOOLS.
Work on OpenTX and EdgeTX. Located on the radio SD card under \SCRIPTS\TOOLS, make sure to copy the DSMLIB folder along with DSM FwdPrg_05_Color.lua or DSM FwdPrg_05_BW.lua.
[![DSM Forward Programming](https://img.youtube.com/vi/sjIaDw5j9nE/0.jpg)](https://www.youtube.com/watch?v=sjIaDw5j9nE)
If some text appears as Unknown_xxx, please report xxx and what the exact text display should be.
## DSM PID Flight log gain parameters for Blade micros
Lua telemetry script from [feathering on RCGroups](https://www.rcgroups.com/forums/showpost.php?p=46033341&postcount=20728) to facilitate setting the Gain Parameters on the Blade 150S FBL. It doesn't use Forward Programming but instead it just reads telemetry data from the Multi-module and displays it on a telemetry display.

View File

@@ -29,7 +29,7 @@ void A7105_WriteData(uint8_t len, uint8_t channel)
A7105_CSN_on;
if(protocol!=PROTO_WFLY2)
{
if(!(protocol==PROTO_FLYSKY || protocol==PROTO_KYOSHO))
if(!(protocol==PROTO_FLYSKY || (protocol==PROTO_KYOSHO && sub_protocol==KYOSHO_HYPE)))
{
A7105_Strobe(A7105_STANDBY); //Force standby mode, ie cancel any TX or RX...
A7105_SetTxRxMode(TX_EN); //Switch to PA
@@ -499,6 +499,11 @@ void A7105_Init(void)
vco_calibration1=0x02;
break;
case PROTO_PELIKAN:
if(sub_protocol == PELIKAN_SCX24)
{
vco_calibration1=0x0A;
break;
}
case PROTO_KYOSHO: //sub_protocol Hype
vco_calibration1=0x0C;
break;

View File

@@ -292,10 +292,17 @@ uint16_t AFHDS2A_callback()
case AFHDS2A_BIND2:
case AFHDS2A_BIND3:
AFHDS2A_build_bind_packet();
data_rx=A7105_ReadReg(A7105_00_MODE); // Check if something has been received...
A7105_WriteData(AFHDS2A_TXPACKET_SIZE, packet_count%2 ? 0x0d : 0x8c);
if(!(A7105_ReadReg(A7105_00_MODE) & (1<<5))) // removed FECF check due to issues with fs-x6b -> & (1<<5 | 1<<6)
{ // CRCF Ok
if(!(A7105_ReadReg(A7105_00_MODE) & (1<<5)) && !(data_rx & 1)) // removed FECF check due to issues with fs-x6b -> & (1<<5 | 1<<6)
{ // RX+CRCF Ok
A7105_ReadData(AFHDS2A_RXPACKET_SIZE);
#if 0
debug("RX");
for(uint8_t i=0; i<AFHDS2A_RXPACKET_SIZE ; i++)
debug(" %02X", packet[i]);
debugln("");
#endif
if(packet[0] == 0xbc && packet[9] == 0x01)
{
uint16_t addr;
@@ -330,7 +337,7 @@ uint16_t AFHDS2A_callback()
if(!(A7105_ReadReg(A7105_00_MODE) & 0x01))
break;
A7105_SetPower();
A7105_SetTxRxMode(TXRX_OFF); // Turn LNA off since we are in near range and we want to prevent swamping
A7105_SetTxRxMode((packet_count & 0x40) ? TXRX_OFF : RX_EN); // Turn LNA off time to time since we are in near range and we want to prevent swamping
A7105_Strobe(A7105_RX);
phase &= ~AFHDS2A_WAIT_WRITE;
phase++;

View File

@@ -16,7 +16,7 @@
#if defined(BUGSMINI_NRF24L01_INO)
#include "iface_nrf24l01.h"
#include "iface_xn297.h"
#define BUGSMINI_INITIAL_WAIT 500
#define BUGSMINI_PACKET_INTERVAL 6840
@@ -59,9 +59,8 @@ enum {
static void __attribute__((unused)) BUGSMINI_RF_init()
{
NRF24L01_Initialize();
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, BUGSMINI_RX_PAYLOAD_SIZE); // bytes of data payload for rx pipe 1
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
//XN297_HoppingCalib(BUGSMINI_NUM_RF_CHANNELS*2);
}
static void __attribute__((unused)) BUGSMINI_check_arming()
@@ -148,15 +147,14 @@ static void __attribute__((unused)) BUGSMINI_send_packet()
hopping_frequency_no++;
if(hopping_frequency_no >= BUGSMINI_NUM_RF_CHANNELS)
hopping_frequency_no = 0;
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? hopping_frequency[hopping_frequency_no+BUGSMINI_NUM_RF_CHANNELS] : hopping_frequency[hopping_frequency_no]);
XN297_Hopping(IS_BIND_IN_PROGRESS ? hopping_frequency_no+BUGSMINI_NUM_RF_CHANNELS : hopping_frequency_no);
}
// Power on, TX mode, 2byte CRC
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
// Send
XN297_SetPower();
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, BUGSMINI_TX_PAYLOAD_SIZE);
NRF24L01_SetPower();
}
// compute final address for the rxid received during bind
@@ -220,37 +218,37 @@ static void __attribute__((unused)) BUGSMINI_make_address()
// Something wrong happened if we arrive here....
}
#if defined(BUGS_HUB_TELEMETRY)
static void __attribute__((unused)) BUGSMINI_update_telemetry()
{
#if defined(BUGS_HUB_TELEMETRY)
uint8_t checksum = 0x6d;
for(uint8_t i=1; i<12; i++)
checksum += packet[i];
if(packet[0] == checksum)
checksum += packet_in[i];
if(packet_in[0] == checksum)
{
RX_RSSI = packet[3];
RX_RSSI = packet_in[3];
if(sub_protocol==BUGS3H)
{
if(packet[11] & 0x40)
if(packet_in[11] & 0x40)
v_lipo1 = 0x40; // Warning
else if(packet[11] & 0x80)
else if(packet_in[11] & 0x80)
v_lipo1 = 0x20; // Critical
else
v_lipo1 = 0x80; // Ok
}
else
{
if(packet[11] & 0x80)
if(packet_in[11] & 0x80)
v_lipo1 = 0x80; // Ok
else if(packet[11] & 0x40)
else if(packet_in[11] & 0x40)
v_lipo1 = 0x40; // Warning
else
v_lipo1 = 0x20; // Critical
}
telemetry_link=1;
}
#endif
}
#endif
uint16_t BUGSMINI_callback()
{
@@ -258,58 +256,52 @@ uint16_t BUGSMINI_callback()
switch(phase)
{
case BUGSMINI_BIND1:
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
if( XN297_IsRX() )
{ // RX fifo data ready
XN297_ReadPayload(packet, BUGSMINI_RX_PAYLOAD_SIZE);
XN297_ReadPayload(packet, BUGSMINI_RX_PAYLOAD_SIZE); // Not checking the CRC??
base_adr=BUGSMINI_EEPROM_OFFSET+(RX_num&0x0F)*2;
eeprom_write_byte((EE_ADDR)(base_adr+0),packet[1]); // Save rxid in EEPROM
eeprom_write_byte((EE_ADDR)(base_adr+1),packet[2]); // Save rxid in EEPROM
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(TX_EN);
eeprom_write_byte((EE_ADDR)(base_adr+0),packet[1]); // Save rxid in EEPROM
eeprom_write_byte((EE_ADDR)(base_adr+1),packet[2]); // Save rxid in EEPROM
BUGSMINI_make_address();
XN297_SetTXAddr(rx_tx_addr, 5);
XN297_SetRXAddr(rx_tx_addr, 5);
XN297_SetRXAddr(rx_tx_addr, BUGSMINI_RX_PAYLOAD_SIZE);
phase = BUGSMINI_DATA1;
BIND_DONE;
return BUGSMINI_PACKET_INTERVAL;
break;
}
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(TX_EN);
BUGSMINI_send_packet();
phase = BUGSMINI_BIND2;
return BUGSMINI_WRITE_WAIT;
case BUGSMINI_BIND2:
// switch to RX mode
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(RX_EN);
NRF24L01_FlushRx();
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO)
| _BV(NRF24L01_00_PWR_UP) | _BV(NRF24L01_00_PRIM_RX));
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
phase = BUGSMINI_BIND1;
return BUGSMINI_PACKET_INTERVAL - BUGSMINI_WRITE_WAIT;
case BUGSMINI_DATA1:
#ifdef MULTI_SYNC
telemetry_set_input_sync(BUGSMINI_PACKET_INTERVAL);
#endif
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
{ // RX fifo data ready => read only 12 bytes to not overwrite channel change flag
XN297_ReadPayload(packet, 12);
BUGSMINI_update_telemetry();
}
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(TX_EN);
#if defined(BUGS_HUB_TELEMETRY)
if( XN297_IsRX() )
{
XN297_ReadPayload(packet_in, BUGSMINI_RX_PAYLOAD_SIZE); // Not checking the CRC??
BUGSMINI_update_telemetry();
}
#endif
BUGSMINI_send_packet();
#if not defined(BUGS_HUB_TELEMETRY)
break;
#else
phase = BUGSMINI_DATA2;
return BUGSMINI_WRITE_WAIT;
case BUGSMINI_DATA2:
// switch to RX mode
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_FlushRx();
NRF24L01_SetTxRxMode(RX_EN);
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO)
| _BV(NRF24L01_00_PWR_UP) | _BV(NRF24L01_00_PRIM_RX));
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
phase = BUGSMINI_DATA1;
return BUGSMINI_PACKET_INTERVAL - BUGSMINI_WRITE_WAIT;
#endif
}
return BUGSMINI_PACKET_INTERVAL;
}
@@ -349,19 +341,19 @@ static void __attribute__((unused)) BUGSMINI_initialize_txid()
void BUGSMINI_init()
{
BUGSMINI_initialize_txid();
memset(packet, (uint8_t)0, BUGSMINI_TX_PAYLOAD_SIZE);
BUGSMINI_RF_init();
memset(packet, (uint8_t)0, BUGSMINI_TX_PAYLOAD_SIZE);
if(IS_BIND_IN_PROGRESS)
{
XN297_SetTXAddr((const uint8_t*)"mjxRC", 5);
XN297_SetRXAddr((const uint8_t*)"mjxRC", 5);
XN297_SetRXAddr((const uint8_t*)"mjxRC", BUGSMINI_RX_PAYLOAD_SIZE);
phase = BUGSMINI_BIND1;
}
else
{
BUGSMINI_make_address();
XN297_SetTXAddr(rx_tx_addr, 5);
XN297_SetRXAddr(rx_tx_addr, 5);
XN297_SetRXAddr(rx_tx_addr, BUGSMINI_RX_PAYLOAD_SIZE);
phase = BUGSMINI_DATA1;
}
armed = 0;

View File

@@ -15,7 +15,7 @@ Multiprotocol is distributed in the hope that it will be useful,
#if defined(BAYANG_RX_NRF24L01_INO)
#include "iface_nrf24l01.h"
#include "iface_xn297.h"
#define BAYANG_RX_PACKET_SIZE 15
#define BAYANG_RX_RF_NUM_CHANNELS 4
@@ -27,18 +27,15 @@ enum {
BAYANG_RX_DATA
};
static void __attribute__((unused)) Bayang_Rx_init_nrf24l01()
static void __attribute__((unused)) Bayang_Rx_RF_init()
{
const uint8_t bind_address[BAYANG_RX_ADDRESS_LENGTH] = { 0,0,0,0,0 };
NRF24L01_Initialize();
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
XN297_SetTXAddr(bind_address, BAYANG_RX_ADDRESS_LENGTH);
XN297_SetRXAddr(bind_address, BAYANG_RX_ADDRESS_LENGTH);
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, BAYANG_RX_PACKET_SIZE + 2); // 2 extra bytes for xn297 crc
NRF24L01_WriteReg(NRF24L01_05_RF_CH, BAYANG_RX_RF_BIND_CHANNEL);
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_FlushRx();
NRF24L01_SetTxRxMode(RX_EN);
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP) | _BV(NRF24L01_00_PRIM_RX));
XN297_SetRXAddr(bind_address, BAYANG_RX_PACKET_SIZE);
XN297_RFChannel(BAYANG_RX_RF_BIND_CHANNEL);
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
}
static uint8_t __attribute__((unused)) Bayang_Rx_check_validity() {
@@ -91,7 +88,7 @@ static void __attribute__((unused)) Bayang_Rx_build_telemetry_packet()
void BAYANG_RX_init()
{
uint8_t i;
Bayang_Rx_init_nrf24l01();
Bayang_Rx_RF_init();
hopping_frequency_no = 0;
rx_data_started = false;
rx_data_received = false;
@@ -105,8 +102,9 @@ void BAYANG_RX_init()
rx_tx_addr[i] = eeprom_read_byte((EE_ADDR)temp++);
for (i = 0; i < BAYANG_RX_RF_NUM_CHANNELS; i++)
hopping_frequency[i] = eeprom_read_byte((EE_ADDR)temp++);
//XN297_HoppingCalib(BAYANG_RX_RF_NUM_CHANNELS);
XN297_SetTXAddr(rx_tx_addr, BAYANG_RX_ADDRESS_LENGTH);
XN297_SetRXAddr(rx_tx_addr, BAYANG_RX_ADDRESS_LENGTH);
XN297_SetRXAddr(rx_tx_addr, BAYANG_RX_PACKET_SIZE);
phase = BAYANG_RX_DATA;
}
}
@@ -116,86 +114,89 @@ uint16_t BAYANG_RX_callback()
uint8_t i;
static int8_t read_retry;
switch (phase) {
case BAYANG_RX_BIND:
if(IS_BIND_DONE)
{
BAYANG_RX_init(); // Abort bind
break;
}
if (NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR)) {
// data received from TX
if (XN297_ReadPayload(packet, BAYANG_RX_PACKET_SIZE) && ( packet[0] == 0xA4 || packet[0] == 0xA2 ) && Bayang_Rx_check_validity()) {
// store tx info into eeprom
uint16_t temp = BAYANG_RX_EEPROM_OFFSET;
for (i = 0; i < 5; i++) {
rx_tx_addr[i] = packet[i + 1];
eeprom_write_byte((EE_ADDR)temp++, rx_tx_addr[i]);
}
for (i = 0; i < 4; i++) {
hopping_frequency[i] = packet[i + 6];
eeprom_write_byte((EE_ADDR)temp++, hopping_frequency[i]);
}
XN297_SetTXAddr(rx_tx_addr, BAYANG_RX_ADDRESS_LENGTH);
XN297_SetRXAddr(rx_tx_addr, BAYANG_RX_ADDRESS_LENGTH);
BIND_DONE;
phase = BAYANG_RX_DATA;
}
NRF24L01_FlushRx();
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
}
break;
case BAYANG_RX_DATA:
if (NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR)) {
if (XN297_ReadPayload(packet, BAYANG_RX_PACKET_SIZE) && packet[0] == 0xA5 && Bayang_Rx_check_validity()) {
if ((telemetry_link & 0x7F) == 0) {
Bayang_Rx_build_telemetry_packet();
telemetry_link = 1;
#ifdef SEND_CPPM
if(sub_protocol>0)
telemetry_link |= 0x80; // Disable telemetry output
#endif
}
rx_data_started = true;
rx_data_received = true;
read_retry = 8;
pps_counter++;
}
}
// packets per second
if (millis() - pps_timer >= 1000) {
pps_timer = millis();
debugln("%d pps", pps_counter);
RX_LQI = pps_counter >> 1;
pps_counter = 0;
}
// frequency hopping
if (read_retry++ >= 8) {
hopping_frequency_no++;
if (hopping_frequency_no >= BAYANG_RX_RF_NUM_CHANNELS)
hopping_frequency_no = 0;
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
NRF24L01_FlushRx();
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
if (rx_data_started)
switch (phase)
{
case BAYANG_RX_BIND:
if(IS_BIND_DONE)
{
if(rx_data_received)
{ // In sync
rx_data_received = false;
read_retry = 5;
return 1500;
BAYANG_RX_init(); // Abort bind
break;
}
if ( XN297_IsRX() )
{
debugln("RX");
// data received from TX
if (XN297_ReadPayload(packet, BAYANG_RX_PACKET_SIZE) && ( packet[0] == 0xA4 || packet[0] == 0xA2 ) && Bayang_Rx_check_validity())
{
// store tx info into eeprom
uint16_t temp = BAYANG_RX_EEPROM_OFFSET;
for (i = 0; i < 5; i++) {
rx_tx_addr[i] = packet[i + 1];
eeprom_write_byte((EE_ADDR)temp++, rx_tx_addr[i]);
}
for (i = 0; i < 4; i++) {
hopping_frequency[i] = packet[i + 6];
eeprom_write_byte((EE_ADDR)temp++, hopping_frequency[i]);
}
//XN297_HoppingCalib(BAYANG_RX_RF_NUM_CHANNELS);
XN297_SetTXAddr(rx_tx_addr, BAYANG_RX_ADDRESS_LENGTH);
XN297_SetRXAddr(rx_tx_addr, BAYANG_RX_PACKET_SIZE);
BIND_DONE;
phase = BAYANG_RX_DATA;
}
XN297_SetTxRxMode(RX_EN);
}
break;
case BAYANG_RX_DATA:
if ( XN297_IsRX() ) {
if (XN297_ReadPayload(packet, BAYANG_RX_PACKET_SIZE) && packet[0] == 0xA5 && Bayang_Rx_check_validity()) {
if ((telemetry_link & 0x7F) == 0) {
Bayang_Rx_build_telemetry_packet();
telemetry_link = 1;
#ifdef SEND_CPPM
if(sub_protocol>0)
telemetry_link |= 0x80; // Disable telemetry output
#endif
}
rx_data_started = true;
rx_data_received = true;
read_retry = 8;
pps_counter++;
}
}
// packets per second
if (millis() - pps_timer >= 1000) {
pps_timer = millis();
debugln("%d pps", pps_counter);
RX_LQI = pps_counter >> 1;
pps_counter = 0;
}
// frequency hopping
if (read_retry++ >= 8) {
hopping_frequency_no++;
if (hopping_frequency_no >= BAYANG_RX_RF_NUM_CHANNELS)
hopping_frequency_no = 0;
XN297_Hopping(hopping_frequency_no);
XN297_SetTxRxMode(RX_EN);
if (rx_data_started)
{
if(rx_data_received)
{ // In sync
rx_data_received = false;
read_retry = 5;
return 1500;
}
else
{ // packet lost
read_retry = 0;
if(RX_LQI==0) // communication lost
rx_data_started=false;
}
}
else
{ // packet lost
read_retry = 0;
if(RX_LQI==0) // communication lost
rx_data_started=false;
}
read_retry = -16; // retry longer until first packet is caught
}
else
read_retry = -16; // retry longer until first packet is caught
}
return 250;
return 250;
}
return 1000;
}

View File

@@ -17,7 +17,7 @@ Multiprotocol is distributed in the hope that it will be useful,
#if defined(BAYANG_NRF24L01_INO)
#include "iface_nrf24l01.h"
#include "iface_xn297.h"
#define BAYANG_BIND_COUNT 1000
#define BAYANG_PACKET_PERIOD 2000
@@ -97,6 +97,8 @@ static void __attribute__((unused)) BAYANG_send_packet()
}
else
{
XN297_Hopping(hopping_frequency_no++);
hopping_frequency_no%=BAYANG_RF_NUM_CHANNELS;
uint16_t val;
uint8_t dyntrim = 1;
switch (sub_protocol)
@@ -192,28 +194,18 @@ static void __attribute__((unused)) BAYANG_send_packet()
for (uint8_t i=0; i < BAYANG_PACKET_SIZE-1; i++)
packet[14] += packet[i];
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? rf_ch_num:hopping_frequency[hopping_frequency_no++]);
hopping_frequency_no%=BAYANG_RF_NUM_CHANNELS;
// Power on, TX mode, 2byte CRC
// Why CRC0? xn297 does not interpret it - either 16-bit CRC or nothing
NRF24L01_FlushTx();
NRF24L01_SetTxRxMode(TX_EN);
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
// Send
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, BAYANG_PACKET_SIZE);
NRF24L01_SetPower(); // Set tx_power
}
#ifdef BAYANG_HUB_TELEMETRY
static void __attribute__((unused)) BAYANG_check_rx(void)
{
if (NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
if( XN297_IsRX() )
{ // data received from model
XN297_ReadPayload(packet, BAYANG_PACKET_SIZE);
NRF24L01_WriteReg(NRF24L01_07_STATUS, 255);
NRF24L01_FlushRx();
XN297_ReadPayload(packet, BAYANG_PACKET_SIZE); // Strange can't test the CRC since it seems to be disabled on telemetry packets...
uint8_t check = packet[0];
for (uint8_t i=1; i < BAYANG_PACKET_SIZE-1; i++)
check += packet[i];
@@ -233,8 +225,8 @@ static void __attribute__((unused)) BAYANG_check_rx(void)
telemetry_link=1;
#if defined HUB_TELEMETRY
// Multiplexed P, I, D values in packet[8] and packet[9].
// The two most significant bits specify which term is sent.
// Remaining 14 bits represent the value: 0 .. 16383
// The two most significant bits specify which term is sent.
// Remaining 14 bits represent the value: 0 .. 16383
frsky_send_user_frame(0x24+(packet[8]>>6), packet[9], packet[8] & 0x3F ); //0x24 = ACCEL_X_ID, so ACCEL_X_ID=P, ACCEL_Y_ID=I, ACCEL_Z_ID=D
#endif
telemetry_counter++;
@@ -242,27 +234,21 @@ static void __attribute__((unused)) BAYANG_check_rx(void)
telemetry_link=0; // Don't send anything yet
}
}
NRF24L01_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(TXRX_OFF);
}
#endif
static void __attribute__((unused)) BAYANG_RF_init()
{
NRF24L01_Initialize();
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
XN297_SetTXAddr((uint8_t *)"\x00\x00\x00\x00\x00", BAYANG_ADDRESS_LENGTH);
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, BAYANG_PACKET_SIZE);
//XN297_HoppingCalib(BAYANG_RF_NUM_CHANNELS);
switch (sub_protocol)
{
case X16_AH:
case IRDRONE:
rf_ch_num = BAYANG_RF_BIND_CHANNEL_X16_AH;
break;
default:
rf_ch_num = BAYANG_RF_BIND_CHANNEL;
break;
}
//Set bind channel
uint8_t ch = BAYANG_RF_BIND_CHANNEL;
if(sub_protocol == X16_AH || sub_protocol == IRDRONE)
ch = BAYANG_RF_BIND_CHANNEL_X16_AH;
XN297_RFChannel(ch);
}
enum {
@@ -287,7 +273,7 @@ uint16_t BAYANG_callback()
{
XN297_SetTXAddr(rx_tx_addr, BAYANG_ADDRESS_LENGTH);
#ifdef BAYANG_HUB_TELEMETRY
XN297_SetRXAddr(rx_tx_addr, BAYANG_ADDRESS_LENGTH);
XN297_SetRXAddr(rx_tx_addr, BAYANG_PACKET_SIZE);
#endif
BIND_DONE;
phase++; //WRITE
@@ -322,9 +308,9 @@ uint16_t BAYANG_callback()
// switch radio to rx as soon as packet is sent
start=(uint16_t)micros();
while ((uint16_t)((uint16_t)micros()-(uint16_t)start) < 1000) // Wait max 1ms
if((NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_TX_DS)))
if(XN297_IsPacketSent())
break;
NRF24L01_WriteReg(NRF24L01_00_CONFIG, 0x03);
XN297_SetTxRxMode(RX_EN);
phase++; // READ
return BAYANG_PACKET_TELEM_PERIOD - BAYANG_CHECK_DELAY - BAYANG_READ_DELAY;
case BAYANG_READ:

View File

@@ -190,7 +190,9 @@ static void __attribute__((unused)) CABELL_send_packet(uint8_t bindMode)
}
TxPacket.option = (bindMode) ? (option & (~CABELL_OPTION_MASK_CHANNEL_REDUCTION)) : option; //remove channel reduction if in bind mode
}
TxPacket.reserved = 0;
rf_ch_num = CABELL_getNextChannel (hopping_frequency,CABELL_RADIO_CHANNELS, rf_ch_num);
TxPacket.reserved = rf_ch_num & 0x3F;
TxPacket.modelNum = RX_num;
uint16_t checkSum = TxPacket.modelNum + TxPacket.option + TxPacket.RxMode + TxPacket.reserved; // Start Calculate checksum
@@ -244,7 +246,6 @@ static void __attribute__((unused)) CABELL_send_packet(uint8_t bindMode)
TxPacket.checkSum_LSB = checkSum & 0x00FF;
// Set channel for next transmission
rf_ch_num = CABELL_getNextChannel (hopping_frequency,CABELL_RADIO_CHANNELS, rf_ch_num);
NRF24L01_WriteReg(NRF24L01_05_RF_CH,rf_ch_num);
//NRF24L01_FlushTx(); //just in case things got hung up
@@ -279,39 +280,44 @@ static void __attribute__((unused)) CABELL_send_packet(uint8_t bindMode)
static void __attribute__((unused)) CABELL_getChannelSequence (uint8_t outArray[], uint8_t numChannels, uint64_t permutation)
{
/* This procedure initializes an array with the sequence progression of channels.
* This is not the actual channels itself, but the sequence base to be used within bands of
* This is not the actual channels itself, but the sequence base to be used within bands of
* channels.
*
*
* There are numChannels! permutations for arranging the channels
* one of these permutations will be calculated based on the permutation input
* permutation should be between 1 and numChannels! but the routine will constrain it
* if these bounds are exceeded. Typically the radio's unique TX ID should be used.
*
*
* The maximum numChannels is 20. Anything larger than this will cause the uint64_t
* variables to overflow, yielding unknown results (possibly infinite loop?). Therefor
* this routine constrains the value.
*/
*/
uint8_t i; //iterator counts numChannels
uint64_t indexOfNextSequenceValue;
uint64_t numChannelsFactorial=1;
uint32_t indexOfNextSequenceValue;
uint32_t numChannelsFactorial=1;
uint32_t perm32 ;
uint8_t sequenceValue;
numChannels = constrain(numChannels,1,20);
numChannels = constrain(numChannels,1,9);
for (i = 1; i <= numChannels;i++)
{
numChannelsFactorial *= i; // Calculate n!
outArray[i-1] = i-1; // Initialize array with the sequence
numChannelsFactorial *= i; // Calculate n!
outArray[i-1] = i-1; // Initialize array with the sequence
}
permutation = (permutation % numChannelsFactorial) + 1; // permutation must be between 1 and n! or this algorithm will infinite loop
perm32 = permutation >> 8 ; // Shift 40 bit input to 32 bit
perm32 = (perm32 % numChannelsFactorial); // permutation must be between 1 and n! or this algorithm will infinite loop
perm32 <<= 8 ; // Shift back 8 bits
perm32 += permutation & 0x00FF ; // Tack on least 8 bits
perm32 = (perm32 % numChannelsFactorial) + 1; // permutation must be between 1 and n! or this algorithm will infinite loop
//Rearrange the array elements based on the permutation selected
for (i=0, permutation--; i<numChannels; i++ )
for (i=0, perm32--; i<numChannels; i++ )
{
numChannelsFactorial /= ((uint64_t)numChannels)-i;
indexOfNextSequenceValue = i+(permutation/numChannelsFactorial);
permutation %= numChannelsFactorial;
numChannelsFactorial /= numChannels-i;
indexOfNextSequenceValue = i+(perm32/numChannelsFactorial);
perm32 %= numChannelsFactorial;
//Copy the value in the selected array position
sequenceValue = outArray[indexOfNextSequenceValue];

View File

@@ -195,6 +195,7 @@ void __attribute__((unused)) CC2500_250K_Init()
// TX Power = 0
// Whitening = false
// Fast Frequency Hopping - no PLL auto calibration
/* //Previous config
CC2500_WriteReg(CC2500_08_PKTCTRL0, 0x01); // Packet Automation Control
CC2500_WriteReg(CC2500_0B_FSCTRL1, 0x0A); // Frequency Synthesizer Control
CC2500_WriteReg(CC2500_0C_FSCTRL0, option); // Frequency offset hack
@@ -217,6 +218,42 @@ void __attribute__((unused)) CC2500_250K_Init()
CC2500_WriteReg(CC2500_23_FSCAL3, 0xEA); // Frequency Synthesizer Calibration
CC2500_WriteReg(CC2500_25_FSCAL1, 0x00); // Frequency Synthesizer Calibration
CC2500_WriteReg(CC2500_26_FSCAL0, 0x11); // Frequency Synthesizer Calibration
*/
CC2500_WriteReg(CC2500_07_PKTCTRL1, 0x05); // Packet Automation Control, address check true auto append RSSI & LQI
CC2500_WriteReg(CC2500_08_PKTCTRL0, 0x00); // Packet Automation Control, fixed packet len
CC2500_WriteReg(CC2500_0B_FSCTRL1, 0x0A); // Frequency Synthesizer Control (IF Frequency)
CC2500_WriteReg(CC2500_0C_FSCTRL0, 0x00); // Frequency Synthesizer Control
CC2500_WriteReg(CC2500_0D_FREQ2, 0x5C); // Frequency Control Word, High Byte
CC2500_WriteReg(CC2500_0E_FREQ1, 0x4E); // Frequency Control Word, Middle Byte
CC2500_WriteReg(CC2500_0F_FREQ0, 0xC5); // Frequency Control Word, Low Byte
CC2500_WriteReg(CC2500_10_MDMCFG4, 0x3D); // Modem Configuration Set to 406kHz BW filter
CC2500_WriteReg(CC2500_11_MDMCFG3, 0x3B); // Modem Configuration
CC2500_WriteReg(CC2500_12_MDMCFG2, 0x10); // Modem Configuration, GFSK, no preambule and no sync word -> TX by default
CC2500_WriteReg(CC2500_13_MDMCFG1, 0x03); // Modem Configuration, 2 bytes of preamble
CC2500_WriteReg(CC2500_14_MDMCFG0, 0xA4); // Modem Configuration
CC2500_WriteReg(CC2500_15_DEVIATN, 0x62); // Modem Deviation Setting
CC2500_WriteReg(CC2500_18_MCSM0, 0x08); // Main Radio Control State Machine Configuration
CC2500_WriteReg(CC2500_19_FOCCFG, 0x1D); // Frequency Offset Compensation Configuration
CC2500_WriteReg(CC2500_1A_BSCFG, 0x1C); // Bit Synchronization Configuration
CC2500_WriteReg(CC2500_1B_AGCCTRL2, 0xC7); // AGC Control
CC2500_WriteReg(CC2500_1C_AGCCTRL1, 0x00); // AGC Control
CC2500_WriteReg(CC2500_1D_AGCCTRL0, 0xB0); // AGC Control
CC2500_WriteReg(CC2500_21_FREND1, 0xB6); // Front End RX Configuration
CC2500_WriteReg(CC2500_23_FSCAL3, 0xEA); // Frequency Synthesizer Calibration
CC2500_WriteReg(CC2500_25_FSCAL1, 0x00); // Frequency Synthesizer Calibration
CC2500_WriteReg(CC2500_26_FSCAL0, 0x11); // Frequency Synthesizer Calibration
//Prep RX
// Set first 3 bytes of rx addr in [0]->SYNC1, [1]->SYNC0 and [2]->ADDR
// CC2500_WriteReg(CC2500_04_SYNC1, [0]); // Sync word, high byte
// CC2500_WriteReg(CC2500_05_SYNC0, [1]); // Sync word, low byte
// CC2500_WriteReg(CC2500_09_ADDR, [2]); // Set addr
//RX
// CC2500_WriteReg(CC2500_12_MDMCFG2, 0x12); // Modem Configuration, GFSK, 16/16 Sync Word TX&RX
//TX
// CC2500_WriteReg(CC2500_12_MDMCFG2, 0x10); // Modem Configuration, GFSK, no preambule and no sync word
// need to set packet length before sending/receiving
// CC2500_WriteReg(CC2500_06_PKTLEN, cc2500_packet_len); // Packet len, fix packet len
CC2500_SetTxRxMode(TX_EN);
CC2500_SetPower();
@@ -250,87 +287,4 @@ void __attribute__((unused)) CC2500_250K_RFChannel(uint8_t number)
CC2500_Strobe(CC2500_SCAL);
delayMicroseconds(900);
}
//NRF emulation layer with CRC16 enabled
uint8_t cc2500_nrf_tx_addr[5], cc2500_nrf_addr_len;
void __attribute__((unused)) CC2500_250K_NRF_SetTXAddr(uint8_t* addr, uint8_t len)
{
cc2500_nrf_addr_len = len;
memcpy(cc2500_nrf_tx_addr, addr, len);
}
void __attribute__((unused)) CC2500_250K_NRF_WritePayload(uint8_t* msg, uint8_t len)
{
#if defined(ESKY150V2_CC2500_INO)
uint8_t buf[158];
#else
uint8_t buf[35];
#endif
uint8_t last = 0;
uint8_t i;
//nrf preamble
if(cc2500_nrf_tx_addr[cc2500_nrf_addr_len - 1] & 0x80)
buf[0]=0xAA;
else
buf[0]=0x55;
last++;
// address
for (i = 0; i < cc2500_nrf_addr_len; ++i)
buf[last++] = cc2500_nrf_tx_addr[cc2500_nrf_addr_len - i - 1];
// payload
for (i = 0; i < len; ++i)
buf[last++] = msg[i];
// crc
crc = 0xffff;
for (uint8_t i = 1; i < last; ++i)
crc16_update( buf[i], 8);
buf[last++] = crc >> 8;
buf[last++] = crc & 0xff;
buf[last++] = 0;
//for(uint8_t i=0;i<last;i++)
// debug("%02X ",buf[i]);
//debugln("");
// stop TX/RX
CC2500_Strobe(CC2500_SIDLE);
// flush tx FIFO
CC2500_Strobe(CC2500_SFTX);
// packet length
CC2500_WriteReg(CC2500_3F_TXFIFO, last);
// transmit nrf packet
uint8_t *buff=buf;
uint8_t status;
if(last>63)
{
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, buff, 63);
CC2500_Strobe(CC2500_STX);
last-=63;
buff+=63;
while(last)
{//Loop until all the data is sent
do
{// Wait for the FIFO to become available
status=CC2500_ReadReg(CC2500_3A_TXBYTES | CC2500_READ_BURST);
}
while((status&0x7F)>31 && (status&0x80)==0);
if(last>31)
{//Send 31 bytes
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, buff, 31);
last-=31;
buff+=31;
}
else
{//Send last bytes
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, buff, last);
last=0;
}
}
}
else
{//Send packet
CC2500_WriteRegisterMulti(CC2500_3F_TXFIFO, buff, last);
CC2500_Strobe(CC2500_STX);
}
}
#endif

View File

@@ -16,7 +16,7 @@
#if defined(CG023_NRF24L01_INO)
#include "iface_nrf24l01.h"
#include "iface_xn297.h"
#define CG023_PACKET_PERIOD 8200 // Timeout for callback in uSec
#define CG023_INITIAL_WAIT 500
@@ -63,9 +63,15 @@ static void __attribute__((unused)) CG023_send_packet()
aileron = convert_channel_16b_limit(AILERON, 0x43, 0xBB);
if (IS_BIND_IN_PROGRESS)
{
packet[0]= 0xaa;
XN297_RFChannel(CG023_RF_BIND_CHANNEL);
}
else
{
packet[0]= 0x55;
XN297_RFChannel(hopping_frequency_no);
}
// transmitter id
packet[1] = rx_tx_addr[0];
packet[2] = rx_tx_addr[1];
@@ -85,7 +91,7 @@ static void __attribute__((unused)) CG023_send_packet()
if(sub_protocol==CG023)
{
// rate
packet[13] = CG023_FLAG_RATE_HIGH
packet[13] = CG023_FLAG_RATE_HIGH
| GET_FLAG(CH5_SW,CG023_FLAG_FLIP)
| GET_FLAG(CH6_SW,CG023_FLAG_LED_OFF)
| GET_FLAG(CH7_SW,CG023_FLAG_STILL)
@@ -95,7 +101,7 @@ static void __attribute__((unused)) CG023_send_packet()
else
{// YD829
// rate
packet[13] = YD829_FLAG_RATE_HIGH
packet[13] = YD829_FLAG_RATE_HIGH
| GET_FLAG(CH5_SW,YD829_FLAG_FLIP)
| GET_FLAG(CH7_SW,YD829_FLAG_STILL)
| GET_FLAG(CH8_SW,YD829_FLAG_VIDEO)
@@ -103,25 +109,15 @@ static void __attribute__((unused)) CG023_send_packet()
}
packet[14] = 0;
// Power on, TX mode, 2byte CRC
// Why CRC0? xn297 does not interpret it - either 16-bit CRC or nothing
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
if (IS_BIND_IN_PROGRESS)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, CG023_RF_BIND_CHANNEL);
else
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency_no);
// clear packet status bits and TX FIFO
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
// Send
XN297_SetTxRxMode(TX_EN);
XN297_SetPower();
XN297_WritePayload(packet, CG023_PACKET_SIZE);
NRF24L01_SetPower(); // Set tx_power
}
static void __attribute__((unused)) CG023_RF_init()
{
NRF24L01_Initialize();
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
XN297_SetTXAddr((uint8_t *)"\x26\xA8\x67\x35\xCC", 5);
}
@@ -144,7 +140,7 @@ uint16_t CG023_callback()
static void __attribute__((unused)) CG023_initialize_txid()
{
rx_tx_addr[0]= 0x80 | (rx_tx_addr[0] % 0x40);
if( rx_tx_addr[0] == 0xAA) // avoid using same freq for bind and data channel
if( rx_tx_addr[0] == 0xAA) // avoid using same freq for bind and data channel
rx_tx_addr[0] ++;
hopping_frequency_no = rx_tx_addr[0] - 0x7D; // rf channel for data packets
}

View File

@@ -17,7 +17,7 @@
#if defined(CX10_NRF24L01_INO)
#include "iface_nrf24l01.h"
#include "iface_xn297.h"
#define CX10_BIND_COUNT 4360 // 6 seconds
#define CX10_PACKET_SIZE 15
@@ -46,12 +46,12 @@ enum {
CX10_DATA
};
static void __attribute__((unused)) CX10_Write_Packet(uint8_t bind)
static void __attribute__((unused)) CX10_Write_Packet()
{
uint8_t offset = 0;
if(sub_protocol == CX10_BLUE)
offset = 4;
packet[0] = bind ? 0xAA : 0x55;
packet[0] = IS_BIND_IN_PROGRESS ? 0xAA : 0x55;
packet[1] = rx_tx_addr[0];
packet[2] = rx_tx_addr[1];
packet[3] = rx_tx_addr[2];
@@ -62,57 +62,57 @@ static void __attribute__((unused)) CX10_Write_Packet(uint8_t bind)
uint16_t throttle=convert_channel_16b_limit(THROTTLE,1000,2000);
uint16_t rudder= convert_channel_16b_limit(RUDDER ,2000,1000);
// Channel 5 - flip flag
packet[12+offset] = GET_FLAG(CH5_SW,CX10_FLAG_FLIP); // flip flag applied on rudder
packet[12+offset] = GET_FLAG(CH5_SW,CX10_FLAG_FLIP); // flip flag applied on rudder
// Channel 6 - rate mode is 2 lsb of packet 13
if(CH6_SW) // rate 3 / headless on CX-10A
if(CH6_SW) // rate 3 / headless on CX-10A
flags = 0x02;
else
if(Channel_data[CH6] < CHANNEL_MIN_COMMAND)
flags = 0x00; // rate 1
flags = 0x00; // rate 1
else
flags = 0x01; // rate 2
uint8_t flags2=0; // packet 14
flags = 0x01; // rate 2
uint8_t flags2=0; // packet 14
uint8_t video_state=packet[14] & 0x21;
switch(sub_protocol)
{
case CX10_BLUE:
flags |= GET_FLAG(!CH7_SW, 0x10) // Channel 7 - picture
|GET_FLAG( CH8_SW, 0x08); // Channel 8 - video
flags |= GET_FLAG(!CH7_SW, 0x10) // Channel 7 - picture
|GET_FLAG( CH8_SW, 0x08); // Channel 8 - video
break;
case F_Q282:
case F_Q242:
case F_Q222:
memcpy(&packet[15], "\x10\x10\xaa\xaa\x00\x00", 6);
//FLIP|LED|PICTURE|VIDEO|HEADLESS|RTH|XCAL|YCAL
flags2 = GET_FLAG(CH5_SW, 0x80) // Channel 5 - FLIP
|GET_FLAG(!CH6_SW, 0x40) // Channel 6 - LED
|GET_FLAG(CH9_SW, 0x08) // Channel 9 - HEADLESS
|GET_FLAG(CH11_SW, 0x04) // Channel 11 - XCAL
|GET_FLAG(CH12_SW, 0x02); // Channel 12 - YCAL or Start/Stop motors on JXD 509
flags2 = GET_FLAG(CH5_SW, 0x80) // Channel 5 - FLIP
|GET_FLAG(!CH6_SW, 0x40) // Channel 6 - LED
|GET_FLAG(CH9_SW, 0x08) // Channel 9 - HEADLESS
|GET_FLAG(CH11_SW, 0x04) // Channel 11 - XCAL
|GET_FLAG(CH12_SW, 0x02); // Channel 12 - YCAL or Start/Stop motors on JXD 509
if(sub_protocol==F_Q242)
{
flags=2;
flags2|= GET_FLAG(CH7_SW,0x01) // Channel 7 - picture
|GET_FLAG(CH8_SW,0x10); // Channel 8 - video
flags2|= GET_FLAG(CH7_SW,0x01) // Channel 7 - picture
|GET_FLAG(CH8_SW,0x10); // Channel 8 - video
packet[17]=0x00;
packet[18]=0x00;
}
else
{ // F_Q282 & F_Q222
flags=3; // expert
if(CH8_SW) // Channel 8 - F_Q282 video / F_Q222 Module 1
flags=3; // expert
if(CH8_SW) // Channel 8 - F_Q282 video / F_Q222 Module 1
{
if (!(video_state & 0x20)) video_state ^= 0x21;
}
else
if (video_state & 0x20) video_state &= 0x01;
flags2 |= video_state
|GET_FLAG(CH7_SW,0x10); // Channel 7 - F_Q282 picture / F_Q222 Module 2
|GET_FLAG(CH7_SW,0x10); // Channel 7 - F_Q282 picture / F_Q222 Module 2
}
if(CH10_SW) flags |=0x80; // Channel 10 - RTH
if(CH10_SW) flags |=0x80; // Channel 10 - RTH
break;
case DM007:
aileron = 3000 - aileron;
@@ -128,16 +128,16 @@ static void __attribute__((unused)) CX10_Write_Packet(uint8_t bind)
if(CH8_SW) packet[12] &= ~CX10_FLAG_FLIP;
case JC3015_1:
//FLIP|MODE|PICTURE|VIDEO
flags2= GET_FLAG(CH7_SW,_BV(3)) // Channel 7
|GET_FLAG(CH8_SW,_BV(4)); // Channel 8
flags2= GET_FLAG(CH7_SW,_BV(3)) // Channel 7
|GET_FLAG(CH8_SW,_BV(4)); // Channel 8
break;
case MK33041:
elevator = 3000 - elevator;
//FLIP|MODE|PICTURE|VIDEO|HEADLESS|RTH
flags|=GET_FLAG(CH7_SW,_BV(7)) // Channel 7 - picture
|GET_FLAG(CH10_SW,_BV(2)); // Channel 10 - rth
flags2=GET_FLAG(CH8_SW,_BV(0)) // Channel 8 - video
|GET_FLAG(CH9_SW,_BV(5)); // Channel 9 - headless
flags|=GET_FLAG(CH7_SW,_BV(7)) // Channel 7 - picture
|GET_FLAG(CH10_SW,_BV(2)); // Channel 10 - rth
flags2=GET_FLAG(CH8_SW,_BV(0)) // Channel 8 - video
|GET_FLAG(CH9_SW,_BV(5)); // Channel 9 - headless
break;
}
packet[5+offset] = lowByte(aileron);
@@ -151,32 +151,23 @@ static void __attribute__((unused)) CX10_Write_Packet(uint8_t bind)
packet[13+offset]=flags;
packet[14+offset]=flags2;
// Power on, TX mode, 2byte CRC
// Why CRC0? xn297 does not interpret it - either 16-bit CRC or nothing
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
if (bind)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, CX10_RF_BIND_CHANNEL);
else
// Send
if(IS_BIND_DONE)
{
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++]);
XN297_Hopping(hopping_frequency_no++);
hopping_frequency_no %= CX10_NUM_RF_CHANNELS;
}
// clear packet status bits and TX FIFO
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, packet_length);
NRF24L01_SetPower();
}
static void __attribute__((unused)) CX10_RF_init()
{
NRF24L01_Initialize();
XN297_SetTXAddr((uint8_t *)"\xcc\xcc\xcc\xcc\xcc",5);
XN297_SetRXAddr((uint8_t *)"\xcc\xcc\xcc\xcc\xcc",5);
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, packet_length); // rx pipe 0 (used only for blue board)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, CX10_RF_BIND_CHANNEL);
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
XN297_SetTXAddr((uint8_t *)"\xcc\xcc\xcc\xcc\xcc", 5);
XN297_SetRXAddr((uint8_t *)"\xcc\xcc\xcc\xcc\xcc", packet_length);
XN297_RFChannel(CX10_RF_BIND_CHANNEL);
}
uint16_t CX10_callback()
@@ -190,43 +181,39 @@ uint16_t CX10_callback()
}
else
{
CX10_Write_Packet(1);
CX10_Write_Packet();
bind_counter--;
}
break;
case CX10_BIND2:
if( NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR))
// switch to TX mode
if( XN297_IsRX() )
{ // RX fifo data ready
XN297_ReadPayload(packet, packet_length);
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(TX_EN);
if(packet[9] == 1)
debugln("RX");
if(XN297_ReadPayload(packet, packet_length) && packet[9] == 1)
{
BIND_DONE;
XN297_SetTxRxMode(TXRX_OFF);
phase = CX10_DATA;
break;
}
}
else
{
// switch to TX mode
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_FlushTx();
NRF24L01_SetTxRxMode(TX_EN);
CX10_Write_Packet(1);
XN297_SetTxRxMode(TXRX_OFF);
CX10_Write_Packet();
// wait for packet to be sent
while( (NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_TX_DS)) == 0); //delayMicroseconds(400);
// switch to RX mode
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_FlushRx();
NRF24L01_SetTxRxMode(RX_EN);
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP) | _BV(NRF24L01_00_PRIM_RX));
while( !XN297_IsPacketSent()); //delayMicroseconds(400);
}
// switch to RX mode
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
break;
case CX10_DATA:
#ifdef MULTI_SYNC
telemetry_set_input_sync(packet_period);
#endif
CX10_Write_Packet(0);
CX10_Write_Packet();
break;
}
return packet_period;

View File

@@ -79,13 +79,24 @@ uint8_t CYRF_Reset()
void CYRF_GetMfgData(uint8_t data[])
{
#ifndef FORCE_CYRF_ID
/* Fuses power on */
CYRF_WriteRegister(CYRF_25_MFG_ID, 0xFF);
if(eeprom_read_byte((EE_ADDR)EEPROM_CID_INIT_OFFSET)==0xf0)
{//read Cyrf ID from EEPROM
for(uint8_t i=0;i<6;i++)
data[i] = eeprom_read_byte((EE_ADDR)EEPROM_CID_OFFSET+i);
}
else
{//read Cyrf ID and store it EEPROM
/* Fuses power on */
CYRF_WriteRegister(CYRF_25_MFG_ID, 0xFF);
CYRF_ReadRegisterMulti(CYRF_25_MFG_ID, data, 6);
CYRF_ReadRegisterMulti(CYRF_25_MFG_ID, data, 6);
for(uint8_t i=0;i<6;i++)
eeprom_write_byte((EE_ADDR)EEPROM_CID_OFFSET+i, data[i]);
eeprom_write_byte((EE_ADDR)EEPROM_CID_INIT_OFFSET, 0xf0);
/* Fuses power off */
CYRF_WriteRegister(CYRF_25_MFG_ID, 0x00);
/* Fuses power off */
CYRF_WriteRegister(CYRF_25_MFG_ID, 0x00);
}
#else
memcpy(data,FORCE_CYRF_ID,6);
#endif
@@ -258,7 +269,7 @@ void CYRF_FindBestChannels(uint8_t *channels, uint8_t len, uint8_t minspace, uin
delayMilliseconds(1);
for(i = 0; i < NUM_FREQ; i++)
{
CYRF_ConfigRFChannel(i);
CYRF_ConfigRFChannel(protocol==PROTO_LOSI?i|1:i);
delayMicroseconds(270); //slow channel require 270usec for synthesizer to settle
if( !(CYRF_ReadRegister(CYRF_05_RX_CTRL) & 0x80)) {
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x80); //Prepare to receive

View File

@@ -16,7 +16,7 @@
#if defined(DM002_NRF24L01_INO)
#include "iface_nrf24l01.h"
#include "iface_xn297.h"
#define DM002_PACKET_PERIOD 6100 // Timeout for callback in uSec
#define DM002_INITIAL_WAIT 500
@@ -24,7 +24,6 @@
#define DM002_RF_BIND_CHANNEL 0x27
#define DM002_BIND_COUNT 655 // 4 seconds
enum DM002_FLAGS {
// flags going to packet[9]
DM002_FLAG_FLIP = 0x01,
@@ -75,28 +74,23 @@ static void __attribute__((unused)) DM002_send_packet()
packet_count&=0x0F;
packet[10] = packet_count;
packet_count++;
XN297_Hopping(hopping_frequency_no);
}
//CRC
for(uint8_t i=0;i<DM002_PACKET_SIZE-1;i++)
packet[11]+=packet[i];
// Power on, TX mode, 2byte CRC
// Why CRC0? xn297 does not interpret it - either 16-bit CRC or nothing
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? DM002_RF_BIND_CHANNEL : hopping_frequency[hopping_frequency_no]);
// clear packet status bits and TX FIFO
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
//Send
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, DM002_PACKET_SIZE);
NRF24L01_SetPower(); // Set tx_power
}
static void __attribute__((unused)) DM002_RF_init()
{
NRF24L01_Initialize();
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
XN297_SetTXAddr((uint8_t *)"\x26\xA8\x67\x35\xCC", 5);
XN297_RFChannel(DM002_RF_BIND_CHANNEL);
}
uint16_t DM002_callback()

View File

@@ -117,7 +117,9 @@ static void __attribute__((unused)) DSM_cyrf_configdata()
static uint8_t __attribute__((unused)) DSM_get_pn_row(uint8_t channel, bool dsmx)
{
return (dsmx ? (channel - 2) % 5 : channel % 5);
if(protocol == PROTO_DSM && sub_protocol == DSMR)
return (channel + 2) % 5;
return (dsmx ? (channel - 2) % 5 : channel % 5);
}
static void __attribute__((unused)) DSM_set_sop_data_crc(bool ch2, bool dsmx)
@@ -127,10 +129,14 @@ static void __attribute__((unused)) DSM_set_sop_data_crc(bool ch2, bool dsmx)
if(ch2)
CYRF_ConfigCRCSeed(seed); //CH2
else
CYRF_ConfigCRCSeed(~seed); //CH1
CYRF_ConfigCRCSeed(~seed); //CH1, DSMR only use CH1
uint8_t pn_row = DSM_get_pn_row(hopping_frequency[hopping_frequency_no], dsmx);
uint8_t code[16];
#if 0
debug_time();
debug(" crc:%04X,row:%d,col:%d,rf:%02X",(~seed)&0xffff,pn_row,sop_col,hopping_frequency[hopping_frequency_no]);
#endif
DSM_read_code(code,pn_row,sop_col,8); // pn_row between 0 and 4, sop_col between 1 and 7
CYRF_ConfigSOPCode(code);
DSM_read_code(code,pn_row,7 - sop_col,8); // 7-sop_col between 0 and 6

View File

@@ -247,10 +247,9 @@ uint16_t DSM_RX_callback()
eeprom_write_byte((EE_ADDR)temp++, cyrfmfg_id[i]);
debug(" %02X", cyrfmfg_id[i]);
}
// check num_ch
// save num_ch
num_ch=packet_in[11];
if(num_ch>12) num_ch=12;
//check DSM_rx_type
// store DSM_rx_type
/*packet[12] 1 byte -> max DSM type allowed:
0x01 => 22ms 1024 DSM2 1 packet => number of channels is <8
0x02 => 22ms 1024 DSM2 2 packets => either a number of channel >7
@@ -262,22 +261,6 @@ uint16_t DSM_RX_callback()
&0xF0 => false=1024, true=2048 */
DSM_rx_type=packet_in[12];
debugln(", num_ch=%d, type=%02X",num_ch, DSM_rx_type);
switch(DSM_rx_type)
{
case 0x01:
if(num_ch>7) DSM_rx_type = 0x02; // Can't be 0x01 with this number of channels
break;
case 0xA2:
if(num_ch>7) DSM_rx_type = 0xB2; // Can't be 0xA2 with this number of channels
break;
case 0x02:
case 0x12:
case 0xB2:
break;
default: // Unknown type, default to DSMX 11ms
DSM_rx_type = 0xB2;
break;
}
eeprom_write_byte((EE_ADDR)temp, DSM_rx_type);
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x20); // Abort RX operation
CYRF_SetTxRxMode(TX_EN); // Force end state TX

View File

@@ -86,12 +86,14 @@ static void __attribute__((unused)) DSM_build_bind_packet()
if(sub_protocol==DSM_AUTO)
packet[11] = 12;
else
packet[11] = num_ch;
packet[11] = num_ch; // DX5 DSMR sends 0x48...
if (sub_protocol==DSM2_1F)
packet[12]=num_ch<8?0x01:0x02; // DSM2/1024 1 or 2 packets depending on the number of channels
if (sub_protocol==DSMR)
packet[12] = 0xa2;
else if (sub_protocol==DSM2_1F)
packet[12] = num_ch<8?0x01:0x02; // DSM2/1024 1 or 2 packets depending on the number of channels
else if(sub_protocol==DSM2_2F)
packet[12]=0x12; // DSM2/2048 2 packets
packet[12] = 0x12; // DSM2/2048 2 packets
else if(sub_protocol==DSMX_1F)
#if defined DSM_TELEMETRY
packet[12] = 0xb2; // DSMX/2048 2 packets
@@ -99,8 +101,9 @@ static void __attribute__((unused)) DSM_build_bind_packet()
packet[12] = num_ch<8? 0xa2 : 0xb2; // DSMX/2048 1 or 2 packets depending on the number of channels
#endif
else // DSMX_2F && DSM_AUTO
packet[12]=0xb2; // DSMX/2048 2 packets
packet[12] = 0xb2; // DSMX/2048 2 packets
packet[13] = 0x00; //???
for(i = 8; i < 14; i++)
sum += packet[i];
@@ -126,6 +129,9 @@ static void __attribute__((unused)) DSM_update_channels()
if(num_ch<3 || num_ch>12)
num_ch=6; // Default to 6 channels if invalid choice...
if(sub_protocol==DSMR && num_ch>7)
num_ch=7; // Max 7 channels in DSMR
// Create channel map based on number of channels and refresh rate
uint8_t idx=num_ch-3;
if((option & 0x40) && num_ch>7 && num_ch<12)
@@ -141,19 +147,32 @@ static void __attribute__((unused)) DSM_build_data_packet(uint8_t upper)
if(prev_option!=option)
DSM_update_channels();
if (sub_protocol==DSMX_2F || sub_protocol==DSMX_1F )
if (sub_protocol==DSMX_2F || sub_protocol==DSMX_1F)
{//DSMX
packet[0] = cyrfmfg_id[2];
packet[1] = cyrfmfg_id[3];
}
else
{//DSM2
{//DSM2 && DSMR
packet[0] = (0xff ^ cyrfmfg_id[2]);
packet[1] = (0xff ^ cyrfmfg_id[3]);
if(sub_protocol==DSM2_1F)
bits=10; // Only DSM2_1F is using a resolution of 1024
}
if(sub_protocol == DSMR)
{
for (uint8_t i = 0; i < 7; i++)
{
uint16_t value = 0x0000;
if(i < num_ch)
value=Channel_data[i]<<1;
packet[i*2+2] = (value >> 8) & 0xff;
packet[i*2+3] = (value >> 0) & 0xff;
}
return;
}
#ifdef DSM_THROTTLE_KILL_CH
uint16_t kill_ch=Channel_data[DSM_THROTTLE_KILL_CH-1];
#endif
@@ -189,8 +208,8 @@ static void __attribute__((unused)) DSM_build_data_packet(uint8_t upper)
if(bits==10) value>>=1;
value |= (upper && i==0 ? 0x8000 : 0) | (idx << bits);
}
packet[i*2+2] = (value >> 8) & 0xff;
packet[i*2+3] = (value >> 0) & 0xff;
packet[i*2+2] = value >> 8;
packet[i*2+3] = value;
}
#ifdef DSM_FWD_PGM
if(upper==0 && DSM_SerialRX && (DSM_SerialRX_val[0]&0xF8)==0x70 )
@@ -230,6 +249,10 @@ static uint8_t __attribute__((unused)) DSM_Check_RX_packet()
uint16_t DSM_callback()
{
#if defined MULTI_EU
if(sub_protocol == DSM2_1F || sub_protocol == DSM2_2F)
return 11000;
#endif
#define DSM_CH1_CH2_DELAY 4010 // Time between write of channel 1 and channel 2
#ifdef STM32_BOARD
#define DSM_WRITE_DELAY 1600 // Time after write to verify write complete
@@ -319,26 +342,32 @@ uint16_t DSM_callback()
DSM_cyrf_configdata();
CYRF_SetTxRxMode(TX_EN);
hopping_frequency_no = 0;
phase = DSM_CH1_WRITE_A; // in fact phase++
DSM_set_sop_data_crc(phase==DSM_CH1_CHECK_A||phase==DSM_CH1_CHECK_B, sub_protocol==DSMX_2F||sub_protocol==DSMX_1F);
phase = DSM_CH1_WRITE_A; // in fact phase++
if(sub_protocol == DSMR)
DSM_set_sop_data_crc(false, true);
else
DSM_set_sop_data_crc(true, sub_protocol==DSMX_2F||sub_protocol==DSMX_1F); //prep CH1
return 10000;
case DSM_CH1_WRITE_A:
#ifdef MULTI_SYNC
telemetry_set_input_sync(11000); // Always request 11ms spacing even if we don't use half of it in 22ms mode
telemetry_set_input_sync(11000); // Always request 11ms spacing even if we don't use half of it in 22ms mode
#endif
CYRF_SetPower(0x28); //Keep transmit power in sync
if(sub_protocol == DSMR)
CYRF_SetPower(0x08); //Keep transmit power in sync
else
CYRF_SetPower(0x28); //Keep transmit power in sync
case DSM_CH1_WRITE_B:
DSM_build_data_packet(phase == DSM_CH1_WRITE_B); // build lower or upper channels
case DSM_CH2_WRITE_A:
case DSM_CH2_WRITE_B:
CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS); // clear IRQ flags
CYRF_ReadRegister(CYRF_04_TX_IRQ_STATUS); // clear IRQ flags
CYRF_WriteDataPacket(packet);
#if 0
for(uint8_t i=0;i<16;i++)
debug(" %02X", packet[i]);
debugln("");
#endif
phase++; // change from WRITE to CHECK mode
phase++; // change from WRITE to CHECK mode
return DSM_WRITE_DELAY;
case DSM_CH1_CHECK_A:
case DSM_CH1_CHECK_B:
@@ -349,7 +378,7 @@ uint16_t DSM_callback()
if((CYRF_ReadRegister(CYRF_02_TX_CTRL) & 0x80) == 0x00)
break;
if(phase==DSM_CH1_CHECK_A || phase==DSM_CH1_CHECK_B)
if((phase==DSM_CH1_CHECK_A || phase==DSM_CH1_CHECK_B) && sub_protocol!=DSMR)
{
#if defined DSM_TELEMETRY
// reset cyrf6936 if freezed after switching from TX to RX
@@ -361,14 +390,19 @@ uint16_t DSM_callback()
CYRF_SetTxRxMode(TX_EN);
}
#endif
DSM_set_sop_data_crc(phase==DSM_CH1_CHECK_A||phase==DSM_CH1_CHECK_B, sub_protocol==DSMX_2F || sub_protocol==DSMX_1F);
phase++; // change from CH1_CHECK to CH2_WRITE
DSM_set_sop_data_crc(true, sub_protocol==DSMX_2F||sub_protocol==DSMX_1F); // prep CH2
phase++; // change from CH1_CHECK to CH2_WRITE
return DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY;
}
#if defined DSM_TELEMETRY
phase++; // change from CH2_CHECK to CH2_READ
CYRF_SetTxRxMode(RX_EN); //Receive mode
CYRF_WriteRegister(CYRF_05_RX_CTRL, 0x87); //0x80??? //Prepare to receive
if(sub_protocol==DSMR)
{
phase = DSM_CH2_READ_B;
return 11000 - DSM_WRITE_DELAY - DSM_READ_DELAY;
}
#ifdef DSM_GR300
if(num_ch==3)
return timing - DSM_CH1_CH2_DELAY - DSM_WRITE_DELAY - DSM_READ_DELAY;
@@ -418,7 +452,7 @@ uint16_t DSM_callback()
phase = DSM_CH1_WRITE_A; //Transmit lower
CYRF_SetTxRxMode(TX_EN); //TX mode
CYRF_WriteRegister(CYRF_29_RX_ABORT, 0x00); //Clear abort RX operation
DSM_set_sop_data_crc(phase==DSM_CH1_CHECK_A||phase==DSM_CH1_CHECK_B, sub_protocol==DSMX_2F||sub_protocol==DSMX_1F);
DSM_set_sop_data_crc(false, sub_protocol==DSMX_2F||sub_protocol==DSMX_1F||sub_protocol==DSMR);
return DSM_READ_DELAY;
#else
// No telemetry
@@ -449,25 +483,65 @@ uint16_t DSM_callback()
return 0;
}
const uint8_t PROGMEM DSMR_ID_FREQ[][4 + 23] = {
{ 0x71, 0x74, 0x1c, 0xe4, 0x11, 0x2f, 0x17, 0x3d, 0x23, 0x3b, 0x0f, 0x21, 0x25, 0x49, 0x1d, 0x13, 0x4d, 0x1f, 0x41, 0x4b, 0x47, 0x05, 0x27, 0x15, 0x19, 0x3f, 0x07 },
{ 0xfe, 0xfe, 0xfe, 0xfe, 0x45, 0x31, 0x33, 0x4b, 0x11, 0x29, 0x49, 0x3f, 0x09, 0x13, 0x47, 0x21, 0x1d, 0x43, 0x1f, 0x05, 0x41, 0x19, 0x1b, 0x2d, 0x15, 0x4d, 0x0f },
{ 0xfe, 0xff, 0xff, 0xff, 0x2a, 0x06, 0x22, 0x28, 0x16, 0x24, 0x38, 0x0e, 0x32, 0x2e, 0x14, 0x3a, 0x04, 0x44, 0x0c, 0x42, 0x1c, 0x4a, 0x10, 0x36, 0x3c, 0x48, 0x26 },
{ 0xff, 0xfe, 0xff, 0xff, 0x28, 0x34, 0x48, 0x46, 0x3a, 0x12, 0x18, 0x32, 0x14, 0x42, 0x16, 0x40, 0x22, 0x44, 0x1c, 0x0a, 0x36, 0x20, 0x10, 0x0c, 0x3c, 0x26, 0x2e },
{ 0xff, 0xff, 0xfe, 0xff, 0x3c, 0x16, 0x04, 0x48, 0x1e, 0x4a, 0x10, 0x18, 0x22, 0x28, 0x38, 0x40, 0x20, 0x06, 0x3e, 0x42, 0x30, 0x1a, 0x2c, 0x1c, 0x46, 0x14, 0x34 },
{ 0xff, 0xff, 0xff, 0xfe, 0x4d, 0x39, 0x1b, 0x13, 0x45, 0x2f, 0x0d, 0x3d, 0x0b, 0x11, 0x47, 0x2d, 0x19, 0x1d, 0x23, 0x35, 0x33, 0x3b, 0x21, 0x31, 0x17, 0x0f, 0x43 },
{ 0xff, 0xff, 0xff, 0xff, 0x14, 0x28, 0x2e, 0x32, 0x3e, 0x10, 0x38, 0x0e, 0x12, 0x06, 0x2c, 0x26, 0x30, 0x4c, 0x34, 0x16, 0x04, 0x3a, 0x42, 0x48, 0x36, 0x46, 0x1a },
{ 0x00, 0xff, 0xff, 0xff, 0x3e, 0x30, 0x42, 0x24, 0x06, 0x0e, 0x14, 0x1c, 0x08, 0x10, 0x20, 0x22, 0x04, 0x32, 0x0c, 0x44, 0x3c, 0x46, 0x4a, 0x26, 0x4c, 0x48, 0x1e },
{ 0xff, 0x00, 0xff, 0xff, 0x38, 0x0e, 0x22, 0x2a, 0x44, 0x3a, 0x4a, 0x3e, 0x16, 0x20, 0x36, 0x24, 0x46, 0x18, 0x1e, 0x12, 0x1c, 0x30, 0x2c, 0x14, 0x06, 0x0c, 0x40 },
{ 0x00, 0x00, 0xff, 0xff, 0x06, 0x4c, 0x26, 0x08, 0x46, 0x3e, 0x30, 0x12, 0x38, 0x1c, 0x04, 0x4a, 0x2c, 0x1a, 0x20, 0x3a, 0x18, 0x36, 0x28, 0x2e, 0x22, 0x40, 0x10 },
{ 0xff, 0xff, 0x00, 0xff, 0x12, 0x06, 0x3c, 0x2a, 0x22, 0x38, 0x48, 0x4c, 0x32, 0x44, 0x26, 0x16, 0x0c, 0x28, 0x2c, 0x36, 0x1c, 0x1a, 0x42, 0x10, 0x08, 0x4a, 0x34 },
{ 0x00, 0xff, 0x00, 0xff, 0x04, 0x4c, 0x4a, 0x28, 0x2a, 0x24, 0x14, 0x1e, 0x40, 0x48, 0x44, 0x2c, 0x2e, 0x1a, 0x12, 0x46, 0x3a, 0x0e, 0x18, 0x1c, 0x20, 0x10, 0x42 },
{ 0xff, 0x00, 0x00, 0xff, 0x06, 0x10, 0x14, 0x16, 0x48, 0x18, 0x44, 0x2c, 0x0a, 0x26, 0x24, 0x42, 0x36, 0x30, 0x38, 0x3e, 0x0c, 0x3c, 0x34, 0x46, 0x2a, 0x32, 0x0e },
{ 0x00, 0x00, 0x00, 0xff, 0x2c, 0x0a, 0x46, 0x28, 0x38, 0x24, 0x14, 0x06, 0x04, 0x10, 0x18, 0x30, 0x12, 0x20, 0x3a, 0x1a, 0x32, 0x3c, 0x3e, 0x4a, 0x1e, 0x44, 0x36 },
{ 0x00, 0x00, 0x00, 0x00, 0x45, 0x23, 0x07, 0x37, 0x4b, 0x13, 0x3d, 0x31, 0x19, 0x2b, 0x2f, 0x2d, 0x1f, 0x4d, 0x3f, 0x1b, 0x43, 0x27, 0x3b, 0x11, 0x05, 0x0d, 0x17 },
{ 0xff, 0xff, 0xff, 0x00, 0x0b, 0x4b, 0x1d, 0x39, 0x09, 0x0f, 0x49, 0x25, 0x07, 0x35, 0x3b, 0x05, 0x33, 0x17, 0x2d, 0x11, 0x2b, 0x29, 0x1f, 0x45, 0x1b, 0x41, 0x47 },
{ 0x00, 0xff, 0xff, 0x00, 0x41, 0x35, 0x11, 0x25, 0x29, 0x27, 0x33, 0x47, 0x4d, 0x31, 0x05, 0x37, 0x15, 0x1f, 0x23, 0x07, 0x1b, 0x0f, 0x3b, 0x49, 0x19, 0x3f, 0x0b },
{ 0xff, 0x00, 0xff, 0x00, 0x25, 0x47, 0x05, 0x0b, 0x45, 0x1f, 0x2b, 0x27, 0x2d, 0x09, 0x07, 0x43, 0x49, 0x29, 0x4d, 0x39, 0x33, 0x41, 0x17, 0x0f, 0x15, 0x19, 0x3b },
{ 0x00, 0x00, 0xff, 0x00, 0x3b, 0x05, 0x21, 0x0d, 0x1b, 0x43, 0x17, 0x2d, 0x1d, 0x25, 0x4b, 0x35, 0x4d, 0x3f, 0x07, 0x09, 0x37, 0x41, 0x15, 0x1f, 0x0f, 0x27, 0x29 },
{ 0xff, 0xff, 0x00, 0x00, 0x2b, 0x35, 0x1b, 0x1d, 0x0f, 0x47, 0x09, 0x0d, 0x45, 0x41, 0x21, 0x11, 0x2f, 0x43, 0x27, 0x33, 0x4b, 0x37, 0x13, 0x19, 0x4d, 0x23, 0x17 },
{ 0x00, 0xff, 0x00, 0x00, 0x1b, 0x1d, 0x33, 0x13, 0x2b, 0x27, 0x09, 0x41, 0x25, 0x17, 0x19, 0x2d, 0x4b, 0x37, 0x45, 0x11, 0x21, 0x0d, 0x3d, 0x4d, 0x07, 0x39, 0x43 },
{ 0xff, 0x00, 0x00, 0x00, 0x37, 0x27, 0x43, 0x4b, 0x39, 0x13, 0x07, 0x0d, 0x25, 0x17, 0x29, 0x1b, 0x1d, 0x45, 0x19, 0x2d, 0x0b, 0x3d, 0x15, 0x47, 0x1f, 0x21, 0x4d } };
void DSM_init()
{
CYRF_GetMfgData(cyrfmfg_id);
//Model match
cyrfmfg_id[3]^=RX_num;
if(sub_protocol == DSMR)
{
uint8_t row = rx_tx_addr[3]%22;
for(uint8_t i=0; i< 4; i++)
cyrfmfg_id[i] = pgm_read_byte_near(&DSMR_ID_FREQ[row][i]);
for(uint8_t i=0; i< 23; i++)
hopping_frequency[i] = pgm_read_byte_near(&DSMR_ID_FREQ[row][i+4]);
}
else
{
CYRF_GetMfgData(cyrfmfg_id);
//Model match
cyrfmfg_id[3]^=RX_num;
}
//Calc sop_col
sop_col = (cyrfmfg_id[0] + cyrfmfg_id[1] + cyrfmfg_id[2] + 2) & 0x07;
//Fix for OrangeRX using wrong DSM_pncodes by preventing access to "Col 8"
if(sop_col==0)
if(sop_col==0 && sub_protocol != DSMR)
{
cyrfmfg_id[rx_tx_addr[0]%3]^=0x01; //Change a bit so sop_col will be different from 0
sop_col = (cyrfmfg_id[0] + cyrfmfg_id[1] + cyrfmfg_id[2] + 2) & 0x07;
}
//Calc CRC seed
seed = (cyrfmfg_id[0] << 8) + cyrfmfg_id[1];
//Hopping frequencies
if (sub_protocol == DSMX_2F || sub_protocol == DSMX_1F)
DSM_calc_dsmx_channel();
else
else if(sub_protocol != DSMR)
{
uint8_t tmpch[10];
CYRF_FindBestChannels(tmpch, 10, 5, 3, 75);
@@ -482,6 +556,7 @@ void DSM_init()
}
hopping_frequency[1] = tmpch[idx];
}
//
DSM_cyrf_config();
CYRF_SetTxRxMode(TX_EN);

View File

@@ -55,14 +55,14 @@ static void __attribute__((unused)) DEVO_add_pkt_suffix()
{
uint8_t bind_state;
#ifdef ENABLE_PPM
if(mode_select && option==0 && IS_BIND_DONE) //PPM mode and option not already set and bind is finished
if(mode_select && (option&0x01)==0 && IS_BIND_DONE) //PPM mode and option not already set and bind is finished
{
BIND_SET_INPUT;
BIND_SET_PULLUP; // set pullup
if(IS_BIND_BUTTON_on)
{
eeprom_write_byte((EE_ADDR)(MODELMODE_EEPROM_OFFSET+RX_num),0x01); // Set fixed id mode for the current model
option=1;
option |= 0x01;
}
BIND_SET_OUTPUT;
}
@@ -72,7 +72,7 @@ static void __attribute__((unused)) DEVO_add_pkt_suffix()
MProtocol_id = RX_num + MProtocol_id_master;
bind_counter=DEVO_BIND_COUNT;
}
if (option)
if (option&0x01)
{
if (bind_counter > 0)
bind_state = 0xc0;
@@ -220,6 +220,7 @@ static void __attribute__((unused)) DEVO_parse_telemetry_packet()
#if defined HUB_TELEMETRY
//Telemetry https://github.com/DeviationTX/deviation/blob/5efb6a28bea697af9a61b5a0ed2528cc8d203f90/src/protocol/devo_cyrf6936.c#L232
uint16_t val, dec;
uint32_t val32;
switch(packet[0])
{
case 0x30: // Volt and RPM packet
@@ -255,18 +256,30 @@ static void __attribute__((unused)) DEVO_parse_telemetry_packet()
break;
// GPS Data
case 0x32: // Longitude
//memcpy(&packet[1],"\x30\x33\x30\x32\x30\x2e\x38\x32\x37\x30\x45\xfb",12); // 030°20.8270E
val = DEVO_text_to_int(&packet[1], 3)*100 + DEVO_text_to_int(&packet[4], 2); // dddmm
//memcpy(&packet[1],"\x30\x33\x30\x32\x30\x2e\x38\x32\x37\x30\x45\xfb",12); // 030°20.8270E in ddmm.mmmm
//memcpy(&packet[1],"\x31\x31\x37\x31\x31\x2e\x35\x39\x34\x37\x57\xfb",12); // RX705 sends 117°11.5947W which should be 11706.95685W in ddmm.mmmm
val = DEVO_text_to_int(&packet[1], 3)*100; // dd00
val32 = DEVO_text_to_int(&packet[4], 2) * 10000 + DEVO_text_to_int(&packet[7], 4); // mmmmmm
if(option&0x02) // if RX705 GPS format
val32 = (val32*3)/5; // then * 6/10 correction
dec = val32/10000;
val = val + dec; // dddmm
frsky_send_user_frame(0x12 , val, val>>8);
val = DEVO_text_to_int(&packet[7], 4); // .mmmm
val = val32 - dec*10000; // .mmmm
frsky_send_user_frame(0x12+8, val, val>>8);
frsky_send_user_frame(0x1A+8, packet[11], 0x00); // 'E'/'W'
break;
case 0x33: // Latitude
//memcpy(&packet[1],"\x35\x39\x35\x34\x2e\x37\x37\x37\x36\x4e\x07\x00",12); // 59°54.776N
val = DEVO_text_to_int(&packet[1], 2)*100 + DEVO_text_to_int(&packet[3], 2); // ddmm
//memcpy(&packet[1],"\x35\x39\x35\x34\x2e\x37\x37\x37\x36\x4e\x07\x00",12); // 59°54.776N in ddmm.mmmm
//memcpy(&packet[1],"\x31\x37\x31\x31\x2e\x35\x39\x34\x37\x4e\xfb\x00",12); // RX705 sends 17°11.5947N which should be 1706.95685N in ddmm.mmmm
val = DEVO_text_to_int(&packet[1], 2)*100; // dd00
val32 = DEVO_text_to_int(&packet[3], 2) * 10000 + DEVO_text_to_int(&packet[6], 4); // mmmmmm
if(option&0x02) // if RX705 GPS format
val32 = (val32*3)/5; // then * 6/10 correction
dec = val32/10000;
val = val + dec; // dddmm
frsky_send_user_frame(0x13 , val, val>>8);
val = DEVO_text_to_int(&packet[6], 4); // .mmmm
val = val32 - dec*10000; // .mmmm
frsky_send_user_frame(0x13+8, val, val>>8);
frsky_send_user_frame(0x1B+8, packet[10], 0x00); // 'N'/'S'
break;
@@ -283,11 +296,12 @@ static void __attribute__((unused)) DEVO_parse_telemetry_packet()
frsky_send_user_frame(0x11+8, dec, dec>>8);
break;
case 0x36: // Time
//memcpy(&packet[1],"\x31\x38\x32\x35\x35\x32\x31\x35\x31\x30\x31\x32",12); // 2012-10-15 18:25:52 (UTC)
//memcpy(&packet[1],"\x31\x38\x32\x35\x35\x32\x31\x35\x31\x30\x31\x32",12); // "182552151012" = 2012-10-15 18:25:52 (UTC)
if(packet[1]!=0)
{
frsky_send_user_frame(0x15, DEVO_text_to_int(&packet[9], 2), DEVO_text_to_int(&packet[7], 2)); // month, day
frsky_send_user_frame(0x16, DEVO_text_to_int(&packet[11], 2)+24, 0x00); // year
val = 2000 + DEVO_text_to_int(&packet[11], 2); // year
frsky_send_user_frame(0x16, val, val>>8);
frsky_send_user_frame(0x17, DEVO_text_to_int(&packet[1], 2), DEVO_text_to_int(&packet[3], 2)); // hour, min
frsky_send_user_frame(0x18, DEVO_text_to_int(&packet[5], 2), 0x00); // second
}
@@ -529,13 +543,13 @@ void DEVO_init()
{
if(IS_BIND_BUTTON_FLAG_on)
{
eeprom_write_byte((EE_ADDR)(MODELMODE_EEPROM_OFFSET+RX_num),0x00); // reset to autobind mode for the current model
option=0;
option &= 0xFE;
eeprom_write_byte((EE_ADDR)(MODELMODE_EEPROM_OFFSET+RX_num),option); // reset to autobind mode for the current model
}
else
{
option=eeprom_read_byte((EE_ADDR)(MODELMODE_EEPROM_OFFSET+RX_num)); // load previous mode: autobind or fixed id
if(option!=1) option=0; // if not fixed id mode then it should be autobind
option=eeprom_read_byte((EE_ADDR)(MODELMODE_EEPROM_OFFSET+RX_num)); // load previous mode: autobind or fixed id
if(option > 3) option = 0; // if invalid then it should be autobind
}
}
#endif //ENABLE_PPM
@@ -569,8 +583,13 @@ void DEVO_init()
packet_count = 0;
prev_option=option;
if(option==0)
if(option&0x01)
{
phase = DEVO_BOUND_1;
bind_counter = 0;
DEVO_cyrf_set_bound_sop_code();
}
else
{
MProtocol_id = ((uint32_t)(hopping_frequency[0] ^ cyrfmfg_id[0] ^ cyrfmfg_id[3]) << 16)
| ((uint32_t)(hopping_frequency[1] ^ cyrfmfg_id[1] ^ cyrfmfg_id[4]) << 8)
@@ -580,12 +599,6 @@ void DEVO_init()
phase = DEVO_BIND;
BIND_IN_PROGRESS;
}
else
{
phase = DEVO_BOUND_1;
bind_counter = 0;
DEVO_cyrf_set_bound_sop_code();
}
}
#endif

View File

@@ -0,0 +1,154 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
// compatible with E016H
#if defined(E016H_NRF24L01_INO)
#include "iface_xn297.h"
//Protocols constants
#define E016H_BIND_COUNT 500
#define E016H_ADDRESS_LENGTH 5
#define E016H_PACKET_PERIOD 4080
#define E016H_PACKET_SIZE 10
#define E016H_BIND_CHANNEL 80
#define E016H_NUM_CHANNELS 4
//Channels
#define E016H_STOP_SW CH5_SW
#define E016H_FLIP_SW CH6_SW
#define E016H_HEADLESS_SW CH8_SW
#define E016H_RTH_SW CH9_SW
// E016H flags packet[1]
#define E016H_FLAG_CALIBRATE 0x80
#define E016H_FLAG_STOP 0x20
#define E016H_FLAG_FLIP 0x04
// E016H flags packet[3]
#define E016H_FLAG_HEADLESS 0x10
#define E016H_FLAG_RTH 0x04
// E016H flags packet[7]
#define E016H_FLAG_TAKEOFF 0x80
#define E016H_FLAG_HIGHRATE 0x08
static void __attribute__((unused)) E016H_send_packet()
{
uint8_t can_flip = 0, calibrate = 1;
if(IS_BIND_IN_PROGRESS)
{
memcpy(packet, &rx_tx_addr[1], 4);
memcpy(&packet[4], hopping_frequency, 4);
packet[8] = 0x23;
}
else
{
// trim commands
packet[0] = 0;
// aileron
uint16_t val = convert_channel_16b_limit(AILERON, 0, 0x3ff);
can_flip |= (val < 0x100) || (val > 0x300);
packet[1] = val >> 8;
packet[2] = val & 0xff;
if(val < 0x300) calibrate = 0;
// elevator
val = convert_channel_16b_limit(ELEVATOR, 0x3ff, 0);
can_flip |= (val < 0x100) || (val > 0x300);
packet[3] = val >> 8;
packet[4] = val & 0xff;
if(val < 0x300) calibrate = 0;
// throttle
val = convert_channel_16b_limit(THROTTLE, 0, 0x3ff);
packet[5] = val >> 8;
packet[6] = val & 0xff;
if(val > 0x100) calibrate = 0;
// rudder
val = convert_channel_16b_limit(RUDDER, 0, 0x3ff);
packet[7] = val >> 8;
packet[8] = val & 0xff;
if(val > 0x100) calibrate = 0;
// flags
packet[1] |= GET_FLAG(E016H_STOP_SW, E016H_FLAG_STOP)
| (can_flip ? GET_FLAG(E016H_FLIP_SW, E016H_FLAG_FLIP) : 0)
| (calibrate ? E016H_FLAG_CALIBRATE : 0);
packet[3] |= GET_FLAG(E016H_HEADLESS_SW, E016H_FLAG_HEADLESS)
| GET_FLAG(E016H_RTH_SW, E016H_FLAG_RTH);
packet[7] |= E016H_FLAG_HIGHRATE;
// frequency hopping
XN297_Hopping(hopping_frequency_no++ & 0x03);
}
// checksum
packet[9] = packet[0];
for (uint8_t i=1; i < E016H_PACKET_SIZE-1; i++)
packet[9] += packet[i];
// Send
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, E016H_PACKET_SIZE);
}
static void __attribute__((unused)) E016H_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
XN297_SetTXAddr((uint8_t *)"\x5a\x53\x46\x30\x31", 5); // bind address
//XN297_HoppingCalib(E016H_NUM_CHANNELS);
XN297_RFChannel(E016H_BIND_CHANNEL);
}
uint16_t E016H_callback()
{
#ifdef MULTI_SYNC
telemetry_set_input_sync(packet_period);
#endif
if(bind_counter)
{
bind_counter--;
if (bind_counter == 0)
{
XN297_SetTXAddr(rx_tx_addr, E016H_ADDRESS_LENGTH);
BIND_DONE;
}
}
E016H_send_packet();
return E016H_PACKET_PERIOD;
}
static void __attribute__((unused)) E016H_initialize_txid()
{
// tx id
rx_tx_addr[0] = 0xa5;
rx_tx_addr[1] = 0x00;
// rf channels
uint32_t lfsr=random(0xfefefefe);
for(uint8_t i=0; i<E016H_NUM_CHANNELS; i++)
{
hopping_frequency[i] = (lfsr & 0xFF) % 80;
lfsr>>=8;
}
}
void E016H_init()
{
BIND_IN_PROGRESS;
E016H_initialize_txid();
E016H_RF_init();
bind_counter = E016H_BIND_COUNT;
hopping_frequency_no = 0;
}
#endif

View File

@@ -14,9 +14,9 @@
*/
// compatible with E012 and E015
#if defined(E01X_NRF24L01_INO)
#if defined(E01X_CYRF6936_INO)
#include "iface_nrf24l01.h"
#include "iface_hs6200.h"
//Protocols constants
#define E01X_BIND_COUNT 500
@@ -28,16 +28,12 @@
#define E012_NUM_RF_CHANNELS 4
#define E012_PACKET_SIZE 15
#define E015_PACKET_PERIOD 4500 // stock Tx=9000, but let's send more packets ...
//#define E015_FORCE_ID
#define E015_PACKET_PERIOD 9000
#define E015_RF_CHANNEL 0x2d // 2445 MHz
#define E015_PACKET_SIZE 10
#define E015_BIND_PACKET_SIZE 9
#define E016H_PACKET_PERIOD 4080
#define E016H_PACKET_SIZE 10
#define E016H_BIND_CHANNEL 80
#define E016H_NUM_CHANNELS 4
//Channels
#define E01X_ARM_SW CH5_SW
#define E016H_STOP_SW CH5_SW
@@ -64,17 +60,6 @@
#define E015_FLAG_EXPERT 0x02
#define E015_FLAG_INTERMEDIATE 0x01
// E016H flags packet[1]
#define E016H_FLAG_CALIBRATE 0x80
#define E016H_FLAG_STOP 0x20
#define E016H_FLAG_FLIP 0x04
// E016H flags packet[3]
#define E016H_FLAG_HEADLESS 0x10
#define E016H_FLAG_RTH 0x04
// E016H flags packet[7]
#define E016H_FLAG_TAKEOFF 0x80
#define E016H_FLAG_HIGHRATE 0x08
static void __attribute__((unused)) E015_check_arming()
{
uint8_t arm_channel = E01X_ARM_SW;
@@ -97,20 +82,21 @@ static void __attribute__((unused)) E015_check_arming()
static void __attribute__((unused)) E01X_send_packet()
{
uint8_t can_flip = 0, calibrate = 1;
if(sub_protocol==E012)
{
packet_length=E012_PACKET_SIZE;
packet[0] = rx_tx_addr[1];
if(IS_BIND_IN_PROGRESS)
{
rf_ch_num = E012_RF_BIND_CHANNEL;
packet[1] = 0xaa;
memcpy(&packet[2], hopping_frequency, E012_NUM_RF_CHANNELS);
memcpy(&packet[6], rx_tx_addr, E01X_ADDRESS_LENGTH);
rf_ch_num=E012_RF_BIND_CHANNEL;
}
else
{
rf_ch_num = hopping_frequency[hopping_frequency_no++];
hopping_frequency_no %= E012_NUM_RF_CHANNELS;
packet[1] = 0x01
| GET_FLAG(E01X_RTH_SW, E012_FLAG_RTH)
| GET_FLAG(E01X_HEADLESS_SW, E012_FLAG_HEADLESS)
@@ -124,16 +110,15 @@ static void __attribute__((unused)) E01X_send_packet()
packet[8] = 0x00;
packet[9] = 0x00;
packet[10]= 0x00;
rf_ch_num=hopping_frequency[hopping_frequency_no++];
hopping_frequency_no %= E012_NUM_RF_CHANNELS;
}
packet[11] = 0x00;
packet[12] = 0x00;
packet[13] = 0x56;
packet[14] = rx_tx_addr[2];
}
else if(sub_protocol==E015)
else
{ // E015
rf_ch_num = E015_RF_CHANNEL;
if(IS_BIND_IN_PROGRESS)
{
packet[0] = 0x18;
@@ -141,10 +126,11 @@ static void __attribute__((unused)) E01X_send_packet()
packet[2] = 0x06;
// data phase address
memcpy(&packet[3], rx_tx_addr, E01X_ADDRESS_LENGTH);
packet[8] = 0x63; // unknown calculation
// checksum
packet[8] = packet[3];
for(uint8_t i=4; i<8; i++)
packet[8] += packet[i];
//packet[8] = packet[3];
//for(uint8_t i=4; i<8; i++)
// packet[8] += packet[i];
packet_length=E015_BIND_PACKET_SIZE;
}
else
@@ -170,90 +156,21 @@ static void __attribute__((unused)) E01X_send_packet()
packet_length=E015_PACKET_SIZE;
}
}
else
{ // E016H
packet_length=E016H_PACKET_SIZE;
if(IS_BIND_IN_PROGRESS)
{
rf_ch_num=E016H_BIND_CHANNEL;
memcpy(packet, &rx_tx_addr[1], 4);
memcpy(&packet[4], hopping_frequency, 4);
packet[8] = 0x23;
}
else
{
// trim commands
packet[0] = 0;
// aileron
uint16_t val = convert_channel_16b_limit(AILERON, 0, 0x3ff);
can_flip |= (val < 0x100) || (val > 0x300);
packet[1] = val >> 8;
packet[2] = val & 0xff;
if(val < 0x300) calibrate = 0;
// elevator
val = convert_channel_16b_limit(ELEVATOR, 0x3ff, 0);
can_flip |= (val < 0x100) || (val > 0x300);
packet[3] = val >> 8;
packet[4] = val & 0xff;
if(val < 0x300) calibrate = 0;
// throttle
val = convert_channel_16b_limit(THROTTLE, 0, 0x3ff);
packet[5] = val >> 8;
packet[6] = val & 0xff;
if(val > 0x100) calibrate = 0;
// rudder
val = convert_channel_16b_limit(RUDDER, 0, 0x3ff);
packet[7] = val >> 8;
packet[8] = val & 0xff;
if(val > 0x100) calibrate = 0;
// flags
packet[1] |= GET_FLAG(E016H_STOP_SW, E016H_FLAG_STOP)
| (can_flip ? GET_FLAG(E01X_FLIP_SW, E016H_FLAG_FLIP) : 0)
| (calibrate ? E016H_FLAG_CALIBRATE : 0);
packet[3] |= GET_FLAG(E01X_HEADLESS_SW, E016H_FLAG_HEADLESS)
| GET_FLAG(E01X_RTH_SW, E016H_FLAG_RTH);
packet[7] |= E016H_FLAG_HIGHRATE;
// frequency hopping
rf_ch_num=hopping_frequency[hopping_frequency_no++ & 0x03];
}
// checksum
packet[9] = packet[0];
for (uint8_t i=1; i < E016H_PACKET_SIZE-1; i++)
packet[9] += packet[i];
}
// Power on, TX mode, CRC enabled
if(sub_protocol==E016H)
XN297_Configure( _BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
else //E012 & E015
HS6200_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
NRF24L01_WriteReg(NRF24L01_05_RF_CH, rf_ch_num);
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
if(sub_protocol==E016H)
XN297_WritePayload(packet, packet_length);
else
HS6200_WritePayload(packet, packet_length);
// Check and adjust transmission power. We do this after
// transmission to not bother with timeout after power
// settings change - we have plenty of time until next
// packet.
NRF24L01_SetPower();
HS6200_RFChannel(rf_ch_num);
HS6200_SetPower();
delayMicroseconds(270); // Wait for RF channel to settle
HS6200_SendPayload(packet, packet_length);
}
static void __attribute__((unused)) E01X_RF_init()
{
NRF24L01_Initialize();
HS6200_Init(true); // CRC enabled
if(sub_protocol==E012)
HS6200_SetTXAddr((uint8_t *)"\x55\x42\x9C\x8F\xC9", E01X_ADDRESS_LENGTH);
else if(sub_protocol==E015)
else //E015
HS6200_SetTXAddr((uint8_t *)"\x62\x54\x79\x38\x53", E01X_ADDRESS_LENGTH);
else //E016H
XN297_SetTXAddr((uint8_t *)"\x5a\x53\x46\x30\x31", 5); // bind address
}
uint16_t E01X_callback()
@@ -266,10 +183,7 @@ uint16_t E01X_callback()
bind_counter--;
if (bind_counter == 0)
{
if(sub_protocol==E016H)
XN297_SetTXAddr(rx_tx_addr, E01X_ADDRESS_LENGTH);
else
HS6200_SetTXAddr(rx_tx_addr, E01X_ADDRESS_LENGTH);
HS6200_SetTXAddr(rx_tx_addr, E01X_ADDRESS_LENGTH);
BIND_DONE;
}
}
@@ -288,21 +202,6 @@ static void __attribute__((unused)) E012_initialize_txid()
}
}
static void __attribute__((unused)) E016H_initialize_txid()
{
// tx id
rx_tx_addr[0] = 0xa5;
rx_tx_addr[1] = 0x00;
// rf channels
uint32_t lfsr=random(0xfefefefe);
for(uint8_t i=0; i<E016H_NUM_CHANNELS; i++)
{
hopping_frequency[i] = (lfsr & 0xFF) % 80;
lfsr>>=8;
}
}
void E01X_init()
{
BIND_IN_PROGRESS;
@@ -311,19 +210,27 @@ void E01X_init()
E012_initialize_txid();
packet_period=E012_PACKET_PERIOD;
}
else if(sub_protocol==E015)
else //E015
{
#ifdef E015_FORCE_ID
rx_tx_addr[0] = 0x06;
rx_tx_addr[1] = 0xC6;
rx_tx_addr[2] = 0xB7;
rx_tx_addr[3] = 0x56;
rx_tx_addr[4] = 0x8A;
#endif
//force the sum to give 0x63 since the id calculation is unknown
uint8_t sum=0x63;
for(uint8_t i=0; i < 4; i++)
sum -= rx_tx_addr[i];
rx_tx_addr[4] = sum;
packet_period=E015_PACKET_PERIOD;
rf_ch_num=E015_RF_CHANNEL;
armed = 0;
arm_flags = 0;
arm_channel_previous = E01X_ARM_SW;
}
else
{ // E016H
E016H_initialize_txid();
packet_period=E016H_PACKET_PERIOD;
}
E01X_RF_init();
bind_counter = E01X_BIND_COUNT;
hopping_frequency_no = 0;

View File

@@ -18,15 +18,17 @@
#include "iface_rf2500.h"
//#define E129_FORCE_ID
//#define C186_FORCE_ID
#define E129_BIND_CH 0x2D //45
#define E129_PAYLOAD_SIZE 16
#define C186_PAYLOAD_SIZE 19
static void __attribute__((unused)) E129_build_data_packet()
{
//Build the packet
memset(packet,0,E129_PAYLOAD_SIZE);
packet[ 0] = 0x0F; // Packet length
memset(packet,0,packet_length);
packet[ 0] = packet_length - 1; // Packet length
if(IS_BIND_IN_PROGRESS)
{
packet[ 1] = 0xA4;
@@ -40,11 +42,21 @@ static void __attribute__((unused)) E129_build_data_packet()
else
{
packet[ 1] = 0xA6;
packet[ 2] = 0xF7; // High rate 0xF7, low rate 0xF4
//packet[ 3] = 0x00; // Mode: short press=0x20->0x00->0x20->..., long press=0x10->0x30->0x10->...
//Flags
if(sub_protocol == E129_E129)
packet[ 2] = 0xF7; // High rate 0xF7, low 0xF4
else //C186
{
packet[ 2] = 0xFA; // High rate 0xFA, medium 0xF7, low 0xF4
packet[13] = bit_reverse(rx_tx_addr[2]);
packet[14] = bit_reverse(rx_tx_addr[3]);
packet[15] = bit_reverse(rx_tx_addr[0]);
packet[16] = bit_reverse(rx_tx_addr[1]);
}
//packet[ 3] = 0x00; // E129 Mode: short press=0x20->0x00->0x20->..., long press=0x10->0x30->0x10->... => C186 throttle trim is doing the same:up=short press and down=long press
packet[ 4] = GET_FLAG(CH5_SW, 0x20) // Take off/Land 0x20
| GET_FLAG(CH6_SW, 0x04); // Emergency stop 0x04
| GET_FLAG(CH6_SW, 0x04); // Emergency stop 0x04
//Channels and trims
uint16_t val = convert_channel_10b(AILERON,false);
uint8_t trim = convert_channel_8b(CH7) & 0xFC;
packet[ 5] = trim | (val >>8); // Trim (0x00..0x1F..0x3E) << 2 | channel >> 8
@@ -67,10 +79,14 @@ static void __attribute__((unused)) E129_build_data_packet()
packet[11] = trim | (val >>8); // Trim (0x00..0x1F..0x3E) << 2 | channel >> 8
packet[12] = val; // channel (0x000...0x200...0x3FF)
}
packet[14] = 0x00; // Check
for(uint8_t i=0;i<14;i++)
packet[14] += packet[i];
//Check
if(sub_protocol == E129_E129)
packet[packet_length-2] = packet[0] + packet[1];
else
packet[packet_length-2] = 0x24 + packet[0] + (packet[1]&0x03); // ??
for(uint8_t i=2;i<packet_length-2;i++)
packet[packet_length-2] += packet[i];
RF2500_BuildPayload(packet);
}
@@ -83,7 +99,7 @@ uint16_t E129_callback()
//Send packet
RF2500_SendPayload();
//Send twice on same channel
//E129 sends twice on same channel, not the C186 but there is a bug somewhere if I don't send the packets back to back
if(phase==0)
{
phase++;
@@ -109,7 +125,9 @@ uint16_t E129_callback()
hopping_frequency_no &= 3;
phase=0;
return 5200-1260;
if(sub_protocol==E129_E129)
return 5200-1260;
return 5500-1260; //E129_C186
}
void E129_init()
@@ -117,8 +135,9 @@ void E129_init()
BIND_IN_PROGRESS; // Autobind protocol
bind_counter = 384; // ~2sec
//RF2500 emu init
RF2500_Init(E129_PAYLOAD_SIZE, true); // 16 bytes, Scrambled
//RF2500 emu init
packet_length = sub_protocol == E129_E129?E129_PAYLOAD_SIZE:C186_PAYLOAD_SIZE;
RF2500_Init(packet_length, true); // 16/19 bytes, Scrambled
//Freq hopping
calc_fh_channels(4);
@@ -136,6 +155,16 @@ void E129_init()
hopping_frequency[2]=0x4B; //75
hopping_frequency[3]=0x41; //65
#endif
#ifdef C186_FORCE_ID
rx_tx_addr[0]=0x91;
rx_tx_addr[1]=0x02;
rx_tx_addr[2]=0x5D;
rx_tx_addr[3]=0x33;
hopping_frequency[0]=0x44 + 2;
hopping_frequency[1]=0x41 + 2;
hopping_frequency[2]=0x43 + 2;
hopping_frequency[3]=0x49 + 2;
#endif
RF2500_SetTXAddr((uint8_t*)"\xE2\x32\xE0\xC8"); // 4 bytes of bind address

View File

@@ -15,7 +15,7 @@
#if defined(ESKY150V2_CC2500_INO)
#include "iface_cc2500.h"
#include "iface_nrf250k.h"
//#define ESKY150V2_FORCE_ID
@@ -74,7 +74,7 @@ static void __attribute__((unused)) ESKY150V2_send_packet()
packet[4+2*i] = channel;
packet[5+2*i] = channel>>8;
}
CC2500_250K_NRF_WritePayload(packet, ESKY150V2_PAYLOADSIZE);
NRF250K_WritePayload(packet, ESKY150V2_PAYLOADSIZE);
}
uint16_t ESKY150V2_callback()
@@ -91,12 +91,12 @@ uint16_t ESKY150V2_callback()
BIND_DONE; //Need full power for bind to work...
CC2500_SetPower(); //Set power level
BIND_IN_PROGRESS;
CC2500_250K_NRF_WritePayload(packet, ESKY150V2_BINDPAYLOADSIZE);
NRF250K_WritePayload(packet, ESKY150V2_BINDPAYLOADSIZE);
if (--bind_counter == 0)
{
BIND_DONE;
// Change TX address from bind to normal mode
CC2500_250K_NRF_SetTXAddr(rx_tx_addr, ESKY150V2_TXID_SIZE);
NRF250K_SetTXAddr(rx_tx_addr, ESKY150V2_TXID_SIZE);
memset(packet,0x00,ESKY150V2_PAYLOADSIZE);
}
return 30000; //ESKY150V2_BINDING_PACKET_PERIOD;
@@ -118,7 +118,7 @@ void ESKY150V2_init()
if(IS_BIND_IN_PROGRESS)
{
CC2500_250K_NRF_SetTXAddr((uint8_t *)"\x73\x73\x74\x63", ESKY150V2_TXID_SIZE); //Bind address
NRF250K_SetTXAddr((uint8_t *)"\x73\x73\x74\x63", ESKY150V2_TXID_SIZE); //Bind address
CC2500_250K_Hopping(ESKY150V2_NFREQCHANNELS); //Bind channel
memcpy(packet,"\x73\x73\x74\x63", ESKY150V2_TXID_SIZE);
memcpy(&packet[ESKY150V2_TXID_SIZE],rx_tx_addr, ESKY150V2_TXID_SIZE);
@@ -132,7 +132,7 @@ void ESKY150V2_init()
bind_counter=100;
}
else
CC2500_250K_NRF_SetTXAddr(rx_tx_addr, ESKY150V2_TXID_SIZE);
NRF250K_SetTXAddr(rx_tx_addr, ESKY150V2_TXID_SIZE);
}
#endif

View File

@@ -1,100 +0,0 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
// Compatible with FEI XIONG P38 plane.
#if defined(FX816_NRF24L01_INO)
#include "iface_nrf24l01.h"
#define FX816_INITIAL_WAIT 500
#define FX816_PACKET_PERIOD 10000
#define FX816_RF_BIND_CHANNEL 0x28 //40
#define FX816_RF_NUM_CHANNELS 4
#define FX816_PAYLOAD_SIZE 6
#define FX816_BIND_COUNT 300 //3sec
static void __attribute__((unused)) FX816_send_packet()
{
packet[0] = IS_BIND_IN_PROGRESS?0x55:0xAA;
packet[1] = rx_tx_addr[0];
packet[2] = rx_tx_addr[1];
uint8_t val=convert_channel_8b(AILERON);
#define FX816_SWITCH 20
if(val>127+FX816_SWITCH)
packet[3] = 1;
else if(val<127-FX816_SWITCH)
packet[3] = 2;
else
packet[3] = 0;
packet[4] = convert_channel_16b_limit(THROTTLE,0,100);
val=0;
for(uint8_t i=0;i<FX816_PAYLOAD_SIZE-1;i++)
val+=packet[i];
packet[5]=val;
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? FX816_RF_BIND_CHANNEL:hopping_frequency[hopping_frequency_no++]);
hopping_frequency_no%=FX816_RF_NUM_CHANNELS;
// clear packet status bits and TX FIFO
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
XN297_WritePayload(packet, FX816_PAYLOAD_SIZE);
// Power on, TX mode, 2byte CRC
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
NRF24L01_SetPower(); // Set tx_power
}
static void __attribute__((unused)) FX816_RF_init()
{
NRF24L01_Initialize();
XN297_SetTXAddr((uint8_t *)"\xcc\xcc\xcc\xcc\xcc", 5);
}
static void __attribute__((unused)) FX816_initialize_txid()
{
//Only 8 IDs: the RX led does not indicate frame loss.
//I didn't open the plane to find out if I could connect there so this is the best I came up with with few trial and errors...
rx_tx_addr[0]=0x35+(rx_tx_addr[3]&0x07); //Original dump=0x35
rx_tx_addr[1]=0x09; //Original dump=0x09
memcpy(hopping_frequency,"\x09\x1B\x30\x42",FX816_RF_NUM_CHANNELS); //Original dump=9=0x09,27=0x1B,48=0x30,66=0x42
for(uint8_t i=0;i<FX816_RF_NUM_CHANNELS;i++)
hopping_frequency[i]+=rx_tx_addr[3]&0x07;
}
uint16_t FX816_callback()
{
#ifdef MULTI_SYNC
telemetry_set_input_sync(FX816_PACKET_PERIOD);
#endif
if(bind_counter)
if(--bind_counter==0)
BIND_DONE;
FX816_send_packet();
return FX816_PACKET_PERIOD;
}
void FX816_init()
{
BIND_IN_PROGRESS; // autobind protocol
FX816_initialize_txid();
FX816_RF_init();
hopping_frequency_no = 0;
bind_counter=FX816_BIND_COUNT;
}
#endif

View File

@@ -0,0 +1,240 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
// Compatible with FEI XIONG P38 plane.
#if defined(FX_NRF24L01_INO)
#include "iface_xn297.h"
#define FX_BIND_COUNT 300 //3sec
#define FX_SWITCH 20
#define FX_NUM_CHANNELS 4
#define FX816_PACKET_PERIOD 10000
#define FX816_BIND_CHANNEL 40
#define FX816_PAYLOAD_SIZE 6
#define FX816_CH_OFFSET 3
#define FX620_PACKET_PERIOD 3250
#define FX620_BIND_PACKET_PERIOD 4500
#define FX620_BIND_CHANNEL 18
#define FX620_PAYLOAD_SIZE 7
#define FX620_CH_OFFSET 1
#define FX9630_PACKET_PERIOD 8124
#define FX9630_BIND_PACKET_PERIOD 8124
#define FX9630_BIND_CHANNEL 51
#define FX9630_PAYLOAD_SIZE 8
#define FX9630_NUM_CHANNELS 3
//#define FORCE_FX620_ID
//#define FORCE_FX9630_ID
static void __attribute__((unused)) FX_send_packet()
{
//Hopp
if(IS_BIND_DONE)
{
XN297_Hopping(hopping_frequency_no++);
if(sub_protocol == FX9630)
{
XN297_SetTXAddr(rx_tx_addr, 4);
if (hopping_frequency_no > FX9630_NUM_CHANNELS)
hopping_frequency_no = 0;
}
else // FX816 and FX620
{
hopping_frequency_no &= 0x03;
}
}
memset(packet,0x00,packet_length);
//Channels
uint8_t val;
if (sub_protocol == FX9630)
{
packet[0] = convert_channel_8b(THROTTLE);
packet[1] = convert_channel_8b(AILERON);
packet[2] = 0xFF - convert_channel_8b(ELEVATOR);
packet[3] = convert_channel_8b(RUDDER);
packet[4] = 0x20;
packet[5] = GET_FLAG(CH5_SW, 0x01); // DR toggle swich: 0 small throw, 1 large throw
packet[5] |= (Channel_data[CH6] < CHANNEL_MIN_COMMAND ? 0x00 : (Channel_data[CH6] > CHANNEL_MAX_COMMAND ? 0x02 : 0x01)) << 1; // Mode A(0) : 6D small throw, B(1) : 6D large throw, C(2) : 3D
}
else // FX816 and FX620
{
uint8_t offset=sub_protocol == FX816 ? FX816_CH_OFFSET:FX620_CH_OFFSET;
val=convert_channel_8b(AILERON);
if(val>127+FX_SWITCH)
packet[offset] = sub_protocol == FX816 ? 1:0xFF;
else if(val<127-FX_SWITCH)
packet[offset] = sub_protocol == FX816 ? 2:0x00;
else
packet[offset] = sub_protocol == FX816 ? 0:0x7F;
packet[offset+1] = convert_channel_16b_limit(THROTTLE,0,100); //FX816:0x00..0x63, FX620:0x00..0x5E but that should work
}
//Bind and specifics
if(sub_protocol == FX816)
{
if(IS_BIND_IN_PROGRESS)
packet[0] = 0x55;
else
packet[0] = 0xAA;
packet[1] = rx_tx_addr[0];
packet[2] = rx_tx_addr[1];
}
else if(sub_protocol == FX620)
{
if(IS_BIND_IN_PROGRESS)
{
memcpy(packet,rx_tx_addr,3);
packet[3] = hopping_frequency[0];
if(bind_counter > (FX_BIND_COUNT >> 1))
packet[5] = 0x78;
}
else
{
packet[0] = 0x1F; // Is it based on ID??
packet[5] = 0xAB; // Is it based on ID??
}
}
else // FX9630
{
if(IS_BIND_IN_PROGRESS)
{
memcpy(packet,rx_tx_addr, 4);
packet[4] = hopping_frequency[1];
packet[5] = hopping_frequency[2];
packet[7] = 0x55;
}
}
//Check
uint8_t last_packet_idx = packet_length-1;
if (sub_protocol == FX9630 && IS_BIND_IN_PROGRESS)
last_packet_idx--;
val=0;
for(uint8_t i=0;i<last_packet_idx;i++)
val+=packet[i];
if (sub_protocol == FX9630)
val = val ^ 0xFF;
packet[last_packet_idx]=val;
//Debug
#if 0
for(uint8_t i=0;i<packet_length;i++)
debug("%02X ",packet[i]);
debugln("");
#endif
// Send
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, packet_length);
}
static void __attribute__((unused)) FX_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
if(sub_protocol == FX816)
{
XN297_SetTXAddr((uint8_t *)"\xcc\xcc\xcc\xcc\xcc", 5);
XN297_RFChannel(FX816_BIND_CHANNEL);
packet_period = FX816_PACKET_PERIOD;
packet_length = FX816_PAYLOAD_SIZE;
}
else if(sub_protocol == FX620)
{
XN297_SetTXAddr((uint8_t *)"\xaa\xbb\xcc", 3);
XN297_RFChannel(FX620_BIND_CHANNEL);
packet_period = FX620_BIND_PACKET_PERIOD;
packet_length = FX620_PAYLOAD_SIZE;
}
else // FX9630
{
XN297_SetTXAddr((uint8_t *)"\x56\x78\x90\x12", 4);
XN297_RFChannel(FX9630_BIND_CHANNEL);
packet_period = FX9630_BIND_PACKET_PERIOD;
packet_length = FX9630_PAYLOAD_SIZE;
}
}
static void __attribute__((unused)) FX_initialize_txid()
{
if(sub_protocol == FX816)
{
//Only 8 IDs: the RX led does not indicate frame loss.
//I didn't open the plane to find out if I could connect there so this is the best I came up with with few trial and errors...
rx_tx_addr[0]=0x35+(rx_tx_addr[3]&0x07); //Original dump=0x35
rx_tx_addr[1]=0x09; //Original dump=0x09
memcpy(hopping_frequency,"\x09\x1B\x30\x42",FX_NUM_CHANNELS); //Original dump=9=0x09,27=0x1B,48=0x30,66=0x42
for(uint8_t i=0;i<FX_NUM_CHANNELS;i++)
hopping_frequency[i]+=rx_tx_addr[3]&0x07;
}
else if(sub_protocol == FX620)
{
rx_tx_addr[0] = rx_tx_addr[3];
hopping_frequency[0] = 0x18 + rx_tx_addr[3]&0x07; // just to try something
#ifdef FORCE_FX620_ID
memcpy(rx_tx_addr,(uint8_t*)"\x34\xA9\x32",3);
hopping_frequency[0] = 0x18; //on dump: 24 34 44 54
#endif
for(uint8_t i=1;i<FX_NUM_CHANNELS;i++)
hopping_frequency[i] = i*10 + hopping_frequency[0];
}
else // FX9630
{
#ifdef FORCE_FX9630_ID
memcpy(rx_tx_addr,(uint8_t*)"\xCE\x31\x9B\x73", 4);
memcpy(hopping_frequency,"\x13\x1A\x38", FX9630_NUM_CHANNELS); //Original dump=19=0x13,26=0x1A,56=0x38
#else
hopping_frequency[0] = 0x13; // constant
hopping_frequency[1] = RX_num & 0x0F + 0x1A;
hopping_frequency[2] = rx_tx_addr[3] & 0x0F + 0x38;
#endif
}
}
uint16_t FX_callback()
{
#ifdef MULTI_SYNC
telemetry_set_input_sync(packet_period);
#endif
if(bind_counter)
if(--bind_counter==0)
{
BIND_DONE;
if(sub_protocol == FX620)
{
XN297_SetTXAddr(rx_tx_addr, 3);
packet_period = FX620_PACKET_PERIOD;
}
}
FX_send_packet();
return packet_period;
}
void FX_init()
{
BIND_IN_PROGRESS; // autobind protocol
FX_initialize_txid();
FX_RF_init();
hopping_frequency_no = 0;
bind_counter=FX_BIND_COUNT;
}
#endif

View File

@@ -19,17 +19,14 @@
static void __attribute__((unused)) FrSkyX_build_bind_packet()
{
uint8_t packet_size = 0x1D;
if(protocol==PROTO_FRSKYX && (FrSkyFormat & 2 ))
packet_size=0x20; // FrSkyX V1 LBT
//Header
packet[0] = packet_size; // Number of bytes in the packet (after this one)
packet[1] = 0x03; // Bind packet
packet[2] = 0x01; // Bind packet
packet[0] = packet_length; // Number of bytes in the packet (after this one)
packet[1] = 0x03; // Bind packet
packet[2] = 0x01; // Bind packet
//ID
packet[3] = rx_tx_addr[3]; // ID
packet[4] = rx_tx_addr[2]; // ID
packet[3] = rx_tx_addr[3]; // ID
packet[4] = rx_tx_addr[2]; // ID
if(protocol==PROTO_FRSKYX)
{
@@ -40,10 +37,10 @@ static void __attribute__((unused)) FrSkyX_build_bind_packet()
packet[8] = hopping_frequency[idx++];
packet[9] = hopping_frequency[idx++];
packet[10] = hopping_frequency[idx++];
packet[11] = rx_tx_addr[1]; // Unknown but constant ID?
packet[11] = rx_tx_addr[1]; // ID
packet[12] = RX_num;
//
memset(&packet[13], 0, packet_size - 14);
memset(&packet[13], 0, packet_length - 14);
if(binding_idx&0x01)
memcpy(&packet[13],(void *)"\x55\xAA\x5A\xA5",4); // Telem off
if(binding_idx&0x02)
@@ -52,30 +49,34 @@ static void __attribute__((unused)) FrSkyX_build_bind_packet()
else
{
//packet 1D 03 01 0E 1C 02 00 00 32 0B 00 00 A8 26 28 01 A1 00 00 00 3E F6 87 C7 00 00 00 00 C9 C9
packet[5] = rx_tx_addr[1]; // Unknown but constant ID?
//Unknown bytes
if(state & 0x01)
memcpy(&packet[7],"\x00\x18\x0A\x00\x00\xE0\x02\x0B\x01\xD3\x08\x00\x00\x4C\xFE\x87\xC7",17);
else
memcpy(&packet[7],"\x27\xAD\x02\x00\x00\x64\xC8\x46\x00\x64\x00\x00\x00\xFB\xF6\x87\xC7",17);
//ID
packet[5] = rx_tx_addr[1]; // ID
packet[6] = RX_num;
//Bind flags
packet[7]=0;
if(binding_idx&0x01)
packet[7] |= 0x40; // Telem off
if(binding_idx&0x02)
packet[7] |= 0x80; // CH9-16
//Unknown bytes
memcpy(&packet[8],"\x32\x0B\x00\x00\xA8\x26\x28\x01\xA1\x00\x00\x00\x3E\xF6\x87\xC7",16);
packet[20]^= 0x0E ^ rx_tx_addr[3]; // Update the ID
packet[21]^= 0x1C ^ rx_tx_addr[2]; // Update the ID
//Replace the ID
packet[20] ^= 0x0E ^ rx_tx_addr[3]; // Update the ID
packet[21] ^= 0x1C ^ rx_tx_addr[2]; // Update the ID
//Xor
for(uint8_t i=3; i<packet_size-1; i++)
for(uint8_t i=3; i<packet_length-1; i++)
packet[i] ^= 0xA7;
}
//CRC
uint16_t lcrc = FrSkyX_crc(&packet[3], packet_size-4);
packet[packet_size-1] = lcrc >> 8;
packet[packet_size] = lcrc;
uint16_t lcrc = FrSkyX_crc(&packet[3], packet_length-4);
packet[packet_length-1] = lcrc >> 8;
packet[packet_length] = lcrc;
/*//Debug
debug("Bind:");
for(uint8_t i=0;i<=packet_size;i++)
for(uint8_t i=0;i<=packet_length;i++)
debug(" %02X",packet[i]);
debugln("");*/
}
@@ -85,11 +86,8 @@ static void __attribute__((unused)) FrSkyX_build_packet()
//0x1D 0xB3 0xFD 0x02 0x56 0x07 0x15 0x00 0x00 0x00 0x04 0x40 0x00 0x04 0x40 0x00 0x04 0x40 0x00 0x04 0x40 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x96 0x12
// data frames sent every 9ms; failsafe every 9 seconds
//
uint8_t packet_size = 0x1D;
if(protocol==PROTO_FRSKYX && (FrSkyFormat & 2 ))
packet_size=0x20; // FrSkyX V1 LBT
//Header
packet[0] = packet_size; // Number of bytes in the packet (after this one)
packet[0] = packet_length; // Number of bytes in the packet (after this one)
packet[1] = rx_tx_addr[3]; // ID
packet[2] = rx_tx_addr[2]; // ID
packet[3] = rx_tx_addr[1]; // Unknown but constant ID?
@@ -102,22 +100,26 @@ static void __attribute__((unused)) FrSkyX_build_packet()
FrSkyX_channels(7); // Set packet[7]=failsafe, packet[8]=0?? and packet[9..20]=channels data
//Sequence and send SPort
FrSkyX_seq_sport(21,packet_size-2); //21=RX|TXseq, 22=bytes count, 23..packet_size-2=data
FrSkyX_seq_sport(21,packet_length-(protocol==PROTO_FRSKYX && (FrSkyFormat & 2 )) ? 4 : 2); //21=RX|TXseq, 22=bytes count, 23..packet_length-2=data
//CRC
uint16_t lcrc = FrSkyX_crc(&packet[3], packet_size-4);
packet[packet_size-1] = lcrc >> 8;
packet[packet_size] = lcrc;
uint16_t lcrc = FrSkyX_crc(&packet[3], packet_length-4);
packet[packet_length-1] = lcrc >> 8;
packet[packet_length] = lcrc;
/*//Debug
debug("Norm:");
for(uint8_t i=0;i<=packet_size;i++)
for(uint8_t i=0;i<=packet_length;i++)
debug(" %02X",packet[i]);
debugln("");*/
}
uint16_t FRSKYX_callback()
{
#if defined MULTI_EU
if(sub_protocol == CH_16 || sub_protocol == CH_8)
return 9000;
#endif
switch(state)
{
default:
@@ -132,7 +134,7 @@ uint16_t FRSKYX_callback()
state = FRSKY_BIND_DONE;
else
state++;
return 9000;
break;
case FRSKY_BIND_DONE:
FrSkyX_initialize_data(0);
hopping_frequency_no=0;
@@ -238,7 +240,7 @@ uint16_t FRSKYX_callback()
state = FRSKY_DATA1;
return 400; // FCC & LBT
}
return 1;
return 9000;
}
void FRSKYX_init()
@@ -248,22 +250,27 @@ void FRSKYX_init()
if (sub_protocol==XCLONE_16||sub_protocol==XCLONE_8)
Frsky_init_clone();
else if(protocol==PROTO_FRSKYX)
{
Frsky_init_hop();
rx_tx_addr[1]=0x02; // ID related, hw version?
}
else
{
#ifdef FRSKYX2_FORCE_ID
rx_tx_addr[3]=0x0E;
rx_tx_addr[2]=0x1C;
FrSkyX_chanskip=18;
#endif
if(protocol==PROTO_FRSKYX)
Frsky_init_hop();
else
{
#ifdef FRSKYX2_FORCE_ID
rx_tx_addr[3]=0x0E;
rx_tx_addr[2]=0x1C;
FrSkyX_chanskip=18;
#endif
FrSkyX2_init_hop();
}
rx_tx_addr[1]=0x02; // ID related, hw version?
FrSkyX2_init_hop();
}
if(protocol==PROTO_FRSKYX && (FrSkyFormat & 2 ))
packet_length = 0x20; // FrSkyX V1 LBT
else
packet_length = 0x1D;
packet_count=0;
while(!FrSkyX_chanskip)
FrSkyX_chanskip=random(0xfefefefe)%47;
@@ -272,14 +279,12 @@ void FRSKYX_init()
if(IS_BIND_IN_PROGRESS)
{
memset(packet, 0, packet_length);
state = FRSKY_BIND;
FrSkyX_initialize_data(1);
}
else
{
state = FRSKY_DATA1;
FrSkyX_initialize_data(0);
}
state = FRSKY_BIND_DONE;
FrSkyX_telem_init();
}
#endif

View File

@@ -16,7 +16,7 @@ Multiprotocol is distributed in the hope that it will be useful,
#if defined(GD00X_CCNRF_INO)
#include "iface_nrf250k.h"
#include "iface_xn297.h"
//#define FORCE_GD00X_ORIGINAL_ID
@@ -113,7 +113,7 @@ static void __attribute__((unused)) GD00X_send_packet()
if(IS_BIND_DONE)
{
XN297L_Hopping(hopping_frequency_no);
XN297_Hopping(hopping_frequency_no);
if(sub_protocol==GD_V1)
{
hopping_frequency_no++;
@@ -121,21 +121,22 @@ static void __attribute__((unused)) GD00X_send_packet()
}
}
XN297L_WritePayload(packet, packet_length);
XN297L_SetPower(); // Set tx_power
XN297L_SetFreqOffset(); // Set frequency offset
// Send
XN297_SetFreqOffset();
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, packet_length);
}
static void __attribute__((unused)) GD00X_RF_init()
{
XN297L_Init();
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_250K);
if(sub_protocol==GD_V1)
XN297L_SetTXAddr((uint8_t*)"\xcc\xcc\xcc\xcc\xcc", 5);
XN297_SetTXAddr((uint8_t*)"\xcc\xcc\xcc\xcc\xcc", 5);
else
XN297L_SetTXAddr((uint8_t*)"GDKNx", 5);
XN297L_HoppingCalib(sub_protocol==GD_V1?GD00X_RF_NUM_CHANNELS:GD00X_V2_RF_NUM_CHANNELS); // Calibrate all channels
XN297L_RFChannel(sub_protocol==GD_V1?GD00X_RF_BIND_CHANNEL:GD00X_V2_RF_BIND_CHANNEL); // Set bind channel
XN297_SetTXAddr((uint8_t*)"GDKNx", 5);
XN297_HoppingCalib(sub_protocol==GD_V1?GD00X_RF_NUM_CHANNELS:GD00X_V2_RF_NUM_CHANNELS); // Calibrate all channels
XN297_RFChannel(sub_protocol==GD_V1?GD00X_RF_BIND_CHANNEL:GD00X_V2_RF_BIND_CHANNEL); // Set bind channel
}
static void __attribute__((unused)) GD00X_initialize_txid()

View File

@@ -19,7 +19,7 @@ Multiprotocol is distributed in the hope that it will be useful,
#if defined(GW008_NRF24L01_INO)
#include "iface_nrf24l01.h"
#include "iface_xn297.h"
#define GW008_INITIAL_WAIT 500
#define GW008_PACKET_PERIOD 2400
@@ -47,6 +47,9 @@ static void __attribute__((unused)) GW008_send_packet()
}
else
{
XN297_Hopping((hopping_frequency_no++)/2);
hopping_frequency_no %= 8;
packet[1] = 0x01 | GET_FLAG(CH5_SW, 0x40); // flip
packet[2] = convert_channel_16b_limit(AILERON , 200, 0); // aileron
packet[3] = convert_channel_16b_limit(ELEVATOR, 0, 200); // elevator
@@ -63,25 +66,21 @@ static void __attribute__((unused)) GW008_send_packet()
}
packet[14] = rx_tx_addr[1];
// Power on, TX mode, CRC enabled
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? GW008_RF_BIND_CHANNEL : hopping_frequency[(hopping_frequency_no++)/2]);
hopping_frequency_no %= 8;
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
// Send
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WriteEnhancedPayload(packet, GW008_PAYLOAD_SIZE, 0);
NRF24L01_SetPower(); // Set tx_power
}
static void __attribute__((unused)) GW008_RF_init()
{
NRF24L01_Initialize();
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
XN297_SetTXAddr((uint8_t*)"\xcc\xcc\xcc\xcc\xcc", 5);
XN297_SetRXAddr((uint8_t*)"\xcc\xcc\xcc\xcc\xcc", 5);
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, GW008_PAYLOAD_SIZE+2); // payload + 2 bytes for pcf
XN297_SetRXAddr((uint8_t*)"\xcc\xcc\xcc\xcc\xcc", GW008_PAYLOAD_SIZE);
//XN297_HoppingCalib(8);
XN297_RFChannel(GW008_RF_BIND_CHANNEL);
}
static void __attribute__((unused)) GW008_initialize_txid()
@@ -96,20 +95,20 @@ uint16_t GW008_callback()
switch(phase)
{
case GW008_BIND1:
if((NRF24L01_ReadReg(NRF24L01_07_STATUS) & _BV(NRF24L01_07_RX_DR)) && // RX fifo data ready
if(XN297_IsRX() && // RX fifo data ready
XN297_ReadEnhancedPayload(packet, GW008_PAYLOAD_SIZE) == GW008_PAYLOAD_SIZE && // check payload size
packet[0] == rx_tx_addr[0] && packet[14] == rx_tx_addr[1]) // check tx id
{
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(TX_EN);
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(TX_EN);
rx_tx_addr[2] = packet[13];
BIND_DONE;
phase = GW008_DATA;
}
else
{
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_SetTxRxMode(TX_EN);
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(TX_EN);
GW008_send_packet();
phase = GW008_BIND2;
return 850; // minimum value 750 for STM32
@@ -117,14 +116,10 @@ uint16_t GW008_callback()
break;
case GW008_BIND2:
// switch to RX mode
NRF24L01_SetTxRxMode(TXRX_OFF);
NRF24L01_FlushRx();
NRF24L01_SetTxRxMode(RX_EN);
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO)
| _BV(NRF24L01_00_PWR_UP) | _BV(NRF24L01_00_PRIM_RX));
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
phase = GW008_BIND1;
return 5000;
break;
case GW008_DATA:
#ifdef MULTI_SYNC
telemetry_set_input_sync(GW008_PACKET_PERIOD);

View File

@@ -19,7 +19,7 @@
#if defined(H8_3D_NRF24L01_INO)
#include "iface_nrf24l01.h"
#include "iface_xn297.h"
#define H8_3D_PACKET_PERIOD 1800
#define H20H_PACKET_PERIOD 9340
@@ -122,17 +122,15 @@ static void __attribute__((unused)) H8_3D_send_packet()
sum += packet[i];
packet[19] = sum; // data checksum
// Power on, TX mode, 2byte CRC
// Why CRC0? xn297 does not interpret it - either 16-bit CRC or nothing
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
// RF channel
if(sub_protocol!=H20H)
{ // H8_3D, H20MINI, H30MINI
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? hopping_frequency[0] : hopping_frequency[hopping_frequency_no++]);
XN297_RFChannel(IS_BIND_IN_PROGRESS ? hopping_frequency[0] : hopping_frequency[hopping_frequency_no++]);
hopping_frequency_no %= H8_3D_RF_NUM_CHANNELS;
}
else
{ //H20H
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? H20H_BIND_RF : hopping_frequency[packet_count>>3]);
{ // H20H
XN297_RFChannel(IS_BIND_IN_PROGRESS ? H20H_BIND_RF : hopping_frequency[packet_count>>3]);
if(IS_BIND_DONE)
{
packet_count++;
@@ -147,17 +145,15 @@ static void __attribute__((unused)) H8_3D_send_packet()
}
}
// clear packet status bits and TX FIFO
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
// Send
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, H8_3D_PACKET_SIZE);
NRF24L01_SetPower(); // Set tx_power
}
static void __attribute__((unused)) H8_3D_RF_init()
{
NRF24L01_Initialize();
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
if(sub_protocol==H20H)
XN297_SetTXAddr((uint8_t *)"\xEE\xDD\xCC\xBB\x11", 5);

View File

@@ -169,66 +169,77 @@ static void __attribute__((unused)) HOTT_TXID_init()
}
}
static void __attribute__((unused)) HOTT_prep_data_packet()
{
static uint8_t upper=0;
static void __attribute__((unused)) HOTT_prep_data_packet() {
static uint8_t upper = 0; // toggles between sending channels 1..8,9..12 and 1..8,13..16
packet[2] = hopping_frequency_no; // send next frequency to be used
packet[3] = upper; // indicate upper or lower channels (only supporting 16 channels)
packet[2] = hopping_frequency_no;
packet[3] = upper; // used for failsafe and upper channels (only supporting 16 channels)
#ifdef FAILSAFE_ENABLE
static uint8_t failsafe_count=0;
if(IS_FAILSAFE_VALUES_on && IS_BIND_DONE)
{
failsafe_count++;
if(failsafe_count>=3)
{
FAILSAFE_VALUES_off;
failsafe_count=0;
static uint8_t failsafe_count = 0; // failsafe packet state machine (need to send two packets)
if(IS_FAILSAFE_VALUES_on && IS_BIND_DONE) { // if TX wants to send failsafe data and RX is connected
failsafe_count++; // prepare to send next packet
if(failsafe_count >= 3) { // done sending two failsafe channel data packets
FAILSAFE_VALUES_off;
failsafe_count = 0;
}
}
else
failsafe_count=0;
failsafe_count = 0;
#endif
// Channels value are PPM*2, -100%=1100µs, +100%=1900µs, order TAER
//
// Note: failsafe packets are differnt to normal operation packets
// normal operation toggles between sending channels 1..8,9..12 and 1..8,13..16 using bit0 as high/low indicator in packet[3]
// while failsafe packets send channels 1..12, 13..24 (and probably 25..32) using bit0 in packet[3] as packet type indicator
// packet[3] = 0x40 -> failsafe packet with data for channels 1..12
// packet[3] = 0x41 -> failsafe packet with data for channels 13..24
//
uint16_t val;
for(uint8_t i=4;i<28;i+=2)
{
uint8_t ch=(i-4)>>1;
if(upper && ch >= 8)
ch+=4; // when upper swap CH9..CH12 by CH13..16
val=Channel_data[ch];
val=(((val<<2)+val)>>2)+860*2; // value range 860<->2140 *2 <-> -125%<->+125%
for(uint8_t i = 0 ; i < 12*2 ; i += 2) { // working 12 channels (using 2 bytes per channel)
uint8_t chIndex = i >> 1 ; // normal operation channel number
uint8_t fschIndex = chIndex; // linear channel number for failsafe
if(upper && chIndex >= 8) chIndex += 4; // for normal operation toggle between channels 1..8,9..12 and 1..8,13..16
val = Channel_data[chIndex]; // get normal operation channel data
val = (((val << 2) + val) >> 2)+ 860*2; // convert channel data 0..2047 to 1720..4278 <-> -125%<->+125%
// val = (val*5/4+860*2)
#ifdef FAILSAFE_ENABLE
if(failsafe_count==1)
{ // first failsafe packet
packet[3] |= 0x40;
uint16_t fs=Failsafe_data[ch];
if( fs == FAILSAFE_CHANNEL_HOLD || fs == FAILSAFE_CHANNEL_NOPULSES)
val|=0x8000; // channel hold flag
else
{
val=(((fs<<2)+fs)>>2)+860*2; // value range 860<->2140 *2 <-> -125%<->+125%
val|=0x4000; // channel specific position flag
if(failsafe_count == 1 || failsafe_count == 2) {// failsafe data needs to be sent to RX
uint16_t fs = 0x8000; // default failsafe mode is hold
if(failsafe_count == 1) { // send fail safe packet containing channels 1..12
packet[3] = 0x40; // indicate packet has failsafe values for channels 1..12
fs = Failsafe_data[fschIndex]; // get failsafe channel data
} else { // send fail safe packet containing channels 13..24
packet[3] = 0x41; // indicate packet has failsafe values for channels 13..24
if(fschIndex < 4) // we only work 16 channels so send channels 13..16, rest default
fs = Failsafe_data[fschIndex+12]; // get failsafe channel data 13..16
}
if( fs == FAILSAFE_CHANNEL_HOLD || // treat HOLD and NOPULSES as channel hold
fs == FAILSAFE_CHANNEL_NOPULSES)
val = 0x8000; // set channel failsafe mode hold flag
else {
val = (((fs << 2) + fs) >> 2) +860*2; // convert channel data 0..2047 to 1720..4278 <-> -125%<->+125%
val |= 0x4000; // set channel failsafe mode position flag
}
}
else if(failsafe_count==2)
{ // second failsafe packet=timing?
packet[3] |= 0x50;
if(i==4)
val=2;
else
val=0;
}
}
#endif
packet[i] = val;
packet[i+1] = val>>8;
packet[i + 4] = val; // first channel data at packet[4] and packet[5]
packet[i + 4+1] = val >> 8;
}
upper ^= 0x01; // toggle between CH9..CH12 and CH13..16
packet[28] = 0x80; // no sensor
packet[29] = 0x02; // 0x02 when bind starts then when RX replies cycle in sequence 0x1A/22/2A/0A/12, 0x02 during normal packets, 0x01->text config menu, 0x0A->no more RX telemetry
upper ^= 0x01; // toggle to upper and lower channels
packet[28] = 0x80; // no sensor
packet[29] = 0x02; // 0x02 when bind starts then when RX replies cycle in sequence 0x1A/22/2A/0A/12, 0x02 during normal packets, 0x01->text config menu, 0x0A->no more RX telemetry
#ifdef HOTT_FW_TELEMETRY
if(IS_BIND_DONE)
{
@@ -390,7 +401,7 @@ uint16_t HOTT_callback()
{ //Telemetry
// [0..4] = TXID
// [5..9] = RXID
// [10] = 0x40 bind, 0x00 normal, 0x80 config menu
// [10] = holds warnings issued by hott devices
// [11] = telmetry pages. For sensors 0x00 to 0x04, for config mennu 0x00 to 0x12.
// Normal telem page 0 = 0x55, 0x32, 0x38, 0x55, 0x64, 0x32, 0xD0, 0x07, 0x00, 0x55
// Page 0 [12] = [21] = [15]
@@ -400,7 +411,7 @@ uint16_t HOTT_callback()
// Page 0 [16] = RX_LQI in %
// Page 0 [17] = RX_Voltage Min*10 in V
// Page 0 [18,19] = [19]<<8+[18]=max lost packet time in ms, max value seems 2s=0x7D0
// Page 0 [20] = 0x00 ??
// Page 0 [20] = rx events
//
// Config menu consists of the different telem pages put all together
// Page X [12] = seems like all the telem pages with the same value are going together to make the full config menu text. Seen so far 'a', 'b', 'c', 'd'
@@ -412,6 +423,7 @@ uint16_t HOTT_callback()
// Reduce telemetry to 14 bytes
packet_in[0]= packet_in[HOTT_RX_PACKET_LEN];
packet_in[1]= TX_LQI;
uint8_t hott_warnings = packet_in[10]; // save warnings from hott devices
bool send_telem=true;
HOTT_sensor_seq++; // Increment RX sequence counter
if(packet[29] & 1)
@@ -464,6 +476,7 @@ uint16_t HOTT_callback()
packet_in[i-8]=packet_in[i];
debug(" %02X",packet_in[i]);
}
packet_in[14] = hott_warnings; // restore saved warnings from hott devices
debugln("");
if(send_telem)
telemetry_link=2;
@@ -527,7 +540,6 @@ void HOTT_init()
packet_count=0;
state=HOTT_SENSOR_SEARCH_PERIOD;
#endif
packet_sent = 0;
phase = HOTT_START;
}

View File

@@ -0,0 +1,112 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef CYRF6936_INSTALLED
#include "iface_hs6200.h"
static bool HS6200_crc;
static uint16_t HS6200_crc_init;
static uint8_t HS6200_address_length, HS6200_tx_addr[5];
static void __attribute__((unused)) HS6200_Init(bool crc_en)
{
CYRF_GFSK1M_Init(32, 1); //Dummy number of bytes for now
HS6200_crc = crc_en;
}
static void __attribute__((unused)) HS6200_SetTXAddr(const uint8_t* addr, uint8_t addr_len)
{
// precompute address crc
crc = 0xffff;
for(uint8_t i=0; i<addr_len; i++)
crc16_update(addr[addr_len-1-i], 8);
HS6200_crc_init=crc;
memcpy(HS6200_tx_addr, addr, addr_len);
HS6200_address_length = addr_len;
}
static uint16_t __attribute__((unused)) HS6200_calc_crc(uint8_t* msg, uint8_t len)
{
uint8_t pos;
crc = HS6200_crc_init;
// pcf + payload
for(pos=0; pos < len-1; pos++)
crc16_update(msg[pos], 8);
// last byte (1 bit only)
if(len > 0)
crc16_update(msg[pos+1], 1);
return crc;
}
static void __attribute__((unused)) HS6200_SendPayload(uint8_t* msg, uint8_t len)
{
static const uint8_t HS6200_scramble[] = { 0x80,0xf5,0x3b,0x0d,0x6d,0x2a,0xf9,0xbc,0x51,0x8e,0x4c,0xfd,0xc1,0x65,0xd0 }; // todo: find all 32 bytes ...
uint8_t payload[32];
const uint8_t no_ack = 1; // never ask for an ack
static uint8_t pid;
uint8_t pos = 0;
if(len > sizeof(HS6200_scramble))
len = sizeof(HS6200_scramble);
// address
for(int8_t i=HS6200_address_length-1; i>=0; i--)
payload[pos++] = HS6200_tx_addr[i];
// guard bytes
payload[pos++] = HS6200_tx_addr[0];
payload[pos++] = HS6200_tx_addr[0];
// packet control field
payload[pos++] = ((len & 0x3f) << 2) | (pid & 0x03);
payload[pos] = (no_ack & 0x01) << 7;
pid++;
// scrambled payload
if(len > 0)
{
payload[pos++] |= (msg[0] ^ HS6200_scramble[0]) >> 1;
for(uint8_t i=1; i<len; i++)
payload[pos++] = ((msg[i-1] ^ HS6200_scramble[i-1]) << 7) | ((msg[i] ^ HS6200_scramble[i]) >> 1);
payload[pos] = (msg[len-1] ^ HS6200_scramble[len-1]) << 7;
}
// crc
if(HS6200_crc)
{
uint16_t crc = HS6200_calc_crc(&payload[HS6200_address_length+2], len+2);
uint8_t hcrc = crc >> 8;
uint8_t lcrc = crc & 0xff;
payload[pos++] |= (hcrc >> 1);
payload[pos++] = (hcrc << 7) | (lcrc >> 1);
payload[pos++] = lcrc << 7;
}
#if 0
debug("E:");
for(uint8_t i=0; i<pos; i++)
debug(" %02X",payload[i]);
debugln("");
#endif
//CYRF wants LSB first
for(uint8_t i=0; i<pos; i++)
payload[i]=bit_reverse(payload[i]);
//Send
CYRF_WriteRegister(CYRF_01_TX_LENGTH, pos);
CYRF_GFSK1M_SendPayload(payload, pos);
}
#endif

View File

@@ -55,7 +55,8 @@ static void __attribute__((unused)) HITEC_CC2500_init()
CC2500_WriteReg(i, pgm_read_byte_near(&HITEC_init_values[i]));
CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
prev_option = option;
CC2500_SetTxRxMode(TX_EN);
CC2500_SetPower();
}

View File

@@ -15,7 +15,7 @@
#if defined(HONTAI_NRF24L01_INO)
#include "iface_nrf24l01.h"
#include "iface_xn297.h" // mix of nrf and xn297 at 1Mb...
#define HONTAI_BIND_COUNT 80
#define HONTAI_PACKET_PERIOD 13500
@@ -44,6 +44,11 @@ static void __attribute__((unused)) HONTAI_send_packet()
}
else
{
/*if(sub_protocol == JJRCX1)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no++]);
else*/
XN297_Hopping(hopping_frequency_no++);
hopping_frequency_no %= 3;
memset(packet,0,HONTAI_PACKET_SIZE);
packet[3] = convert_channel_16b_limit(THROTTLE, 0, 127) << 1; // Throttle
packet[4] = convert_channel_16b_limit(AILERON, 63, 0); // Aileron
@@ -113,42 +118,44 @@ static void __attribute__((unused)) HONTAI_send_packet()
packet[packet_length-1]=bit_reverse(crc);
// Power on, TX mode, 2byte CRC
if(sub_protocol == JJRCX1)
/*if(sub_protocol == JJRCX1)
{
NRF24L01_SetPower();
NRF24L01_SetTxRxMode(TX_EN);
else
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
NRF24L01_WriteReg(NRF24L01_05_RF_CH, IS_BIND_IN_PROGRESS ? HONTAI_RF_BIND_CHANNEL : hopping_frequency[hopping_frequency_no++]);
hopping_frequency_no %= 3;
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
}
else*/
{
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
}
if(sub_protocol == JJRCX1)
NRF24L01_WritePayload(packet, packet_length);
else
XN297_WritePayload(packet, packet_length);
NRF24L01_SetPower();
}
static void __attribute__((unused)) HONTAI_RF_init()
{
NRF24L01_Initialize();
if(sub_protocol == JJRCX1)
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t*)"\xd2\xb5\x99\xb3\x4a", 5);
else
XN297_SetTXAddr((const uint8_t*)"\xd2\xb5\x99\xb3\x4a", 5);
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M); // this will select the nrf and initialize it, therefore both sub protocols can use common instructions
if(sub_protocol == JJRCX1)
{
//NRF24L01_Initialize();
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t*)"\xd2\xb5\x99\xb3\x4a", 5);
NRF24L01_WriteReg(NRF24L01_04_SETUP_RETR, 0xff); // JJRC uses dynamic payload length
NRF24L01_WriteReg(NRF24L01_1C_DYNPD, 0x3f); // match other stock settings even though AA disabled...
NRF24L01_WriteReg(NRF24L01_1D_FEATURE, 0x07);
//NRF24L01_WriteReg(NRF24L01_05_RF_CH, HONTAI_RF_BIND_CHANNEL);
}
NRF24L01_SetTxRxMode(TX_EN); // Clear data ready, data sent, retransmit and enable CRC 16bits, ready for TX
else
{
XN297_SetTXAddr((const uint8_t*)"\xd2\xb5\x99\xb3\x4a", 5);
//XN297_HoppingCalib(3);
}
XN297_RFChannel(HONTAI_RF_BIND_CHANNEL);
}
const uint8_t PROGMEM HONTAI_hopping_frequency_nonels[][3] = {
@@ -172,9 +179,9 @@ static void __attribute__((unused)) HONTAI_init2()
data_tx_addr[3] = pgm_read_byte_near( &HONTAI_addr_vals[3][(rx_tx_addr[4] >> 4) & 0x0f]);
data_tx_addr[4] = 0x24;
if(sub_protocol == JJRCX1)
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, data_tx_addr, sizeof(data_tx_addr));
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, data_tx_addr, 5);
else
XN297_SetTXAddr(data_tx_addr, sizeof(data_tx_addr));
XN297_SetTXAddr(data_tx_addr, 5);
//Hopping frequency table
for(uint8_t i=0;i<3;i++)

View File

@@ -0,0 +1,122 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
// This module makes it possible to bind to and control the IKEA "Ansluta" line
// of remote-controlled lights.
// To bind, first switch the receiver into binding mode, then the TX.
// Once bound, the TX can send one of three commands:
// lights off, lights dimmed 50% and lights on.
// Those are mapped to throttle ranges 0..0x55, 0x56..0xAA, 0xAB..0xFF.
#if defined(IKEAANSLUTA_CC2500_INO)
#include "iface_cc2500.h"
#define IKEAANSLUTA_BIND_COUNT 30 // ~ 2sec autobind/65ms per loop = 30 binding packets
// Commands
#define IKEAANSLUTA_LIGHT_OFF 0x01
#define IKEAANSLUTA_LIGHT_DIM 0x02 // 50% dimmed light
#define IKEAANSLUTA_LIGHT_ON 0x03
#define IKEAANSLUTA_PAIR 0xFF
void IKEAANSLUTA_send_command(uint8_t command){
CC2500_Strobe(CC2500_SIDLE);
packet[4] = option;
packet[5] = command;
CC2500_WriteData(packet, 8);
}
uint16_t IKEAANSLUTA_callback(void)
{
if (bind_counter) {
IKEAANSLUTA_send_command(IKEAANSLUTA_PAIR);
if(--bind_counter == 0) {
BIND_DONE;
CC2500_SetPower();
}
}
else {
uint8_t throttle = convert_channel_8b(THROTTLE);
uint8_t cmd = throttle <= 0x55 ? IKEAANSLUTA_LIGHT_OFF :
throttle <= 0xAA ? IKEAANSLUTA_LIGHT_DIM :
IKEAANSLUTA_LIGHT_ON;
IKEAANSLUTA_send_command(cmd);
}
return 65535; // 65ms loop cycle is more than enough here (we could make it even longer if not for uint16_t)
}
// Register initialization values as a continuous memory block (to save on flash memory)
const PROGMEM uint8_t IKEAANSLUTA_init_values[] = {
0xFF, // CC2500_06_PKTLEN
0x04, // CC2500_07_PKTCTRL1
0x05, // CC2500_08_PKTCTRL0
0x00, // CC2500_09_ADDR (unused, default)
0x10, // CC2500_0A_CHANNR
0x09, // CC2500_0B_FSCTRL1
0x00, // CC2500_0C_FSCTRL0
0x5D, // CC2500_0D_FREQ2
0x93, // CC2500_0E_FREQ1
0xB1, // CC2500_0F_FREQ0
0x2D, // CC2500_10_MDMCFG4
0x3B, // CC2500_11_MDMCFG3
0x73, // CC2500_12_MDMCFG2
0xA2, // CC2500_13_MDMCFG1
0xF8, // CC2500_14_MDMCFG0
0x01, // CC2500_15_DEVIATN
0x07, // CC2500_16_MCSM2
0x30, // CC2500_17_MCSM1
0x18, // CC2500_18_MCSM0
0x1D, // CC2500_19_FOCCFG
0x1C, // CC2500_1A_BSCFG
0xC7, // CC2500_1B_AGCCTRL2
0x00, // CC2500_1C_AGCCTRL1
0xB2, // CC2500_1D_AGCCTRL0
0x87, // CC2500_1E_WOREVT1 (unused, default)
0x6b, // CC2500_1F_WOREVT0 (unused, default)
0xf8, // CC2500_20_WORCTRL (unused, default)
0xB6, // CC2500_21_FREND1
0x10, // CC2500_22_FREND0
0xEA, // CC2500_23_FSCAL3
0x0A, // CC2500_24_FSCAL2
0x00, // CC2500_25_FSCAL1
0x11, // CC2500_26_FSCAL0
0x41, // CC2500_27_RCCTRL1
0x00, // CC2500_28_RCCTRL0
};
void IKEAANSLUTA_init(void)
{
if (IS_BIND_DONE) bind_counter = 0;
else bind_counter = IKEAANSLUTA_BIND_COUNT;
// All packets we send are the same
packet[0] = 0x06;
packet[1] = 0x55;
packet[2] = 0x01;
// Bytes 3&4 are the transmitter address (to which the RX binds)
// Byte 5 is the command.
packet[3] = rx_tx_addr[3]; // <pseudorandom tx-fixed value> + <rx_num>
// packet[4] = option;
// packet[5] = 0x00; // Command goes here
packet[6] = 0xAA;
packet[7] = 0xFF;
// Configure & initialize CC2500
for (uint8_t i = 0; i <= CC2500_28_RCCTRL0-CC2500_06_PKTLEN; ++i)
CC2500_WriteReg(CC2500_06_PKTLEN+i, pgm_read_byte_near(&IKEAANSLUTA_init_values[i]));
CC2500_SetTxRxMode(TX_EN);
CC2500_SetPower();
}
#endif

View File

@@ -36,7 +36,7 @@ enum PktState {
};
const uint8_t PROGMEM j6pro_bind_sop_code[] = {0x62, 0xdf, 0xc1, 0x49, 0xdf, 0xb1, 0xc0, 0x49};
const uint8_t j6pro_data_code[] = {0x02, 0xf9, 0x93, 0x97, 0x02, 0xfa, 0x5c, 0xe3, 0x01, 0x2b, 0xf1, 0xdb, 0x01, 0x32, 0xbe, 0x6f};
//const uint8_t j6pro_data_code[] = {0x02, 0xf9, 0x93, 0x97, 0x02, 0xfa, 0x5c, 0xe3, 0x01, 0x2b, 0xf1, 0xdb, 0x01, 0x32, 0xbe, 0x6f}; // unneeded since this is the default table after a reset
static void __attribute__((unused)) j6pro_build_bind_packet()
{
@@ -84,7 +84,7 @@ static void __attribute__((unused)) j6pro_cyrf_init()
CYRF_WriteRegister(CYRF_10_FRAMING_CFG, 0xee);
CYRF_WriteRegister(CYRF_1F_TX_OVERRIDE, 0x00);
CYRF_WriteRegister(CYRF_1E_RX_OVERRIDE, 0x00);
CYRF_ConfigDataCode(j6pro_data_code, 16);
//CYRF_ConfigDataCode(j6pro_data_code, 16);
CYRF_WritePreamble(0x333302);
CYRF_GetMfgData(cyrfmfg_id);

View File

@@ -16,7 +16,7 @@
#if defined(JJRC345_NRF24L01_INO)
#include "iface_nrf24l01.h"
#include "iface_xn297.h"
//#define JJRC345_FORCE_ID
@@ -71,7 +71,7 @@ static void __attribute__((unused)) JJRC345_send_packet()
}
else
{ //00 41 00 0A 00 80 80 80 00 00 40 46 00 49 F1 18
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
XN297_Hopping(hopping_frequency_no);
hopping_frequency_no++;
hopping_frequency_no %= JJRC345_NUM_CHANNELS;
packet[1] = hopping_frequency[hopping_frequency_no]; // next packet will be sent on this channel
@@ -121,21 +121,18 @@ static void __attribute__((unused)) JJRC345_send_packet()
packet[14] = rx_tx_addr[2];
packet[15] = rx_tx_addr[3];
// Power on, TX mode
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
// Send
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, JJRC345_PACKET_SIZE);
NRF24L01_SetPower(); // Set tx_power
}
static void __attribute__((unused)) JJRC345_RF_init()
{
NRF24L01_Initialize();
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
XN297_SetTXAddr((uint8_t*)"\xcc\xcc\xcc\xcc\xcc", 5);
NRF24L01_WriteReg(NRF24L01_05_RF_CH, sub_protocol == JJRC345 ? JJRC345_RF_BIND_CHANNEL:SKYTMBLR_RF_BIND_CHANNEL); // Bind channel
//XN297_HoppingCalib(JJRC345_NUM_CHANNELS);
XN297_RFChannel(sub_protocol == JJRC345 ? JJRC345_RF_BIND_CHANNEL:SKYTMBLR_RF_BIND_CHANNEL); // Bind channel
}
uint16_t JJRC345_callback()

View File

@@ -16,9 +16,11 @@ Multiprotocol is distributed in the hope that it will be useful,
#if defined(KF606_CCNRF_INO)
#include "iface_nrf250k.h"
#include "iface_xn297.h"
//#define FORCE_KF606_ORIGINAL_ID
//#define FORCE_MIG320_ORIGINAL_ID
//#define FORCE_ZCZ50_ORIGINAL_ID
#define KF606_INITIAL_WAIT 500
#define KF606_PACKET_PERIOD 3000
@@ -29,30 +31,68 @@ Multiprotocol is distributed in the hope that it will be useful,
static void __attribute__((unused)) KF606_send_packet()
{
uint8_t len = KF606_PAYLOAD_SIZE;
if(IS_BIND_IN_PROGRESS)
{
packet[0] = 0xAA;
memcpy(&packet[1],rx_tx_addr,3);
if(sub_protocol != KF606_ZCZ50)
{
packet[0] = 0xAA;
memcpy(&packet[1],rx_tx_addr,3);
}
else
memcpy(packet,rx_tx_addr,4);
}
else
{
packet[0]= 0x55;
packet[1]= convert_channel_8b(THROTTLE); // 0..255
// Deadband is needed on aileron, 40 gives +-6%
packet[2]=convert_channel_8b_limit_deadband(AILERON,0x20,0x80,0xE0,40); // Aileron: Max values:20..80..E0, Low rates:50..80..AF, High rates:3E..80..C1
// Aileron trim must be on a separated channel C1..D0..DF
packet[3]= convert_channel_16b_limit(CH5,0xC1,0xDF);
}
if(IS_BIND_DONE)
{
XN297L_Hopping(hopping_frequency_no);
XN297_Hopping(hopping_frequency_no);
hopping_frequency_no ^= 1; // 2 RF channels
packet[0] = 0x55;
packet[1] = convert_channel_8b(THROTTLE); // 0..255
// Deadband is needed on aileron, 40 gives +-6%
switch(sub_protocol)
{
case KF606_KF606:
packet[2] = convert_channel_8b_limit_deadband(AILERON,0x20,0x80,0xE0,40); // Aileron: Max values:20..80..E0, Low rates:50..80..AF, High rates:3E..80..C1
packet[3] = convert_channel_16b_limit(CH5,0xC1,0xDF); // Aileron trim must be on a separated channel C1..D0..DF
break;
case KF606_MIG320:
packet[2] = convert_channel_8b_limit_deadband(AILERON,0x00,0x80,0xFF,40); // Aileron: High rate:2B..80..DA
packet[3] = convert_channel_16b_limit(CH5,0x01,0x1F); // Aileron trim must be on a separated channel 01..10..1F
packet[3] += (packet[2]-0x80)>>3; // Drive trims for more aileron authority
if(packet[3] > 0x80)
packet[3] = 0x01;
else if(packet[3] > 0x1F)
packet[3] = 0x1F;
packet[3] |= GET_FLAG(CH6_SW, 0xC0); // 0xC0 and 0xE0 are both turning the LED off, not sure if there is another hidden feature
break;
case KF606_ZCZ50:
len--; // uses only 3 bytes of payload
packet[0] = packet[1]; // Throttle: 0x00..0xFF
packet[1] = convert_channel_8b_limit_deadband(AILERON,0x20,0x80,0xE0,40); // Aileron: Max values:20..80..E0, low rate 0x52..0x80..0xB1, high rate: 0x41..0x80..0xC3.
packet[2] = convert_channel_16b_limit(CH5,0x01,0x1F); // Trim: 0x01..0x10..0x1F
packet[2] |= GET_FLAG(CH6_SW, 0xC0); // Unknown: 0x00 or 0xC0. Left top switch on original TX changes nothing on my plane. Maybe ON/OFF for main motor?
break;
}
}
XN297L_WritePayload(packet, KF606_PAYLOAD_SIZE);
if(sub_protocol == KF606_MIG320)
{
len++;
packet[4] = 0; // additional channel?
}
XN297L_SetPower(); // Set tx_power
XN297L_SetFreqOffset(); // Set frequency offset
#if 0
for(uint8_t i=0; i<len; i++)
debug("%02X ",packet[i]);
debugln("");
#endif
// Send
XN297_SetPower();
XN297_SetFreqOffset();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, len);
}
static void __attribute__((unused)) KF606_initialize_txid()
@@ -66,22 +106,42 @@ static void __attribute__((unused)) KF606_initialize_txid()
rx_tx_addr[1]=0x02;
rx_tx_addr[2]=0x00;
hopping_frequency[0]=0x20;
hopping_frequency[0]=0x23;
hopping_frequency[1]=0x23;
//TX2
rx_tx_addr[0]=0x25;
rx_tx_addr[1]=0x04;
rx_tx_addr[2]=0x00;
hopping_frequency[0]=0x2E;
hopping_frequency[0]=0x31;
hopping_frequency[1]=0x31;
#endif
#ifdef FORCE_MIG320_ORIGINAL_ID
rx_tx_addr[0]=0xBB;
rx_tx_addr[1]=0x13;
rx_tx_addr[2]=0x00;
hopping_frequency[0]=68;
hopping_frequency[1]=71;
#endif
if(sub_protocol == KF606_ZCZ50)
{
rx_tx_addr[1] = rx_tx_addr[0];
rx_tx_addr[0]=0xAA;
}
#ifdef FORCE_ZCZ50_ORIGINAL_ID
rx_tx_addr[0]=0xAA;
rx_tx_addr[1]=0x67;
rx_tx_addr[2]=0x64;
rx_tx_addr[3]=0x01;
hopping_frequency[0]=48;
hopping_frequency[1]=51;
#endif
}
static void __attribute__((unused)) KF606_RF_init()
{
XN297L_Init();
XN297L_SetTXAddr((uint8_t*)"\xe7\xe7\xe7\xe7\xe7", 5);
XN297L_HoppingCalib(KF606_RF_NUM_CHANNELS); // Calibrate all channels
XN297L_RFChannel(KF606_RF_BIND_CHANNEL); // Set bind channel
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_250K);
XN297_SetTXAddr((uint8_t*)"\xe7\xe7\xe7\xe7\xe7", 5);
XN297_HoppingCalib(KF606_RF_NUM_CHANNELS); // Calibrate all channels
XN297_RFChannel(KF606_RF_BIND_CHANNEL); // Set bind channel
}
uint16_t KF606_callback()
@@ -93,7 +153,7 @@ uint16_t KF606_callback()
if(--bind_counter==0)
{
BIND_DONE;
XN297L_SetTXAddr(rx_tx_addr, 3);
XN297_SetTXAddr(rx_tx_addr, sub_protocol != KF606_ZCZ50 ? 3 : 4);
}
KF606_send_packet();
return KF606_PACKET_PERIOD;
@@ -109,3 +169,25 @@ void KF606_init()
}
#endif
// MIG320 protocol
// Bind
// 250K C=7 S=Y A= E7 E7 E7 E7 E7 P(5)= AA BB 13 00 00
// 3ms on ch7
// Normal
// 250K C=68 S=Y A= BB 13 00 P(5)= 55 00 80 10 00
// P[1] = THR 00..FF
// P[2] = AIL 2B..80..DA
// P[3] = TRIM 01..10..1F
// channels 68=BB&3F+9 and 71
// ZCZ50v2 protocol (with fake front propeller)
// Bind
// 250K C=7 S=Y A= E7 E7 E7 E7 E7 P(4)= AA 67 64 01
// 3ms on ch7
// Normal
// 250K C=48 S=Y A= AA 67 64 01 P(3)= 00 80 10
// P[0] = THR 0x00..0xFF
// P[1] = AIL low rate 0x52..0x80..0xB1, high rate: 0x41..0x80..0xC3
// P[2] = TRIM 0x01..0x10..0x1F + UNKNOWN 0x00 or 0xC0

View File

@@ -0,0 +1,141 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(KYOSHO2_NRF24L01_INO)
#include "iface_nrf24l01.h"
#define KYOSHO2_PACKET_PERIOD 1120
#define KYOSHO2_BIND_PACKET_PERIOD 1600
#define KYOSHO2_BIND_COUNT 6000 // about 9sec
#define KYOSHO2_BIND_CHANNEL 0x50
#define KYOSHO2_PAYLOAD_SIZE 28
#define KYOSHO2_RF_CHANNELS 15
#define KYOSHO2_START_RF_CHANNEL 0x13 // No idea where it comes from... ID or unknown bytes during the bind?
#define KYOSHO2_NUM_CHANNEL 10 // Only 4 on the dumps but there is space for 10 channels in the payload...
#define FORCE_KYOSHO2_ID
bool KYOSHO2_resend;
//
static void __attribute__((unused)) KYOSHO2_send_packet()
{
if(KYOSHO2_resend == true)
{
NRF24L01_Strobe(NRF24L01_E3_REUSE_TX_PL);
if(IS_BIND_DONE)
KYOSHO2_resend = false;
return;
}
memset(packet,0x00,KYOSHO2_PAYLOAD_SIZE);
if(IS_BIND_IN_PROGRESS)
{
memcpy(packet, (uint8_t*)"\x01\x02\x05\x08\x1A\x2B\x3C\x4D", 8); // unknown bytes, parameters on how to build the rf channels?
memcpy(&packet[8], rx_tx_addr, 4);
}
else
{
memcpy(packet, rx_tx_addr, 4);
//Hopp
packet[6] = hopping_frequency_no + KYOSHO2_START_RF_CHANNEL;
packet[7] = hopping_frequency[hopping_frequency_no];
NRF24L01_WriteReg(NRF24L01_05_RF_CH, packet[6+(rf_ch_num&0x01)]);
rf_ch_num++;
//Channels
uint16_t temp;
for (uint8_t i = 0; i< KYOSHO2_NUM_CHANNEL; i++)
{
temp=convert_channel_16b_limit(i,0,0x3FF);
packet[8+i*2] = temp >> 8;
packet[9+i*2] = temp;
}
}
//Send
NRF24L01_WriteReg(NRF24L01_07_STATUS, (_BV(NRF24L01_07_TX_DS) | _BV(NRF24L01_07_MAX_RT))); // Reset flags
NRF24L01_FlushTx();
NRF24L01_WritePayload(packet,KYOSHO2_PAYLOAD_SIZE);
NRF24L01_SetPower();
KYOSHO2_resend = true;
#if 0
for(uint8_t i=0;i<KYOSHO2_PAYLOAD_SIZE;i++)
debug("%02X ", packet[i]);
debugln("");
#endif
}
static void __attribute__((unused)) KYOSHO2_RF_init()
{
NRF24L01_Initialize();
NRF24L01_WriteReg(NRF24L01_05_RF_CH, KYOSHO2_BIND_CHANNEL);
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, (uint8_t*)"\x69\x53\x10\xAC\xEF", 5);
}
static void __attribute__((unused)) KYOSHO2_initialize_tx_id()
{
hopping_frequency_no = rx_tx_addr[3]%KYOSHO2_RF_CHANNELS;
hopping_frequency[0] = 0x4A;
#ifdef FORCE_KYOSHO2_ID
memcpy(rx_tx_addr, (uint8_t*)"\x0A\xBD\x31\xDF", 4);
hopping_frequency[0] = 0x4A; // No idea where it comes from... ID or unknown bytes during the bind?
#endif
for(uint8_t i=1;i<KYOSHO2_RF_CHANNELS;i++)
{
if(hopping_frequency[i-1]+5 < 0x50)
hopping_frequency[i] = hopping_frequency[i-1]+5;
else
hopping_frequency[i] = hopping_frequency[i-1]-0x21;
}
#if 0
for(uint8_t i=0;i<KYOSHO2_RF_CHANNELS;i++)
debugln("1:%02X, 2: %02X", i + KYOSHO2_START_RF_CHANNEL, hopping_frequency[i]);
debugln("Selected 1:%02X, 2: %02X", hopping_frequency_no + KYOSHO2_START_RF_CHANNEL, hopping_frequency[hopping_frequency_no]);
#endif
}
uint16_t KYOSHO2_callback()
{
#ifdef MULTI_SYNC
telemetry_set_input_sync(KYOSHO2_PACKET_PERIOD);
#endif
KYOSHO2_send_packet();
if(bind_counter)
{
if(--bind_counter==0)
{
BIND_DONE;
KYOSHO2_resend = false;
}
return KYOSHO2_BIND_PACKET_PERIOD;
}
return KYOSHO2_PACKET_PERIOD;
}
void KYOSHO2_init()
{
KYOSHO2_initialize_tx_id();
KYOSHO2_RF_init();
rf_ch_num = 0;
if(IS_BIND_IN_PROGRESS)
bind_counter = KYOSHO2_BIND_COUNT;
else
bind_counter = 0;
KYOSHO2_resend = false;
}
#endif

View File

@@ -0,0 +1,147 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(LOSI_CYRF6936_INO)
#include "iface_cyrf6936.h"
#define LOSI_FORCE_ID
const uint8_t PROGMEM LOSI_bind_sop_code[] = {0x62, 0xdf, 0xc1, 0x49, 0xdf, 0xb1, 0xc0, 0x49};
const uint8_t LOSI_data_code[][16] = {
{ 0xD7, 0xA1, 0x54, 0xB1, 0x5E, 0x89, 0xAE, 0x86, 0xC9, 0x2C, 0x06, 0x93, 0x86, 0xB9, 0x9E, 0xD7 }, //bind
/* { 0xE1, 0xD6, 0x31, 0x26, 0x5F, 0xBD, 0x40, 0x93, 0xDC, 0x68, 0x08, 0x99, 0x97, 0xAE, 0xAF, 0x8C },
{ 0xDC, 0x68, 0x08, 0x99, 0x97, 0xAE, 0xAF, 0x8C, 0xC3, 0x0E, 0x01, 0x16, 0x0E, 0x32, 0x06, 0xBA },
{ 0xC3, 0x0E, 0x01, 0x16, 0x0E, 0x32, 0x06, 0xBA, 0xE0, 0x83, 0x01, 0xFA, 0xAB, 0x3E, 0x8F, 0xAC },
{ 0xE0, 0x83, 0x01, 0xFA, 0xAB, 0x3E, 0x8F, 0xAC, 0x5C, 0xD5, 0x9C, 0xB8, 0x46, 0x9C, 0x7D, 0x84 },
{ 0x5C, 0xD5, 0x9C, 0xB8, 0x46, 0x9C, 0x7D, 0x84, 0xF1, 0xC6, 0xFE, 0x5C, 0x9D, 0xA5, 0x4F, 0xB7 },
{ 0xF1, 0xC6, 0xFE, 0x5C, 0x9D, 0xA5, 0x4F, 0xB7, 0x58, 0xB5, 0xB3, 0xDD, 0x0E, 0x28, 0xF1, 0xB0 },
{ 0x58, 0xB5, 0xB3, 0xDD, 0x0E, 0x28, 0xF1, 0xB0, 0x5F, 0x30, 0x3B, 0x56, 0x96, 0x45, 0xF4, 0xA1 },*/
{ 0x5F, 0x30, 0x3B, 0x56, 0x96, 0x45, 0xF4, 0xA1, 0x03, 0xBC, 0x6E, 0x8A, 0xEF, 0xBD, 0xFE, 0xF8 } //normal
};
static uint16_t __attribute__((unused)) LOSI_check(uint16_t val)
{
const uint8_t PROGMEM tab[] = { 0xF1, 0xDA, 0xB6, 0xC8 };
uint8_t res = 0x0B, tmp;
uint16_t calc = val>>2; // don't care about the 2 first bits
for(uint8_t i=0; i<5; i++)
{
tmp=pgm_read_byte_near(&tab[i&0x03]);
if(calc&0x0001)
res ^= tmp>>4;
calc >>= 1;
if(calc&0x0001)
res ^= tmp;
calc >>= 1;
}
return val ^ (res<<12);
}
static void __attribute__((unused)) LOSI_send_packet()
{
memcpy(packet, rx_tx_addr, 4);
if(IS_BIND_IN_PROGRESS)
{
memcpy(&packet[4], rx_tx_addr, 4);
packet[8] = 0x05; // CRC?
packet[9] = 0x52; // CRC?
}
else
{
for(uint8_t i=0; i<3; i++)
{
uint16_t val = LOSI_check(Channel_data[i]<<1);
packet[4+i*2] = val >> 8;
packet[5+i*2] = val;
}
}
CYRF_SetPower(0x38);
CYRF_WriteDataPacketLen(packet, 0x0A);
#if 0
for(uint8_t i=0; i < 0x0A; i++)
debug("%02X ", packet[i]);
debugln();
#endif
}
static void __attribute__((unused)) LOSI_cyrf_init()
{
/* Initialise CYRF chip */
CYRF_WriteRegister(CYRF_28_CLK_EN, 0x02);
CYRF_WriteRegister(CYRF_32_AUTO_CAL_TIME, 0x3C);
CYRF_WriteRegister(CYRF_35_AUTOCAL_OFFSET, 0x14);
CYRF_WriteRegister(CYRF_06_RX_CFG, 0x48);
CYRF_WriteRegister(CYRF_1B_TX_OFFSET_LSB, 0x55);
CYRF_WriteRegister(CYRF_1C_TX_OFFSET_MSB, 0x05);
//CYRF_WriteRegister(CYRF_0F_XACT_CFG, 0x24);
CYRF_SetPower(0x38);
CYRF_WriteRegister(CYRF_12_DATA64_THOLD, 0x0A);
CYRF_WriteRegister(CYRF_39_ANALOG_CTRL, 0x01);
CYRF_WritePreamble(0x333304);
//CYRF_WriteRegister(CYRF_27_CLK_OVERRIDE, 0x00);
CYRF_WriteRegister(CYRF_10_FRAMING_CFG, 0x4A);
CYRF_WriteRegister(CYRF_1F_TX_OVERRIDE, 0x04); // No CRC
//CYRF_WriteRegister(CYRF_1E_RX_OVERRIDE, 0x14);
//CYRF_WriteRegister(CYRF_14_EOP_CTRL, 0x02);
}
uint16_t LOSI_callback()
{
#ifdef MULTI_SYNC
telemetry_set_input_sync(19738);
#endif
LOSI_send_packet();
if(bind_counter)
{
bind_counter--;
if(bind_counter==0)
{
BIND_DONE;
CYRF_ConfigDataCode(LOSI_data_code[1], 16); // Load normal data code
}
return 8763;
}
return 19738;
}
void LOSI_init()
{
LOSI_cyrf_init();
//CYRF_FindBestChannels(hopping_frequency, 1, 0, 0x13, 75); // 75 is unknown since dump stops at 0x27, this routine resets the CRC Seed to 0
//CYRF_ConfigRFChannel(hopping_frequency[0] | 1); // Only odd channels
#ifdef LOSI_FORCE_ID
rx_tx_addr[0] = 0x47;
rx_tx_addr[1] = 0x52;
rx_tx_addr[2] = 0xAE;
rx_tx_addr[3] = 0xAA;
CYRF_ConfigRFChannel(0x27);
#endif
if(IS_BIND_IN_PROGRESS)
{
bind_counter = 300;
CYRF_ConfigDataCode(LOSI_data_code[0], 16); // Load bind data code
}
else
{
CYRF_ConfigDataCode(LOSI_data_code[1], 16); // Load normal data code
bind_counter = 0;
}
}
#endif

View File

@@ -15,10 +15,9 @@
// compatible with MJX WLH08, X600, X800, H26D, Eachine E010
// Last sync with hexfet new_protocols/mjxq_nrf24l01.c dated 2016-01-17
#if defined(MJXQ_NRF24L01_INO)
#if defined(MJXQ_CCNRF_INO)
#include "iface_nrf24l01.h"
#include "iface_nrf250k.h"
#include "iface_xn297.h"
#define MJXQ_BIND_COUNT 150
#define MJXQ_PACKET_PERIOD 4000 // Timeout for callback in uSec
@@ -103,6 +102,12 @@ static uint8_t __attribute__((unused)) MJXQ_pan_tilt_value()
#define MJXQ_CHAN2TRIM(X) (((X) & 0x80 ? (X) : 0x7f - (X)) >> 1)
static void __attribute__((unused)) MJXQ_send_packet(uint8_t bind)
{
//RF freq
hopping_frequency_no++;
XN297_Hopping(hopping_frequency_no / 2);
hopping_frequency_no %= 2 * MJXQ_RF_NUM_CHANNELS; // channels repeated
//Build packet
packet[0] = convert_channel_8b(THROTTLE);
packet[1] = convert_channel_s8b(RUDDER);
packet[4] = 0x40; // rudder does not work well with dyntrim
@@ -192,35 +197,26 @@ static void __attribute__((unused)) MJXQ_send_packet(uint8_t bind)
uint8_t sum = packet[0];
for (uint8_t i=1; i < MJXQ_PACKET_SIZE-1; i++) sum += packet[i];
packet[15] = sum;
hopping_frequency_no++;
if (sub_protocol == E010 || sub_protocol == PHOENIX)
// Send
XN297_SetTxRxMode(TX_EN);
XN297_SetPower();
#ifdef NRF24L01_INSTALLED
if (sub_protocol == H26D || sub_protocol == H26WH)
{
XN297L_Hopping(hopping_frequency_no / 2);
XN297L_SetFreqOffset();
XN297L_SetPower();
XN297L_WritePayload(packet, MJXQ_PACKET_SIZE);
//NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no / 2]);
//NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
//NRF24L01_FlushTx();
//NRF24L01_SetTxRxMode(TX_EN);
//NRF24L01_SetPower();
NRF24L01_WritePayload(packet, MJXQ_PACKET_SIZE);
}
else
{
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no / 2]);
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
// Power on, TX mode, 2byte CRC and send packet
if (sub_protocol == H26D || sub_protocol == H26WH)
{
NRF24L01_SetTxRxMode(TX_EN);
NRF24L01_WritePayload(packet, MJXQ_PACKET_SIZE);
}
else
{
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP));
XN297_WritePayload(packet, MJXQ_PACKET_SIZE);
}
NRF24L01_SetPower();
#endif
{//E010, PHOENIX, WLH08, X600, X800
XN297_SetFreqOffset();
XN297_WritePayload(packet, MJXQ_PACKET_SIZE);
}
hopping_frequency_no %= 2 * MJXQ_RF_NUM_CHANNELS; // channels repeated
}
static void __attribute__((unused)) MJXQ_RF_init()
@@ -239,22 +235,20 @@ static void __attribute__((unused)) MJXQ_RF_init()
}
if (sub_protocol == E010 || sub_protocol == PHOENIX)
{
XN297L_Init();
XN297L_SetTXAddr(addr, sizeof(addr));
XN297L_HoppingCalib(MJXQ_RF_NUM_CHANNELS);
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_250K);
XN297_SetTXAddr(addr, MJXQ_ADDRESS_LENGTH);
XN297_HoppingCalib(MJXQ_RF_NUM_CHANNELS);
}
else
{
NRF24L01_Initialize();
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M); // this will select the nrf and initialize it, therefore both H26 sub protocols can use common instructions
#ifdef NRF24L01_INSTALLED
if (sub_protocol == H26D || sub_protocol == H26WH)
{
NRF24L01_WriteRegisterMulti(NRF24L01_10_TX_ADDR, addr, MJXQ_ADDRESS_LENGTH);
}
else
#endif
XN297_SetTXAddr(addr, MJXQ_ADDRESS_LENGTH);
NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, MJXQ_PACKET_SIZE);
//NRF24L01_WriteReg(NRF24L01_11_RX_PW_P0, MJXQ_PACKET_SIZE); // no RX???
}
}
@@ -275,7 +269,7 @@ static void __attribute__((unused)) MJXQ_init2()
hopping_frequency[i]=pgm_read_byte_near( &E010_map_rfchan[rx_tx_addr[3]&0x0F][i] );
hopping_frequency[i+2]=hopping_frequency[i]+0x10;
}
XN297L_HoppingCalib(MJXQ_RF_NUM_CHANNELS);
XN297_HoppingCalib(MJXQ_RF_NUM_CHANNELS);
break;
case WLH08:
// do nothing

View File

@@ -0,0 +1,540 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
// compatible with MT99xx, Eachine H7, Yi Zhan i6S and LS114/124
// Last sync with Goebish mt99xx_nrf24l01.c dated 2016-01-29
#if defined(MT99XX_CCNRF_INO)
#include "iface_xn297.h"
#define MT99XX_BIND_COUNT 928
#define MT99XX_PACKET_PERIOD_FY805 2460
#define MT99XX_PACKET_PERIOD_MT 2625
#define MT99XX_PACKET_PERIOD_YZ 3125
#define MT99XX_PACKET_PERIOD_A180 3400 // timing changes between the packets 2 x 27220 then 1x 26080, it seems that it is only on the first RF channel which jitters by 1.14ms but hard to pinpoint with XN297dump
#define MT99XX_PACKET_PERIOD_DRAGON 1038 // there is a pause of 2x1038 between two packets, no idea why and how since it is not even stable on a same dump...
#define MT99XX_PACKET_PERIOD_DRAGON_TELEM 10265 // long pause to receive the telemetry packets, 3 are sent by the RX one after the other
#define MT99XX_PACKET_PERIOD_F949G 3450
#define MT99XX_PACKET_PERIOD_PA18 1338
#define MT99XX_INITIAL_WAIT 500
#define MT99XX_PACKET_SIZE 9
//#define FORCE_A180_ID
//#define FORCE_DRAGON_ID
//#define FORCE_F949G_ID
#define FORCE_PA18_ID1
//#define FORCE_PA18_ID2
enum {
MT99XX_DATA,
MT99XX_RX,
MT99XX_CHECK,
};
enum{
// flags going to packet[6] (MT99xx, H7)
FLAG_MT_RATE1 = 0x01, // (H7 & A180 high rate)
FLAG_MT_RATE2 = 0x02, // (MT9916 only)
FLAG_MT_VIDEO = 0x10,
FLAG_MT_SNAPSHOT= 0x20,
FLAG_MT_FLIP = 0x80,
};
enum{
// flags going to packet[6] (LS)
FLAG_LS_INVERT = 0x01,
FLAG_LS_RATE = 0x02,
FLAG_LS_HEADLESS= 0x10,
FLAG_LS_SNAPSHOT= 0x20,
FLAG_LS_VIDEO = 0x40,
FLAG_LS_FLIP = 0x80,
};
enum{
// flags going to packet[7] (FY805)
FLAG_FY805_HEADLESS= 0x10,
};
enum{
// flags going to packet[6] (A180)
FLAG_A180_3D6G = 0x01,
FLAG_A180_RATE = 0x02,
};
enum{
// flags going to packet[6] (DRAGON)
FLAG_DRAGON_RATE = 0x01,
FLAG_DRAGON_RTH = 0x80,
FLAG_DRAGON_UNK = 0x04,
};
enum{
// flags going to packet[6] (F949G)
FLAG_F949G_LIGHT = 0x01,
FLAG_F949G_3D6G = 0x20,
};
enum{
// flags going to packet[6] (PA18)
FLAG_PA18_RTH = 0x08,
FLAG_PA18_FLIP = 0x80,
};
const uint8_t h7_mys_byte[] = {
0x01, 0x11, 0x02, 0x12, 0x03, 0x13, 0x04, 0x14,
0x05, 0x15, 0x06, 0x16, 0x07, 0x17, 0x00, 0x10
};
const uint8_t ls_mys_byte[] = {
0x05, 0x15, 0x25, 0x06, 0x16, 0x26,
0x07, 0x17, 0x27, 0x00, 0x10, 0x20,
0x01, 0x11, 0x21, 0x02, 0x12, 0x22,
0x03, 0x13, 0x23, 0x04, 0x14, 0x24
};
const uint8_t yz_p4_seq[] = {0xa0, 0x20, 0x60};
#ifdef MT99XX_HUB_TELEMETRY
const uint8_t DRAGON_seq[] = {0x20, 0x60, 0x20, 0x80};
#endif
static void __attribute__((unused)) MT99XX_send_packet()
{
static uint8_t seq_num=0;
//Set RF freq
if(sub_protocol == LS)
XN297_RFChannel(0x2D); // LS always transmits on the same channel
else
if(sub_protocol==FY805)
XN297_RFChannel(0x4B); // FY805 always transmits on the same channel
else // MT99 & H7 & YZ & A180 & DRAGON & F949G & PA18
XN297_Hopping(hopping_frequency_no);
if(IS_BIND_IN_PROGRESS)
{
//Bind packet
packet[0] = 0x20;
switch(sub_protocol)
{
case YZ:
packet[1] = 0x15;
packet[2] = 0x05;
packet[3] = 0x06;
break;
case LS:
packet[1] = 0x14;
packet[2] = 0x05;
packet[3] = 0x11;
break;
case FY805:
packet[1] = 0x15;
packet[2] = 0x12;
packet[3] = 0x17;
break;
default: // MT99 & H7 & A180 & DRAGON & F949G & PA18
packet[1] = 0x14;
packet[2] = 0x03;
packet[3] = 0x25;
break;
}
packet[4] = rx_tx_addr[0];
packet[5] = rx_tx_addr[1];
packet[6] = rx_tx_addr[2];
if(sub_protocol == PA18+8)
{
packet[7] = num_ch; // checksum offset
packet[8] = 0x55; // fixed
}
else
{ // all others
packet[7] = crc8; // checksum offset
if(sub_protocol != F949G)
packet[8] = 0xAA; // fixed
else
packet[8] = 0x00;
}
}
else
{
if(sub_protocol != YZ)
{ // MT99XX & H7 & LS & FY805 & A180 & DRAGON & F949G & PA18
packet[0] = convert_channel_16b_limit(THROTTLE,0xE1,0x00); // throttle
packet[1] = convert_channel_16b_limit(RUDDER ,0x00,0xE1); // rudder
packet[2] = convert_channel_16b_limit(AILERON ,0xE1,0x00); // aileron
packet[3] = convert_channel_16b_limit(ELEVATOR,0x00,0xE1); // elevator
packet[4] = 0x20; // pitch trim (0x3f-0x20-0x00)
packet[5] = 0x20; // roll trim (0x00-0x20-0x3f)
packet[6] = GET_FLAG( CH5_SW, FLAG_MT_FLIP );
if(sub_protocol != PA18+8)
packet[7] = h7_mys_byte[hopping_frequency_no]; // next rf channel index ?
else
packet[7] = (packet[7]&0xBF)|0x20;
switch(sub_protocol)
{
case MT99:
packet[6] |= 0x40 | FLAG_MT_RATE2 // max rate on MT99xx
| GET_FLAG( CH7_SW, FLAG_MT_SNAPSHOT )
| GET_FLAG( CH8_SW, FLAG_MT_VIDEO );
break;
case H7:
packet[6] |= FLAG_MT_RATE1; // max rate on H7
break;
case LS:
packet[6] |= FLAG_LS_RATE // max rate
| GET_FLAG( CH6_SW, FLAG_LS_INVERT ) // invert
| GET_FLAG( CH7_SW, FLAG_LS_SNAPSHOT ) // snapshot
| GET_FLAG( CH8_SW, FLAG_LS_VIDEO ) // video
| GET_FLAG( CH9_SW, FLAG_LS_HEADLESS ); // Headless
packet[7] = ls_mys_byte[seq_num++];
if(seq_num >= sizeof(ls_mys_byte))
seq_num=0;
break;
case FY805:
packet[6]=0x20;
//Rate 0x01?
//Flip ?
packet[7]=0x01
|GET_FLAG( CH5_SW, FLAG_MT_FLIP )
|GET_FLAG( CH9_SW, FLAG_FY805_HEADLESS ); //HEADLESS
crc8=0;
break;
case A180:
packet[6] = GET_FLAG( !CH6_SW,FLAG_A180_RATE) // 0x02, A180=RATE, F949S=LED
|GET_FLAG( CH5_SW, FLAG_A180_3D6G ) // 0x01, A180=3D_6G, F949S=RATE
|GET_FLAG( CH7_SW, 0x20 ); // 0x20, F949S=3D_6G
packet[7] = 0x00;
break;
case DRAGON:
if(CH5_SW) // Advanced mode
packet[5] |= 0x80;
else
if(Channel_data[CH5] > CHANNEL_MIN_COMMAND) // Beginner mode
packet[5] |= 0x40;
packet[6] = FLAG_DRAGON_RATE
| GET_FLAG( CH6_SW, FLAG_DRAGON_RTH );
#ifdef MT99XX_HUB_TELEMETRY
//Telemetry
if(hopping_frequency_no == 0)
{
seq_num++;
seq_num &= 0x03;
packet_count++;
if(packet_count > 11)
packet_count = 0;
}
if(packet_count > 10) // Telemetry packet request every 10 or 11 packets
{
packet[6] |= 0x04; // Request telemetry flag
phase = MT99XX_RX;
}
packet[7] = DRAGON_seq[seq_num]; // seq: 20 80 20 60 20 80 20 60... 80 changes to 80+batt from telem
if(seq_num==3)
packet[7] |= v_lipo1;
#else
packet[7] = 0x20;
#endif
break;
case F949G:
packet[6] = 0x02
| GET_FLAG( CH5_SW, FLAG_F949G_3D6G )
| GET_FLAG( CH6_SW, FLAG_F949G_LIGHT );
packet[7] = 0x00;
break;
case PA18+8:
if(Channel_data[CH5] > CHANNEL_MAX_COMMAND) // Expert mode
packet[5] = 0xA0;
else
if(Channel_data[CH5] > CHANNEL_MIN_COMMAND) // Intermediate mode
packet[5] = 0x60;
packet[6] = GET_FLAG( CH6_SW, FLAG_PA18_FLIP ) // Flip
| GET_FLAG( CH7_SW, FLAG_PA18_RTH ); // RTH
if(hopping_frequency_no == 0)
packet[7] ^= 0x40;
break;
}
uint8_t result=crc8;
for(uint8_t i=0; i<8; i++)
result += packet[i];
if(sub_protocol == PA18+8)
result += num_ch;
packet[8] = result;
}
else
{ // YZ
packet[0] = convert_channel_16b_limit(THROTTLE,0x00,0x64); // throttle
packet[1] = convert_channel_16b_limit(RUDDER ,0x64,0x00); // rudder
packet[2] = convert_channel_16b_limit(ELEVATOR,0x00,0x64); // elevator
packet[3] = convert_channel_16b_limit(AILERON ,0x64,0x00); // aileron
if(packet_count++ >= 23)
{
seq_num ++;
if(seq_num > 2)
seq_num = 0;
packet_count=0;
}
packet[4] = yz_p4_seq[seq_num];
packet[5] = 0x02 // expert ? (0=unarmed, 1=normal)
| GET_FLAG(CH8_SW, 0x10) //VIDEO
| GET_FLAG(CH5_SW, 0x80) //FLIP
| GET_FLAG(CH9_SW, 0x04) //HEADLESS
| GET_FLAG(CH7_SW, 0x20); //SNAPSHOT
packet[6] = GET_FLAG(CH6_SW, 0x80); //LED
packet[7] = packet[0];
for(uint8_t idx = 1; idx < MT99XX_PACKET_SIZE-2; idx++)
packet[7] += packet[idx];
packet[8] = 0xff;
}
}
//Hopp
hopping_frequency_no++;
if(sub_protocol == YZ || sub_protocol == A180 || sub_protocol == DRAGON || sub_protocol == F949G || sub_protocol == PA18+8)
hopping_frequency_no++; // skip every other channel
if(hopping_frequency_no > 15)
hopping_frequency_no = 0;
// Send
XN297_SetPower();
XN297_SetFreqOffset();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, MT99XX_PACKET_SIZE);
#if 0
for(uint8_t i=0; i<MT99XX_PACKET_SIZE; i++)
debug(" %02X",packet[i]);
debugln();
#endif
}
static void __attribute__((unused)) MT99XX_RF_init()
{
if(sub_protocol == YZ)
XN297_Configure(XN297_CRCEN, XN297_UNSCRAMBLED, XN297_250K);
else if(sub_protocol == F949G)
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_250K);
else
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
XN297_SetTXAddr((uint8_t *)"\xCC\xCC\xCC\xCC\xCC", 5);
XN297_HoppingCalib(16);
#ifdef MT99XX_HUB_TELEMETRY
XN297_SetRXAddr(rx_tx_addr, MT99XX_PACKET_SIZE);
#endif
}
static void __attribute__((unused)) MT99XX_initialize_txid()
{
num_ch = rx_tx_addr[1]; // PA18
rx_tx_addr[1] = rx_tx_addr[3]; // RX_Num
switch(sub_protocol)
{
case YZ:
rx_tx_addr[0] = 0x53; // test (SB id)
rx_tx_addr[1] = 0x00;
rx_tx_addr[2] = 0x00;
break;
case FY805:
rx_tx_addr[0] = 0x81; // test (SB id)
rx_tx_addr[1] = 0x0F;
rx_tx_addr[2] = 0x00;
break;
case LS:
rx_tx_addr[0] = 0xCC;
break;
#ifdef FORCE_A180_ID
case A180:
rx_tx_addr[0] = 0x84; // MikeHRC ID
rx_tx_addr[1] = 0x62;
rx_tx_addr[2] = 0x4A;
//crc8 = 0x30
//channel_offset = 0x03;
break;
#endif
#ifdef FORCE_DRAGON_ID
case DRAGON:
rx_tx_addr[0] = 0x6C; // Laurie ID
rx_tx_addr[1] = 0x00;
rx_tx_addr[2] = 0x22;
//crc8 = 0x8E
//channel_offset = 0x06
break;
#endif
#ifdef FORCE_F949G_ID
case F949G:
rx_tx_addr[0] = 0x7E; // LilTeo14 ID
rx_tx_addr[1] = 0x2F;
rx_tx_addr[2] = 0x29;
//crc8 = 0xD6
//channel_offset = 0x03
break;
#endif
#ifdef FORCE_PA18_ID1
case PA18+8:
rx_tx_addr[0] = 0xC9; // zebble ID
rx_tx_addr[1] = 0x02;
rx_tx_addr[2] = 0x13;
num_ch = 0x89; // additional crc init. How is this calculated? or could it be random?
//crc8 = 0xDE
//channel_offset = 0x03
//1Mb C=5 S=Y A= C9 02 13 CC CC P(9)= E1 70 70 70 20 20 00 20 1A -> 0x91 + 0x89 => 0x1A
// S=Y A= C9 02 13 CC CC P(9)= E1 70 70 70 20 A0 00 20 9A -> 0x11 + 0x89 => 0x9A
//bind S=Y A= CC CC CC CC CC P(9)= 20 14 03 25 C9 02 13 89 55
break;
#endif
#ifdef FORCE_PA18_ID2
case PA18+8:
rx_tx_addr[0] = 0x0E;
rx_tx_addr[1] = 0x05;
rx_tx_addr[2] = 0x13;
num_ch = 0xD1; // additional crc init. How is this calculated? or could it be random?
//crc8 = 0x28
//channel_offset = 0x00
//1Mb C=2 S=Y A= 0E 05 13 CC CC P(9)= E1 70 70 70 20 60 00 60 E2 -> 0x11 + 0xD1 => 0xE2
//bind S=Y A= CC CC CC CC CC P(9)= 20 14 03 25 0E 05 13 D1 55
break;
#endif
default: //MT99 & H7 & A180 & DRAGON & F949G & PA18
rx_tx_addr[2] = 0x00;
if(sub_protocol == PA18+8)
rx_tx_addr[2] = 0x13;
break;
}
rx_tx_addr[3] = 0xCC;
rx_tx_addr[4] = 0xCC;
crc8 = rx_tx_addr[0] + rx_tx_addr[1] + rx_tx_addr[2];
//memcpy(hopping_frequency,"\x02\x48\x0C\x3e\x16\x34\x20\x2A\x2A\x20\x34\x16\x3e\x0c\x48\x02",16);
for(uint8_t i=0; i<8; i++)
{
hopping_frequency[(i<<1) ]=0x02 + (10*i);
hopping_frequency[(i<<1)+1]=0x48 - (10*i);
}
hopping_frequency_no=0;
}
uint16_t MT99XX_callback()
{
switch(phase)
{
case MT99XX_DATA:
#ifdef MULTI_SYNC
telemetry_set_input_sync(packet_period);
#endif
if(bind_counter)
{
bind_counter--;
if (bind_counter == 0)
{
// set tx address for data packets
XN297_SetTXAddr(rx_tx_addr, 5);
// set rf channels
uint8_t channel_offset = ((crc8>>4) + (crc8 & 0x0f)) % 8;
for(uint8_t i=0;i<16;i++)
hopping_frequency[i] += channel_offset;
XN297_HoppingCalib(16);
BIND_DONE;
}
}
MT99XX_send_packet();
break;
#ifdef MT99XX_HUB_TELEMETRY
case MT99XX_RX:
//Switch to RX
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(RX_EN);
phase++;
return MT99XX_PACKET_PERIOD_DRAGON_TELEM - MT99XX_PACKET_PERIOD_DRAGON - 500;
case MT99XX_CHECK:
//Check telem
if(XN297_IsRX())
{
//debug("RX");
if(XN297_ReadPayload(packet_in, MT99XX_PACKET_SIZE))
{
// C=48 S=Y A= 6C 00 22 CC CC P(9)= 6C 00 22 27 00 00 00 00 60
// C=48 S=Y A= 6C 00 22 CC CC P(9)= 6C 00 22 28 00 00 00 00 61
// C=18 S=Y A= 6C 00 22 CC CC P(9)= 6C 00 22 24 00 00 00 00 5D
// 6C 00 22 = TX address, 27/28/24=vbatt, check = sum(P[0..7]) + AB
// D2 EE 00 25 00 00 00 00 90 -> check also + AB
//for(uint8_t i=0; i<MT99XX_PACKET_SIZE; i++)
// debug(" %02X",packet_in[i]);
uint8_t check=0xAB;
for(uint8_t i=0; i<8; i++)
check += packet_in[i];
if(packet_in[8] == check && packet_in[0] == rx_tx_addr[0] && packet_in[1] == rx_tx_addr[1] && packet_in[2] == rx_tx_addr[2])
{ // checksum and address are ok
// debug(" OK");
v_lipo1 = packet_in[3] & 0x7F; // Batt
v_lipo2 = packet_in[3] & 0x80; // Low batt flag
RX_RSSI=100;
telemetry_link = 1;
}
}
//debugln("");
}
//Switch to TX
XN297_SetTxRxMode(TXRX_OFF);
XN297_SetTxRxMode(TX_EN);
phase=MT99XX_DATA;
return 500;
#endif
}
return packet_period;
}
void MT99XX_init(void)
{
if(protocol == PROTO_MT99XX2)
sub_protocol|=0x08; // Increase the number of sub_protocols for MT99XX
bind_counter = MT99XX_BIND_COUNT;
if(IS_BIND_DONE)
{
if(sub_protocol != A180 && sub_protocol != DRAGON && sub_protocol != F949G && sub_protocol != PA18+8)
BIND_IN_PROGRESS; // autobind protocol
else
bind_counter = 1;
}
MT99XX_initialize_txid();
MT99XX_RF_init();
switch(sub_protocol)
{
case YZ:
packet_period = MT99XX_PACKET_PERIOD_YZ;
break;
case FY805:
packet_period = MT99XX_PACKET_PERIOD_FY805;
break;
case F949G:
case A180:
packet_period = MT99XX_PACKET_PERIOD_A180;
break;
case PA18+8:
packet_period = MT99XX_PACKET_PERIOD_PA18;
break;
default:
packet_period = MT99XX_PACKET_PERIOD_MT;
break;
}
packet_count=0;
phase=MT99XX_DATA;
}
#endif

View File

@@ -1,384 +0,0 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
// compatible with MT99xx, Eachine H7, Yi Zhan i6S and LS114/124
// Last sync with Goebish mt99xx_nrf24l01.c dated 2016-01-29
#if defined(MT99XX_NRF24L01_INO)
#include "iface_nrf24l01.h"
#include "iface_nrf250k.h"
#define MT99XX_BIND_COUNT 928
#define MT99XX_PACKET_PERIOD_FY805 2460
#define MT99XX_PACKET_PERIOD_MT 2625
#define MT99XX_PACKET_PERIOD_YZ 3125
#define MT99XX_PACKET_PERIOD_A180 3400 // timing changes between the packets 2 x 27220 then 1x 26080, it seems that it is only on the first RF channel which jitters by 1.14ms but hard to pinpoint with XN297dump
#define MT99XX_PACKET_PERIOD_DRAGON 1038 // there is a pause of 2x1038 between the packets 3 and 4 from what XN297dump detects which I think are used for telemetry
#define MT99XX_INITIAL_WAIT 500
#define MT99XX_PACKET_SIZE 9
//#define FORCE_A180_ID
//#define FORCE_DRAGON_ID
enum{
// flags going to packet[6] (MT99xx, H7)
FLAG_MT_RATE1 = 0x01, // (H7 & A180 high rate)
FLAG_MT_RATE2 = 0x02, // (MT9916 only)
FLAG_MT_VIDEO = 0x10,
FLAG_MT_SNAPSHOT= 0x20,
FLAG_MT_FLIP = 0x80,
};
enum{
// flags going to packet[6] (LS)
FLAG_LS_INVERT = 0x01,
FLAG_LS_RATE = 0x02,
FLAG_LS_HEADLESS= 0x10,
FLAG_LS_SNAPSHOT= 0x20,
FLAG_LS_VIDEO = 0x40,
FLAG_LS_FLIP = 0x80,
};
enum{
// flags going to packet[7] (FY805)
FLAG_FY805_HEADLESS= 0x10,
};
enum{
// flags going to packet[6] (A180)
FLAG_A180_3D6G = 0x01,
FLAG_A180_RATE = 0x02,
};
enum{
// flags going to packet[6] (DRAGON)
FLAG_DRAGON_RATE = 0x01,
FLAG_DRAGON_RTH = 0x80,
FLAG_DRAGON_UNK = 0x04,
};
enum {
MT99XX_INIT = 0,
MT99XX_BIND,
MT99XX_DATA
};
const uint8_t h7_mys_byte[] = {
0x01, 0x11, 0x02, 0x12, 0x03, 0x13, 0x04, 0x14,
0x05, 0x15, 0x06, 0x16, 0x07, 0x17, 0x00, 0x10
};
const uint8_t ls_mys_byte[] = {
0x05, 0x15, 0x25, 0x06, 0x16, 0x26,
0x07, 0x17, 0x27, 0x00, 0x10, 0x20,
0x01, 0x11, 0x21, 0x02, 0x12, 0x22,
0x03, 0x13, 0x23, 0x04, 0x14, 0x24
};
const uint8_t yz_p4_seq[] = {0xa0, 0x20, 0x60};
#ifdef DRAGON_HUB_TELEMETRY
const uint8_t DRAGON_seq[] = {0x20, 0x60, 0x20, 0x80};
#endif
static void __attribute__((unused)) MT99XX_send_packet()
{
static uint8_t seq_num=0;
if(IS_BIND_IN_PROGRESS)
{
//Bind packet
packet[0] = 0x20;
switch(sub_protocol)
{
case YZ:
packet_period = MT99XX_PACKET_PERIOD_YZ;
packet[1] = 0x15;
packet[2] = 0x05;
packet[3] = 0x06;
break;
case LS:
packet[1] = 0x14;
packet[2] = 0x05;
packet[3] = 0x11;
break;
case FY805:
packet_period = MT99XX_PACKET_PERIOD_FY805;
packet[1] = 0x15;
packet[2] = 0x12;
packet[3] = 0x17;
break;
case A180:
packet_period = MT99XX_PACKET_PERIOD_A180;
default: // MT99 & H7 & A180 & DRAGON
packet[1] = 0x14;
packet[2] = 0x03;
packet[3] = 0x25;
break;
}
packet[4] = rx_tx_addr[0];
packet[5] = rx_tx_addr[1];
packet[6] = rx_tx_addr[2];
packet[7] = crc8; // checksum offset
packet[8] = 0xAA; // fixed
}
else
{
if(sub_protocol != YZ)
{ // MT99XX & H7 & LS & FY805 & A180 & DRAGON
packet[0] = convert_channel_16b_limit(THROTTLE,0xE1,0x00); // throttle
packet[1] = convert_channel_16b_limit(RUDDER ,0x00,0xE1); // rudder
packet[2] = convert_channel_16b_limit(AILERON ,0xE1,0x00); // aileron
packet[3] = convert_channel_16b_limit(ELEVATOR,0x00,0xE1); // elevator
packet[4] = 0x20; // pitch trim (0x3f-0x20-0x00)
packet[5] = 0x20; // roll trim (0x00-0x20-0x3f)
packet[6] = GET_FLAG( CH5_SW, FLAG_MT_FLIP );
packet[7] = h7_mys_byte[hopping_frequency_no]; // next rf channel index ?
switch(sub_protocol)
{
case MT99:
packet[6] |= 0x40 | FLAG_MT_RATE2 // max rate on MT99xx
| GET_FLAG( CH7_SW, FLAG_MT_SNAPSHOT )
| GET_FLAG( CH8_SW, FLAG_MT_VIDEO );
break;
case H7:
packet[6] |= FLAG_MT_RATE1; // max rate on H7
break;
case LS:
packet[6] |= FLAG_LS_RATE // max rate
| GET_FLAG( CH6_SW, FLAG_LS_INVERT ) // invert
| GET_FLAG( CH7_SW, FLAG_LS_SNAPSHOT ) // snapshot
| GET_FLAG( CH8_SW, FLAG_LS_VIDEO ) // video
| GET_FLAG( CH9_SW, FLAG_LS_HEADLESS ); // Headless
packet[7] = ls_mys_byte[seq_num++];
if(seq_num >= sizeof(ls_mys_byte))
seq_num=0;
break;
case FY805:
packet[6]=0x20;
//Rate 0x01?
//Flip ?
packet[7]=0x01
|GET_FLAG( CH5_SW, FLAG_MT_FLIP )
|GET_FLAG( CH9_SW, FLAG_FY805_HEADLESS ); //HEADLESS
crc8=0;
break;
case A180:
packet[6] = FLAG_A180_RATE
| GET_FLAG( CH5_SW, FLAG_A180_3D6G );
packet[7] = 0x00;
break;
case DRAGON:
if(CH5_SW) // Advanced mode
packet[5] |= 0x80;
else
if(Channel_data[CH5] > CHANNEL_MIN_COMMAND) // Beginner mode
packet[5] |= 0x40;
packet[6] = FLAG_DRAGON_RATE
| GET_FLAG( CH6_SW, FLAG_DRAGON_RTH );
#ifdef DRAGON_HUB_TELEMETRY
//Telemetry
// C=48 S=Y A= 6C 00 22 CC CC P(9)= 6C 00 22 27 00 00 00 00 60
// C=48 S=Y A= 6C 00 22 CC CC P(9)= 6C 00 22 28 00 00 00 00 61
// C=18 S=Y A= 6C 00 22 CC CC P(9)= 6C 00 22 24 00 00 00 00 5D
// 6C 00 22 = TX address, 27/28/24=vbatt, check = sum(P[0..7]) + AB, where AB comes from? Is it constant?
if(hopping_frequency_no == 0)
{
seq_num++;
seq_num &= 0x03;
packet_count++;
if(packet_count > 11)
packet_count = 0;
}
if(packet_count > 10) // Telemetry packet request every 10 or 11 packets
packet[6] |= 0x04; // Request telemetry flag
packet[7] = DRAGON_seq[seq_num]; // seq: 20 80 20 60 20 80 20 60... 80 changes to 80+batt from telem
if(seq_num==3)
packet[7] |= v_lipo1;
#else
packet[7] = 0x20;
#endif
break;
}
uint8_t result=crc8;
for(uint8_t i=0; i<8; i++)
result += packet[i];
packet[8] = result;
}
else
{ // YZ
packet[0] = convert_channel_16b_limit(THROTTLE,0x00,0x64); // throttle
packet[1] = convert_channel_16b_limit(RUDDER ,0x64,0x00); // rudder
packet[2] = convert_channel_16b_limit(ELEVATOR,0x00,0x64); // elevator
packet[3] = convert_channel_16b_limit(AILERON ,0x64,0x00); // aileron
if(packet_count++ >= 23)
{
seq_num ++;
if(seq_num > 2)
seq_num = 0;
packet_count=0;
}
packet[4] = yz_p4_seq[seq_num];
packet[5] = 0x02 // expert ? (0=unarmed, 1=normal)
| GET_FLAG(CH8_SW, 0x10) //VIDEO
| GET_FLAG(CH5_SW, 0x80) //FLIP
| GET_FLAG(CH9_SW, 0x04) //HEADLESS
| GET_FLAG(CH7_SW, 0x20); //SNAPSHOT
packet[6] = GET_FLAG(CH6_SW, 0x80); //LED
packet[7] = packet[0];
for(uint8_t idx = 1; idx < MT99XX_PACKET_SIZE-2; idx++)
packet[7] += packet[idx];
packet[8] = 0xff;
}
}
if(sub_protocol == LS)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 0x2D); // LS always transmits on the same channel
else
if(sub_protocol==FY805)
NRF24L01_WriteReg(NRF24L01_05_RF_CH, 0x4B); // FY805 always transmits on the same channel
else // MT99 & H7 & YZ & A180 & DRAGON
NRF24L01_WriteReg(NRF24L01_05_RF_CH, hopping_frequency[hopping_frequency_no]);
NRF24L01_WriteReg(NRF24L01_07_STATUS, 0x70);
NRF24L01_FlushTx();
XN297_WritePayload(packet, MT99XX_PACKET_SIZE);
if(sub_protocol == DRAGON)
{
if(hopping_frequency_no == 6)
packet_period = 3 * MT99XX_PACKET_PERIOD_DRAGON; // hole probably for the telemetry
else
packet_period = MT99XX_PACKET_PERIOD_DRAGON;
}
hopping_frequency_no++;
if(sub_protocol == YZ || sub_protocol == A180 || sub_protocol == DRAGON )
hopping_frequency_no++; // skip every other channel
if(hopping_frequency_no > 15)
hopping_frequency_no = 0;
NRF24L01_SetPower();
}
static void __attribute__((unused)) MT99XX_RF_init()
{
NRF24L01_Initialize();
if(sub_protocol == YZ)
XN297_SetScrambledMode(XN297_UNSCRAMBLED);
XN297_SetTXAddr((uint8_t *)"\xCC\xCC\xCC\xCC\xCC", 5);
if(sub_protocol == YZ)
NRF24L01_SetBitrate(NRF24L01_BR_250K); // 250Kbps (nRF24L01+ only)
XN297_Configure(_BV(NRF24L01_00_EN_CRC) | _BV(NRF24L01_00_CRCO) | _BV(NRF24L01_00_PWR_UP) );
}
static void __attribute__((unused)) MT99XX_initialize_txid()
{
rx_tx_addr[1] = rx_tx_addr[3]; // RX_Num
switch(protocol)
{
case YZ:
rx_tx_addr[0] = 0x53; // test (SB id)
rx_tx_addr[1] = 0x00;
rx_tx_addr[2] = 0x00;
break;
case FY805:
rx_tx_addr[0] = 0x81; // test (SB id)
rx_tx_addr[1] = 0x0F;
rx_tx_addr[2] = 0x00;
break;
case LS:
rx_tx_addr[0] = 0xCC;
break;
#ifdef FORCE_A180_ID
case A180:
rx_tx_addr[0] = 0x84; // MikeHRC ID
rx_tx_addr[1] = 0x62;
rx_tx_addr[2] = 0x4A;
//crc8 = 0x30
//channel_offset = 0x03;
break;
#endif
#ifdef FORCE_DRAGON_ID
case DRAGON:
rx_tx_addr[0] = 0x6C; // Laurie ID
rx_tx_addr[1] = 0x00;
rx_tx_addr[2] = 0x22;
//crc8 = 0x8E
//channel_offset = 0x06
break;
#endif
default: //MT99 & H7 & A180 & DRAGON
rx_tx_addr[2] = 0x00;
break;
}
rx_tx_addr[3] = 0xCC;
rx_tx_addr[4] = 0xCC;
crc8 = rx_tx_addr[0] + rx_tx_addr[1] + rx_tx_addr[2];
//memcpy(hopping_frequency,"\x02\x48\x0C\x3e\x16\x34\x20\x2A\x2A\x20\x34\x16\x3e\x0c\x48\x02",16);
for(uint8_t i=0; i<8; i++)
{
hopping_frequency[(i<<1) ]=0x02 + (10*i);
hopping_frequency[(i<<1)+1]=0x48 - (10*i);
}
hopping_frequency_no=0;
}
uint16_t MT99XX_callback()
{
#ifdef MULTI_SYNC
telemetry_set_input_sync(packet_period);
#endif
if(bind_counter)
{
bind_counter--;
if (bind_counter == 0)
{
// set tx address for data packets
XN297_SetTXAddr(rx_tx_addr, 5);
// set rf channels
uint8_t channel_offset = ((crc8>>4) + (crc8 & 0x0f)) % 8;
for(uint8_t i=0;i<16;i++)
hopping_frequency[i] += channel_offset;
BIND_DONE;
}
}
MT99XX_send_packet();
return packet_period;
}
void MT99XX_init(void)
{
if(sub_protocol != A180 && sub_protocol != DRAGON)
BIND_IN_PROGRESS; // autobind protocol
bind_counter = MT99XX_BIND_COUNT;
MT99XX_initialize_txid();
MT99XX_RF_init();
packet_period = MT99XX_PACKET_PERIOD_MT;
packet_count=0;
}
#endif

View File

@@ -0,0 +1,241 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(MOULDKG_NRF24L01_INO)
#include "iface_xn297.h"
//#define FORCE_MOULDKG_ORIGINAL_ID
#define MOULDKG_PACKET_PERIOD 5000
#define MOULDKG_BIND_PACKET_PERIOD 12000
#define MOULDKG_TX_BIND_CHANNEL 11
#define MOULDKG_RX_BIND_CHANNEL 76
#define MOULDKG_PAYLOAD_SIZE_DIGIT 5
#define MOULDKG_PAYLOAD_SIZE_ANALOG 10
#define MOULDKG_BIND_PAYLOAD_SIZE 7
#define MOULDKG_BIND_COUNT 300
#define MOULDKG_RF_NUM_CHANNELS 4
enum {
MOULDKG_BINDTX=0,
MOULDKG_BINDRX,
MOULDKG_PREP_DATA,
MOULDKG_PREP_DATA1,
MOULDKG_DATA,
};
uint8_t MOULDKG_RX_id[4*3];
static void __attribute__((unused)) MOULDKG_send_packet()
{
uint8_t len = MOULDKG_BIND_PAYLOAD_SIZE;
memcpy(&packet[1],rx_tx_addr,3);
if(IS_BIND_IN_PROGRESS)
{
packet[0] = 0xC0;
memset(&packet[4], 0x00, 3);
}
else
{
uint8_t n = num_ch<<2;
if(sub_protocol == MOULDKG_ANALOG)
{
packet[0] = 0x36;
uint8_t ch[]={ 1,0,2,3 };
for(uint8_t i=0;i<4;i++)
packet[i+4] = convert_channel_8b(ch[i]+n);
len = MOULDKG_PAYLOAD_SIZE_ANALOG;
}
else
{//DIGIT
len = MOULDKG_PAYLOAD_SIZE_DIGIT;
uint8_t val=0;
if(packet_count&1)
{
packet[0] = 0x31;
//Button B
if(Channel_data[CH2+n]>CHANNEL_MAX_COMMAND) val |= 0x40;
else if(Channel_data[CH2+n]<CHANNEL_MIN_COMMAND) val |= 0x80;
//Button C
if(Channel_data[CH3+n]>CHANNEL_MAX_COMMAND) val |= 0x10;
else if(Channel_data[CH3+n]<CHANNEL_MIN_COMMAND) val |= 0x20;
}
else
{
packet[0] = 0x30;
val = 0x60;
// | GET_FLAG(CH5_SW, 0x80) // Button E
// | GET_FLAG(CH6_SW, 0x10); // Button F
}
//Button A
if(Channel_data[CH1+n]>CHANNEL_MAX_COMMAND) val |= 0x01;
else if(Channel_data[CH1+n]<CHANNEL_MIN_COMMAND) val |= 0x02;
//Button D
if(Channel_data[CH4+n]>CHANNEL_MAX_COMMAND) val |= 0x04;
else if(Channel_data[CH4+n]<CHANNEL_MIN_COMMAND) val |= 0x08;
packet[4]= val;
}
}
// Send
XN297_SetPower();
XN297_SetTxRxMode(TX_EN);
XN297_WritePayload(packet, len);
#if 0
for(uint8_t i=0; i < len; i++)
debug("%02X ", packet[i]);
debugln();
#endif
}
static void __attribute__((unused)) MOULDKG_initialize_txid()
{
//rx_tx_addr[0] = rx_tx_addr[3]; // Use RX_num;
#ifdef FORCE_MOULDKG_ORIGINAL_ID
rx_tx_addr[0]=0x57;
rx_tx_addr[1]=0x1B;
rx_tx_addr[2]=0xF8;
#endif
}
static void __attribute__((unused)) MOULDKG_RF_init()
{
XN297_Configure(XN297_CRCEN, XN297_SCRAMBLED, XN297_1M);
XN297_SetTXAddr((uint8_t*)"KDH", 3);
XN297_SetRXAddr((uint8_t*)"KDH", MOULDKG_BIND_PAYLOAD_SIZE);
}
uint16_t MOULDKG_callback()
{
uint8_t rf,n;
uint16_t addr;
switch(phase)
{
case MOULDKG_BINDTX:
if(XN297_IsRX())
{
//Example: TX: C=11 S=Y A= 4B 44 48 P(7)= C0 37 02 4F 00 00 00
// RX: C=76 S=Y A= 4B 44 48 P(7)= 5A 37 02 4F 03 0D 8E
// 03 0D 8E => RF channels 0F,1C,39,3C
XN297_ReadPayload(packet_in, MOULDKG_BIND_PAYLOAD_SIZE);
for(uint8_t i=0; i < MOULDKG_BIND_PAYLOAD_SIZE; i++)
debug("%02X ", packet_in[i]);
debugln();
//Not sure if I should test packet_in[0]
if(memcmp(&packet_in[1],&packet[1],3)==0)
{//TX ID match
if(option == 0)
{
memcpy(MOULDKG_RX_id,&packet_in[4],3);
phase = MOULDKG_PREP_DATA1;
}
else
{// Store RX ID
addr=MOULDKG_EEPROM_OFFSET+RX_num*3;
for(uint8_t i=0;i<3;i++)
eeprom_write_byte((EE_ADDR)(addr+i),packet_in[4+i]);
phase = MOULDKG_PREP_DATA;
}
break;
}
}
XN297_RFChannel(MOULDKG_TX_BIND_CHANNEL); // Set TX bind channel
XN297_SetTxRxMode(TXRX_OFF);
MOULDKG_send_packet();
phase++;
return 600;
case MOULDKG_BINDRX:
//Wait for the packet transmission to finish
while(XN297_IsPacketSent()==false);
//Switch to RX
XN297_SetTxRxMode(TXRX_OFF);
XN297_RFChannel(MOULDKG_RX_BIND_CHANNEL); // Set RX bind channel
XN297_SetTxRxMode(RX_EN);
phase = MOULDKG_BINDTX;
return MOULDKG_BIND_PACKET_PERIOD-600;
case MOULDKG_PREP_DATA:
addr=MOULDKG_EEPROM_OFFSET+RX_num*3;
debug("RXID: ");
for(uint8_t i=0;i<3*4;i++)
{ // load 4 consecutive RX IDs
MOULDKG_RX_id[i]=eeprom_read_byte((EE_ADDR)(addr+i));
debug(" %02X",MOULDKG_RX_id[i]);
}
debugln("");
case MOULDKG_PREP_DATA1:
//Switch to normal mode
BIND_DONE;
XN297_SetTxRxMode(TXRX_OFF);
phase = MOULDKG_DATA;
break;
case MOULDKG_DATA:
#ifdef MULTI_SYNC
if(num_ch==0)
telemetry_set_input_sync(MOULDKG_PACKET_PERIOD);
#endif
if(option == 0) option++;
if(num_ch<option)
{
//Set RX ID address
n = num_ch*3;
XN297_SetTXAddr(&MOULDKG_RX_id[n], 3);
//Set frequency based on current RX ID and packet number
rf = 0x0C;
if(packet_count & 0x04)
{
n++;
rf += 0x20;
}
if(packet_count & 0x02)
rf += 0x10 + (MOULDKG_RX_id[n] >> 4);
else
rf += MOULDKG_RX_id[n] & 0x0F;
XN297_RFChannel(rf);
#if 1
debugln("num_ch=%d,packet_count=%d,rf=%02X,ID=%02X %02X %02X",num_ch,packet_count,rf,MOULDKG_RX_id[num_ch*3],MOULDKG_RX_id[num_ch*3+1],MOULDKG_RX_id[num_ch*3+2]);
#endif
MOULDKG_send_packet();
if(num_ch==0)
packet_count++;
}
num_ch++;
num_ch &= 0x03;
break;
}
return MOULDKG_PACKET_PERIOD/4;
}
void MOULDKG_init()
{
if(option == 0)
BIND_IN_PROGRESS;
MOULDKG_initialize_txid();
MOULDKG_RF_init();
bind_counter = MOULDKG_BIND_COUNT;
if(IS_BIND_IN_PROGRESS)
phase = MOULDKG_BINDTX;
else
phase = MOULDKG_PREP_DATA;
packet_count = 0;
num_ch = 0;
}
#endif
// Analog
// Bind TX: C=11 S=Y A= 4B 44 48 P(7)= C0 46 01 00 00 00 00
// Bind RX: 5A 46 01 00 63 82 4E
// Norm: C=15 S=Y A= 63 82 4E P(10)= 36 46 01 00 80 80 80 80 00 00

View File

@@ -3,7 +3,7 @@
3,FrskyD,D8,Cloned
4,Hisky,Hisky,HK310
5,V2x2,V2x2,JXD506,MR101
6,DSM,DSM2_1F,DSM2_2F,DSMX_1F,DSMX_2F,AUTO
6,DSM,DSM2_1F,DSM2_2F,DSMX_1F,DSMX_2F,AUTO,DSMR_1F
7,Devo,8CH,10CH,12CH,6CH,7CH
8,YD717,YD717,SKYWLKR,SYMAX4,XINXUN,NIHUI
9,KN,WLTOYS,FEILUN
@@ -14,7 +14,7 @@
14,Bayang,Bayang,H8S3D,X16_AH,IRDRONE,DHD_D4,QX100
15,FrskyX,CH_16,CH_8,EU_16,EU_8,Cloned,Clon_8
16,ESky,Std,ET4
17,MT99xx,MT,H7,YZ,LS,FY805,A180,Dragon
17,MT99xx,MT,H7,YZ,LS,FY805,A180,Dragon,F949G
18,MJXq,WLH08,X600,X800,H26D,E010,H26WH,PHOENIX
19,Shenqi
20,FY326,FY326,FY319
@@ -25,7 +25,7 @@
25,FrskyV
26,HONTAI,HONTAI,JJRCX1,X5C1,FQ777_951
27,OpnLrs
28,AFHDS2A,PWM_IBUS,PPM_IBUS,PWM_SBUS,PPM_SBUS,PWM_IB16,PPM_IB16
28,AFHDS2A,PWM_IBUS,PPM_IBUS,PWM_SBUS,PPM_SBUS,PWM_IB16,PPM_IB16,PWM_SB16,PPM_SB16
29,Q2X2,Q222,Q242,Q282
30,WK2x01,WK2801,WK2401,W6_5_1,W6_6_1,W6_HEL,W6_HEL_I
31,Q303,Q303,CX35,CX10D,CX10WD
@@ -42,11 +42,11 @@
42,BUGSMINI,BUGSMINI,BUGS3H
43,Traxxas,RX6519
44,NCC1701
45,E01X,E012,E015,E016H
45,E01X,E012,E015
46,V911S,V911S,E119
47,GD00x,GD_V1,GD_V2
48,V761,3CH,4CH
49,KF606
48,V761,3CH,4CH,TOPRC
49,KF606,KF606,MIG320,ZCZ50
50,Redpine,Fast,Slow
51,Potensic,A20
52,ZSX,280
@@ -55,9 +55,9 @@
55,Frsky_RX,Multi,CloneTX,EraseTX,CPPM
56,AFHDS2A_RX,Multi,CPPM
57,HoTT,Sync,No_Sync
58,FX816,P38
58,FX,816,620,9630
59,Bayang_RX,Multi,CPPM
60,Pelikan,Pro,Lite
60,Pelikan,Pro,Lite,SCX24
61,Tiger
62,XK,X450,X420
63,XN_DUMP,250K,1M,2M,AUTO
@@ -80,5 +80,14 @@
80,E016H,E016Hv2
81,E010r5
82,LOLI
83,E129
84,JOYSWAY
83,E129,E129,C186
84,JOYSWAY
85,E016H
87,IKEA
88,WILLIFM
89,Losi
90,MouldKg,Analog,Digit
91,Xerall
92,MT99xx,PA18
93,Kyosho2,KT-17
94,Scorpio

View File

@@ -0,0 +1,192 @@
/*
This project is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Multiprotocol is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(MULTI_CONFIG_INO)
#ifdef CYRF6936_INSTALLED
#include "iface_cyrf6936.h"
#endif
void CONFIG_write_GID(uint32_t id)
{
for(uint8_t i=0;i<4;i++)
eeprom_write_byte((EE_ADDR)EEPROM_ID_OFFSET+i,id >> (i*8));
//eeprom_write_byte((EE_ADDR)(EEPROM_ID_OFFSET+10),0xf0);
}
void CONFIG_write_CID(uint8_t *data)
{
for(uint8_t i=0;i<6;i++)
eeprom_write_byte((EE_ADDR)EEPROM_CID_OFFSET+i, data[i]);
//eeprom_write_byte((EE_ADDR)EEPROM_CID_INIT_OFFSET, 0xf0);
}
uint16_t CONFIG_callback()
{
static uint8_t line=0, page=0;
uint32_t id=0;
// [0] = page<<4|line number
// [1..6] = max 6 bytes
if(CONFIG_SerialRX)
{
debug("config");
for(uint8_t i=0; i<7; i++)
debug("%02X ",CONFIG_SerialRX_val[i]);
debugln("");
CONFIG_SerialRX = false;
switch(CONFIG_SerialRX_val[0]&0x0F)
{
//case 0:
// Page change
// break;
case 1:
for(uint8_t i=0; i<4; i++)
{
id <<= 8;
id |= CONFIG_SerialRX_val[i+1];
}
debugln("Update ID to %lx", id);
CONFIG_write_GID(id);
break;
case 2:
if(CONFIG_SerialRX_val[1]==0xAA)
{
#define STM32_UUID ((uint32_t *)0x1FFFF7E8)
id = STM32_UUID[0] ^ STM32_UUID[1] ^ STM32_UUID[2];
debugln("Reset GID to %lx", id);
CONFIG_write_GID(id);
}
break;
#ifdef CYRF6936_INSTALLED
case 4:
debug("Update CID to ");
for(uint8_t i=0; i<6; i++)
debug("%02X ",CONFIG_SerialRX_val[i+1]);
debugln("");
CONFIG_write_CID(&CONFIG_SerialRX_val[1]);
case 5:
if(CONFIG_SerialRX_val[1]==0xAA)
{
uint8_t data[6];
CYRF_WriteRegister(CYRF_25_MFG_ID, 0xFF); /* Fuses power on */
CYRF_ReadRegisterMulti(CYRF_25_MFG_ID, data, 6);
CYRF_WriteRegister(CYRF_25_MFG_ID, 0x00); /* Fuses power off */
debug("Reset CID to ");
for(uint8_t i=0; i<6; i++)
debug("%02X ",data[i]);
debugln("");
CONFIG_write_CID(data);
}
break;
#endif
case 7:
if(CONFIG_SerialRX_val[1]==0xAA)
{
debugln("Format EE");
#if defined(STM32_BOARD)
EEPROM.format();
#else
for (uint16_t i = 0; i < 512; i++)
eeprom_write_byte((EE_ADDR)i, 0xFF);
#endif
}
break;
}
}
if( telemetry_link )
return 10000;
// [0] = page<<4|line number
// line=0: VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_PATCH_LEVEL, Channel order:RUDDER<<6|THROTTLE<<4|ELEVATOR<<2|AILERON
// [1..21] = max 20 characters, any displayable chars followed by:
// 0x00 : end of line
// 0x80+len: selectable text to follow
// 0x90+len: selectable text to follow with "Are you sure?"
// 0xA0+len: not editable dec value
// 0xB0+len: editable dec value
// 0xC0+len: not editable hex value
// 0xD0+len: editable hex value
memset(&packet_in[1],0,20);
do
{
packet_in[0] = (page<<4) | line;
switch(line)
{
case 0:
packet_in[1]=VERSION_MAJOR;
packet_in[2]=VERSION_MINOR;
packet_in[3]=VERSION_REVISION;
packet_in[4]=VERSION_PATCH_LEVEL;
packet_in[5]=RUDDER<<6|THROTTLE<<4|ELEVATOR<<2|AILERON;
break;
case 1:
//Global ID
#ifndef FORCE_GLOBAL_ID
memcpy(&packet_in[1],"Global ID",9);
packet_in[10] = 0xD0 + 4;
#else
memcpy(&packet_in[1],"Fixed ID ",9);
packet_in[10] = 0xC0 + 4;
#endif
MProtocol_id_master = random_id(EEPROM_ID_OFFSET,false);
set_rx_tx_addr(MProtocol_id_master);
for(uint8_t i=0; i<4; i++)
packet_in[11+i]=rx_tx_addr[i];
break;
#if defined(STM32_BOARD) && not defined(FORCE_GLOBAL_ID)
case 2:
//Reset global ID
packet_in[1] = 0x90+9;
memcpy(&packet_in[2],"Reset GID",9);
break;
#endif
#ifdef CYRF6936_INSTALLED
case 4:
//Cyrf ID
#ifndef FORCE_CYRF_ID
memcpy(&packet_in[1],"Cyrf ID",7);
packet_in[8] = 0xD0 + 6;
CYRF_GetMfgData(&packet_in[9]);
#else
memcpy(&packet_in[1],"Fixed CID",9);
packet_in[10] = 0xC0 + 6;
CYRF_GetMfgData(&packet_in[11]);
#endif
break;
#ifndef FORCE_CYRF_ID
case 5:
//Reset Cyrf ID
packet_in[1] = 0x90+9;
memcpy(&packet_in[2],"Reset CID",9);
break;
#endif
#endif
case 7:
packet_in[1] = 0x90+13;
memcpy(&packet_in[2],"Format EEPROM",13);
break;
}
line++;
line %= 8;
}
while(packet_in[1]==0); // next line if empty
telemetry_link = 1;
return 10000;
}
void CONFIG_init()
{
}
#endif

Some files were not shown because too many files have changed in this diff Show More