Skip to content

Commit

Permalink
Merge pull request #3 from TylerWilley/pcb
Browse files Browse the repository at this point in the history
Pcb
  • Loading branch information
TylerWilley authored Mar 10, 2024
2 parents fa1dffb + 44549d5 commit 6522797
Show file tree
Hide file tree
Showing 18 changed files with 448 additions and 119 deletions.
62 changes: 49 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,56 @@

A Flipper Zero app that allows the flipper to communicate with Digimon V-Pets.

Currently tested:
- DM20
- DMX
- PenZ
- DMC
- PenOG
- PenProg
- PenX
Currently tested devices:
Classic:
- 1997 Digital Monster
- 1998 Digimon Pendulum
- 1999 Digivice
- 2000 D-3
- 2000 D-Terminal
- 2002 D-Scanner
- 2002 Digimon Pendulum Progress
- 2003 Digimon Pendulum X
- 2005 Digimon Accel
- 2005 Digimon Mini
- 2006 Digivice iC

Modern:
- 2017 Digital Monster Ver.20th
- 2018 Digimon Pendulum Ver.20th
- 2019 Digital Monster X
- 2020 Digimon Pendulum Z
- 2021 Digivice Ver.Complete

Color:
- 2023 Digital Monster COLOR

Currently tested apps:
- W0rld (via Chrome web browser serial)
- Alpha Terminal and Alpha Serial (Android)
- Alpha Terminal (Windows)
- ACom Wiki (Android)

Unsupported Apps:
- Alpha Serial (Windows) still doesn't seem to detect it

Untested:
- All other pets (Pen20, Original Pets, etc)
- PenC
- All other pets
- Listen Mode

Thanks to [Joushiikuta](https://www.youtube.com/@joushiikuta) for testing the classic devices which I do not have.

Based on:
- The DMComm project by BladeSabre: https://github.com/dmcomm/dmcomm-project
- The updated DMComm Arduino library by BladeSabre: https://github.com/dmcomm/dmcomm-arduino-lib
- The Flipper Zero Boilerplate App: https://github.com/leedave/flipper-zero-fap-boilerplate
- The Flipper Zero Firmware: https://github.com/flipperdevices/flipperzero-firmware

### Known Issues

Occasionally when using the USB A-Com mode, the flipper zero USB driver will crash the flipper due to the way I'm overwriting the USB VID/PID. I'm not sure why yet and am still debugging this. Subsequent attempts seem to work and this is uncommon (although not rare).

### Youtube Example

[![Video showing app in action](https://img.youtube.com/vi/pggRzHnXlF4/0.jpg)](https://www.youtube.com/watch?v=pggRzHnXlF4)
Expand All @@ -33,12 +65,18 @@ You will need to construct a circuit similar to the A-Com circuit described in t

![Schematic](screenshots/flipper_vpet_circuit.png)

Pin C3 - 2k resistor - Pin B2 - 10k resistor - Pin GND
Pin C3 - 4k resistor - Pin B2 - 20k resistor - Pin GND

Pin B2 - VPet+

Pin GND - VPet-

The 4k/20k resistor pairing is confirmed to work with the older devices. If you only need modern device support 2k/10k is also compatible, though it will not work with older devices.

[Joushiikuta](https://www.youtube.com/@joushiikuta) has created an awesome gerber file for a compatible PCB located [here](pcb/20240225_FlipperZero_F-Com_PCB_Thickness_1.6mm_Gerber.zip) (included with permission)

You will also need compatible right angle header pins and SMD resistors.

## Installation

The F-Com app is compiled and installed with [ufbt](https://github.com/flipperdevices/flipperzero-ufbt)
Expand All @@ -54,7 +92,7 @@ flipper to make them accessible under the "Saved" menu option.

### Listen

Listen is currently untested, as I have not created a jig to make it function!!
Listen mode is now partially functional. It is difficult to get a good read at the moment although it is possible. I need to re-work the code to use rising/falling edge detection instead of looping though so the flipper GUI does not hang, and we don't have to spend the whole time looping on a GPIO read.

Listen allows you to connect 2 vpets to each other and eavesdrop on the data they send. Both codes will show up on the flipper after communication completes, and you can save either code to the flipper for later use. Leaving this screen will pause dmcomm.

Expand All @@ -76,6 +114,4 @@ The flipper will change the USB port from CLI mode into Serial mode and behave a

Dmcomm supports a voltage test. However, the flipper zero firmware does not yet have ADC support implemented. This is still possible as demonstrated by the flipper zero oscilloscope project. At some point I may implement this. A-Com's themselves use analog input in order to support a wider range of devices logic levels. This may limit the flipper app's compatibility for now.

DMC support isn't included in the dmcomm-project ino, but has been added to the updated https://github.com/dmcomm/dmcomm-arduino-lib project. At some point I may update this to port the CPP code onto the flipper.

Debug mode support could be added at some point.
2 changes: 1 addition & 1 deletion application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ App(
stack_size=3 * 1024,
fap_category="GPIO",
# Optional values
# fap_version="0.1",
fap_version="1.1",
fap_icon="template.png", # 10x10 1-bit PNG
# fap_description="A simple app",
# fap_author="J. Doe",
Expand Down
7 changes: 7 additions & 0 deletions dmcomm-lib/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ void Stream::set_callback(DmcommCallback callback)

size_t Stream::write(uint8_t i)
{
if(cb == NULL) return 0;
char str[10];
snprintf(str, 10, "%c", (char)i);
size_t sent = furi_stream_buffer_send(
Expand All @@ -23,6 +24,7 @@ size_t Stream::write(uint8_t i)

size_t Stream::write(const char *buffer, size_t size)
{
if(cb == NULL) return 0;
char str[10];
size_t sent = 0;
for(size_t i = 0; i < size; i++)
Expand All @@ -39,6 +41,7 @@ size_t Stream::write(const char *buffer, size_t size)

size_t Stream::print(const char buffer[])
{
if(cb == NULL) return 0;
return furi_stream_buffer_send(
app->dmcomm_output_stream,
buffer,
Expand All @@ -48,6 +51,7 @@ size_t Stream::print(const char buffer[])

size_t Stream::print(int i, int fmt)
{
if(cb == NULL) return 0;
UNUSED(fmt);
char str[10];
snprintf(str, 10, "%d", i);
Expand All @@ -62,6 +66,7 @@ size_t Stream::print(int i, int fmt)

size_t Stream::println(void)
{
if(cb == NULL) return 0;
size_t sent = furi_stream_buffer_send(
app->dmcomm_output_stream,
"\n",
Expand All @@ -74,6 +79,7 @@ size_t Stream::println(void)

size_t Stream::println(const char buffer[])
{
if(cb == NULL) return 0;
size_t sent = furi_stream_buffer_send(
app->dmcomm_output_stream,
buffer,
Expand All @@ -91,6 +97,7 @@ size_t Stream::println(const char buffer[])

size_t Stream::println(int i, int fmt)
{
if(cb == NULL) return 0;
UNUSED(fmt);
char str[10];
snprintf(str, 10, "%d\n", i);
Expand Down
7 changes: 7 additions & 0 deletions dmcomm-lib/Print.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/*
implementation of the Print/Stream classes
but the underlying I/O is to two flipper stream buffers
instead of an arduino serial interface.
*/
#include "arduino.h"

#define DEC 10
Expand Down
2 changes: 1 addition & 1 deletion dmcomm-lib/dmcomm_pin_control.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class BaseProngInput {
void setActiveLevel(uint8_t level);
uint32_t waitForActive(uint32_t timeout);
uint32_t waitForIdle(uint32_t timeout);
uint32_t waitFor(bool active, uint32_t timeout);
virtual uint32_t waitFor(bool active, uint32_t timeout);
ReceiveOutcome waitFrom(bool active, uint32_t dur_min, uint32_t dur_max, int16_t current_bit);
protected:
uint8_t idle_level_;
Expand Down
8 changes: 8 additions & 0 deletions dmcomm-lib/dmcomm_serial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,21 @@ uint8_t SerialFollower::serialRead() {
int16_t incoming_int;
uint8_t incoming_byte;
uint8_t i = 0;
bool first = true;
if (serial_.available() == 0) {
return 0;
}
time_start = millis();
do {
do {
incoming_int = serial_.read();
// if we have nothing to read, and this is the first character
// then exit immediately, this makes our loop really tight while we are active
if(first && incoming_int == -1)
{
return 0;
}
first = false;
time = millis() - time_start;
if (time > DMCOMM_SERIAL_TIMEOUT_MILLIS) {
serial_.println(F("[No EOL received]"));
Expand Down
2 changes: 1 addition & 1 deletion dmcomm-lib/fcom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ static FComState fcom_state;

void set_serial_callback(DmcommCallback cb)
{
FURI_LOG_I(TAG, "set_serial_callback %p", cb);
//FURI_LOG_I(TAG, "set_serial_callback %p", cb);
fcom_state.stream->set_callback(cb);
}

Expand Down
56 changes: 56 additions & 0 deletions dmcomm-lib/flipper_pin_control.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "flipper_pin_control.h"
#include "DMComm.h"

namespace DMComm {

Expand Down Expand Up @@ -52,4 +53,59 @@ bool FComInput::isActive() {
return level == active_level_;
}

/*
NOTE: We override this because if we loop without delays on the
flipper, we starve the process scheduler thread... which means
the UI locks up. This isn't an issue for _most_ operations because
they are all 13ms or less. But in listen mode or go-second mode
we are waiting for _FIVE SECONDS_ which is really annoying for the
user being unable to back out of the app during that time.
SO. If the delay is really big, we're going to add a 1ms delay
every loop to allow the UI to process events and stuff so the
user isn't left in the dust.
For tight timeouts though, we will keep the original code.
Technically, this should be done using rising/falling edge
interrupts... However, that's pretty complicated to implement
because we need to determine which state is active, what current
state we are in, call into C code and manage waiting on a lock.
And even then lock timeouts aren't microsecond accurate, so it
still wouldn't be something we could use for everything...
This is "good enough".
*/
uint32_t FComInput::waitFor(bool active, uint32_t timeout) {
if(timeout < 250000)
{
uint32_t start_time = micros();
uint32_t duration;
while (true) {
duration = micros() - start_time;
if (duration > timeout) {
return DMCOMM_SIGNAL_TIMED_OUT;
}
if (active == isActive()) {
return duration;
}
}
}
else
{
uint32_t start_time = micros();
uint32_t duration;
while (true) {
duration = micros() - start_time;
if (duration > timeout) {
return DMCOMM_SIGNAL_TIMED_OUT;
}
if (active == isActive()) {
return duration;
}
delay(1);
}
}
}

}
1 change: 1 addition & 0 deletions dmcomm-lib/flipper_pin_control.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class FComInput : public BaseProngInput {
bool isActive();
void setThreshold(uint16_t threshold_mV);
uint16_t voltage();
uint32_t waitFor(bool active, uint32_t timeout);
private:
const GpioPin* pin_in_;
};
Expand Down
16 changes: 2 additions & 14 deletions dmcomm_link.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,7 @@
* dmcomm thread, runs the dmcomm loop. call init first.
* exits when the app dmcomm_run var is set to false.
* App runs this constantly on startup until app close.
*
int32_t dmcomm_reader(void* context) {
FURI_LOG_I(TAG, "dmcomm_reader start");
App* app = context;
setup();
while(app->dmcomm_run) {
loop();
}
FURI_LOG_I(TAG, "dmcomm_reader end");
return 0;
}*/

*/
int32_t fcom_thread(void* context) {
FURI_LOG_I(TAG, "fcom_thread start");
App* app = context;
Expand All @@ -37,7 +25,7 @@ Used for comms by app (non USB)
*/
void dmcomm_sendcommand(void* context, const char* cmd)
{
FURI_LOG_I(TAG, "dmcomm_sendcommand");
FURI_LOG_I(TAG, "dmcomm_sendcommand: %s", cmd);
App* app = context;

size_t sent = furi_stream_buffer_send(
Expand Down
Binary file not shown.
8 changes: 8 additions & 0 deletions scene_listen_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,29 @@ bool fcom_listen_menu_scene_on_event(void* context, SceneManagerEvent event) {
switch(event.event) {
case ListenMenuSceneSelectionEvent2Prong:
strncpy(app->state->current_code, "V0\n", MAX_DIGIROM_LEN);
furi_string_reset(app->state->r_code);
furi_string_reset(app->state->s_code);
scene_manager_next_scene(app->scene_manager, FcomReadCodeScene);
consumed = true;
break;
case ListenMenuSceneSelectionEvent3Prong:
strncpy(app->state->current_code, "X0\n", MAX_DIGIROM_LEN);
furi_string_reset(app->state->r_code);
furi_string_reset(app->state->s_code);
scene_manager_next_scene(app->scene_manager, FcomReadCodeScene);
consumed = true;
break;
case ListenMenuSceneSelectionEventXrosMini:
strncpy(app->state->current_code, "Y0\n", MAX_DIGIROM_LEN);
furi_string_reset(app->state->r_code);
furi_string_reset(app->state->s_code);
scene_manager_next_scene(app->scene_manager, FcomReadCodeScene);
consumed = true;
break;
case ListenMenuSceneSelectionEventColor:
strncpy(app->state->current_code, "C0\n", MAX_DIGIROM_LEN);
furi_string_reset(app->state->r_code);
furi_string_reset(app->state->s_code);
scene_manager_next_scene(app->scene_manager, FcomReadCodeScene);
consumed = true;
break;
Expand Down
3 changes: 3 additions & 0 deletions scene_main_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Setup our scene widgets and create callback hooks
*/
void fcom_menu_callback(void* context, uint32_t index);
void fcom_main_menu_scene_on_enter(void* context) {
FURI_LOG_I(TAG, "fcom_main_menu_scene_on_enter");
App* app = context;
submenu_reset(app->submenu);
submenu_add_item(app->submenu, "Listen", MainMenuSelectionListen, fcom_menu_callback, app);
Expand Down Expand Up @@ -52,6 +53,7 @@ void fcom_menu_callback(void* context, uint32_t index) {

/** main menu event handler - switches scene based on the event */
bool fcom_main_menu_scene_on_event(void* context, SceneManagerEvent event) {
FURI_LOG_I(TAG, "fcom_main_menu_scene_on_event");
App* app = context;
bool consumed = false;
switch(event.type) {
Expand Down Expand Up @@ -83,6 +85,7 @@ bool fcom_main_menu_scene_on_event(void* context, SceneManagerEvent event) {
}

void fcom_main_menu_scene_on_exit(void* context) {
FURI_LOG_I(TAG, "fcom_main_menu_scene_on_exit");
App* app = context;
submenu_reset(app->submenu);
}
Loading

0 comments on commit 6522797

Please sign in to comment.