You'll want to create some flavor of message broker that acts as an intermediary. The OTP applications that you run on a Nerves-based device are running on the same BEAM so all of the regular messaging features and patterns are available to you.
You can use the pub-sub mechanism in Phoenix channels to effectively broker events between a JS frontend and the BEAM applications running on your Nerves-based device. There are Phoenix channel client implementations available such as PhoenixChannelClient that you can use from any OTP application for this purpose.
If that's too heavy for what you're doing, a simpler alternative would be to globally register the name of a GenServer
in your Nerves app and then send messages to it directly from your Phoenix controller.
A third solution that might be worth considering that is somewhere in between the two above on the level of investment/complexity: if you like the idea of the pub-sub model but you still want processes to only communicate internally within the BEAM, you can use pg2 to emulate pub-sub topics and subscriptions by using named process groups. Interested subscriber processes can join globally named groups which publisher processes can then query for the list of members and then proceed to send them messages.