Skip to content

HT813 + BSNL FTTH FXO trunk

A customer can connect their existing BSNL FTTH landline into Astradial as an inbound SIP trunk via a Grandstream HT813 ATA. The HT813 bridges the analog RJ11 voice line coming out of the BSNL ONT into a SIP trunk that registers to the Astradial cloud Asterisk, so the BSNL number rings inside the org's Astradial dashboard with full DID routing, IVR/queue handling, call recording, and CDR.

Inbound first proven at om-chambers (org 875c0285-6355-4336-b21e-9aac67070b52, prefix mo8vbv60) with BSNL number 04298251139 (Salem): BSNL → Astradial ext 1001, audio + recording end-to-end. Outbound later proven at thangam (prefix mov8ht65) — see Outbound.

What this is not

Architecture

graph LR
    PSTN["PSTN<br/>BSNL exchange"]
    ONT["BSNL FTTH ONT<br/>(RJ11 voice out = FXS)"]
    HT813["Grandstream HT813<br/>FXO ← BSNL line<br/>LAN → home router<br/>FXS → optional phone"]
    NAT["Home NAT<br/>WAN: 61.0.194.109"]
    PROD["Prod Asterisk<br/>147.93.168.216<br/>(devsip.astradial.com)"]
    EXT["Org extension<br/>e.g. 1001"]

    PSTN --> ONT
    ONT -->|"RJ11"| HT813
    HT813 -->|"SIP REGISTER (UDP 5062)"| NAT
    NAT -->|"WAN out"| PROD
    PROD --> EXT

The HT813 sits between the BSNL ONT's RJ11 voice port and the org's WAN. It registers to the Astradial cloud Asterisk as an inbound-type SIP trunk (dynamic registration; Asterisk is the registrar, HT813 is the client). When BSNL rings the FXO port, the HT813 answers and sends a SIP INVITE to Asterisk with the configured BSNL number as the destination. Asterisk's DID dialplan picks it up and routes to the configured extension / IVR / queue.

Hardware cabling

Cable From To
RJ11 voice BSNL ONT voice/phone port (FXS) HT813 FXO port (LINE)
Ethernet HT813 LAN port Home router / ONT LAN
RJ11 optional HT813 FXS port (PHONE) Analog handset (if a local extension is desired)

Critical: BSNL voice from the ONT goes to HT813 FXO, not FXS. FXO is the input port that consumes line voltage (HT813 behaves like a phone). FXS is the output port that provides line voltage to a phone. Wiring BSNL voice to FXS does nothing — the HT813 silently stays Idle.

Status page on HT813 should show FXO: Idle once cabled and line voltage is detected.

Astradial-side setup (one-time per BSNL line)

1. Create an inbound trunk

In the org's dashboard → Trunks → Add Trunk:

Field Value
Name Descriptive — e.g. BSNL-Salem-251139
Type Inbound ← critical
Host (leave blank — inbound trunks have dynamic host)
Port 5060
Transport UDP
Username anything; the API overrides this for inbound type
Max Channels 1 (single FXO port = 1 concurrent call)
Password click Generate (32-char hex)

After creation, a credentials dialog appears showing Server, Port, Transport, Username, Password.

⚠️ Editor UI gotcha: the dialog's "Username" field shows the form input value (e.g. bsnl_salem), but the actual SIP auth username for inbound trunks is the auto-generated asterisk_peer_name (e.g. org_mo8vbv60_trunk1780043044537). See api/src/services/asterisk/sipTrunkService.js:45:

const authUsername = trunkType === 'inbound' ? peerName : (trunk.username || peerName);

To find the correct username, after creating the trunk:

ssh root@147.93.168.216 'grep -A6 "Type: inbound" /etc/asterisk/pjsip_<org>.conf'

The username= line inside the [<peer_name>_auth] section is what the HT813 needs.

2. Deploy + Reload

Org's dashboard → Settings → Asterisk Configuration → Deploy Config → Reload Asterisk.

Reload is module reload res_pjsip.so — does not drop active calls.

3. Add the BSNL number as a DID

Phone Numbers → Add Number:

Field Value
Number The BSNL number — leading 0 is stripped by Indian normalization, so 04298251139 stores as 4298251139
Description e.g. BSNL FTTH Salem line
Routing Extension / IVR / Queue
Destination The target

Deploy Config + Reload Asterisk again to push the dialplan change.

The DID dialplan generator emits matches for multiple formats: the bare number, with +, with 91 country code prefix, etc. So whichever format the HT813 forwards in the INVITE, it will match.

HT813 configuration

Tested on hardware version V1.1E, firmware:

Boot 1.0.17.1 · Core 1.0.17.1 · Base 1.0.17.3 · Prog 1.0.17.3 · CPE 1.0.4.60

Find HT813 IP

The HT813 doesn't have a display. Either: - Check the home router's DHCP client list for a Grandstream device (MAC OUI EC:74:D7) - Or from a Mac on the same LAN: arp -a (after a ping -c1 sweep of the subnet) — look for the new MAC - Or plug an analog phone into FXS, lift handset, press *** then 02 — IVR reads out the IP

Open http://<ht813-ip> in a browser. Default admin password is admin.

FXO PORT tab settings

Only the values that differ from defaults are listed; everything else stays default.

Section Field Value
Account Account Active Yes
Primary SIP Server devsip.astradial.com (= 147.93.168.216)
SIP Transport UDP
NAT Traversal Keep-Alive
SIP User ID the peer name from the deployed pjsip config (e.g. org_mo8vbv60_trunk1780043044537)
Authenticate ID same as SIP User ID
Authenticate Password from the trunk credentials dialog
Name descriptive (e.g. BSNL-Salem-251139)
SIP Registration Yes
Register Expiration 5 (minutes)
SIP REGISTER Contact Header Uses WAN Address ← critical for NAT
Codec Preferred Vocoder choice 1 / 2 PCMA / PCMU (Indian carriers prefer A-law)
Preferred DTMF method Priority 1 RFC2833 (default — fine)
FXO Termination Enable Current Disconnect Yes
AC Termination Model Country-based: INDIA ← change from USA default
Enable Polarity Reversal No (BSNL doesn't send polarity reversal by default)
Enable PSTN Disconnect Tone Detection Yes
PSTN Disconnect Tone f1=400@-32,f2=0@0,c=750/750; (India busy tone: single 400Hz, 750ms on/off)
Number of Rings 2 (lower = quicker answer)
PSTN Ring Thru FXS No (don't ring the optional analog handset; route everything to SIP)
Channel Dialing Stage Method 1 (one-stage forwarding)
Wait for Dial-Tone Yes

BASIC SETTINGS tab — the "forward to" field

⚠️ Easy to miss: HT813 firmware 1.0.17.x puts the inbound forward destination on the BASIC SETTINGS page, not the FXO PORT page. Scroll to the bottom of BASIC SETTINGS:

Unconditional Call Forward to VOIP row:

Field Value
User ID The DID number — e.g. 4298251139 (must match the DID added on the Astradial side)
@ Sip Server devsip.astradial.com
Sip Destination Port 5060

Without this, the HT813 answers BSNL calls after Number of Rings but has no SIP destination → drops the call immediately. From the caller's side this is heard as "busy immediately, no ring" because the line answered then hung up before any ring-back tone was generated.

Apply sequence

  1. Update — saves changes to flash
  2. Apply — when the prompt "Configuration changed. Click Apply to save settings." appears
  3. Reboot — for SIP/network changes to take effect (waits ~30 seconds)

Verification

From HT813 web UI

Status page → Port Status table:

FXO  Idle  org_mo8vbv60_trunk1780043044537  Registered

From Asterisk

ssh root@147.93.168.216 \
  'asterisk -rx "pjsip show endpoint <peer_name>" | grep -E "Endpoint:|Contact:|Aor:"'

Endpoint state should be: - Not in use — registered and reachable (qualify OK) — best - Unavailable — registered but qualify failing — see Qualify Unreachable

pjsip show aor <peer_name> will show the full contact URI with x-ast-orig-host confirming NAT rewriting worked:

contact: sip:<peer_name>@<wan-ip>:5062;x-ast-orig-host=<ht813-lan-ip>:5062

End-to-end test

  1. Call the BSNL number from a mobile
  2. Watch Asterisk log: tail -F /var/log/asterisk/full.log | grep <peer_name>
  3. Expected sequence:
  4. INVITE arrives from HT813's WAN IP
  5. Dialplan matches DID
  6. Goto org's internal context, target extension
  7. RTP audio flows on a high UDP port (e.g. 61.0.194.109:5014)
  8. MixMonitor starts recording

If the target extension isn't logged in, Asterisk plays "the person at extension X is not available" and hangs up cleanly — that's not a bug, just an unanswered call.

Known limitations

Qualify "Unreachable" (known, not blocking for inbound)

After registration, Asterisk's keepalive OPTIONS (qualify) often shows the contact as Unreachable / Unavail. This happens because the home router's NAT doesn't keep the inbound pinhole open between the HT813's outbound packets — typical CGNAT / strict NAT behavior on Indian residential FTTH.

For inbound calls this is harmless. The HT813 originates the SIP INVITE outbound to Asterisk; the NAT pinhole opens automatically for that direction. The call reaches Asterisk, audio flows, recording works. The qualify failure only matters for direction-reversed traffic — see Outbound below.

The editor's Trunks page will show this as Registration: Unreachable even though calls work.

Outbound (Astradial → BSNL) — PROVEN WORKING

Outbound is confirmed end-to-end at thangam (prefix org_mov8ht65_, BSNL Salem 04298 292455): the FXS handset on ext 1010 dialled a mobile, it rang out, two-way audio, and the mobile showed the BSNL number as caller ID.

Why it works here but failed at om-chambers: outbound requires Asterisk to originate a SIP INVITE toward the HT813, so the ONT's NAT must keep the HT813's pinhole open for cloud→device packets. The Syrotech ONT at om-chambers had strict NAT that dropped them (and exposed no port-forward UI). This client's ONT cooperates — with SIP OPTIONS Keep Alive = Yes (interval 30) on the FXO port, the keep-alive + registration hold the binding and the cloud's INVITE gets through. No manual ONT port-forward was needed here; on a stricter ONT, fall back to an ISP TR-069 port-forward (WAN UDP <local-sip-port> → HT813 LAN IP) or a WireGuard tunnel (see customer-tunnels).

Steps to enable outbound via the BSNL line:

  1. Point the org's outbound route at the BSNL trunk. There is no outbound-trunk selector in the editor UI — do it via the API:
    curl -X PUT http://localhost:8000/api/v1/outbound-routes/<route_id> \
      -H "X-API-Key: $ORG_KEY" -H 'Content-Type: application/json' \
      -d '{"trunk_id":"<bsnl_trunk_id>"}'
    
    (Or POST /api/v1/outbound-routes to add a prefix-gated route — e.g. dial_pattern:'_0X.', strip_digits:1 — if you want only some calls on BSNL and the rest on the existing trunk.)
  2. Deploy + reload (UI changes don't auto-push). Verify the dialplan now dials the FXO trunk: grep 'Dial(PJSIP' /etc/asterisk/ext_<org>.confDial(PJSIP/${EXTEN}@<peer_name>,60).
  3. Test: dial a mobile from the FXS handset → it rings showing the BSNL number.

Caller ID is automatic and not configurable. An analog FXO line presents the BSNL line's own number to the PSTN regardless of what the cloud sends — don't set caller_id_override. (If outbound shows an AstraDial pool DID like 80659…, the call went out the wrong trunk — Tata, not BSNL — not a caller-ID setting.)

Repointing the default route sends ALL org outbound via the 1-channel BSNL line

A single FXO = one concurrent outbound call for the whole org; a second simultaneous outbound call gets busy. If the org also uses Tata for outbound, use a prefix-gated second route instead of repointing the default.

Single concurrent call

HT813 has one FXO port = one BSNL line = one concurrent call. Max Channels is set to 1 on the trunk to reflect this. If a second call comes in while one is active, BSNL itself returns busy upstream.

No fax tested

Fax Mode is left at T.38 default but fax over this path has not been tested. For voice-only deployments, leave T.38 or set Pass-Through — either works for voice.

If the ISP (e.g. thangam, who controls their own ONT's TR-069 ACS) controls the ONT, the ONT's built-in POTS port can register directly to Astradial without an HT813. See the partner-facing doc at ~/Desktop/astradial-onu-tr069-provisioning-guide.md (not committed to internal-docs; intended for sharing with the ISP's NOC team).

The HT813 path documented here is for customers whose ISP won't / can't provision TR-069 on the ONT, or for legacy BSNL copper lines where there's no ONT in the picture.

Troubleshooting

Symptom Likely cause Fix
HT813 status shows FXO Idle but Asterisk doesn't see a REGISTER Wrong SIP server / username / password, or NAT blocking UDP 5060 outbound Verify the auth username matches the deployed pjsip config (not the editor dialog). Check home router doesn't block outbound UDP.
HT813 status shows FXO: Not Registered Authentication failing Most common: using the editor dialog's username (bsnl_salem) instead of the peer name. Pull the correct username from pjsip_<org>.conf.
Mobile call hears "busy immediately, no ring" No SIP destination configured on HT813 — it answers BSNL then drops Set Unconditional Call Forward to VOIP → User ID to the DID number on the BASIC SETTINGS page.
Mobile hears ringing then "the person at extension X is not available" Working as intended. Target extension isn't logged in. Log in to that extension's softphone, or route the DID to a queue / IVR.
One-way audio NAT / RTP port blocking; or codec mismatch Force PCMA only on HT813. Confirm rewrite_contact=yes and rtp_symmetric=yes in the trunk endpoint (auto-set by sipTrunkService.js).
Echo on the BSNL side Wrong AC Termination Model Confirm Country-based: INDIA, not USA default.
FXS extension's REGISTER reaches Asterisk but no contact / endpoint missing from config The user's status is inactive — the config generator silently skips inactive users, so the endpoint doesn't exist Set the user active (direct DB UPDATE users SET status='active'not the editor PUT, which would also revert a bare-extension endpoint to the long form), then Deploy + Reload.
Editor shows trunk / DID / outbound-route created, but Asterisk doesn't have it UI writes the DB row but does not auto-deploy Run Deploy Config → Reload (or POST /api/v1/config/deploy) after every create/edit.
Outbound shows an AstraDial pool DID (e.g. 80659…) instead of the BSNL number Call left via the Tata trunk, not BSNL — the outbound route still points at Tata Repoint the outbound route to the BSNL trunk (see Outbound section), Deploy + Reload. CLI then becomes the BSNL line's own number automatically.
Device says Registered but server has no contact Wrong server/port (often :5060 where the ISP blocks it) or a typo in the long peer-name SIP User ID Use port 5080; tcpdump on the server (udp port 5080 or 5060) while rebooting the device to see what it actually sends.

Source references

  • API trunk service: api/src/services/asterisk/sipTrunkService.js:38-203
  • Trunk schema + types: api/database/migrations/20241218120002-create-sip-trunks.js, 20241218120011-add-trunk-type.js
  • Editor Trunks page: editor/app/dashboard/[orgId]/trunks/page.tsx
  • DID dialplan: generated into /etc/asterisk/ext_<org>.conf on Deploy
  • Per-org pjsip: generated into /etc/asterisk/pjsip_<org>.conf on Deploy

History

  • Inbound proven (om-chambers, BSNL Salem) — BSNL → Astradial ext 1001 verified; recording, CDR, NAT rewriting all working. Outbound failed here: the Syrotech ONT (S-XPON-1110-WDONT, Airtel firmware) had strict NAT and exposed no port-forward UI; port change (5062→35062) and UPnP NAT Traversal both left qualify Unreachable.
  • Outbound proven (thangam, BSNL Salem 04298 292455) — FXS ext 1010 dialled a mobile → rang out, two-way audio, mobile showed the BSNL number. Difference vs om-chambers: this client's ONT keeps the NAT pinhole open with SIP OPTIONS keep-alive, so the cloud→device INVITE gets through without a manual port-forward. Enabled by repointing the org outbound route to the BSNL trunk via PUT /api/v1/outbound-routes. Also captured: inactive-user-skipped-from-config and UI-changes-need-deploy gotchas.